4 const assert
= require('node:assert');
5 const sinon
= require('sinon'); // eslint-disable-line node/no-unpublished-require
6 const Logger
= require('../../lib/logger');
7 const http
= require('node:http');
8 const { AsyncLocalStorage
} = require('node:async_hooks');
9 const asyncLocalStorage
= new AsyncLocalStorage();
11 describe('Logger', function () {
12 let config
, logger
, commonObject
, scope
, message
;
14 beforeEach(function () {
17 nodeId: '3c100e84-9a7f-11ec-9b4e-0025905f714a',
19 logger
= new Logger(config
, commonObject
);
20 Object
.keys(Logger
.nullLogger
).forEach((level
) => sinon
.stub(logger
.backend
, level
));
24 this.afterEach(function () {
28 it('logs a message', function () {
29 logger
.info(scope
, message
, { baz: 'quux' }, { foo: 1 }, 'more other');
30 assert(logger
.backend
.info
.called
);
31 assert(logger
.backend
.info
.args
[0][0].includes(message
));
34 it('stubs missing levels', function () {
38 logger
= new Logger(config
, commonObject
, undefined, backend
);
39 assert
.strictEqual(typeof logger
.info
, 'function');
42 it('logs BigInts', function () {
43 logger
.info(scope
, message
, { aBigInteger: BigInt(2) });
44 assert(logger
.backend
.info
.called
);
45 assert(logger
.backend
.info
.args
[0][0].includes('"2"'));
48 it('logs Errors', function () {
49 logger
.error(scope
, message
, { e: new Error('an error') });
50 assert(logger
.backend
.error
.called
);
51 assert(logger
.backend
.error
.args
[0][0].includes('an error'));
54 it('covers config settings', function () {
55 config
.ignoreBelowLevel
= 'info';
56 logger
= new Logger(config
);
57 logger
.debug(scope
, message
, {});
58 assert(logger
.backend
.debug
.notCalled
);
61 it('covers config error', function () {
62 config
.ignoreBelowLevel
= 'not a level';
64 logger
= new Logger(config
);
65 assert
.fail('expected RangeError here');
67 assert(e
instanceof RangeError
);
71 it('covers empty fields', function () {
73 assert(logger
.backend
.info
.called
);
74 assert(logger
.backend
.info
.args
[0][0].includes('[unknown]'));
77 it('sanitizes', function () {
78 logger
.dataSanitizers
.push((data
, sanitize
= true) => {
80 const credentialLength
= data
?.ctx
?.parsedBody
?.credential
?.length
;
81 if (credentialLength
) {
84 if (unclean
&& sanitize
) {
85 data
.ctx
.parsedBody
.credential
= '*'.repeat(credentialLength
);
89 logger
.info(scope
, message
, {
96 assert(logger
.backend
.info
.called
);
97 assert(logger
.backend
.info
.args
[0][0].includes('******'));
100 it('logs http client requests', function () {
101 const incomingMessage
= new http
.IncomingMessage();
102 incomingMessage
.method
= 'GET';
103 incomingMessage
.url
= new URL('http://example.com/');
104 logger
.info(scope
, message
, { incomingMessage
});
105 assert(logger
.backend
.info
.called
);
106 assert(logger
.backend
.info
.args
[0][0].includes('GET'));
107 assert(logger
.backend
.info
.args
[0][0].includes('http://example.com/'));
110 it('logs http client requests with scrubbed auth', function () {
111 const incomingMessage
= Object
.create(http
.IncomingMessage
.prototype);
112 incomingMessage
.headers
= {
113 authorization: 'Basic eW8=',
115 logger
.info(scope
, message
, { incomingMessage
});
116 assert(logger
.backend
.info
.called
);
117 assert(logger
.backend
.info
.args
[0][0].includes('****'));
120 it('logs http server responses', function () {
121 const serverResponse
= Object
.create(http
.ServerResponse
.prototype);
122 logger
.info(scope
, message
, { serverResponse
});
123 assert(logger
.backend
.info
.args
[0][0].includes('"statusCode":200'));
126 it('follows expected level ordering', function () {
127 const levels
= Object
.keys(Logger
.nullLogger
);
128 const expected
= ['error', 'warn', 'info', 'log', 'debug'];
129 assert
.deepStrictEqual(levels
, expected
);
132 it('logs async storage data', async
function () {
133 logger
= new Logger(config
, commonObject
, asyncLocalStorage
);
134 const data
= { foo: 'bar' };
135 asyncLocalStorage
.run(data
, async () => {
136 logger
.info(scope
, message
, { baz: 3 });
138 assert(logger
.backend
.info
.called
);
139 assert(logger
.backend
.info
.args
[0][0].includes('"foo":"bar"'));
142 it('covers no async storage', function () {
143 logger
= new Logger(config
);
144 const data
= { foo: 'bar' };
145 asyncLocalStorage
.run(data
, async () => {
146 logger
.info(scope
, message
, { baz: 3 });
148 assert(logger
.backend
.info
.called
);
149 assert(!logger
.backend
.info
.args
[0][0].includes('"foo":"bar"'));