Green
Green

Reputation: 30805

How to use Morgan logger?

I cannot log with Morgan. It doesn't log info to console. The documentation doesn't tell how to use it.

I want to see what a variable is. This is a code from response.js file of expressjs framework:

var logger = require("morgan");

res.render = function(view, options, fn){
  options = options || {};
  var self = this;
  var req = this.req;
  var app = req.app;

  // support callback function as second arg
  if ('function' == typeof options) {
    fn = options, options = {};
  }

  // merge res.locals
  options._locals = self.locals;

  // default callback to respond
  fn = fn || function(err, str){
    if (err) return req.next(err);
    self.send(str);
  };

  // Here I want to see what fn is
  // But it doesn't show me anything in console
  // How to use it?
  logger(fn);

  // render
  app.render(view, options, fn);
};

How to use Morgan?

Upvotes: 124

Views: 183981

Answers (14)

Brenton Scott
Brenton Scott

Reputation: 169

Just log the stream to the console

app.use(
  morgan('dev', {
    'stream': {
        write: (value) => console.log(value)
      }
    }
  )
);

Upvotes: 0

Nick Steele
Nick Steele

Reputation: 7992

I think what this question intended, and anyone of the 4+ million Morgan logger users may search for with Morgan, is just correlation logging. Unfortunately this doesn't come with Morgan out of the box but it's simple to add!

For example, Morgan "just works" and logs your services automatically in whatever format you want, BUT, if you console.log something, which request was that for? What part of your service process was going on at the time, etc?

This is the question that drives you, Neo.

The answer is, you'll never know; without adding it. This issue makes people (including me years ago) abandon Morgan, which stinks, because if there is one thing you don't want to get wrong, it's logging, and every single other Node logger is an order of magnitude more complex to implement and reason about, and so has more bug surface.

To correlate Morgan with regular logging, all you have to do is make a request ID that is unique (which you should have anyway) then include it in both your logs and Morgan. That's it!

Next, you just need to work out the chronological order they occurred. To do that, just add a timestamp. Now you're done.

Then while debugging, you can see everything you logged and what it was related to, and in production, when you agregate your logs after orchistration into a centralized place, you'll STILL be able to reason about everything, as far back in time as you'd like. Here's how:

Step 1

Just create a request id (there are a million ways to do this, here is just one that is super fast and will scale).

const crypto = require('crypto')
// This is middleware that creates a uuid v4 request ID
app.use((req, res, next) => {
  // This is 100x faster than anything you could write in js
  req.id = crypto.randomUUID() 
  next()
})

Step 2

Setup Morgan to use at least your ID and a timestamp (which we'll use too)

const morgan = require('morgan')

// Add a request ID to Morgan 
morgan.token('id', (req) => req.id )

// Add a morgan format (you can use anything you want, just make sure to add the ID and the time)
const format = ':id :date[iso] :method :url :status :response-time ms'  
// Add morgan to express, now Morgan is done!
app.use(morgan(format))

// Add a req.log to express...
app.use((req, res, next) => {
  req.log = (message) => {
    const currentTime = new Date().toISOString()
    console.log(`${req.id} ${currentTime} ${message}`)
  }
  next()
})

Step 3

There is no step 3 except using your logger. Anywhere in any request code, just do this:

req.log('Hello world!') // This will be correlated!

You'll now see beautifully orchistrated log correlation with almost no moving parts, like this...

695f9a64-2596-49a1-b9b5-e472ca6ef114 2024-08-03T20:01:31.140Z Hello world!
695f9a64-2596-49a1-b9b5-e472ca6ef114 2024-08-03T20:01:31.143Z Here's another message!
695f9a64-2596-49a1-b9b5-e472ca6ef114 2024-08-03T20:01:31.144Z GET /fine 200 2.483 ms
747cdece-0dde-4151-b771-11c9e76bd18a 2024-08-03T20:01:32.069Z Hello world!
747cdece-0dde-4151-b771-11c9e76bd18a 2024-08-03T20:01:32.070Z Here's another message!
747cdece-0dde-4151-b771-11c9e76bd18a 2024-08-03T20:01:32.071Z GET /fine 200 1.023 ms

Tracable execution flow at scale

Notice how different logs made on different requests now include different request IDs? And since you're using UUIDs, you won't have collisions even if you have 1,000 copies of your service running on Kubernetes; the logs can all be organized chronologically, and you can still trace every single aspect of any request! Right down to WHICH request, including exactly WHERE in the execution path it occurred, every time you log anything, all built-in and nothing to fail or maintain!

Chronologic perfection

Morgan now takes care of the final line and recording the total transaction time, explaining any details you want to capture about the request itself, but now to time any aspect of the request, you simply use req.log and you'll automatically log the time that transpired from then during the request! Split this up as much as you want by just simply logging about it.

Bonus

At scale, logs can get huge. Using JSON and other shenanigans can send things out of hand quickly.

What is great about Morgan is you only write the req/res data once, at the end of the transaction, and this approach lets you still correlate everything that happened per log without writing it.

Many people will tell you "logging is hardly an optimization I would care about". But they are wrong.

Even Pino, the "fastest" logger, can only log 10k reqs per second. This is often slower than the entire request itself :) Why did you optimize your code only to have your logger take 2x longer than your whole request?

