Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | 14x 12x 1x 11x 1x 11x 11x 11x 1x 10x 10x 11x 11x 11x 11x 11x 11x 2x 11x 1x 3x 3x 2x 2x 2x 1x | 'use strict'; /** * This is a wrapper for the logger provided by service runner. * It provides more user-friendly logging APIs and better error * signaling for when it is used incorrectly. * * This is the logger that other scripts in this project will interact with. * Usage: * const logger = new LoggerWrapper( <somelogger> ); * logger.log('warn', 'hello this is a message'); * */ class LoggerWrapper { constructor( logger ) { this._logger = logger; } /** * Logs a message on a given severity level. * Acceptable levels: 'trace', 'debug', 'info', 'warn', 'error', and 'fatal'. * * @param {string} level Severity level using one of the level options. * Can also be: 'trace' or 'trace/request'. * * @param {Object} data Contains message and any relevant info for the log. * @param {string|undefined} data.message Log message string * @param {string|undefined} data.requestId The 'x-request-id' HTTP header, for traceability * @param {string|Object|undefined} data.info request details or JSON object */ log( level, data = { message: 'No message set!', requestId: 'No requestId set!', info: 'No info set!' } ) { // TODO (T369560): // confirm in Logstash Prod that message param is key of an object, not string; // and that is what actually gets emitted as 'message' && 'msg'; i.e. function-evaluator if ( !level || !data ) { // The service runner implementation will just silently no-op // in this situation. We want to alert the caller here. throw new Error( `Incorrect usage of the logger. Both arguments need to be present. E.g. logger.log(level, data).` ); } // temporarily adding this in case there's an undetected old version of logging somewhere if ( typeof data === 'string' ) { data = { message: data }; } // We want to output the request ID under this special name, but it's awkward. data[ 'x-request-id' ] = data.requestId; delete data.requestId; // add or verify timestamp per log let timeStamp; if ( data.time && this._isValidTimeStamp( data.time ) ) { timeStamp = data.time; } else { timeStamp = new Date().toISOString(); data.time = timeStamp; } // add stacktrace per log const stackTrace = new Error().stack; const detailedStack = stackTrace.split( '\n' ).slice( 2 ).join( '\n' ); // adding compacted details to message, in case data object gets swallowed up in Logstash const simpleTrace = detailedStack.split( '\n' )[ 0 ].trim(); data.trace = simpleTrace; data.message = data.message + `, time: ${ timeStamp }, reqId: ${ data[ 'x-request-id' ] }, trace: ${ simpleTrace }`; // this is so we can easily debug/detect in docker logs if ( process.env.WIKIFUNCTIONS_DEBUG_LOCAL ) { // eslint-disable-next-line no-console console.log( 'Logging LEVEL:', level, ', MESSAGE:', data.message, ', DATA:', data, ', DETAILS:', detailedStack ); } this._logger.log( level, data ); } /** * Creates a child logger for a sub-component of your application. * This directly wraps its core logger obj's implementation. * * @param {*} args arguments for the child wrapper. * @return {LoggerWrapper} A new logger for the sub-component. */ child( args ) { return new LoggerWrapper( this._logger.child( args ) ); } /** * Validates given timestamp. * (Expected to come from the Executor which does not have logger access) * * @param {string} currentTimeStamp an ISO date string OR nothing OR something invalid... * @return {boolean} true || false */ _isValidTimeStamp( currentTimeStamp ) { const dateFromIsoDateString = new Date( currentTimeStamp ); // Check if the date is valid and matches the original input return !isNaN( dateFromIsoDateString.getTime() ) && dateFromIsoDateString.toISOString() === currentTimeStamp && this._isCurrentDate( dateFromIsoDateString ); } /** * Checks if the date is current and valid * * @param {Object} dateInput date object, i.e. Tue Jun 25 2024 12:34:56 GMT+0000 * @return {boolean} true || false */ _isCurrentDate( dateInput ) { const dateToday = new Date(); const pastLimit = new Date( dateToday.getFullYear() - 100, dateToday.getMonth(), dateToday.getDate() ); return dateInput <= dateToday && dateInput >= pastLimit; } } module.exports = { LoggerWrapper }; |