const flatted = require('flatted'); const { test } = require('tap'); const LoggingEvent = require('../../lib/LoggingEvent'); const levels = require('../../lib/levels'); test('LoggingEvent', (batch) => { batch.test('should throw error for invalid location', (t) => { t.throws( () => new LoggingEvent( 'cheese', levels.DEBUG, ['log message'], undefined, [] ), 'Invalid location type passed to LoggingEvent constructor' ); t.end(); }); batch.test('should serialise to flatted', (t) => { const nullPrototype = Object.create(null); nullPrototype.hello = 'world'; const event = new LoggingEvent( 'cheese', levels.DEBUG, [ 'log message', Number('abc'), 'NaN', 1 / 0, 'Infinity', -1 / 0, '-Infinity', undefined, 'undefined', nullPrototype, ], { user: 'bob', } ); // set the event date to a known value event.startTime = new Date(Date.UTC(2018, 1, 4, 18, 30, 23, 10)); const rehydratedEvent = flatted.parse(event.serialise()); t.equal(rehydratedEvent.startTime, '2018-02-04T18:30:23.010Z'); t.equal(rehydratedEvent.categoryName, 'cheese'); t.equal(rehydratedEvent.level.levelStr, 'DEBUG'); t.equal(rehydratedEvent.data.length, 10); t.equal(rehydratedEvent.data[0], 'log message'); t.equal(rehydratedEvent.data[1], '__LOG4JS_NaN__'); t.equal(rehydratedEvent.data[2], 'NaN'); t.equal(rehydratedEvent.data[3], '__LOG4JS_Infinity__'); t.equal(rehydratedEvent.data[4], 'Infinity'); t.equal(rehydratedEvent.data[5], '__LOG4JS_-Infinity__'); t.equal(rehydratedEvent.data[6], '-Infinity'); t.equal(rehydratedEvent.data[7], '__LOG4JS_undefined__'); t.equal(rehydratedEvent.data[8], 'undefined'); t.equal( Object.entries(rehydratedEvent.data[9]).length, Object.entries(nullPrototype).length ); t.equal(rehydratedEvent.data[9].hello, 'world'); t.equal(rehydratedEvent.context.user, 'bob'); t.end(); }); batch.test('should deserialise from flatted', (t) => { const dehydratedEvent = flatted.stringify({ startTime: '2018-02-04T10:25:23.010Z', categoryName: 'biscuits', level: { levelStr: 'INFO', }, data: [ 'some log message', { x: 1 }, '__LOG4JS_NaN__', 'NaN', '__LOG4JS_Infinity__', 'Infinity', '__LOG4JS_-Infinity__', '-Infinity', '__LOG4JS_undefined__', 'undefined', ], context: { thing: 'otherThing' }, pid: '1234', functionName: 'bound', fileName: 'domain.js', lineNumber: 421, columnNumber: 15, callStack: 'at bound (domain.js:421:15)\n', }); const event = LoggingEvent.deserialise(dehydratedEvent); t.type(event, LoggingEvent); t.same(event.startTime, new Date(Date.UTC(2018, 1, 4, 10, 25, 23, 10))); t.equal(event.categoryName, 'biscuits'); t.same(event.level, levels.INFO); t.equal(event.data.length, 10); t.equal(event.data[0], 'some log message'); t.equal(event.data[1].x, 1); t.ok(Number.isNaN(event.data[2])); t.equal(event.data[3], 'NaN'); t.equal(event.data[4], 1 / 0); t.equal(event.data[5], 'Infinity'); t.equal(event.data[6], -1 / 0); t.equal(event.data[7], '-Infinity'); t.equal(event.data[8], undefined); t.equal(event.data[9], 'undefined'); t.equal(event.context.thing, 'otherThing'); t.equal(event.pid, '1234'); t.equal(event.functionName, 'bound'); t.equal(event.fileName, 'domain.js'); t.equal(event.lineNumber, 421); t.equal(event.columnNumber, 15); t.equal(event.callStack, 'at bound (domain.js:421:15)\n'); t.end(); }); batch.test('Should correct construct with/without location info', (t) => { // console.log([Error('123').stack.split('\n').slice(1).join('\n')]) const callStack = ' at repl:1:14\n at ContextifyScript.Script.runInThisContext (vm.js:50:33)\n at REPLServer.defaultEval (repl.js:240:29)\n at bound (domain.js:301:14)\n at REPLServer.runBound [as eval] (domain.js:314:12)\n at REPLServer.onLine (repl.js:468:10)\n at emitOne (events.js:121:20)\n at REPLServer.emit (events.js:211:7)\n at REPLServer.Interface._onLine (readline.js:280:10)\n at REPLServer.Interface._line (readline.js:629:8)'; // eslint-disable-line max-len const fileName = '/log4js-node/test/tap/layouts-test.js'; const lineNumber = 1; const columnNumber = 14; const className = ''; const functionName = ''; const functionAlias = ''; const callerName = ''; const location = { fileName, lineNumber, columnNumber, callStack, className, functionName, functionAlias, callerName, }; const event = new LoggingEvent( 'cheese', levels.DEBUG, ['log message'], { user: 'bob' }, location ); t.equal(event.fileName, fileName); t.equal(event.lineNumber, lineNumber); t.equal(event.columnNumber, columnNumber); t.equal(event.callStack, callStack); t.equal(event.className, className); t.equal(event.functionName, functionName); t.equal(event.functionAlias, functionAlias); t.equal(event.callerName, callerName); const event2 = new LoggingEvent('cheese', levels.DEBUG, ['log message'], { user: 'bob', }); t.equal(event2.fileName, undefined); t.equal(event2.lineNumber, undefined); t.equal(event2.columnNumber, undefined); t.equal(event2.callStack, undefined); t.equal(event2.className, undefined); t.equal(event2.functionName, undefined); t.equal(event2.functionAlias, undefined); t.equal(event2.callerName, undefined); t.end(); }); batch.test('Should contain class, method and alias names', (t) => { // console.log([Error('123').stack.split('\n').slice(1).join('\n')]) const callStack = ' at Foo.bar [as baz] (repl:1:14)\n at ContextifyScript.Script.runInThisContext (vm.js:50:33)\n at REPLServer.defaultEval (repl.js:240:29)\n at bound (domain.js:301:14)\n at REPLServer.runBound [as eval] (domain.js:314:12)\n at REPLServer.onLine (repl.js:468:10)\n at emitOne (events.js:121:20)\n at REPLServer.emit (events.js:211:7)\n at REPLServer.Interface._onLine (readline.js:280:10)\n at REPLServer.Interface._line (readline.js:629:8)'; // eslint-disable-line max-len const fileName = '/log4js-node/test/tap/layouts-test.js'; const lineNumber = 1; const columnNumber = 14; const className = 'Foo'; const functionName = 'bar'; const functionAlias = 'baz'; const callerName = 'Foo.bar [as baz]'; const location = { fileName, lineNumber, columnNumber, callStack, className, functionName, functionAlias, callerName, }; const event = new LoggingEvent( 'cheese', levels.DEBUG, ['log message'], { user: 'bob' }, location ); t.equal(event.fileName, fileName); t.equal(event.lineNumber, lineNumber); t.equal(event.columnNumber, columnNumber); t.equal(event.callStack, callStack); t.equal(event.className, className); t.equal(event.functionName, functionName); t.equal(event.functionAlias, functionAlias); t.equal(event.callerName, callerName); t.end(); }); batch.test('Should correctly serialize and deserialize', (t) => { const error = new Error('test'); const location = { fileName: __filename, lineNumber: 123, columnNumber: 52, callStack: error.stack, className: 'Foo', functionName: 'test', functionAlias: 'baz', callerName: 'Foo.test [as baz]', }; const event = new LoggingEvent( 'cheese', levels.DEBUG, [ error, 'log message', Number('abc'), 'NaN', 1 / 0, 'Infinity', -1 / 0, '-Infinity', undefined, 'undefined', ], { user: 'bob', }, location, error ); const event2 = LoggingEvent.deserialise(event.serialise()); t.match(event2, event); t.end(); }); batch.end(); });