Don't do that. Waste is waste, whenever it occurs. Don't trap yourself in the idea that you have a free waste card because you're doing x.

Don't use JSON

Also, many people will tell you to log JSON. You may think this is fine, but this not only adds up fast, it makes debugging and finding out what happened in your system extremely slow, logging in JSON is also going to make your brain hurt while debugging.

One of the awesome things about morgan is it doesn't form a log opinion ahead of time.

Today, all cloud providers and orchistrators take your stdout output and agregate them into a centralized logger and store them in a time-series database anyway, which is CERTAINLY not going to be JSON format, so, outputting JSON will just make your life harder, AND cost more compute to parse your JSON in the log aggregator anyway.

The notion of logging in JSON is pre-timeseries databases; There is not a single cloud provider that stores logs in JSON in 2024, so why are you converting your logs into a format HARD for you to understand, when the computer is going to convert it into a timeseries database binary format anyway? There is literally no reason to.

Upvotes: 0

Shrey Banugaria
Shrey Banugaria

Reputation: 165

If you want get logs in your terminal, you can simply use

app.use(morgan('dev'));

Upvotes: -1

era5tone
era5tone

Reputation: 647

As per the docs, you can use a predefined method to log request data

app.use(morgan('tiny'));

Suppose you want to log a post request data, you could make a custom token and use it like this

morgan.token('postData', (request) => {
  if (request.method == 'POST') return ' ' + JSON.stringify(request.body);
  else return ' ';
});

app.use(
  morgan(
    ':method :url :status :res[content-length] - :response-time ms :postData'
  )
);

This will log a POST request in the console as

POST /api/test2 409 33 - 0.253 ms  {"name":"Test name","number":"323-322-55"}

Note - You should never log request data as it may violate local privacy law (e.g. GDPR in EU) or business-standard. This is just an example

Upvotes: 2

Nz  Mumbere
Nz Mumbere

Reputation: 19

Just do this:

app.use(morgan('tiny'));

and it will work.

Upvotes: 1

Moe
Moe

Reputation: 129

Using morgan is pretty much straightforward. As the documentation suggests, there are different ways to get your desired output with morgan. It comes with preconfigured logging methods or you can define one yourself. Eg.

const morgan = require('morgan')

