zeddemore-logger 1.2.2 → 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';
@@ -58,6 +59,7 @@ class ZeddemoreLogger {
58
59
  /** @type {Logger} */
59
60
  #logger = null;
60
61
  #logLevel = wll.INFO;
62
+ #morganFormat = null;
61
63
 
62
64
  constructor(options) {
63
65
  options = options || { };
@@ -65,21 +67,22 @@ class ZeddemoreLogger {
65
67
  this.apiRouteCategories = options.apiRouteCategories || [ ];
66
68
  this.apiRoutes = options.apiRoutes || [ ];
67
69
  this.apiVersion = options.apiVersion || null;
70
+ this.decodeUrls = _.isBoolean(options.decodeUrls) ? options.decodeUrls : true;
68
71
  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;
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;
75
78
  this.customCallerSettings = options.customCallerSettings || [ ];
76
79
  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;
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;
80
83
  this.routeModel = options.routeModel || null;
81
- this.suppressNonMutatingRequestApiCallLogging = options.hasOwnProperty('suppressNonMutatingRequestApiCallLogging') ? options.suppressNonMutatingRequestApiCallLogging : false;
82
- 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;
83
86
  this.timestampFormat = options.timestampFormat || 'YYYY-MM-DD HH:mm:ss';
84
87
  this.requestIdAttribute = options.requestIdAttribute || DEFAULT_REQUEST_ID_ATTRIBUTE;
85
88
  this.userGetFn = options.userGetFn || this.#userGetFn;
@@ -89,6 +92,15 @@ class ZeddemoreLogger {
89
92
  return !this.disableApiCallLogging && !!this.apiCallModel;
90
93
  }
91
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
+
92
104
  get forceLogLevel() {
93
105
  return this.#forceLogLevel;
94
106
  }
@@ -118,6 +130,35 @@ class ZeddemoreLogger {
118
130
  this.#logLevel = massageLogLevel(level);
119
131
  }
120
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
+
121
162
  #buildWinstonFormat = (options, additionalFormats = [ ]) => {
122
163
  options = options || { };
123
164
  const formats = [
@@ -242,22 +283,28 @@ class ZeddemoreLogger {
242
283
  }
243
284
 
244
285
  #initializeMorgan() {
245
- function buildContentLengthFormatToken(res) {
246
- const contentLength = convertBytes(res.get('content-length'), { digits: 2 });
286
+ function buildContentLengthFormatToken(res, digits = 2) {
287
+ const contentLength = convertBytes(res.get('content-length'), { digits });
247
288
  return contentLength ? ` - ${ contentLength }` : '';
248
289
  }
249
- 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) {
250
295
  const responseTime = getRequestResponseTime(req, res);
251
- return responseTime ? convertMilliseconds(responseTime, { digits: 0 }) : '-';
296
+ return responseTime ? convertMilliseconds(responseTime, { digits }) : '-';
252
297
  }
253
298
  function buildStatusColoredToken(res) {
254
299
  const statusCode = areHeadersSent(res) ? res.statusCode : null;
255
300
  if (!statusCode) return '-';
256
301
  return chalkHttpStatuses(statusCode);
257
302
  }
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); });
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));
261
308
  }
262
309
 
263
310
  log = (logLevel) => {
@@ -268,7 +315,7 @@ class ZeddemoreLogger {
268
315
 
269
316
  #logServerInfo = (options, logLevel = wll.INFO) => {
270
317
  options = options || { };
271
- const showLogLevel = options.hasOwnProperty('showLogLevel') ? options.showLogLevel : true;
318
+ const showLogLevel = _.isBoolean(options.showLogLevel) ? options.showLogLevel : true;
272
319
  const logLevelLabel = chalkLogLevel(this.#logLevel, this.logLevelName.toUpperCase());
273
320
  const msgParts = [ `Server is ${ chalk.bold(chalk.yellowBright('⚡live⚡')) }` ];
274
321
  if (showLogLevel) msgParts.push(`with log level ${ logLevelLabel }${ this.#forceLogLevel ? ` (${ chalk.italic('forced') })` : '' }`);
@@ -302,7 +349,7 @@ class ZeddemoreLogger {
302
349
 
303
350
  #logServerRoutesAdded = (options, logLevel = wll.INFO) => {
304
351
  options = options || { };
305
- const showCategories = options.hasOwnProperty('showCategories') ? options.showCategories : true;
352
+ const showCategories = _.isBoolean(options.showCategories) ? options.showCategories : true;
306
353
  const routes = options.routes || this.apiRoutes || [ ];
307
354
  const routesLabel = (routes.length === 1) ? 'route' : 'routes';
308
355
  const addedLabel = chalk.green('added');
@@ -316,10 +363,10 @@ class ZeddemoreLogger {
316
363
 
317
364
  logServerStartup = (options, logLevel = wll.INFO) => {
318
365
  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;
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;
323
370
  if (showListening) this.#logServerListening(options, logLevel);
324
371
  if (showPackages && options.packages) this.#logServerPackagesInfo(options, options.packages, logLevel);
325
372
  if (showRoutes) this.#logServerRoutesAdded(options, logLevel);
@@ -340,8 +387,8 @@ class ZeddemoreLogger {
340
387
 
341
388
  morganMiddleware = (options) => {
342
389
  options = options || { };
343
- const disableMorganLogging = options.hasOwnProperty('disableMorganLogging') ? options.disableMorganLogging : this.disableMorganLogging;
344
- 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);
345
392
  const userGetFn = (_.isFunction(options.userGetFn) ? options.userGetFn : null) || this.userGetFn;
346
393
  return (req, res, next) => {
347
394
  this.#initializeMorgan();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zeddemore-logger",
3
- "version": "1.2.2",
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",