diff --git a/README.md b/README.md index c9a84d7..a651109 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,13 @@ The exported `expressRequestIdMiddleware` function takes one argument, [`options #### `options` (Object) ##### `loggerFn` (Function) -Logger function used for start and end log actions. +Logger function used for start and end log actions with the default message format. + +##### `startFn` (Function) +Function used in start log actions, mainly for custom messages. + +##### `endFn` (Function) +Function used in end log actions, mainly for custom messages. ##### `obfuscatePlaceholder` (String) Default: [SECURE] diff --git a/lib/middlewares.js b/lib/middlewares.js index c3b20ad..17841ab 100644 --- a/lib/middlewares.js +++ b/lib/middlewares.js @@ -23,11 +23,39 @@ const getSecureBody = ({ params, url, method, body, placeholder }) => { } }; +const logStartMsg = ({ req, formattedBody, loggerFn, startFn }) => { + if (startFn) { + return startFn({ req, formattedBody }); + } + + const { method, params, query } = req; + const url = req.originalUrl || req.url; + return loggerFn( + `Started ${url} ${method} with params: %j query: %j body: %j`, + params || {}, + query || {}, + formattedBody || {} + ); +}; + +const logEndMsg = ({ req, res, loggerFn, endFn, responseTime }) => { + if (endFn) { + return endFn({ req, res, responseTime }); + } + + const { method } = req; + const url = req.originalUrl || req.url; + const status = res.statusCode; + return loggerFn(`Ended ${method} ${url} with status: ${status} in ${responseTime} ms`); +}; + const expressMiddleware = opts => { // TODO: add a check that all the config is safe - const { loggerFn, obfuscateBody = true, obfuscatePlaceholder = secure } = opts || {}; + const { loggerFn, startFn, endFn, obfuscateBody = true, obfuscatePlaceholder = secure } = opts || {}; + if (!loggerFn && (!startFn || !endFn)) throw Error('You need to define a loggerFn or a startFn and endFn'); + return (req, res, next) => { - const { method, params, query, body } = req; + const { method, body } = req; const url = req.originalUrl || req.url; const formattedBody = getSecureBody({ params: obfuscateBody, @@ -37,18 +65,12 @@ const expressMiddleware = opts => { placeholder: obfuscatePlaceholder }); - loggerFn( - `Started ${url} ${method} with params: %j query: %j body: %j`, - params || {}, - query || {}, - formattedBody || {} - ); + logStartMsg({ req, formattedBody, loggerFn, startFn }); const begin = Date.now(); const onFinish = namespace.bind((error, response) => { const end = Date.now(); const responseTime = error ? '-' : end - begin; - const status = response.statusCode; - loggerFn(`Ended ${method} ${url} with status: ${status} in ${responseTime} ms`); + logEndMsg({ req, res: response, loggerFn, endFn, responseTime }); }); onFinished(res, onFinish); next(); diff --git a/package-lock.json b/package-lock.json index 2e82dce..6223c2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "express-wolox-logger", - "version": "3.0.0", + "version": "3.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index d7f19e3..b0436d3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "express-wolox-logger", - "version": "3.0.0", + "version": "3.1.0", "description": "ExpressJS Wolox Logger", "main": "index.js", "scripts": { diff --git a/test/middlewares.spec.js b/test/middlewares.spec.js index a3c8398..ec9d76f 100644 --- a/test/middlewares.spec.js +++ b/test/middlewares.spec.js @@ -32,40 +32,66 @@ describe('middlewares', () => { const testUrl = '/test_url'; const makeRequest = (server, method = 'get') => request(server)[method](testUrl); - describe('express middleware', () => { + const startFn = jest.fn(); + const endFn = jest.fn(); + const middlewaresScenarios = [ + ['with loggerFn', { config: {} }], + ['with custom hooks', { config: { startFn, endFn } }] + ]; + + describe.each(middlewaresScenarios)('express middleware (%s)', (name, scenario) => { const loggerMock = jest.spyOn(logger, 'info').mockImplementation(() => {}); // eslint-disable-line const server = options => createServer( expressMiddleware({ loggerFn: logger.info, + ...scenario.config, ...options }) ); + afterEach(() => { + startFn.mockClear(); + endFn.mockClear(); + loggerMock.mockClear(); + }); + const getLoggerCalledParams = num => loggerMock.mock.calls[num].map(JSON.stringify).join(''); + const getLoggerMsg = () => + loggerMock.mock.calls.length > 0 ? getLoggerCalledParams(0) : startFn.mock.calls[0][0].formattedBody; + test('should log when request starts', done => { makeRequest(server()).end(() => { - expect(getLoggerCalledParams(0)).toEqual( - expect.stringMatching(/Started \/test_url GET with params:.*query:.*body.*/) - ); + if (scenario.config.startFn) { + expect(startFn).toHaveBeenCalledTimes(1); + } else { + expect(getLoggerCalledParams(0)).toEqual( + expect.stringMatching(/Started \/test_url GET with params:.*query:.*body.*/) + ); + } done(); }); }); test('should log when request finishes', done => { makeRequest(server()).end(() => { - expect(getLoggerCalledParams(1)).toEqual( - expect.stringMatching(/Ended GET \/test_url with status: [2-5]+00 in [0-9]+ ms/) - ); + if (scenario.config.endFn) { + expect(endFn).toHaveBeenCalledTimes(1); + } else { + expect(getLoggerCalledParams(1)).toEqual( + expect.stringMatching(/Ended GET \/test_url with status: [2-5]+00 in [0-9]+ ms/) + ); + } done(); }); }); + test('should obfuscate full body of any endpoint', done => { makeRequest(server({ obfuscateBody: true }), 'post') .send({ secure: 'secretValue' }) .end(() => { - expect(getLoggerCalledParams(0)).toContain('[SECURE]'); - expect(getLoggerCalledParams(0)).not.toContain('secretValue'); + expect(getLoggerMsg()).toContain('[SECURE]'); + expect(getLoggerMsg()).not.toContain('secretValue'); done(); }); }); @@ -73,8 +99,8 @@ describe('middlewares', () => { makeRequest(server({ obfuscateBody: { [testUrl]: { POST: true } } }), 'post') .send({ secure: 'secretValue' }) .end(() => { - expect(getLoggerCalledParams(0)).toContain('[SECURE]'); - expect(getLoggerCalledParams(0)).not.toContain('secretValue'); + expect(getLoggerMsg()).toContain('[SECURE]'); + expect(getLoggerMsg()).not.toContain('secretValue'); done(); }); });