├── README.md ├── index.js ├── lib └── koa-bunyan.js ├── package.json └── tests └── logger-tests.js /README.md: -------------------------------------------------------------------------------- 1 | koa-bunyan 2 | ========== 3 | 4 | Using node-bunyan as koa logging middleware 5 | 6 | # Installation 7 | 8 | ``` 9 | npm install koa-bunyan 10 | ``` 11 | 12 | ## Example 13 | ```Javascript 14 | var koaLogger = require('koa-bunyan'); 15 | 16 | var koa = require('koa'); 17 | var app = koa(); 18 | 19 | var bunyan = require('bunyan'); 20 | // setup you logger instance 21 | var logger = bunyan.createLogger({name: "myapp"}); 22 | 23 | app.use(koaLogger(logger, { 24 | // which level you want to use for logging? 25 | // default is info 26 | level: 'debug', 27 | // this is optional. Here you can provide request time in ms, 28 | // and all requests longer than specified time will have level 'warn' 29 | timeLimit: 100, 30 | headers: ['Accept'] 31 | // optional - provide a list of request headers you'd like to log 32 | })); 33 | ``` 34 | 35 | ```bash 36 | // then start server 37 | // let we say... node app.js | ./node_modules/bunyan/bin/bunyan -o short 38 | 39 | // result 40 | 22:36:34.043Z WARN myapp: [RES] GET /api/products?top=5 (200) took 610 ms 41 | 22:36:34.172Z INFO myapp: [REQ] GET /api/categories/535c4375bcbcc794660b6c1d headers {"Accept": "*/*"} 42 | 22:36:34.184Z INFO myapp: [RES] GET /api/categories/535c4375bcbcc794660b6c1d (200) took 12 ms 43 | 22:36:34.375Z INFO myapp: [REQ] GET /Screenshot%20from%202014-03-15%2011:17:20.png 44 | 22:36:34.378Z INFO myapp: [REQ] GET /51a2035604cea64219.jpg 45 | 22:36:34.381Z INFO myapp: [REQ] GET /matrix.jpg 46 | 22:36:34.394Z INFO myapp: [REQ] GET /fonts/fontawesome-webfont.woff?v=4.1.0 47 | 22:36:34.400Z INFO myapp: [RES] GET /Screenshot%20from%202014-03-15%2011:17:20.png (200) took 26 ms 48 | 22:36:34.406Z INFO myapp: [RES] GET /fonts/fontawesome-webfont.woff?v=4.1.0 (200) took 12 ms 49 | 22:36:34.415Z INFO myapp: [RES] GET /51a2035604cea64219.jpg (200) took 38 ms 50 | 22:36:34.417Z INFO myapp: [RES] GET /matrix.jpg (200) took 36 ms 51 | ``` 52 | 53 | Logs also can go to somewhere else. It depends on you passed logger configuration. 54 | For more details how to configure bunyan look at [node-bunyan](https://github.com/trentm/node-bunyan) 55 | 56 | ## Note 57 | 58 | Version >= 1.0.0 supports koa2, version < 1.0.0 supports koa1 59 | 60 | # License 61 | **MIT** 62 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('./lib/koa-bunyan'); -------------------------------------------------------------------------------- /lib/koa-bunyan.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | 5 | module.exports = function (logger, opts) { 6 | opts = opts || {}; 7 | 8 | const defaultLevel = opts.level || 'info'; 9 | const requestTimeLevel = opts.timeLimit; 10 | 11 | return function (ctx, next) { 12 | const startTime = new Date().getTime(); 13 | logger[defaultLevel](requestMessage(ctx, opts.headers)); 14 | 15 | const done = function () { 16 | const requestTime = new Date().getTime() - startTime; 17 | let localLevel = defaultLevel; 18 | 19 | if (requestTimeLevel && requestTime > requestTimeLevel) { 20 | localLevel = 'warn'; 21 | } 22 | logger[localLevel](util.format('[RES] %s %s (%s) took %s ms', ctx.method, ctx.originalUrl, ctx.status, requestTime)); 23 | }; 24 | 25 | ctx.res.once('finish', done); 26 | ctx.res.once('close', done); 27 | 28 | 29 | return next(); 30 | }; 31 | }; 32 | 33 | function requestMessage (ctx, headersToLog) { 34 | const message = util.format('[REQ] %s %s', ctx.method, ctx.url); 35 | if (headersToLog) { 36 | const filteredHeaders = headersToLog.reduce((acc, cur) => { 37 | const key = cur.toLowerCase(); 38 | acc[key] = ctx.headers[key]; 39 | return acc; 40 | }, {}); 41 | return message + ' headers ' + JSON.stringify(filteredHeaders); 42 | } 43 | return message; 44 | } 45 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "koa-bunyan", 3 | "version": "1.0.2", 4 | "description": "Using node-bunyan as koa logging middleware", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "tape tests/**/*.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/ivpusic/koa-bunyan.git" 12 | }, 13 | "keywords": [ 14 | "koa", 15 | "bunyan", 16 | "logging", 17 | "koajs" 18 | ], 19 | "author": "Ivan Pusic", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/ivpusic/koa-bunyan/issues" 23 | }, 24 | "devDependencies": { 25 | "tape": "^4.9.1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/logger-tests.js: -------------------------------------------------------------------------------- 1 | const test = require('tape'); 2 | const logging = require('../lib/koa-bunyan'); 3 | 4 | test('it logs the request', assert => { 5 | const messages = []; 6 | const logger = { 7 | info: (message) => { 8 | messages.push(message); 9 | } 10 | }; 11 | const log = logging(logger); 12 | const ctx = context(); 13 | log(ctx, () => { 14 | assert.equal(messages.length, 2); 15 | assert.equal(messages[0], '[REQ] GET /test', 'logs request'); 16 | assert.equal(messages[1], '[RES] GET /test (200) took 0 ms', 'logs response'); 17 | assert.end(); 18 | }); 19 | }); 20 | 21 | test('it logs requested headers', assert => { 22 | const messages = []; 23 | const logger = { 24 | info: (message) => { 25 | messages.push(message); 26 | } 27 | }; 28 | const log = logging(logger, {headers: ['Accept-Language']}); 29 | const ctx = context({ 30 | 'accept-language': 'en-GB', 'accept': 'application/json' 31 | }); 32 | 33 | log(ctx, () => { 34 | assert.equal( 35 | messages[0], 36 | '[REQ] GET /test headers {"accept-language":"en-GB"}', 37 | 'logs headers' 38 | ); 39 | assert.end(); 40 | }); 41 | }); 42 | 43 | function context(headers) { 44 | const ctx = { 45 | res: {}, 46 | headers 47 | }; 48 | ctx.method = 'GET'; 49 | ctx.url = '/test'; 50 | ctx.status = 200; 51 | ctx.originalUrl = '/test'; 52 | ctx.res.once = (event, fn) => { 53 | if (event === 'finish') { 54 | fn(); 55 | } 56 | }; 57 | return ctx; 58 | } 59 | --------------------------------------------------------------------------------