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 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 | 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 57x 57x 57x 57x 57x 57x 57x 1x 56x 1x 1x 1x 57x 57x 57x 57x 57x 5x 57x 47x 47x 47x 376x 47x 82x 82x 82x 2x 80x 80x 80x 80x 89x 89x 47x 42x 8x 8x 1x 7x 8x 10x 10x 10x 10x 10x 10x 47x 88x 9x 9x 79x 1x 1x 54x 54x 54x 24x 80x 64x 16x 16x 16x 16x 16x 1x 3x 15x 54x 54x 46x 8x 54x 47x 47x 47x 47x 47x 47x 47x 47x 47x 11x | /* eslint-disable security/detect-object-injection */ /** * Logality * Extensible JSON Logger. * https://github.com/thanpolas/logality * * Copyright © Thanos Polychronakis * Licensed under the ISC license. */ /** * @fileoverview bootstrap and master exporting module. */ /** * Custom JSDoc Type definitions */ /** * The logality instance. * * @typedef {(Object)} Logality */ /** * A writable stream. * * @typedef {(Object)} WriteStream */ const os = require('os'); const assign = require('lodash.assign'); const middlewarify = require('middlewarify'); const fn = require('./functions'); const prettyPrint = require('./pretty-print'); const utils = require('./utils'); const { version } = require('../package.json'); const userSerializer = require('./serializers/user.serializer'); const errorSerializer = require('./serializers/error.serializer'); const reqSerializer = require('./serializers/express-request.serializer'); const customSerializer = require('./serializers/custom.serializer'); /** @const {Array.<string>} ALLOWED_LEVELS All levels, sequence MATTERS */ const ALLOWED_LEVELS = [ 'emergency', // Syslog level 0 'alert', // Syslog level 1 'critical', // Syslog level 2 'error', // Syslog level 3 'warn', // Syslog level 4 'notice', // Syslog level 5 'info', // Syslog level 6 'debug', // Syslog level 7 ]; /** @type {string} Get the Current Working Directory of the app */ const CWD = process.cwd(); /** * The Logality Class * */ class Logality { /** * Initialize the logging service * * @param {Object} opts Set of options to configure Logality: * @param {string=} opts.appName The application name to log. * @param {function=} opts.output Overwrite the final output operation. * @param {boolean=} opts.async Use Asynchronous API returning a promise * on writes. * @param {boolean|Object=} opts.prettyPrint Enable and configure pretty print * to stdout, default false. * @param {number|string=} opts.minLevel Define the minimum log level. * @return {Logality} Logality instance. */ constructor(opts = {}) { // Force instantiation Iif (!(this instanceof Logality)) { return new Logality(opts); } /** @type {string} Store the current logality version */ this.version = version; /** @type {boolean} indicates if the current instance is piped to a parent */ this._isPiped = false; /** @type {?Logality} stores the parent logality instance when piped */ this._parentLogality = null; const outputHandler = opts.output || fn.returnArg; let minLevel = 7; if (typeof opts.minLevel === 'number') { minLevel = opts.minLevel; } else if (typeof opts.minLevel === 'string') { const level = ALLOWED_LEVELS.indexOf(opts.minLevel.toLowerCase()); Eif (level !== -1) { minLevel = level; } } /** @type {Object} Logality configuration */ this._opts = { appName: opts.appName || 'Logality', prettyPrint: opts.prettyPrint || false, async: opts.async || false, minLevel, }; // Create Middleware functionality middlewarify.make(this, '_middleware', outputHandler, { async: this._opts.async, }); this.use = this._middleware.use; /** @type {Object} Logality serializers */ this._serializers = { user: userSerializer, error: errorSerializer, req: reqSerializer, custom: customSerializer, }; if (opts.serializers) { this._serializers = assign(this._serializers, opts.serializers); } /** @type {string} Cache the hostname */ this._hostname = os.hostname(); } /** * Get a logger and hard-assign the filepath location of the invoking * module to populate with the rest of the log data. * * Do not reuse loggers returned from this function in multiple modules. * * @return {Logality.log} The log method partialed with the filePath of the * invoking module. */ get() { const filePath = this._getFilePath(); // Do a partial application on log and return it. const partialedLog = this.log.bind(this, filePath); // Attach log levels as methods ALLOWED_LEVELS.forEach((level) => { partialedLog[level] = this.log.bind(this, filePath, level); }); return partialedLog; } /** * The main logging method. * * @param {string} filePath Path of module that the log originated from. * @param {string} level The level of the log. * @param {string} message Human readable log message. * @param {Object|null} context Extra data to log. * @return {Promise|void} Returns promise when async opt is enabled. */ log(filePath, level, message, context) { const levelSeverity = ALLOWED_LEVELS.indexOf(level); Iif (levelSeverity === -1) { throw new Error('Invalid log level'); } // Check for level filtering if (levelSeverity > this._opts.minLevel) { return; } const logContext = fn.getContext( level, levelSeverity, message, filePath, this._opts.appName, ); fn.assignSystem(logContext, this._hostname); this._applySerializers(logContext, context); return this.invokeOutput(logContext); } /** * Invokes any defined middleware and the output methods, custom or built-in * depending on configuration. * * @param {Object|*} logContext The log context or any value a piped child * might output, usually either string or undefined. * @param {Object=} pipedCall Set to true if this method is invoked from * piped child. * @return {Promise|void} Returns promise when async opt is enabled. */ invokeOutput(logContext, pipedCall = false) { // run Middleware, they can mutate the logContext. const result = this._middleware(logContext, pipedCall); if (this._opts.async) { return this._handleAsync(result); } this._handleOutput(result); } /** * Pipes output of other logality instances to this one. * * @param {Logality|Array<Logality>} logality Single or multiple logality * instances. */ pipe(logality) { let logalities = []; if (Array.isArray(logality)) { logalities = logality; } else { logalities.push(logality); } logalities.forEach((logalityInstance) => { Iif (!logalityInstance.version) { throw new Error('Argument passed not a Logality instance'); } logalityInstance.youArePiped(this); }); } /** * Internal method that's invoked by the parent logality instance when * pipe() method is used, instructs the current logality instance to pipe * the LogContext to the parent. * * @param {Logality} parentLogality The parent logality. */ youArePiped(parentLogality) { Iif (!parentLogality.version) { throw new Error( 'Argument passed for youArePiped() not a Logality instance', ); } Iif (this._isPiped) { throw new Error('This instance is already piped to another parent'); } this._isPiped = true; this._parentLogality = parentLogality; } /** * Handles asynchronous output of the log context. * * @param {Promise<Object|string|void>} prom Promise outcome of custom output * or logContext. * @return {Promise} A Promise. * @private */ async _handleAsync(prom) { this._handleOutput(await prom); } /** * Final output handler method, determines if the log message needs * further processing or output to standard out. * * @param {Object|string|void} result Outcome of custom output or logContext. */ _handleOutput(result) { if (this._isPiped && result) { this._parentLogality.invokeOutput(result, true); return; } let logMessage; switch (typeof result) { case 'string': fn.output(result); break; case 'object': logMessage = this._getLogMessage(result); fn.output(logMessage); break; default: // no action break; } } /** * Apply serializers on context contents. * * @param {Object} logContext The log context to write. * @param {Object|null} context Extra data to log. */ _applySerializers(logContext, context) { if (!context) { return; } const contextKeys = Object.keys(context); contextKeys.forEach(function (key) { Eif (this._serializers[key]) { const res = this._serializers[key](context[key]); if (Array.isArray(res)) { res.forEach(function (serial) { utils.assignPath(serial.path, logContext, serial.value); }); } else { utils.assignPath(res.path, logContext, res.value); } } }, this); } /** * Determines the kind of output to be send downstream to the writable stream. * * * @param {Object} logContext The log context to serialize to string. * @return {string} Log Message to be output. * @private */ _getLogMessage(logContext) { let stringOutput = ''; if (this._opts.prettyPrint) { stringOutput = prettyPrint.writePretty( logContext, this._opts.prettyPrint, ); } else { stringOutput = fn.masterSerialize(logContext); } return stringOutput; } /** * Get the relative filepath to the invoking module. * * @return {string} Relative filepath of callee. * @private */ _getFilePath() { try { throw new Error(); } catch (ex) { const stackLines = ex.stack.split('\n'); // Select the invoking module from the stack trace lines. // This is highly arbitrary based on how / when this function // got invoked, however once set for a codebase it will remain constant. const invokerRaw = stackLines[3]; const startSplit = invokerRaw.split('('); const finalSplit = startSplit[1].split(':'); const invokerPath = finalSplit[0]; // invokerPath now stores the full path, we need to extract the // relative path of the app which is the "root folder" of the app. const filePath = invokerPath.substr(CWD.length); return filePath; } } } module.exports = Logality; |