zeddemore-logger 1.2.1 → 1.2.3

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,10 +16,11 @@ 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_MORGAN_FORMAT_TOKENS = [ mft.METHOD, mft.DECODED_URL, mft.STATUS_COLORED, mft.RESPONSE_TIME_FORMAT, mft.CONTENT_LENGTH_FORMAT ];
23
24
  const DEFAULT_REQUEST_ID_ATTRIBUTE = 'requestId';
24
25
  const DEFAULT_USER_LOG_LEVEL = -1;
25
26
  const WINSTON_CONSOLE_TRANSPORT_NAME = 'console';
@@ -35,6 +36,13 @@ function massageLogLevel(logLevel) {
35
36
  return winstonLogLevelIdToName(logLevel);
36
37
  }
37
38
 
39
+ /**
40
+ * Monkey patch console to allow unlimited depth with `console.dirp`
41
+ */
42
+ function monkeyPatchConsole() {
43
+ console.dirp = (arguments) => console.dir(arguments, { depth: null });
44
+ }
45
+
38
46
  /**
39
47
  * Convert a log level ID to a Winston log level name
40
48
  * @param levelId {number}
@@ -51,6 +59,7 @@ class ZeddemoreLogger {
51
59
  /** @type {Logger} */
52
60
  #logger = null;
53
61
  #logLevel = wll.INFO;
62
+ #morganFormat = null;
54
63
 
