diff --git a/README.md b/README.md index d63f848..109c1d8 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ Create a new morgan logger middleware function using the given `format` and `opt The `format` argument may be a string of a predefined name (see below for the names), a string of a format string, or a function that will produce a log entry. +For the syntax of strings and functions see [morgan.compile](#morgancompileformat). + #### Options Morgan accepts these properties in the options object. @@ -103,6 +105,14 @@ To define a token, simply invoke `morgan.token()` with the name and a callback f morgan.token('type', function (req, res) { return req.headers['content-type'] }) ``` +Tokens can accept a string argument passed in from `[]` brackets by specifying +a third argument `arg`: +```js +morgan.token('type', function (req, res, arg) { return arg }) +``` + +Falsey values returned from this function will be replaced with `-`. + Calling `morgan.token()` using the same name as an existing token will overwrite that token definition. ##### :date[format] @@ -178,6 +188,11 @@ be passed using `[]`, for example: `:token-name[pretty]` would pass the string Normally formats are defined using `morgan.format(name, format)`, but for certain advanced uses, this compile function is directly available. +The function returned takes arguments `tokens` , `req`, and `res` where `tokens` +refers to `morgan` itself. If a log should be skipped the function will return +`null`. The function returned or a custom function can be passed directly to +morgan using `morgan(myFn)`. + ## Examples ### express/connect @@ -310,6 +325,54 @@ function assignId (req, res, next) { } ``` +#### arguments to custom token formats + +Example of a custom token format that uses an argument. It will output the +header related to the argument passed to the token. + +```js +var express = require('express') +var morgan = require('morgan') + +morgan.token('header', function getId (req, res, arg) { + return String(req.headers[arg]) +}) + +var app = express() + +app.use(morgan(':header[host] :response-time')) + +app.get('/', function (req, res) { + res.send('hello, world!') +}) +``` + +### use custom format function + +Sample app that will use a custom formatting function. This will print JSON +instead of a simple string. + +```js +var express = require('express') +var morgan = require('morgan') + +var formatter = morgan.compile('[:date[clf]] :method :url :res[content-length]') +var JSONOutput = function (morgan, req, res) { + return JSON.stringify({ + status: morgan.status(req,res), + default: formatter(morgan, req, res), + }) +} + +var app = express() + +app.use(morgan(JSONOutput)) + +app.get('/', function (req, res) { + res.send('hello, world!') +}) +``` + ## License [MIT](LICENSE) diff --git a/test/morgan.js b/test/morgan.js index 786cda1..7cc89ba 100644 --- a/test/morgan.js +++ b/test/morgan.js @@ -7,6 +7,7 @@ var join = require('path').join var morgan = require('..') var request = require('supertest') var split = require('split') +var url = require('url') describe('morgan()', function () { describe('arguments', function () { @@ -1316,6 +1317,102 @@ describe('morgan.compile(format)', function () { }) }) +describe('morgan.token(name, function)', function () { + it('should overwrite existing functions if called multiple times', function (done) { + function token (req, res, arg) { + return arg && url.parse(req.url)[arg] + } + var urlToken = morgan.url + morgan.token('url', token) + assert.equal(morgan.url, token) + morgan.token('url', urlToken) + done() + }) + + describe('should use the string `-` if return value of the token function is falsey', function () { + function test (v, done) { + morgan.token('ret', function ret (req, res, arg) { + return arg + }) + var cb = after(2, function (err, res, line) { + if (err) return done(err) + assert.equal(line, '-') + done() + }) + + var stream = createLineStream(function (line) { + cb(null, null, line) + }) + + var server = createServer(':ret', { stream: stream }, function (req, res, next) { + next() + }) + + request(server) + .get('/') + .expect(200, cb) + } + [ + 0, + NaN, + false, + '', + null, + undefined + ].forEach(function (v) { + it('for ' + (String(v) || JSON.stringify(v)), function (done) { + test(v, done) + }) + }) + }) + + it('should not see empty brackets as an argument value', function (done) { + morgan.token('checkempty', function ret (req, res, arg) { + assert.equal(arg, undefined) + assert.equal(arguments.length, 2) + cb(null) + }) + var cb = after(3, function (err, res, line) { + if (err) return done(err) + assert.equal(line, '-[]') + done() + }) + + var stream = createLineStream(function (line) { + cb(null, null, line) + }) + + var server = createServer(':checkempty[]', { stream: stream }, function (req, res, next) { + next() + }) + + request(server) + .get('/') + .expect(200, cb) + }) + + it('should use the result', function (done) { + morgan.token('urlpart', function ret (req, res, arg) { + return url.parse(req.url)[arg] + }) + var cb = after(2, function (err, res, line) { + if (err) return done(err) + assert.equal(line, 'foo=bar') + done() + }) + + var stream = createLineStream(function (line) { + cb(null, null, line) + }) + + var server = createServer(':urlpart[query]', { stream: stream }, function (req, res, next) { + next() + }) + + request(server).post('/test').query('foo=bar').expect(200, cb) + }) +}) + function after (count, callback) { var args = new Array(3) var i = 0