diff --git a/packages/log-structured/CHANGELOG.md b/packages/log-structured/CHANGELOG.md index 06dfb9eb..420fe0d3 100644 --- a/packages/log-structured/CHANGELOG.md +++ b/packages/log-structured/CHANGELOG.md @@ -1,5 +1,9 @@ # @idearium/log-structured +## Unreleased + +- Now supports error logs. + ## v1.0.0 - First version of the package. diff --git a/packages/log-structured/index.js b/packages/log-structured/index.js index 828a2edb..ff73139b 100644 --- a/packages/log-structured/index.js +++ b/packages/log-structured/index.js @@ -10,26 +10,42 @@ const write = (callback, line) => { return callback(null, `${JSON.stringify(line)}\n`); }; +const createHttpRequest = (req, res) => ({ + protocol: req.protocol, + referer: req.headers.referer || '', + remoteIp: + req.headers['x-forwarded-for'] || + // This is for < v13 + req.remoteIp || + // This is for > v14 + '', + requestMethod: req.method, + requestSize: req.headers['content-length'] || 0, + requestUrl: req.url, + responseSize: res.size || 0, + status: res.statusCode, + userAgent: req.headers['user-agent'] || '' +}); + +const transformErroredRequest = (log) => { + const { err, req, res } = log; + + const updated = Object.assign({}, log, { + '@type': err['@type'], + 'context': { httpRequest: createHttpRequest(req, res) }, + 'exception': err.message + }); + + delete updated.err; + + return updated; +}; + const transformRequest = (log) => { const updated = Object.assign({}, log); const { req, res } = updated; - updated.httpRequest = { - protocol: req.protocol, - referer: req.headers.referer || '', - remoteIp: - req.headers['x-forwarded-for'] || - // This is for < v13 - req.remoteIp || - // This is for > v14 - '', - requestMethod: req.method, - requestSize: req.headers['content-length'] || 0, - requestUrl: req.url, - responseSize: res.size || 0, - status: res.statusCode, - userAgent: req.headers['user-agent'] || '' - }; + updated.httpRequest = createHttpRequest(req, res); return updated; }; @@ -60,6 +76,10 @@ module.exports = () => { return write(callback, data); } + if (line.err && line.req && line.res) { + return write(callback, transformErroredRequest(line)); + } + return write(callback, transformRequest(line)); } }); diff --git a/packages/log-structured/tests/structured.test.js b/packages/log-structured/tests/structured.test.js index 043185e2..2927d00a 100644 --- a/packages/log-structured/tests/structured.test.js +++ b/packages/log-structured/tests/structured.test.js @@ -164,6 +164,45 @@ test('logs res when logging http requests', async (done) => { get(server); }); +test('does not include error information when an error did not occur', async (done) => { + expect.assertions(1); + + const stream = structured(); + const log = middleware(stream); + const server = await setup(log); + + once(stream, 'data').then((result) => { + const line = JSON.parse(result.toString()); + + expect(line).not.toHaveProperty('@type'); + + return done(); + }); + + get(server); +}); + +test('logs err when an error occurs during http request/response lifecycle', async (done) => { + expect.assertions(2); + + const stream = structured(); + const log = middleware(stream); + const server = await setup(log); + + once(stream, 'data').then((result) => { + const line = JSON.parse(result.toString()); + + expect(line).toHaveProperty('@type'); + expect(line['@type']).toEqual( + 'type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent' + ); + + return done(); + }); + + get(server, '/error'); +}); + test('uses structured logging format when logging http requests', async (done) => { expect.assertions(7); @@ -379,3 +418,32 @@ test('http requests includes referer', async (done) => { get(server); }); + +test('uses structured error logging when an error occurs during http request/response lifecycle', async (done) => { + expect.assertions(10); + + const stream = structured(); + const log = middleware(stream); + const server = await setup(log); + + once(stream, 'data').then((result) => { + const line = JSON.parse(result.toString()); + + expect(line).toHaveProperty('@type'); + expect(line['@type']).toEqual( + 'type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent' + ); + expect(line).toHaveProperty('message'); + expect(line.message).toBe('request errored'); + expect(line).not.toHaveProperty('httpRequest'); + expect(line).not.toHaveProperty('err'); + expect(line).toHaveProperty('context'); + expect(line.context).toHaveProperty('httpRequest'); + expect(line).toHaveProperty('exception'); + expect(line.exception).toContain('Testing errors...'); + + return done(); + }); + + get(server, '/error'); +});