Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How are you supposed to create Winston logger stream for Morgan in TypeScript

What is the correct way to create a winston logger in TypeScript that will log the express Morgan middleware logging? I found a number of JavaScript samples but have had trouble converting them over to TypeScript, because I get an error Type '{ write: (message: string, encoding: any) => {}; logger: any; }' is not assignable to type '(options?: any) => ReadableStream'. Object literal may only specify known properties, and 'write' does not exist in type '(options?: any) => ReadableStream'.

Here is my code:

import { Logger, transports } from 'winston';

// http://tostring.it/2014/06/23/advanced-logging-with-nodejs/
// https://www.loggly.com/ultimate-guide/node-logging-basics/

const logger = new Logger({
    transports: [
        new (transports.Console)({
            level: process.env.NODE_ENV === 'production' ? 'error' : 'debug',
            handleExceptions: true,
            json: false,
            colorize: true
        }),
        new (transports.File)({
            filename: 'debug.log', level: 'info',
            handleExceptions: true,
            json: true,
            colorize: false
        })
    ],
    exitOnError: false,
});



if (process.env.NODE_ENV !== 'production') {
    logger.debug('Logging initialized at debug level');
}



// [ts]
// Type '{ write: (message: string, encoding: any) => {}; logger: any; }' is not assignable to type '(options?: any) => ReadableStream'.
//   Object literal may only specify known properties, and 'write' does not exist in type '(options?: any) => ReadableStream'.
logger.stream = {
    write: function (message: string, encoding: any) {
        logger.info(message);
    };
}


export default logger;

I have been able to work around this by adjusting my code to use const winston = require('winston'); but would like to know how you are supposed to do this maintaining types?

like image 934
JoAMoS Avatar asked Apr 26 '18 16:04

JoAMoS


People also ask

How do I set up a Winston logger?

Here's a guide that will help you understand NPM in detail. Install the Winston package using the command npm install winston . Logging with Winston is simple, with just four steps, as shown in the example below, and you have your log recorded. Add the Winston module with a require() function.

What is Winston logger?

winston is designed to be a simple and universal logging library with support for multiple transports. A transport is essentially a storage device for your logs. Each winston logger can have multiple transports (see: Transports) configured at different levels (see: Logging levels).

What is Winston in node JS?

Winston is the most popular logging library for Node. js. It aims to make logging more flexible and extensible by decoupling different aspects such as log levels, formatting, and storage so that each API is independent and many combinations are supported. It also uses Node.


3 Answers

Thanks to @estus who got me past where I was hung up. Here is the solution I ended up using:

import { Logger, transports } from 'winston';
import stream from 'stream';
import split from 'split';

// http://tostring.it/2014/06/23/advanced-logging-with-nodejs/
// https://www.loggly.com/ultimate-guide/node-logging-basics/

const logger = new Logger({
    transports: [
        new (transports.Console)({
            level: process.env.NODE_ENV === 'production' ? 'error' : 'debug',
            handleExceptions: true,
            json: false,
            colorize: true
        }),
        new (transports.File)({
            filename: 'debug.log', level: 'info',
            handleExceptions: true,
            json: true,
            colorize: false
        })
    ],
    exitOnError: false,
});

if (process.env.NODE_ENV !== 'production') {
    logger.debug('Logging initialized at debug level');
}

logger.stream = split().on('data', function (message: string) {
    logger.info(message);
});

export default logger;

Ultimately this issue got me to the final solution - https://github.com/expressjs/morgan/issues/70

like image 140
JoAMoS Avatar answered Oct 13 '22 16:10

JoAMoS


If you're using TypeScript classes for logger, you can declare a stream() function that returns a StreamOptions type from morgan:

import { StreamOptions } from 'morgan';

//...rest of the class code

public stream(): StreamOptions {
return {
  write: (message: string): void => {
    this.info(message.trim());
  }
};

Then you can use this stream function inside express middleware:

app.use('combined', { stream: this.logger.stream()})
like image 42
gonzaloplaza Avatar answered Oct 13 '22 16:10

gonzaloplaza


stream is expected to be factory function that returns a a stream, not a stream itself.

A stream is expected to be a real readable stream, not an object that mimics it.

Since it is supposed to be writable as well, it should be a duplex:

logger.stream = (options?: any) => new stream.Duplex({
    write: function (message: string, encoding: any) {
        logger.info(message);
    }
});

This is a solution that is suggested by Winston TS types. I cannot confirm if it works correctly.

like image 31
Estus Flask Avatar answered Oct 13 '22 16:10

Estus Flask