55
64
  constructor(options) {
56
65
  options = options || { };
@@ -58,21 +67,22 @@ class ZeddemoreLogger {
58
67
  this.apiRouteCategories = options.apiRouteCategories || [ ];
59
68
  this.apiRoutes = options.apiRoutes || [ ];
60
69
  this.apiVersion = options.apiVersion || null;
70
+ this.decodeUrls = _.isBoolean(options.decodeUrls) ? options.decodeUrls : true;
61
71
  this.defaultLabel = options.defaultLabel || 'zeddemore-logger';
62
- this.disableApiCallLogging = options.hasOwnProperty('disableApiCallLogging') ? options.disableApiCallLogging : false;
63
- this.disableRouteLogging = options.hasOwnProperty('disableRouteLogging') ? options.disableRouteLogging : false;
64
- this.disableMorganLogging = options.hasOwnProperty('disableMorganLogging') ? options.disableMorganLogging : false;
65
- this.displayCaller = options.hasOwnProperty('displayCaller') ? options.displayCaller : true;
66
- this.displayIpAddress = options.hasOwnProperty('displayIpAddress') ? options.displayIpAddress : true;
67
- this.displayRequestId = options.hasOwnProperty('displayRequestId') ? options.displayRequestId : true;
72
+ this.disableApiCallLogging = _.isBoolean(options.disableApiCallLogging) ? options.disableApiCallLogging : false;
73
+ this.disableRouteLogging = _.isBoolean(options.disableRouteLogging) ? options.disableRouteLogging : false;
74
+ this.disableMorganLogging = _.isBoolean(options.disableMorganLogging) ? options.disableMorganLogging : false;
75
+ this.displayCaller = _.isBoolean(options.displayCaller) ? options.displayCaller : true;
76
+ this.displayIpAddress = _.isBoolean(options.displayIpAddress) ? options.displayIpAddress : true;
77
+ this.displayRequestId = _.isBoolean(options.displayRequestId) ? options.displayRequestId : true;
68
78
  this.customCallerSettings = options.customCallerSettings || [ ];
69
79
  this.defaultUserLogLevel = options.defaultUserLogLevel || DEFAULT_USER_LOG_LEVEL;
70
- this.#forceLogLevel = options.hasOwnProperty('forceLogLevel') ? options.forceLogLevel : false;
71
- this.#logLevel = options.hasOwnProperty('logLevel') ? options.logLevel : wll.INFO;
72
- this.morganFormat = options.morganFormat || mf.DEV_ENHANCED;
80
+ this.#forceLogLevel = _.isBoolean(options.forceLogLevel) ? options.forceLogLevel : false;
81
+ this.#logLevel = _.isInteger(options.logLevel) ? options.logLevel : wll.INFO;
82
+ this.morganFormat = _.isString(options.morganFormat) ? options.morganFormat : null;
73
83
  this.routeModel = options.routeModel || null;
74
- this.suppressNonMutatingRequestApiCallLogging = options.hasOwnProperty('suppressNonMutatingRequestApiCallLogging') ? options.suppressNonMutatingRequestApiCallLogging : false;
75
- this.suppressSuccessfulRequestApiCallLogging = options.hasOwnProperty('suppressSuccessfulRequestApiCallLogging') ? options.suppressSuccessfulRequestApiCallLogging : false;
84
+ this.suppressNonMutatingRequestApiCallLogging = _.isBoolean(options.suppressNonMutatingRequestApiCallLogging) ? options.suppressNonMutatingRequestApiCallLogging : false;
85
+ this.suppressSuccessfulRequestApiCallLogging = _.isBoolean(options.suppressSuccessfulRequestApiCallLogging) ? options.suppressSuccessfulRequestApiCallLogging : false;
76
86
  this.timestampFormat = options.timestampFormat || 'YYYY-MM-DD HH:mm:ss';
77
87
  this.requestIdAttribute = options.requestIdAttribute || DEFAULT_REQUEST_ID_ATTRIBUTE;
78
88
  this.userGetFn = options.userGetFn || this.#userGetFn;
@@ -82,6 +92,15 @@ class ZeddemoreLogger {
82
92
  return !this.disableApiCallLogging && !!this.apiCallModel;
83
93
  }
84
94
 
95
+ get morganFormat() {
96
+ if (!!this.#morganFormat) return this.#morganFormat;
97
+ return mf.ZEDDEMORE;
98
+ }
99
+
100
+ set morganFormat(format) {
101
+ if (_.isString(format) && !!format.length) this.#morganFormat = format;
102
+ }
103
+
85
104
  get forceLogLevel() {
86
105
  return this.#forceLogLevel;
87
106
  }
@@ -111,6 +130,35 @@ class ZeddemoreLogger {
111
130
  this.#logLevel = massageLogLevel(level);
112
131
  }
113
132
 
133
+ #buildMorganFormat = (morganTokens = [ ]) => {
134
+ function addArgument(token, argument) {
135
+ if (hasArgument(token)) return token;
136
+ const openBracketIndex = token.indexOf('[');
137
+ const _token = (openBracketIndex > -1) ? token.substring(0, openBracketIndex) : token;
138
+ return `${ _token }[${ argument }]`;
139
+ }
140
+ function hasArgument(token) {
141
+ const openBracketIndex = token.indexOf('[');
142
+ const closeBracketIndex = token.indexOf(']');
143
+ return ((openBracketIndex > -1) && (closeBracketIndex > -1) && (closeBracketIndex > (openBracketIndex + 1)));
144
+ }
145
+ const DEFAULT_CONTENT_LENGTH_FORMAT_DIGITS = 0;
146
+ const DEFAULT_RESPONSE_TIME_DIGITS = 2;
147
+ if (!morganTokens || !_.isArray(morganTokens) || !morganTokens.length) return mf.DEV;
148
+ const _morganTokens = _.map(morganTokens, (token) => {
149
+ if (!_.isString(token) || !token.length) return null;
150
+ let _token = token.trim();
151
+ if (_token[ 0 ] !== ':') _token = `:${ _token }`;
152
+ switch (_token) {
153
+ case mft.CONTENT_LENGTH_FORMAT: return hasArgument(_token) ? _token : addArgument(_token, DEFAULT_CONTENT_LENGTH_FORMAT_DIGITS);
154
+ case mft.RESPONSE_TIME:
155
+ case mft.RESPONSE_TIME_FORMAT: return hasArgument(_token) ? _token : addArgument(_token, DEFAULT_RESPONSE_TIME_DIGITS);
156
+ default: return _token;
157
+ }
158
+ });
159
+ return (_.compact(_morganTokens)).join(' ');
160
+ }
161
+
114
162
  #buildWinstonFormat = (options, additionalFormats = [ ]) => {
115
163
  options = options || { };
116
164
  const formats = [
@@ -213,6 +261,10 @@ class ZeddemoreLogger {
213
261
  return this.createLogger(loggerOptions);
214
262
  }
215
263
 
264
+ enableConsoleDirp() {
265
+ monkeyPatchConsole();
266
+ }
267
+
216
268
  #generateUuid() {
217
269
  return uuid(null, null, null);
218
270
  }
@@ -231,22 +283,28 @@ class ZeddemoreLogger {
231
283
  }
232
284
 
233
285
  #initializeMorgan() {
234
- function buildContentLengthFormatToken(res) {
235
- const contentLength = convertBytes(res.get('content-length'), { digits: 2 });
286
+ function buildContentLengthFormatToken(res, digits = 2) {
287
+ const contentLength = convertBytes(res.get('content-length'), { digits });
236
288
  return contentLength ? ` - ${ contentLength }` : '';
237
289
  }
238
- function buildResponseTimeFormatToken(req, res) {
290
+ function buildDecodedUrl(req) {
291
+ const url = req.originalUrl || req.url;
292
+ return decodeURI(url);
293
+ }
294
+ function buildResponseTimeFormatToken(req, res, digits = 0) {
239
295
  const responseTime = getRequestResponseTime(req, res);
240
- return responseTime ? convertMilliseconds(responseTime, { digits: 0 }) : '-';
296
+ return responseTime ? convertMilliseconds(responseTime, { digits }) : '-';
241
297
  }
242
298
  function buildStatusColoredToken(res) {
243
299
  const statusCode = areHeadersSent(res) ? res.statusCode : null;
244
300
  if (!statusCode) return '-';
245
301
  return chalkHttpStatuses(statusCode);
246
302
  }
247
- morgan.token('content-length-format', (req, res) => { return buildContentLengthFormatToken(res); });
248
- morgan.token('response-time-format', (req, res) => { return buildResponseTimeFormatToken(req, res); });
249
- morgan.token('status-colored', (req, res) => { return buildStatusColoredToken(res); });
303
+ morgan.token(mft.CONTENT_LENGTH_FORMAT, (req, res, digits) => { return buildContentLengthFormatToken(res, digits); });
304
+ morgan.token(mft.DECODED_URL, (req, res) => { return buildDecodedUrl(req); });
305
+ morgan.token(mft.RESPONSE_TIME_FORMAT, (req, res, digits) => { return buildResponseTimeFormatToken(req, res, digits); });
306
+ morgan.token(mft.STATUS_COLORED, (req, res) => { return buildStatusColoredToken(res); });
307
+ morgan.format(mf.ZEDDEMORE, this.#buildMorganFormat(DEFAULT_MORGAN_FORMAT_TOKENS));
250
308
  }
251
309
 
252
310
  log = (logLevel) => {
@@ -257,7 +315,7 @@ class ZeddemoreLogger {
257
315
 
258
316
  #logServerInfo = (options, logLevel = wll.INFO) => {
259
317
  options = options || { };
260
- const showLogLevel = options.hasOwnProperty('showLogLevel') ? options.showLogLevel : true;
318
+ const showLogLevel = _.isBoolean(options.showLogLevel) ? options.showLogLevel : true;
261
319
  const logLevelLabel = chalkLogLevel(this.#logLevel, this.logLevelName.toUpperCase());
262
320
  const msgParts = [ `Server is ${ chalk.bold(chalk.yellowBright('⚡live⚡')) }` ];
263
321
  if (showLogLevel) msgParts.push(`with log level ${ logLevelLabel }${ this.#forceLogLevel ? ` (${ chalk.italic('forced') })` : '' }`);
@@ -291,7 +349,7 @@ class ZeddemoreLogger {
291
349
 
292
350
  #logServerRoutesAdded = (options, logLevel = wll.INFO) => {
293
351
  options = options || { };
294
- const showCategories = options.hasOwnProperty('showCategories') ? options.showCategories : true;
352
+ const showCategories = _.isBoolean(options.showCategories) ? options.showCategories : true;
295
353
  const routes = options.routes || this.apiRoutes || [ ];
296
354
  const routesLabel = (routes.length === 1) ? 'route' : 'routes';
297
355
  const addedLabel = chalk.green('added');
@@ -305,10 +363,10 @@ class ZeddemoreLogger {
305
363
 
306
364
  logServerStartup = (options, logLevel = wll.INFO) => {
307
365
  options = options || { };
308
- const showListening = options.hasOwnProperty('showListening') ? options.showListening : true;
309
- const showPackages = options.hasOwnProperty('showPackages') ? options.showPackages : true;
310
- const showRoutes = options.hasOwnProperty('showRoutes') ? options.showRoutes : true;
311
- const showInfo = options.hasOwnProperty('showInfo') ? options.showInfo : true;
366
+ const showListening = _.isBoolean(options.showListening) ? options.showListening : true;
367
+ const showPackages = _.isBoolean(options.showPackages) ? options.showPackages : true;
368
+ const showRoutes = _.isBoolean(options.showRoutes) ? options.showRoutes : true;
369
+ const showInfo = _.isBoolean(options.showInfo) ? options.showInfo : true;
312
370
  if (showListening) this.#logServerListening(options, logLevel);
313
371
  if (showPackages && options.packages) this.#logServerPackagesInfo(options, options.packages, logLevel);
314
372
  if (showRoutes) this.#logServerRoutesAdded(options, logLevel);
@@ -329,8 +387,8 @@ class ZeddemoreLogger {
329
387
 
330
388
  morganMiddleware = (options) => {
331
389
  options = options || { };
332
- const disableMorganLogging = options.hasOwnProperty('disableMorganLogging') ? options.disableMorganLogging : this.disableMorganLogging;
333
- const morganFormat = options.morganFormat || this.morganFormat;
390
+ const disableMorganLogging = _.isBoolean(options.disableMorganLogging) ? options.disableMorganLogging : this.disableMorganLogging;
391
+ const morganFormat = options.morganFormat || this.#buildMorganFormat(DEFAULT_MORGAN_FORMAT_TOKENS);
334
392
  const userGetFn = (_.isFunction(options.userGetFn) ? options.userGetFn : null) || this.userGetFn;
335
393
  return (req, res, next) => {
336
394
  this.#initializeMorgan();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zeddemore-logger",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "Node.js Express logger using Morgan and Winston for thread and caller info tracking",
5
5
  "repository": {
6
6
  "type": "git",