koa-bunyan-logger
Advanced tools
Comparing version
161
index.js
@@ -1,8 +0,21 @@ | ||
'use strict'; | ||
const bunyan = require('bunyan'); | ||
const uuid = require('uuid'); | ||
const util = require('util'); | ||
const onFinished = require('on-finished'); | ||
var bunyan = require('bunyan'); | ||
var uuid = require('uuid'); | ||
var util = require('util'); | ||
var onFinished = require('on-finished'); | ||
const updateFields = (ctx, func, data, err) => { | ||
if (!func) return data; | ||
try { | ||
if (err) { | ||
return func.call(ctx, data, err) || data; | ||
} | ||
return func.call(ctx, data) || data; | ||
} catch (e) { | ||
ctx.log.error(e); | ||
return data; | ||
} | ||
}; | ||
/* | ||
@@ -12,5 +25,5 @@ * If logger is a bunyan logger instance, return it; | ||
*/ | ||
function createOrUseLogger(logger) { | ||
const createOrUseLogger = (logger) => { | ||
if (!logger || !logger.info || !logger.child) { | ||
var loggerOpts = logger || {}; | ||
const loggerOpts = logger || {}; | ||
loggerOpts.name = loggerOpts.name || 'koa'; | ||
@@ -23,3 +36,3 @@ loggerOpts.serializers = loggerOpts.serializers || bunyan.stdSerializers; | ||
return logger; | ||
} | ||
}; | ||
@@ -35,6 +48,6 @@ /* | ||
*/ | ||
module.exports = function (loggerInstance) { | ||
module.exports = (loggerInstance) => { | ||
loggerInstance = createOrUseLogger(loggerInstance); | ||
return function logger(ctx, next) { | ||
return (ctx, next) => { | ||
ctx.log = loggerInstance; | ||
@@ -60,13 +73,12 @@ | ||
*/ | ||
module.exports.requestIdContext = function (opts) { | ||
module.exports.requestIdContext = (opts) => { | ||
opts = opts || {}; | ||
var header = opts.header || 'X-Request-Id'; | ||
var ctxProp = opts.prop || 'reqId'; | ||
var requestProp = opts.requestProp || 'reqId'; | ||
var logField = opts.field || 'req_id'; | ||
var fallbackLogger; | ||
const header = opts.header || 'X-Request-Id'; | ||
const ctxProp = opts.prop || 'reqId'; | ||
const requestProp = opts.requestProp || 'reqId'; | ||
const logField = opts.field || 'req_id'; | ||
return function requestIdContext(ctx, next) { | ||
var reqId = ctx.request.get(header) || uuid.v4(); | ||
return (ctx, next) => { | ||
const reqId = ctx.request.get(header) || uuid.v4(); | ||
@@ -76,3 +88,3 @@ ctx[ctxProp] = reqId; | ||
var logFields = {}; | ||
const logFields = {}; | ||
logFields[logField] = reqId; | ||
@@ -105,32 +117,34 @@ | ||
*/ | ||
module.exports.requestLogger = function (opts) { | ||
module.exports.requestLogger = (opts) => { | ||
opts = opts || {}; | ||
var levelFn = opts.levelFn || function (status, err) { | ||
const levelFn = opts.levelFn || function (status) { | ||
if (status >= 500) { | ||
return 'error'; | ||
} else if (status >= 400) { | ||
} if (status >= 400) { | ||
return 'warn'; | ||
} else { | ||
return 'info'; | ||
} | ||
return 'info'; | ||
}; | ||
var durationField = opts.durationField || 'duration'; | ||
const durationField = opts.durationField || 'duration'; | ||
var formatRequestMessage = opts.formatRequestMessage || function (data) { | ||
const formatRequestMessage = opts.formatRequestMessage || function () { | ||
return util.format(' <-- %s %s', | ||
this.request.method, this.request.originalUrl); | ||
this.request.method, this.request.originalUrl); | ||
}; | ||
var formatResponseMessage = opts.formatResponseMessage || function (data) { | ||
const formatResponseMessage = opts.formatResponseMessage || function (data) { | ||
return util.format(' --> %s %s %d %sms', | ||
this.request.method, this.request.originalUrl, | ||
this.status, data[durationField]); | ||
this.request.method, this.request.originalUrl, | ||
this.status, data[durationField]); | ||
}; | ||
return function requestLogger(ctx, next) { | ||
var url = ctx.url; | ||
return (ctx, next) => { | ||
if (Array.isArray(opts.ignorePath) && opts.ignorePath.includes(ctx.path)) { | ||
return next(); | ||
} | ||
var requestData = { | ||
let requestData = { | ||
req: ctx.req | ||
@@ -144,7 +158,7 @@ }; | ||
var startTime = new Date().getTime(); | ||
var err; | ||
const startTime = new Date().getTime(); | ||
let err; | ||
var onResponseFinished = function () { | ||
var responseData = { | ||
const onResponseFinished = () => { | ||
let responseData = { | ||
req: ctx.req, | ||
@@ -158,12 +172,12 @@ res: ctx.res | ||
responseData[durationField] = new Date().getTime() - startTime; | ||
responseData[durationField] = Date.now() - startTime; | ||
responseData = updateFields(ctx, opts.updateLogFields, responseData); | ||
responseData = updateFields(ctx, opts.updateResponseLogFields, | ||
responseData, err); | ||
responseData, err); | ||
var level = levelFn.call(ctx, ctx.status, err); | ||
const level = levelFn.call(ctx, ctx.status, err); | ||
ctx.log[level](responseData, | ||
formatResponseMessage.call(ctx, responseData)); | ||
formatResponseMessage.call(ctx, responseData)); | ||
@@ -174,3 +188,3 @@ // Remove log object to mitigate accidental leaks | ||
return next().catch(e => { | ||
return next().catch((e) => { | ||
err = e; | ||
@@ -189,17 +203,2 @@ }).then(() => { // Emulate a finally | ||
function updateFields (ctx, func, data, err) { | ||
if (!func) return data; | ||
try { | ||
if (err) { | ||
return func.call(ctx, data, err) || data; | ||
} else { | ||
return func.call(ctx, data) || data; | ||
} | ||
} catch (e) { | ||
ctx.log.error(e); | ||
return data; | ||
} | ||
} | ||
/** | ||
@@ -218,21 +217,12 @@ * Middleware which adds methods this.time(label) and this.timeEnd(label) | ||
*/ | ||
module.exports.timeContext = function (opts) { | ||
module.exports.timeContext = (opts) => { | ||
opts = opts || {}; | ||
var logLevel = opts.logLevel || 'trace'; | ||
var updateLogFields = opts.updateLogFields; | ||
const logLevel = opts.logLevel || 'trace'; | ||
const { updateLogFields } = opts; | ||
return function timeContext(ctx, next) { | ||
ctx._timeContextStartTimes = {}; | ||
function time(label) { | ||
/* jshint validthis:true */ | ||
const startTimes = this._timeContextStartTimes; | ||
ctx.time = time; | ||
ctx.timeEnd = timeEnd; | ||
return next(); | ||
}; | ||
function time (label) { | ||
/*jshint validthis:true */ | ||
var startTimes = this._timeContextStartTimes; | ||
if (startTimes[label]) { | ||
@@ -245,6 +235,6 @@ this.log.warn('time() called for previously used label %s', label); | ||
function timeEnd (label) { | ||
/*jshint validthis:true */ | ||
var startTimes = this._timeContextStartTimes; | ||
var startTime = startTimes[label]; | ||
function timeEnd(label) { | ||
/* jshint validthis:true */ | ||
const startTimes = this._timeContextStartTimes; | ||
const startTime = startTimes[label]; | ||
@@ -256,7 +246,7 @@ if (!startTime) { // whoops! | ||
var duration = new Date().getTime() - startTime; | ||
var fields = { | ||
label: label, | ||
duration: duration, | ||
msg: label + ': ' + duration + 'ms' | ||
const duration = new Date().getTime() - startTime; | ||
let fields = { | ||
label, | ||
duration, | ||
msg: `${label}: ${duration}ms` | ||
}; | ||
@@ -269,2 +259,11 @@ | ||
} | ||
return (ctx, next) => { | ||
ctx._timeContextStartTimes = {}; | ||
ctx.time = time; | ||
ctx.timeEnd = timeEnd; | ||
return next(); | ||
}; | ||
}; | ||
@@ -271,0 +270,0 @@ |
{ | ||
"name": "koa-bunyan-logger", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"description": "Koa middleware for logging requests using bunyan", | ||
@@ -10,3 +10,5 @@ "main": "index.js", | ||
"scripts": { | ||
"test": "make test" | ||
"test": "mocha", | ||
"test:coverage": "npx istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage", | ||
"lint": "eslint index.js" | ||
}, | ||
@@ -30,15 +32,15 @@ "keywords": [ | ||
"dependencies": { | ||
"bunyan": "~1.5.0", | ||
"on-finished": "~2.1.1", | ||
"bunyan": "~1.8.12", | ||
"on-finished": "~2.3.0", | ||
"uuid": "^3.0.0" | ||
}, | ||
"devDependencies": { | ||
"co-mocha": "~1.1.2", | ||
"co-supertest": "~0.0.10", | ||
"istanbul": "~0.4.0", | ||
"jshint": "~2.5.8", | ||
"coveralls": "^3.0.2", | ||
"eslint": "^5.6.1", | ||
"eslint-config-sexy": "^5.0.0", | ||
"istanbul": "^0.4.5", | ||
"koa": "^2.0.0", | ||
"mocha": "~2.3.3", | ||
"supertest": "~1.1.0" | ||
"mocha": "^5.2.0", | ||
"supertest": "^3.3.0" | ||
} | ||
} |
@@ -94,2 +94,10 @@ # bunyan-logger | ||
### Ignoring specific path from logging | ||
It is possible to skip logs from some endpoints with `ignorePath` option. | ||
```js | ||
app.use(koaBunyanLogger.requestLogger({ ignorePath: ['/ping'] })) | ||
``` | ||
### Suppressing default error stack traces | ||
@@ -96,0 +104,0 @@ |
@@ -1,11 +0,8 @@ | ||
var Koa = require('koa'); | ||
var koaBunyanLogger = require('../'); | ||
var supertest = require('supertest'); | ||
var assert = require('assert'); | ||
var bunyan = require('bunyan'); | ||
const Koa = require('koa'); | ||
const koaBunyanLogger = require('../'); | ||
const supertest = require('supertest'); | ||
const assert = require('assert'); | ||
const bunyan = require('bunyan'); | ||
require('co-mocha'); | ||
require('co-supertest'); | ||
describe('koaBunyanLogger', function () { | ||
describe('koaBunyanLogger', () => { | ||
var app; | ||
@@ -15,5 +12,5 @@ var server; | ||
beforeEach(function *() { | ||
beforeEach(async () => { | ||
app = new Koa(); | ||
app.on('error', function () {}); // suppress errors | ||
app.on('error', () => {}); // suppress errors | ||
@@ -31,3 +28,3 @@ ringBuffer = new bunyan.RingBuffer({limit: 100}); | ||
afterEach(function *() { | ||
afterEach(async () => { | ||
if (server) { | ||
@@ -41,3 +38,3 @@ server.close(); | ||
var request = function () { | ||
const request = () => { | ||
if (!server) { | ||
@@ -50,3 +47,3 @@ server = app.listen(0); | ||
var record = function (i) { | ||
const record = (i) => { | ||
assert.ok(i >= 0 || i < ringBuffer.records.length); | ||
@@ -56,9 +53,13 @@ return ringBuffer.records[i]; | ||
var helloWorld = function (ctx) { | ||
const helloWorld = (ctx) => { | ||
ctx.body = 'Hello world'; | ||
}; | ||
it('creates a default logger', function *() { | ||
const pingResponse = (ctx) => { | ||
ctx.body = 'ping'; | ||
}; | ||
it('creates a default logger', async () => { | ||
app.use(koaBunyanLogger()); | ||
app.use(function (ctx) { | ||
app.use(ctx => { | ||
assert.ok(ctx.log); | ||
@@ -68,9 +69,9 @@ ctx.body = ''; | ||
yield request().get('/').expect(200).end(); | ||
await request().get('/').expect(200); | ||
}); | ||
it('can log simple requests', function * () { | ||
it('can log simple requests', async () => { | ||
app.use(koaBunyanLogger(ringLogger)); | ||
app.use(function (ctx) { | ||
app.use(ctx => { | ||
ctx.log.info('Got request'); | ||
@@ -80,3 +81,3 @@ ctx.body = 'Hello world'; | ||
yield request().get('/').expect(200).end(); | ||
await request().get('/').expect(200); | ||
@@ -86,11 +87,11 @@ assert.equal(record(0).msg, 'Got request'); | ||
describe('koaBunyanLogger.requestLogger', function () { | ||
var REQ_MESSAGE = / <-- GET \//; | ||
var RES_MESSAGE = / --> GET \/ \d+ \d+ms/; | ||
describe('koaBunyanLogger.requestLogger', () => { | ||
const REQ_MESSAGE = / <-- GET \//; | ||
const RES_MESSAGE = / --> GET \/ \d+ \d+ms/; | ||
beforeEach(function () { | ||
beforeEach(() => { | ||
app.use(koaBunyanLogger(ringLogger)); | ||
}); | ||
function checkRequestResponse (status) { | ||
const checkRequestResponse = status => { | ||
assert.equal(ringBuffer.records.length, 2); | ||
@@ -102,7 +103,7 @@ assert.ok(record(0).msg.match(REQ_MESSAGE)); | ||
it('logs requests', function *() { | ||
it('logs requests', async () => { | ||
app.use(koaBunyanLogger.requestLogger()); | ||
app.use(helloWorld); | ||
yield request().get('/').expect(200).end(); | ||
await request().get('/').expect(200); | ||
@@ -112,10 +113,17 @@ checkRequestResponse(200); | ||
it('logs 404 errors', function *() { | ||
it('ignore logs requests', async () => { | ||
app.use(koaBunyanLogger.requestLogger({ ignorePath: ['/ping'] })); | ||
app.use(pingResponse); | ||
await request().get('/ping?t=xxx').expect(200); | ||
assert.equal(ringBuffer.records.length, 0); | ||
}); | ||
it('logs 404 errors', async () => { | ||
app.use(koaBunyanLogger.requestLogger()); | ||
app.use(function (ctx) { | ||
app.use(ctx => { | ||
ctx.throw(404); | ||
}); | ||
yield request().get('/').expect(404).end(); | ||
await request().get('/').expect(404); | ||
@@ -125,10 +133,10 @@ checkRequestResponse(404); | ||
it('logs 500 errors', function *() { | ||
it('logs 500 errors', async () => { | ||
app.use(koaBunyanLogger.requestLogger()); | ||
app.use(function () { | ||
app.use(() => { | ||
throw new Error('oh no'); | ||
}); | ||
yield request().get('/').expect(500).end(); | ||
await request().get('/').expect(500); | ||
@@ -138,5 +146,5 @@ checkRequestResponse(500); | ||
it('allows adding fields to request/response log data', function *() { | ||
it('allows adding fields to request/response log data', async () => { | ||
app.use(koaBunyanLogger.requestLogger({ | ||
updateLogFields: function (fields) { | ||
updateLogFields: fields => { | ||
fields.foo = 'bar'; | ||
@@ -147,3 +155,3 @@ fields.baz1 = 'fizz'; | ||
updateRequestLogFields: function (fields) { | ||
updateRequestLogFields: fields => { | ||
fields.addedToReq = 'hello'; | ||
@@ -153,3 +161,3 @@ delete fields.baz1; | ||
updateResponseLogFields: function (fields, err) { | ||
updateResponseLogFields: (fields, err) => { | ||
fields.addedToRes = 'world'; | ||
@@ -164,7 +172,7 @@ delete fields.baz2; | ||
app.use(function () { | ||
app.use(() => { | ||
throw new Error('uh oh'); | ||
}); | ||
yield request().get('/').expect(500).end(); | ||
await request().get('/').expect(500); | ||
@@ -184,5 +192,5 @@ checkRequestResponse(500); | ||
it('logs errors in update methods and then continues', function *() { | ||
it('logs errors in update methods and then continues', async () => { | ||
app.use(koaBunyanLogger.requestLogger({ | ||
updateResponseLogFields: function (fields) { | ||
updateResponseLogFields: fields => { | ||
throw new Error('clumsy'); | ||
@@ -194,3 +202,3 @@ } | ||
yield request().get('/').expect(200).end(); | ||
await request().get('/').expect(200); | ||
@@ -209,13 +217,13 @@ assert.equal(ringBuffer.records.length, 3); | ||
describe('koaBunyanLogger.requestIdContext', function () { | ||
it('throws an exception if this.log is not available', function *() { | ||
describe('koaBunyanLogger.requestIdContext', () => { | ||
it('throws an exception if this.log is not available', async () => { | ||
app.use(koaBunyanLogger.requestIdContext()); | ||
yield request().get('/').expect(500).end(); | ||
await request().get('/').expect(500); | ||
}); | ||
it('adds req_id from X-Request-Header to log messages', function *() { | ||
it('adds req_id from X-Request-Header to log messages', async () => { | ||
app.use(koaBunyanLogger(ringLogger)); | ||
app.use(koaBunyanLogger.requestIdContext()); | ||
app.use(function (ctx) { | ||
app.use(ctx => { | ||
ctx.log.info('hello world'); | ||
@@ -225,3 +233,3 @@ ctx.body = ""; | ||
yield request().get('/').set({'X-Request-Id': '1234'}).expect(200).end(); | ||
await request().get('/').set({'X-Request-Id': '1234'}).expect(200); | ||
@@ -231,7 +239,7 @@ assert.equal(ringBuffer.records[0].req_id, '1234'); | ||
it('adds generated req_id to log messages if there is no header', function *() { | ||
it('adds generated req_id to log messages if there is no header', async () => { | ||
app.use(koaBunyanLogger(ringLogger)); | ||
app.use(koaBunyanLogger.requestIdContext()); | ||
app.use(function (ctx) { | ||
app.use(ctx => { | ||
ctx.log.info('hello world'); | ||
@@ -241,3 +249,3 @@ ctx.body = ""; | ||
yield request().get('/').expect(200).end(); | ||
await request().get('/').expect(200); | ||
@@ -248,8 +256,8 @@ assert.equal(ringBuffer.records[0].req_id.length, 36); | ||
describe('koaBunyanLogger.timeContext', function () { | ||
it('records the time between time() and timeEnd()', function *() { | ||
describe('koaBunyanLogger.timeContext', () => { | ||
it('records the time between time() and timeEnd()', async () => { | ||
app.use(koaBunyanLogger(ringLogger)); | ||
app.use(koaBunyanLogger.timeContext()); | ||
app.use(function (ctx) { | ||
app.use(ctx => { | ||
ctx.time('foo'); | ||
@@ -260,3 +268,3 @@ ctx.timeEnd('foo'); | ||
yield request().get('/').expect(200).end(); | ||
await request().get('/').expect(200); | ||
assert.equal(ringBuffer.records[0].label, 'foo'); | ||
@@ -266,7 +274,7 @@ assert.equal(typeof ringBuffer.records[0].duration, 'number'); | ||
it('handles nested calls to time()', function *() { | ||
it('handles nested calls to time()', async () => { | ||
app.use(koaBunyanLogger(ringLogger)); | ||
app.use(koaBunyanLogger.timeContext()); | ||
app.use(function (ctx) { | ||
app.use(ctx => { | ||
ctx.time('foo'); | ||
@@ -279,3 +287,3 @@ ctx.time('bar'); | ||
yield request().get('/').expect(200).end(); | ||
await request().get('/').expect(200); | ||
assert.equal(ringBuffer.records[0].label, 'bar'); | ||
@@ -287,7 +295,7 @@ assert.equal(typeof ringBuffer.records[0].duration, 'number'); | ||
it('warns if time() is called twice for the same label', function *() { | ||
it('warns if time() is called twice for the same label', async () => { | ||
app.use(koaBunyanLogger(ringLogger)); | ||
app.use(koaBunyanLogger.timeContext()); | ||
app.use(function (ctx) { | ||
app.use(ctx => { | ||
ctx.time('x'); | ||
@@ -298,3 +306,3 @@ ctx.time('x'); | ||
yield request().get('/').expect(200).end(); | ||
await request().get('/').expect(200); | ||
assert.equal(ringBuffer.records[0].level, bunyan.WARN); | ||
@@ -304,7 +312,7 @@ assert.ok(ringBuffer.records[0].msg.match(/called for previously/)); | ||
it('warns if timeEnd(label) is called without time(label)', function *() { | ||
it('warns if timeEnd(label) is called without time(label)', async () => { | ||
app.use(koaBunyanLogger(ringLogger)); | ||
app.use(koaBunyanLogger.timeContext()); | ||
app.use(function (ctx) { | ||
app.use(ctx => { | ||
ctx.timeEnd('blam'); | ||
@@ -314,3 +322,3 @@ ctx.body = ''; | ||
yield request().get('/').expect(200).end(); | ||
await request().get('/').expect(200); | ||
assert.equal(ringBuffer.records[0].level, bunyan.WARN); | ||
@@ -320,6 +328,6 @@ assert.ok(ringBuffer.records[0].msg.match(/called without/)); | ||
it('allows returning custom log fields', function *() { | ||
it('allows returning custom log fields', async () => { | ||
app.use(koaBunyanLogger(ringLogger)); | ||
app.use(koaBunyanLogger.timeContext({ | ||
updateLogFields: function (fields) { | ||
updateLogFields: fields => { | ||
return { | ||
@@ -334,3 +342,3 @@ request_trace: { | ||
app.use(function (ctx) { | ||
app.use(ctx => { | ||
ctx.time('foo'); | ||
@@ -341,3 +349,3 @@ ctx.timeEnd('foo'); | ||
yield request().get('/').expect(200).end(); | ||
await request().get('/').expect(200); | ||
assert.equal(ringBuffer.records[0].request_trace.name, 'foo'); | ||
@@ -344,0 +352,0 @@ assert.equal(typeof ringBuffer.records[0].request_trace.time, 'number'); |
Sorry, the diff of this file is not supported yet
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
536
0.94%360
2.27%30330
-2.19%12
-14.29%1
Infinity%+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
Updated
Updated