121

we use morgan in order to log our express transformation:

var morgan  = require('morgan');
morgan('combined');
// a format string
morgan(':remote-addr :method :url :uuid');
// a custom function
morgan(function (req, res) {
  return req.method + ' ' + req.url + ' ' + req.uuid;
})

Also, we use winston in order to log our other logging:

var winston = require('winston');
var logger = new (winston.Logger)({
  transports: [
         new (winston.transports.Console)({ level: 'info' }),
          new (winston.transports.File)({ filename: '/var/log/log-file.log' })
  ]
});

Is there any way to combine the two loggers together? the situation now is that morgan is write to my standard output, when winston writes to /var/log/log-file.log.

I wish that the logger file will combine from the express transformation information, and from the other information I want (logger.info())..

Roberto Pegoraro
  • 1,313
  • 2
  • 16
  • 31
Or Smith
  • 3,556
  • 13
  • 42
  • 69
  • What's the point of doing this, I mean, why did you need morgan at the beggining, why not just write a winston middlewrare for express? – Dimitri Kopriwa Feb 27 '20 at 07:03

7 Answers7

169

This article does an excellent job for what you want to do.

http://tostring.it/2014/06/23/advanced-logging-with-nodejs/

For your specific code you probably need something like this:

var logger = new winston.Logger({
    transports: [
        new winston.transports.File({
            level: 'info',
            filename: './logs/all-logs.log',
            handleExceptions: true,
            json: true,
            maxsize: 5242880, //5MB
            maxFiles: 5,
            colorize: false
        }),
        new winston.transports.Console({
            level: 'debug',
            handleExceptions: true,
            json: false,
            colorize: true
        })
    ],
    exitOnError: false
});

logger.stream = {
    write: function(message, encoding){
        logger.info(message);
    }
};

app.use(require("morgan")("combined", { "stream": logger.stream }));

This will set up Winston to write a log to the console as well as a file. Then you can use the last expression to pass output from the morgan middleware into winston.

Leos Literak
  • 8,805
  • 19
  • 81
  • 156
lindsaymacvean
  • 4,419
  • 2
  • 19
  • 21
  • Can we use a timestamp in this process? – godimedia May 18 '15 at 10:03
  • @theChinmay yes, just add `timestamp: true` into the `Console` or `File` object – Skam Jun 23 '16 at 23:11
  • 35
    It doesn't seem like it's necessary to override logger.stream.. In my case, I was able to do `app.use(morgan("combined", { stream: { write: message => logger.info(message) }}));` – Devon Sams Nov 18 '16 at 17:03
  • 25
    If you're using @DevonSams's method, you'll get an empty line between logged lines because both morgan and winston are adding a linebreak in the end of the logged message. You can simply trim the linebreak out from the message with `logger.info(message.trim())` – Timo Dec 08 '16 at 09:13
  • 4
    What if I want to use logger.error instead of logger.info, if server responds with 500? – Maksim Nesterenko Aug 21 '17 at 14:51
  • @Alendorff Not very pretty, but you could do something like `message => message.indexOf('status:5') >= 0 ? logger.error(message) : logger.info(message)` if you are using a specific format to filter based on `5xx` status. – Martti Laine Jan 24 '18 at 21:07
  • This has been answered since long time, can any one please update the answer with latest implementation. Also, with the fact that one should use Morgan or Winston VS both? – Nah Jan 29 '18 at 12:19
  • Getting an error `TypeError: stream.write is not a function` – Harshal Yeole Jul 25 '18 at 15:11
  • i am passing the module, on the app.use. Same i am also getting stream.write is not a function. If i change the stream to streamX i am able to print to the console but not able to write the morgan logs to the log file – Learner Dec 11 '18 at 07:35
  • I have raised as a question, please check this https://stackoverflow.com/questions/53718192/stream-write-is-not-a-function-when-using-morgan-with-logger – Learner Dec 11 '18 at 07:36
  • 5
    For `winston: ^3.0.0` use `winston.createLogger` instead of `new winston.Logger` – streof Apr 26 '19 at 08:57
  • How can I pass additional information (such as the res status code) to the logger stream function? – omer Jul 23 '19 at 16:30
24

In Typescript:

let logger = new (winston.Logger)({
    exitOnError: false,
    level: 'info',
    transports: [
        new (winston.transports.Console)(),
        new (winston.transports.File)({ filename: 'app.log'})
    ]
})

