zeddemore-logger 1.2.2 → 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/date.js CHANGED
@@ -1,9 +1,11 @@
1
1
  // Copyright (c) 2025 by Beardon Services, Inc.
2
2
 
3
+ const _ = require('lodash');
4
+
3
5
  function convertMilliseconds(milliseconds, options) {
4
6
  options = options || { };
5
- const digits = options.digits || 3;
6
- const spaced = options.hasOwnProperty('spaced') ? options.spaced : false;
7
+ const digits = _.isInteger(options.digits) ? options.digits : 3;
8
+ const spaced = _.isBoolean(options.spaced) ? options.spaced : false;
7
9
  let value = milliseconds || 0;
8
10
  let unit = 'ms';
9
11
  if (milliseconds) {
package/lib/enums.js CHANGED
@@ -119,9 +119,29 @@ const morganFormats = {
119
119
  COMBINED: 'combined',
120
120
  COMMON: 'common',
121
121
  DEV: 'dev',
122
- DEV_ENHANCED: ':method :url :status-colored :response-time-format :content-length-format',
123
122
  SHORT: 'short',
124
123
  TINY: 'tiny',
124
+ ZEDDEMORE: 'zeddemore', // added for `zeddemore-logger`
125
+ };
126
+
127
+ const morganFormatTokens = {
128
+ CONTENT_LENGTH_FORMAT: 'content-length-format', // added for `zeddemore-logger`
129
+ DATE: 'date', // :date[format]
130
+ DECODED_URL: 'decoded-url', // added for `zeddemore-logger`
131
+ HTTP_VERSION: 'http-version',
132
+ METHOD: 'method',
133
+ REFERRER: 'referrer',
134
+ REMOTE_ADDR: 'remote-addr',
135
+ REMOTE_USER: 'remote-user',
136
+ REQ: 'req', // :req[header]
137
+ RES: 'res', // :res[header]
138
+ RESPONSE_TIME: 'response-time', // :response-time[digits]
139
+ RESPONSE_TIME_FORMAT: 'response-time-format', // added for `zeddemore-logger`
140
+ STATUS: 'status',
141
+ STATUS_COLORED: 'status-colored', // added for `zeddemore-logger`
142
+ TOTAL_TIME: 'total-time', // :total-time[digits]
143
+ URL: 'url',
144
+ USER_AGENT: 'user-agent',
125
145
  };
126
146
 
127
147
  const winstonLogLevelNames = {
@@ -158,6 +178,7 @@ module.exports = {
158
178
  httpRequestHeaders,
159
179
  httpResponseHeaders,
160
180
  morganFormats,
181
+ morganFormatTokens,
161
182
  winstonLogLevelNames,
162
183
  winstonLogLevels,
163
184
  };
package/lib/math.js CHANGED
@@ -5,9 +5,9 @@ const _ = require('lodash');
5
5
  function convertBytes(bytes, options = { }) {
6
6
  options = options || { };
7
7
  if (!bytes) return null;
8
- const useBinaryUnits = options.useBinaryUnits || false;
9
- const decimals = options.decimals || 2;
10
- const spaced = options.hasOwnProperty('spaced') ? options.spaced : false;
8
+ const useBinaryUnits = _.isBoolean(options.useBinaryUnits) ? options.useBinaryUnits : false;
9
+ const decimals = _.isInteger(options.decimals) ? options.decimals : 2;
10
+ const spaced = _.isBoolean(options.spaced) ? options.spaced : false;
11
11
  const base = useBinaryUnits ? 1024 : 1000;
12
12
  const units = useBinaryUnits
13
13
  ? [ 'B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB' ]
@@ -16,11 +16,14 @@ const { extractToken, getRequestResponseTime, isRequestOfMethod, parseRequest }
16
16
  const { logLevelMap } = require('./mappings/log_mappings');
17
17
 
18
18
  const { colorize, combine, label, metadata, printf, timestamp } = format;
19
- const { morganFormats: mf, httpMethods: http, httpRequestHeaders: rqh, httpResponseHeaders: rsh, winstonLogLevelNames: wlln,
20
- winstonLogLevels: wll } = enums;
19
+ const { morganFormats: mf, morganFormatTokens: mft, httpMethods: http, httpRequestHeaders: rqh, httpResponseHeaders: rsh,
20
+ winstonLogLevelNames: wlln, winstonLogLevels: wll } = enums;
21
21
 
22
22
  const COLOR_CONSOLE_WHITE_HEX = '#ECECEC';
23
+ const DEFAULT_CONTENT_LENGTH_DIGITS = 2;
24
+ const DEFAULT_MORGAN_FORMAT_TOKENS = [ mft.METHOD, mft.DECODED_URL, mft.STATUS_COLORED, mft.RESPONSE_TIME_FORMAT, mft.CONTENT_LENGTH_FORMAT ];
23
25
  const DEFAULT_REQUEST_ID_ATTRIBUTE = 'requestId';
26
+ const DEFAULT_RESPONSE_TIME_DIGITS = 0;
24
27
  const DEFAULT_USER_LOG_LEVEL = -1;
25
28
  const WINSTON_CONSOLE_TRANSPORT_NAME = 'console';
26
29
 
@@ -58,6 +61,7 @@ class ZeddemoreLogger {
58
61
  /** @type {Logger} */
59
62
  #logger = null;
60
63
  #logLevel = wll.INFO;
64
+ #morganFormat = null;
61
65
 
62
66
  constructor(options) {
63
67
  options = options || { };
@@ -65,21 +69,24 @@ class ZeddemoreLogger {
65
69
  this.apiRouteCategories = options.apiRouteCategories || [ ];
66
70
  this.apiRoutes = options.apiRoutes || [ ];
67
71
  this.apiVersion = options.apiVersion || null;
72
+ this.contentLengthDigits = _.isInteger(options.contentLengthDigits) ? options.contentLengthDigits : DEFAULT_CONTENT_LENGTH_DIGITS;
73
+ this.decodeUrls = _.isBoolean(options.decodeUrls) ? options.decodeUrls : true;
68
74
  this.defaultLabel = options.defaultLabel || 'zeddemore-logger';
69
- this.disableApiCallLogging = options.hasOwnProperty('disableApiCallLogging') ? options.disableApiCallLogging : false;
70
- this.disableRouteLogging = options.hasOwnProperty('disableRouteLogging') ? options.disableRouteLogging : false;
71
- this.disableMorganLogging = options.hasOwnProperty('disableMorganLogging') ? options.disableMorganLogging : false;
72
- this.displayCaller = options.hasOwnProperty('displayCaller') ? options.displayCaller : true;
73
- this.displayIpAddress = options.hasOwnProperty('displayIpAddress') ? options.displayIpAddress : true;
74
- this.displayRequestId = options.hasOwnProperty('displayRequestId') ? options.displayRequestId : true;
75
+ this.disableApiCallLogging = _.isBoolean(options.disableApiCallLogging) ? options.disableApiCallLogging : false;
76
+ this.disableRouteLogging = _.isBoolean(options.disableRouteLogging) ? options.disableRouteLogging : false;
77
+ this.disableMorganLogging = _.isBoolean(options.disableMorganLogging) ? options.disableMorganLogging : false;
78
+ this.displayCaller = _.isBoolean(options.displayCaller) ? options.displayCaller : true;
79
+ this.displayIpAddress = _.isBoolean(options.displayIpAddress) ? options.displayIpAddress : true;
80
+ this.displayRequestId = _.isBoolean(options.displayRequestId) ? options.displayRequestId : true;
75
81
  this.customCallerSettings = options.customCallerSettings || [ ];
76
82
  this.defaultUserLogLevel = options.defaultUserLogLevel || DEFAULT_USER_LOG_LEVEL;
77
- this.#forceLogLevel = options.hasOwnProperty('forceLogLevel') ? options.forceLogLevel : false;
78
- this.#logLevel = options.hasOwnProperty('logLevel') ? options.logLevel : wll.INFO;
79
- this.morganFormat = options.morganFormat || mf.DEV_ENHANCED;
83
+ this.#forceLogLevel = _.isBoolean(options.forceLogLevel) ? options.forceLogLevel : false;
84
+ this.#logLevel = _.isInteger(options.logLevel) ? options.logLevel : wll.INFO;
85
+ this.morganFormat = _.isString(options.morganFormat) ? options.morganFormat : null;
86
+ this.responseTimeDigits = _.isInteger(options.responseTimeDigits) ? options.responseTimeDigits : DEFAULT_RESPONSE_TIME_DIGITS;
80
87
  this.routeModel = options.routeModel || null;
81
- this.suppressNonMutatingRequestApiCallLogging = options.hasOwnProperty('suppressNonMutatingRequestApiCallLogging') ? options.suppressNonMutatingRequestApiCallLogging : false;
82
- this.suppressSuccessfulRequestApiCallLogging = options.hasOwnProperty('suppressSuccessfulRequestApiCallLogging') ? options.suppressSuccessfulRequestApiCallLogging : false;
88
+ this.suppressNonMutatingRequestApiCallLogging = _.isBoolean(options.suppressNonMutatingRequestApiCallLogging) ? options.suppressNonMutatingRequestApiCallLogging : false;
89
+ this.suppressSuccessfulRequestApiCallLogging = _.isBoolean(options.suppressSuccessfulRequestApiCallLogging) ? options.suppressSuccessfulRequestApiCallLogging : false;
83
90
  this.timestampFormat = options.timestampFormat || 'YYYY-MM-DD HH:mm:ss';
84
91
  this.requestIdAttribute = options.requestIdAttribute || DEFAULT_REQUEST_ID_ATTRIBUTE;
85
92
  this.userGetFn = options.userGetFn || this.#userGetFn;
@@ -89,6 +96,15 @@ class ZeddemoreLogger {
89
96
  return !this.disableApiCallLogging && !!this.apiCallModel;
90
97
  }
91
98
 
99
+ get morganFormat() {
100
+ if (!!this.#morganFormat) return this.#morganFormat;
101
+ return mf.ZEDDEMORE;
102
+ }
103
+
104
+ set morganFormat(format) {
105
+ if (_.isString(format) && !!format.length) this.#morganFormat = format;
106
+ }
107
+
92
108
  get forceLogLevel() {
93
109
  return this.#forceLogLevel;
94
110
  }
@@ -118,6 +134,33 @@ class ZeddemoreLogger {
118
134
  this.#logLevel = massageLogLevel(level);
119
135
  }
120
136
 
137
+ #buildMorganFormat = (morganTokens = [ ]) => {
138
+ function addArgument(token, argument) {
139
+ if (hasArgument(token)) return token;
140
+ const openBracketIndex = token.indexOf('[');
141
+ const _token = (openBracketIndex > -1) ? token.substring(0, openBracketIndex) : token;
142
+ return `${ _token }[${ argument }]`;
143
+ }
144
+ function hasArgument(token) {
145
+ const openBracketIndex = token.indexOf('[');
146
+ const closeBracketIndex = token.indexOf(']');
147
+ return ((openBracketIndex > -1) && (closeBracketIndex > -1) && (closeBracketIndex > (openBracketIndex + 1)));
148
+ }
149
+ if (!morganTokens || !_.isArray(morganTokens) || !morganTokens.length) return mf.DEV;
150
+ const _morganTokens = _.map(morganTokens, (token) => {
151
+ if (!_.isString(token) || !token.length) return null;
152
+ let _token = token.trim();
153
+ if (_token[ 0 ] !== ':') _token = `:${ _token }`;
154
+ switch (_token) {
155
+ case mft.CONTENT_LENGTH_FORMAT: return hasArgument(_token) ? _token : addArgument(_token, this.contentLengthDigits);
156
+ case mft.RESPONSE_TIME:
157
+ case mft.RESPONSE_TIME_FORMAT: return hasArgument(_token) ? _token : addArgument(_token, this.responseTimeDigits);
158
+ default: return _token;
159
+ }
160
+ });
161
+ return (_.compact(_morganTokens)).join(' ');
162
+ }
163
+
121
164
  #buildWinstonFormat = (options, additionalFormats = [ ]) => {
122
165
  options = options || { };
123
166
  const formats = [
@@ -242,22 +285,28 @@ class ZeddemoreLogger {
242
285
  }
243
286
 
244
287
  #initializeMorgan() {
245
- function buildContentLengthFormatToken(res) {
246
- const contentLength = convertBytes(res.get('content-length'), { digits: 2 });
288
+ function buildContentLengthFormatToken(res, decimals = DEFAULT_CONTENT_LENGTH_DIGITS) {
289
+ const contentLength = convertBytes(res.get('content-length'), { decimals });
247
290
  return contentLength ? ` - ${ contentLength }` : '';
248
291
  }
249
- function buildResponseTimeFormatToken(req, res) {
292
+ function buildDecodedUrl(req) {
293
+ const url = req.originalUrl || req.url;
294
+ return decodeURI(url);
295
+ }
296
+ function buildResponseTimeFormatToken(req, res, digits = DEFAULT_RESPONSE_TIME_DIGITS) {
250
297
  const responseTime = getRequestResponseTime(req, res);
251
- return responseTime ? convertMilliseconds(responseTime, { digits: 0 }) : '-';
298
+ return responseTime ? convertMilliseconds(responseTime, { digits }) : '-';
252
299
  }
253
300
  function buildStatusColoredToken(res) {
254
301
  const statusCode = areHeadersSent(res) ? res.statusCode : null;
255
302
  if (!statusCode) return '-';
256
303
  return chalkHttpStatuses(statusCode);
257
304
  }
258
- morgan.token('content-length-format', (req, res) => { return buildContentLengthFormatToken(res); });
259
- morgan.token('response-time-format', (req, res) => { return buildResponseTimeFormatToken(req, res); });
260
- morgan.token('status-colored', (req, res) => { return buildStatusColoredToken(res); });
305
+ morgan.token(mft.CONTENT_LENGTH_FORMAT, (req, res, decimals) => { return buildContentLengthFormatToken(res, decimals); });
306
+ morgan.token(mft.DECODED_URL, (req, res) => { return buildDecodedUrl(req); });
307
+ morgan.token(mft.RESPONSE_TIME_FORMAT, (req, res, digits) => { return buildResponseTimeFormatToken(req, res, digits); });
308
+ morgan.token(mft.STATUS_COLORED, (req, res) => { return buildStatusColoredToken(res); });
309
+ morgan.format(mf.ZEDDEMORE, this.#buildMorganFormat(DEFAULT_MORGAN_FORMAT_TOKENS));
261
310
  }
262
311
 
263
312
  log = (logLevel) => {
@@ -268,7 +317,7 @@ class ZeddemoreLogger {
268
317
 
269
318
  #logServerInfo = (options, logLevel = wll.INFO) => {
270
319
  options = options || { };
271
- const showLogLevel = options.hasOwnProperty('showLogLevel') ? options.showLogLevel : true;
320
+ const showLogLevel = _.isBoolean(options.showLogLevel) ? options.showLogLevel : true;
272
321
  const logLevelLabel = chalkLogLevel(this.#logLevel, this.logLevelName.toUpperCase());
273
322
  const msgParts = [ `Server is ${ chalk.bold(chalk.yellowBright('⚡live⚡')) }` ];
274
323
  if (showLogLevel) msgParts.push(`with log level ${ logLevelLabel }${ this.#forceLogLevel ? ` (${ chalk.italic('forced') })` : '' }`);
@@ -302,7 +351,7 @@ class ZeddemoreLogger {
302
351
 
303
352
  #logServerRoutesAdded = (options, logLevel = wll.INFO) => {
304
353
  options = options || { };
305
- const showCategories = options.hasOwnProperty('showCategories') ? options.showCategories : true;
354
+ const showCategories = _.isBoolean(options.showCategories) ? options.showCategories : true;
306
355
  const routes = options.routes || this.apiRoutes || [ ];
307
356
  const routesLabel = (routes.length === 1) ? 'route' : 'routes';
308
357
  const addedLabel = chalk.green('added');
@@ -316,10 +365,10 @@ class ZeddemoreLogger {
316
365
 
317
366
  logServerStartup = (options, logLevel = wll.INFO) => {
318
367
  options = options || { };
319
- const showListening = options.hasOwnProperty('showListening') ? options.showListening : true;
320
- const showPackages = options.hasOwnProperty('showPackages') ? options.showPackages : true;
321
- const showRoutes = options.hasOwnProperty('showRoutes') ? options.showRoutes : true;
322
- const showInfo = options.hasOwnProperty('showInfo') ? options.showInfo : true;
368
+ const showListening = _.isBoolean(options.showListening) ? options.showListening : true;
369
+ const showPackages = _.isBoolean(options.showPackages) ? options.showPackages : true;
370
+ const showRoutes = _.isBoolean(options.showRoutes) ? options.showRoutes : true;
371
+ const showInfo = _.isBoolean(options.showInfo) ? options.showInfo : true;
323
372
  if (showListening) this.#logServerListening(options, logLevel);
324
373
  if (showPackages && options.packages) this.#logServerPackagesInfo(options, options.packages, logLevel);
325
374
  if (showRoutes) this.#logServerRoutesAdded(options, logLevel);
@@ -340,8 +389,8 @@ class ZeddemoreLogger {
340
389
 
341
390
  morganMiddleware = (options) => {
342
391
  options = options || { };
343
- const disableMorganLogging = options.hasOwnProperty('disableMorganLogging') ? options.disableMorganLogging : this.disableMorganLogging;
344
- const morganFormat = options.morganFormat || this.morganFormat;
392
+ const disableMorganLogging = _.isBoolean(options.disableMorganLogging) ? options.disableMorganLogging : this.disableMorganLogging;
393
+ const morganFormat = options.morganFormat || this.#buildMorganFormat(DEFAULT_MORGAN_FORMAT_TOKENS);
345
394
  const userGetFn = (_.isFunction(options.userGetFn) ? options.userGetFn : null) || this.userGetFn;
346
395
  return (req, res, next) => {
347
396
  this.#initializeMorgan();
@@ -490,10 +539,10 @@ class ZeddemoreLogger {
490
539
  const prefix = logOptions.prefix ? `[${ logOptions.prefix }]` : null;
491
540
  const status = chalkHttpStatuses(_response.status);
492
541
  const _duration = axiosError.duration || _response.duration;
493
- const duration = _duration ? convertMilliseconds(_duration) : '-';
542
+ const duration = _duration ? convertMilliseconds(_duration, logger.responseTimeDigits) : '-';
494
543
  let _contentLength = _headers ? _headers[ rsh.CONTENT_LENGTH ] : null;
495
544
  if (!_contentLength) _contentLength = _data ? sizeof(_data) : null;
496
- const contentLength = _contentLength ? `- ${ convertBytes(_contentLength) }` : null;
545
+ const contentLength = _contentLength ? `- ${ convertBytes(_contentLength, logger.contentLengthDigits) }` : null;
497
546
  const data = _data ? JSON.stringify(_data) : null;
498
547
  const parts = [ prefix, method, path, status, duration, contentLength, data ];
499
548
  const message = _.compact(parts).join(' ');
@@ -519,10 +568,10 @@ class ZeddemoreLogger {
519
568
  const responseData = (logOptions.response && _.isObject(_data)) ? JSON.stringify(_data) : null;
520
569
  const status = chalkHttpStatuses(axiosResponse.status);
521
570
  const _duration = axiosResponse.duration;
522
- const duration = _duration ? convertMilliseconds(_duration) : '-';
571
+ const duration = _duration ? convertMilliseconds(_duration, logger.responseTimeDigits) : '-';
523
572
  let _contentLength = _headers ? _headers[ rsh.CONTENT_LENGTH ] : null;
524
573
  if (!_contentLength) _contentLength = _data ? sizeof(_data) : null;
525
- const contentLength = _contentLength ? `- ${ convertBytes(_contentLength) }` : null;
574
+ const contentLength = _contentLength ? `- ${ convertBytes(_contentLength, logger.contentLengthDigits) }` : null;
526
575
  const parts = [ prefix, method, path, configData, status, responseData, duration, contentLength ];
527
576
  const message = _.compact(parts).join(' ');
528
577
  logger[ logLevel ](message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zeddemore-logger",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "description": "Node.js Express logger using Morgan and Winston for thread and caller info tracking",
5
5
  "repository": {
6
6
  "type": "git",