app.use(morgan('tiny')

This will give you the preconfiguration called tiny. You will notice in your terminal what it does. In case you are not satisfied with this and you want deeper e.g. lets say the request url, then this is where tokens come in.

morgan.token('url', function (req, res){ return '/api/myendpoint' })

then use it like so:

app.use(morgan(' :url ')

Check the documentation its all highlighted there.

Upvotes: 1

Sagan
Sagan

Reputation: 2343

In my case:

-console.log()  // works
-console.error() // works
-app.use(logger('dev')) // Morgan is NOT logging requests that look like "GET /myURL 304 9.072 ms - -"

FIX: I was using Visual Studio code, and I had to add this to my Launch Config

"outputCapture": "std"

Suggestion, in case you are running from an IDE, run directly from the command line to make sure the IDE is not causing the problem.

Upvotes: 4

Sunil Verma
Sunil Verma

Reputation: 111

Morgan :- Morgan is a middleware which will help us to identify the clients who are accessing our application. Basically a logger.

To Use Morgan, We need to follow below steps :-

  1. Install the morgan using below command:

npm install --save morgan

This will add morgan to json.package file

  1. Include the morgan in your project

var morgan = require('morgan');

3> // create a write stream (in append mode)

var accessLogStream = fs.createWriteStream(
      path.join(__dirname, 'access.log'), {flags: 'a'}
 );
// setup the logger 
app.use(morgan('combined', {stream: accessLogStream}));

Note: Make sure you do not plumb above blindly make sure you have every conditions where you need .

Above will automatically create a access.log file to your root once user will access your app.

Upvotes: 10

Gautam Anand
Gautam Anand

Reputation: 19

You might want to try using mongo-morgan-ext

The usage is:

var logger = require('mongo-morgan-ext');

var db = 'mongodb://localhost:27017/MyDB';

var collection = 'Logs'

var skipfunction = function(req, res) {

return res.statusCode > 399;
} //Thiw would skip if HTTP request response is less than 399 i.e no errors.

app.use(logger(db,collection,skipfunction)); //In your express-application

The expected output is

{
    "RequestID": "",
    "status": "",
    "method": "",
    "Remote-user": "",
    "Remote-address": "",
    "URL": "",
    "HTTPversion": "",
    "Response-time": "",
    "date":"",
    "Referrer": "",
    "REQUEST": { //10
        "Accept": "",
        "Accept-Charset": "",
        "Accept-Encoding": "",
        "Accept-Language": "",
        "Authorization": "",
        "Cache-Control": "",
        "Connection": "",
        "Cookie": "",
        "Content-Length": "",
        "Content-MD5": "",
        "Content-Type": "",
        "Expect": "",
        "Forwarded": "",
        "From": "",
        "Host": "",
        "Max-Forwards": "",
        "Origin": "",
        "Pragma": "",
        "Proxy-Authorization": "",
        "Range": "",
        "TE": "",
        "User-Agent": "",
        "Via": "",
        "Warning": "",
        "Upgrade": "",
        "Referer": "",
        "Date": "",
        "X-requested-with": "",
        "X-Csrf-Token": "",
        "X-UIDH": "",
        "Proxy-Connection": "",
        "X-Wap-Profile": "",
        "X-ATT-DeviceId": "",
        "X-Http-Method-Override":"",
        "Front-End-Https": "",
        "X-Forwarded-Proto": "",
        "X-Forwarded-Host": "",
        "X-Forwarded-For": "",
        "DNT": "",
        "Accept-Datetime": "",
        "If-Match": "",
        "If-Modified-Since": "",
        "If-None-Match": "",
        "If-Range": "",
        "If-Unmodified-Since": ""
    },
    "RESPONSE": {
        "Status": "",
        "Content-MD5":"",
        "X-Frame-Options": "",
        "Accept-Ranges": "",
        "Age": "",
        "Allow": "",
        "Cache-Control": "",
        "Connection": "",
        "Content-Disposition": "",
        "Content-Encoding": "",
        "Content-Language": "",
        "Content-Length": "",
        "Content-Location": "",
        "Content-Range": "",
        "Content-Type":"",
        "Date":"",
        "Last-Modified": "",
        "Link": "",
        "Location": "",
        "P3P": "",
        "Pragma": "",
        "Proxy-Authenticate": "",
        "Public-Key-Pins": "",
        "Retry-After": "",
        "Server": "",
        "Trailer": "",
        "Transfer-Encoding": "",
        "TSV": "",
        "Upgrade": "",
        "Vary": "",
        "Via": "",
        "Warning": "",
        "WWW-Authenticate": "",
        "Expires": "",
        "Set-Cookie": "",
        "Strict-Transport-Security": "",
        "Refresh":"",
        "Access-Control-Allow-Origin": "",
        "X-XSS-Protection": "",
        "X-WebKit-CSP":"",
        "X-Content-Security-Policy": "",
        "Content-Security-Policy": "",
        "X-Content-Type-Options": "",
        "X-Powered-By": "",
        "X-UA-Compatible": "",
        "X-Content-Duration": "",
        "Upgrade-Insecure-Requests": "",
        "X-Request-ID": "",
        "ETag": "",
        "Accept-Patch": ""
    }

}

Upvotes: 0

akrsmv
akrsmv

Reputation: 830

I faced the same problem ago and instead, I used winston. As fellas above said, morgan is for automated logging of request/response. Winston can be configured pretty much same way as log4Net/log4J, has severity levels, different streams to which you can log etc.

For example:

npm install winston

Then, if you call the below code somewhere on you application initialization:

var winston = require('winston');

// setup default logger (no category)
winston.loggers.add('default', {
    console: {
        colorize: 'true',
        handleExceptions: true,
        json: false,
        level: 'silly',
        label: 'default',
    },
    file: {
        filename: 'some/path/where/the/log/file/reside/default.log',
        level: 'silly',
        json: false,
        handleExceptions: true,
    },
});

//
// setup logger for category `usersessions`
// you can define as many looggers as you like
//
winston.loggers.add('usersessions', {
    console: {
        level: 'silly',
        colorize: 'true',
        label: 'usersessions',
        json: false,
        handleExceptions: true,
    },
    file: {
        filename: 'some/path/where/the/log/file/reside/usersessions.log',
        level: 'silly',
        json: false,
        handleExceptions: true,
    },
});

note: before calling above code, winston.loggers is empty, i.e you dont have any loggers configured yet. Pretty much like Log4Net/J XmlConfigure methods - you need to first call them, to init your logging.

Then, later wherever in you application server side code you may do:

var winston = require('winston');
// log instances as defined in first snippet
var defaultLog = winston.loggers.get('default'); 
var userSessionsLog = winston.loggers.get('usersessions');

defaultLog.info('this goes to file default.log');
userSessionsLog.debug('this goes to file usersessions.log')

Hope that helps.

for further documentation reference: https://www.npmjs.com/package/winston

Upvotes: 21

NikhilWanpal
NikhilWanpal

Reputation: 3000

Seems you too are confused with the same thing as I was, the reason I stumbled upon this question. I think we associate logging with manual logging as we would do in Java with log4j (if you know java) where we instantiate a Logger and say log 'this'.

Then I dug in morgan code, turns out it is not that type of a logger, it is for automated logging of requests, responses and related data. When added as a middleware to an express/connect app, by default it should log statements to stdout showing details of: remote ip, request method, http version, response status, user agent etc. It allows you to modify the log using tokens or add color to them by defining 'dev' or even logging out to an output stream, like a file.

For the purpose we thought we can use it, as in this case, we still have to use:

console.log(..);

Or if you want to make the output pretty for objects:

var util = require("util");
console.log(util.inspect(..));

Upvotes: 131

Carlos Ariza
Carlos Ariza

Reputation: 377

var express = require('express');

var fs = require('fs');

var morgan = require('morgan')

var app = express();

// create a write stream (in append mode)
var accessLogStream = fs.createWriteStream(__dirname + '/access.log',{flags: 'a'});


// setup the logger
app.use(morgan('combined', {stream: accessLogStream}))


app.get('/', function (req, res) {
  res.send('hello, world!')
});

example nodejs + express + morgan

Upvotes: 8

wgp
wgp

Reputation: 1157

Morgan should not be used to log in the way you're describing. Morgan was built to do logging in the way that servers like Apache and Nginx log to the error_log or access_log. For reference, this is how you use morgan:

var express     = require('express'),
    app         = express(),
    morgan      = require('morgan'); // Require morgan before use

// You can set morgan to log differently depending on your environment
if (app.get('env') == 'production') {
  app.use(morgan('common', { skip: function(req, res) { return res.statusCode < 400 }, stream: __dirname + '/../morgan.log' }));
} else {
  app.use(morgan('dev'));
}

Note the production line where you see morgan called with an options hash {skip: ..., stream: __dirname + '/../morgan.log'}

The stream property of that object determines where the logger outputs. By default it's STDOUT (your console, just like you want) but it'll only log request data. It isn't going to do what console.log() does.

If you want to inspect things on the fly use the built in util library:

var util = require('util');
console.log(util.inspect(anyObject)); // Will give you more details than console.log

So the answer to your question is that you're asking the wrong question. But if you still want to use Morgan for logging requests, there you go.

Upvotes: 45

mflo999
mflo999

Reputation: 691

I think I have a way where you may not get exactly get what you want, but you can integrate Morgan's logging with log4js -- in other words, all your logging activity can go to the same place. I hope this digest from an Express server is more or less self-explanatory:

var express = require("express");
var log4js = require("log4js");
var morgan = require("morgan");
...
var theAppLog = log4js.getLogger();
var theHTTPLog = morgan({
  "format": "default",
  "stream": {
    write: function(str) { theAppLog.debug(str); }
  }
});
....
var theServer = express();
theServer.use(theHTTPLog);

Now you can write whatever you want to theAppLog and Morgan will write what it wants to the same place, using the same appenders etc etc. Of course, you can call info() or whatever you like in the stream wrapper instead of debug() -- that just reflects the logging level you want to give to Morgan's req/res logging.

Upvotes: 69

Related Questions