class MyStream {
    write(text: string) {
        logger.info(text)
    }
}
let myStream = new MyStream()
app.use(morgan('tiny', { stream: myStream }));
Mahyar SEPEHR
  • 249
  • 2
  • 3
  • Why tiny and not combined ? – An-droid Jan 31 '18 at 14:44
  • I wasn't able to get this to work anymore. It seems like the typeings are incorrect. https://stackoverflow.com/questions/50048193/how-are-you-supposed-to-create-winston-logger-stream-for-morgan-in-typescript – jpetitte Dec 31 '18 at 15:22
7

Update the last line to remove warning

app.use(require("morgan")("combined", { stream: logger.stream }));
Johnny
  • 71
  • 1
  • 2
7

Morgan has the bad habit of ending the message with a \n so to make things orderly you may want to remove that before writing it to winston.

This can be done in many different ways like on the format side in winston, or by updating your stream to not write the \n

class MyStream {
    write(text: string) {
        logger.info(text.replace(/\n$/, ''));
    }
}
let myStream = new MyStream()
app.use(morgan('tiny', { stream: myStream }));
Leos Literak
  • 8,805
  • 19
  • 81
  • 156
user566245
  • 4,011
  • 1
  • 30
  • 36
3

for Typescript another way to go about it, without needing to create a class is

let logger = new (winston.Logger)({
    exitOnError: false,
    level: 'info',
    transports: [
        new (winston.transports.Console)(),
        new (winston.transports.File)({ filename: 'app.log'})
    ]
})

const myStream = {
    write: (text: string) => {
        logger.info(text)
    }
}

app.use(morgan('combined', { stream: myStream }));

This solution was hived from this Github page https://github.com/winstonjs/winston/issues/1385. However, it is important to note that there is a slight difference between our codes. Instead of:

app.use(morgan('combined', { myStream }));

I use:

app.use(morgan('combined', { stream: myStream }));

This helped me out as am not too big on creating classes.

Cris Shaki
  • 51
  • 4
2

Inspired by this question, this is my easily customizable logger.

import * as winston from "winston";
import * as morgan from "morgan";

export const logger = winston.createLogger({
  transports: [
    new winston.transports.Console({
      level: 'debug',
      handleExceptions: true,
      format: winston.format.combine(
        winston.format.timestamp({ format: 'HH:mm:ss:ms' }),
        winston.format.colorize(),
        winston.format.printf(
          (info) => `${info.timestamp} ${info.level}: ${info.message}`,
        ),
        //  winston.format.simple(),
      ),
    }),
  ],
  exitOnError: false,
});

if (process.env.NODE_ENV === "dev") {
  logger.add(
    new winston.transports.File({
      level: 'info',
      filename: './logs/all-logs.log',
      handleExceptions: true,
      format: winston.format.combine(
        winston.format.timestamp({
          format: 'YYYY-MM-DD HH:mm:ss',
        }),
        winston.format.errors({ stack: true }),
        winston.format.printf(
          (info) => `${info.timestamp} ${info.level}: ${info.message}`,
        ),
        // winston.format.splat(),
        // winston.format.json()
      ),
      maxsize: 5242880, //5MB
      maxFiles: 5,
    }));
}
logger.info("logging started");

app.use(morgan(function (tokens, req, res) {
    const msg = [
        tokens.method(req, res),
        tokens.url(req, res),
        tokens.status(req, res),
        tokens.res(req, res, 'content-length'), '-',
        tokens['response-time'](req, res), 'ms',
    ].join(' ');
    logger.http(msg);
    return null;
    // return msg;
})
);

sample output

# console
16:32:30:3230 http: GET /users/614daca689f8773a247af93d 200 417 - 1087.858 ms 

# file 
2021-09-24 16:40:08 http: GET /users/614daca689f8773a247af93d 200 417 - 856.263 ms
Ali80
  • 6,333
  • 2
  • 43
  • 33
0

Simplest way for logging data into files in nodejs backend @grdon/logger

Here is example

 const logger = require('@grdon/logger')({
  defaultLogDirectory : __dirname + "/myLogDir",
})
// ...

logger(arg1, 'logfile1.log')
logger([arg1, arg2 ,...], 'lofgfile2.log')
Kandrat
  • 464
  • 7
  • 16