zeddemore-logger 1.0.5 → 1.0.7

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/color.js CHANGED
@@ -1,43 +1,119 @@
1
- // Copyright (c) 2025 by Beardon Services, Inc.
1
+ // Copyright (c) 2024-2025 by Beardon Services, Inc.
2
2
 
3
3
  const _ = require('lodash');
4
4
  const chalk = require('chalk');
5
5
 
6
6
  const colorMappings = require('./mappings/color_mappings');
7
+ const enums = require('./enums');
7
8
 
8
- function chalkDatabaseOperation(operation, string = null) {
9
- if (!string) string = operation;
10
- return chalkViaColorMap(operation, string, colorMappings.databaseOperationColors, true);
9
+ const { chalkLayers: cl, chalkFormats: cs } = enums;
10
+
11
+ /**
12
+ * @param ansiColor {number}
13
+ * @param layer {string}
14
+ * @param string {string}
15
+ * @returns {string}
16
+ */
17
+ function chalkAnsi(ansiColor, layer = cl.FOREGROUND, string) {
18
+ if (!string) return '';
19
+ if (!ansiColor) return string;
20
+ switch (layer) {
21
+ case cl.BACKGROUND: return `\x1B[${ ansiColor }m${ string }\x1B[49m`;
22
+ case cl.FOREGROUND: return `\x1B[${ ansiColor }m${ string }\x1B[39m`;
23
+ default: return string;
24
+ }
25
+ }
26
+
27
+ /**
28
+ * @param ansiColor {number}
29
+ * @param layer {string}
30
+ * @param string {string}
31
+ * @returns {string}
32
+ */
33
+ function chalkAnsi256(ansiColor, layer = cl.FOREGROUND, string) {
34
+ if (!string) return '';
35
+ if (!ansiColor) return string;
36
+ switch (layer) {
37
+ case cl.BACKGROUND: return chalk.bgAnsi256(ansiColor)(string);
38
+ case cl.FOREGROUND: return chalk.ansi256(ansiColor)(string);
39
+ default: return string;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * @param hexColor {string}
45
+ * @param layer {string}
46
+ * @param string {string}
47
+ * @returns {string}
48
+ */
49
+ function chalkHex(hexColor, layer = cl.FOREGROUND, string) {
50
+ if (!string) return '';
51
+ if (!hexColor) return string;
52
+ hexColor = (hexColor[ 0 ] === '#') ? hexColor : `#${ hexColor }`;
53
+ switch (layer) {
54
+ case cl.BACKGROUND: return chalk.bgHex(hexColor)(string);
55
+ case cl.FOREGROUND: return chalk.hex(hexColor)(string);
56
+ default: return string;
57
+ }
11
58
  }
12
59
 
13
- function chalkHttpStatus(statusCode) {
14
- const colorMap = _.find(colorMappings.httpStatusColors, (httpStatusColor) => httpStatusColor.range.includes(statusCode));
15
- if (!colorMap) return statusCode;
16
- let chalkedMatch = statusCode;
17
- if (colorMap.ansi) chalkedMatch = `\x1B[${ colorMap.ansi }m${ statusCode }\x1B[39m`;
60
+ function chalkHttpStatuses(statusCode, string = null) {
61
+ if (!string) string = statusCode;
62
+ const colorMap = _.find(colorMappings.httpStatusColors, (httpStatusColor) => httpStatusColor.range.includes(string));
63
+ if (!colorMap || !colorMap.style) return string;
64
+ let chalkedMatch = string;
65
+ if (colorMap.style.fore) chalkedMatch = `\x1B[${ colorMap.style.fore }m${ string }\x1B[39m`;
18
66
  return chalkedMatch;
19
67
  }
20
68
 
21
- function chalkHttpVerb(verb, string = null) {
69
+ function chalkHttpVerbs(verb, string = null, replaceGlobal = false) {
22
70
  if (!string) string = verb;
23
- return chalkViaColorMap(verb, string, colorMappings.httpVerbColors, false);
71
+ return chalkViaColorMap(verb, string, colorMappings.httpVerbColors, replaceGlobal);
72
+ }
73
+
74
+ /**
75
+ * @param redColor {number}
76
+ * @param greenColor {number}
77
+ * @param blueColor {number}
78
+ * @param layer {string}
79
+ * @param string {string}
80
+ * @returns {string}
81
+ */
82
+ function chalkRgb(redColor, greenColor, blueColor, layer = cl.FOREGROUND, string) {
83
+ if (!string) return '';
84
+ if (!redColor || !greenColor || !blueColor) return string;
85
+ switch (layer) {
86
+ case cl.BACKGROUND: return chalk.bgRgb(redColor, greenColor, blueColor)(string);
87
+ case cl.FOREGROUND: return chalk.rgb(redColor, greenColor, blueColor)(string);
88
+ default: return string;
89
+ }
90
+ }
91
+
92
+ function chalkTarget(target, style) {
93
+ if (!style) return target;
94
+ const fore = style.fore;
95
+ const back = style.back;
96
+ switch (style.format) {
97
+ case cs.ANSI: return chalkAnsi(back, cl.BACKGROUND, chalkAnsi(fore, cl.FOREGROUND, target));
98
+ case cs.ANSI256: return chalkAnsi256(back, cl.BACKGROUND, chalkAnsi256(fore, cl.FOREGROUND, target));
99
+ case cs.HEX: return chalkHex(back, cl.BACKGROUND, chalkHex(fore, cl.FOREGROUND, target));
100
+ case cs.RGB: return chalkRgb(back.r, back.g, back.b, cl.BACKGROUND, chalkRgb(fore.r, fore.g, fore.b, cl.FOREGROUND, target));
101
+ default: return target;
102
+ }
24
103
  }
25
104
 
26
- function chalkViaColorMap(match, string, colorMap, useGlobal = true) {
105
+ function chalkViaColorMap(match, target, colorsMap, replaceGlobal = true) {
27
106
  function fixPattern(pattern) {
28
- return pattern.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
107
+ return _.isString(pattern) ? pattern.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1') : pattern;
29
108
  }
30
- if (!match || !_.isString(match) || !string || !_.isString(string) || !_.isObject(colorMap)) return string;
109
+ if (!match || !_.isString(match) || !target || !_.isString(target) || !_.isObject(colorsMap)) return target;
31
110
  const cleanMatch = match.replace(/[^A-Z]/g, '');
32
- const mapping = _.find(colorMap, { match: cleanMatch });
33
- if (!mapping) return string;
34
- const flags = useGlobal ? 'g' : '';
111
+ const colorMap = _.find(colorsMap, { match: cleanMatch });
112
+ if (!colorMap || !colorMap.style) return target;
113
+ const flags = replaceGlobal ? 'g' : '';
35
114
  const re = new RegExp(fixPattern(match), flags);
36
- if (!string.match(re)) return string;
37
- let chalkedMatch = cleanMatch;
38
- if (mapping.fore) chalkedMatch = chalk.hex(mapping.fore)(chalkedMatch);
39
- if (mapping.back) chalkedMatch = chalk.bgHex(mapping.back)(chalkedMatch);
40
- return string.replace(re, chalkedMatch);
115
+ if (!target.match(re)) return target;
116
+ return target.replace(re, chalkTarget(cleanMatch, colorMap.style));
41
117
  }
42
118
 
43
119
  // adapted from https://stackoverflow.com/a/44134328
@@ -100,8 +176,7 @@ function stringToIdempotentHslValues(str, hslOptions) {
100
176
  }
101
177
 
102
178
  module.exports = {
103
- chalkDatabaseOperation,
104
- chalkHttpStatus,
105
- chalkHttpVerb,
179
+ chalkHttpStatuses,
180
+ chalkHttpVerbs,
106
181
  stringToIdempotentHexColor,
107
182
  };
package/lib/enums.js CHANGED
@@ -22,6 +22,18 @@ const apiLogLevels = {
22
22
  SILLY: 6,
23
23
  };
24
24
 
25
+ const chalkFormats = {
26
+ ANSI: 'ansi',
27
+ ANSI256: 'ansi256',
28
+ HEX: 'hex',
29
+ RGB: 'rgb',
30
+ };
31
+
32
+ const chalkLayers = {
33
+ BACKGROUND: 'bg',
34
+ FOREGROUND: 'fg',
35
+ };
36
+
25
37
  const databaseOperations = {
26
38
  DELETE: 'DELETE',
27
39
  INSERT: 'INSERT',
@@ -86,6 +98,8 @@ const winstonLogLevels = {
86
98
  module.exports = {
87
99
  apiLogLevelCaptions,
88
100
  apiLogLevels,
101
+ chalkFormats,
102
+ chalkLayers,
89
103
  databaseOperations,
90
104
  httpMethods,
91
105
  httpRequestHeaders,
@@ -1,40 +1,51 @@
1
- // Copyright (c) 2025 by Beardon Services, Inc.
1
+ // Copyright (c) 2024-2025 by Beardon Services, Inc.
2
2
 
3
3
  const _ = require('lodash');
4
4
  const enums = require('../enums');
5
5
 
6
- const { databaseOperations: dbo, httpMethods: http } = enums;
6
+ const { databaseOperations: dbo, chalkFormats: cs, httpMethods: http, winstonLogLevels: wll } = enums;
7
7
 
8
8
  const databaseOperationColors = [
9
- { match: dbo.DELETE, fore: '#F22613', back: null },
10
- { match: dbo.INSERT, fore: '#00B16A', back: null },
11
- { match: dbo.SELECT, fore: '#1E90FF', back: null },
12
- { match: dbo.TRUNCATE, fore: '#750505', back: null },
13
- { match: dbo.UPDATE, fore: '#F9690E', back: null },
9
+ { match: dbo.DELETE, style: { format: cs.HEX, fore: '#F22613', back: null } },
10
+ { match: dbo.INSERT, style: { format: cs.HEX, fore: '#00B16A', back: null } },
11
+ { match: dbo.SELECT, style: { format: cs.HEX, fore: '#1E90FF', back: null } },
12
+ { match: dbo.TRUNCATE, style: { format: cs.HEX, fore: '#750505', back: null } },
13
+ { match: dbo.UPDATE, style: { format: cs.HEX, fore: '#F9690E', back: null } },
14
14
  ];
15
15
 
16
16
  const httpStatusColors = [
17
- { range: _.range(500, 600), ansi: 31 },
18
- { range: _.range(400, 500), ansi: 33 },
19
- { range: _.range(300, 400), ansi: 36 },
20
- { range: _.range(200, 300), ansi: 32 },
21
- { range: _.range(100, 200), ansi: 0 },
17
+ { range: _.range(500, 600), style: { format: cs.ANSI, fore: 31, back: null } },
18
+ { range: _.range(400, 500), style: { format: cs.ANSI, fore: 33, back: null } },
19
+ { range: _.range(300, 400), style: { format: cs.ANSI, fore: 36, back: null } },
20
+ { range: _.range(200, 300), style: { format: cs.ANSI, fore: 32, back: null } },
21
+ { range: _.range(100, 200), style: { format: cs.ANSI, fore: 0, back: null } },
22
22
  ];
23
23
 
24
24
  const httpVerbColors = [
25
- { match: http.GET, fore: '#000000', back: '#67D193' },
26
- { match: http.HEAD, fore: '#000000', back: '#68D696' },
27
- { match: http.POST, fore: '#000000', back: '#F4DA7A' },
28
- { match: http.PUT, fore: '#000000', back: '#74AEF6' },
29
- { match: http.DELETE, fore: '#000000', back: '#EF968A' },
30
- { match: http.CONNECT, fore: '#000000', back: '#FFFFFF' },
31
- { match: http.OPTIONS, fore: '#000000', back: '#E55AA8' },
32
- { match: http.TRACE, fore: '#000000', back: '#FFFFFF' },
33
- { match: http.PATCH, fore: '#000000', back: '#C0A8E1' },
25
+ { match: http.GET, style: { format: cs.HEX, fore: '#000000', back: '#67D193' } },
26
+ { match: http.HEAD, style: { format: cs.HEX, fore: '#000000', back: '#68D696' } },
27
+ { match: http.POST, style: { format: cs.HEX, fore: '#000000', back: '#F4DA7A' } },
28
+ { match: http.PUT, style: { format: cs.HEX, fore: '#000000', back: '#74AEF6' } },
29
+ { match: http.DELETE, style: { format: cs.HEX, fore: '#000000', back: '#EF968A' } },
30
+ { match: http.CONNECT, style: { format: cs.HEX, fore: '#000000', back: '#FFFFFF' } },
31
+ { match: http.OPTIONS, style: { format: cs.HEX, fore: '#000000', back: '#E55AA8' } },
32
+ { match: http.TRACE, style: { format: cs.HEX, fore: '#000000', back: '#FFFFFF' } },
33
+ { match: http.PATCH, style: { format: cs.HEX, fore: '#000000', back: '#C0A8E1' } },
34
+ ];
35
+
36
+ const logLevelColors = [
37
+ { match: wll.ERROR, style: { format: cs.ANSI256, fore: 31, back: null } }, // red
38
+ { match: wll.WARNING, style: { format: cs.ANSI256, fore: 33, back: null } }, // yellow
39
+ { match: wll.INFO, style: { format: cs.ANSI256, fore: 32, back: null } }, // green
40
+ { match: wll.HTTP, style: { format: cs.ANSI256, fore: 32, back: null } },
41
+ { match: wll.VERBOSE, style: { format: cs.ANSI256, fore: 36, back: null } }, // cyan
42
+ { match: wll.DEBUG, style: { format: cs.ANSI256, fore: 34, back: null } }, // blue
43
+ { match: wll.SILLY, style: { format: cs.ANSI256, fore: 35, back: null } }, // magenta
34
44
  ];
35
45
 
36
46
  module.exports = {
37
47
  databaseOperationColors,
38
48
  httpStatusColors,
39
49
  httpVerbColors,
50
+ logLevelColors,
40
51
  };
@@ -8,14 +8,15 @@ const sizeof = require('object-sizeof');
8
8
  const { v4: uuid } = require('uuid');
9
9
 
10
10
  const { areHeadersSent, getRequestResponseTime, parseRequest } = require('./http');
11
- const { chalkHttpStatus, chalkHttpVerb, stringToIdempotentHexColor } = require('./color');
11
+ const { chalkHttpStatuses, chalkHttpVerbs, stringToIdempotentHexColor } = require('./color');
12
12
  const { convertBytes } = require('./math');
13
13
  const { convertMilliseconds } = require('./date');
14
14
  const enums = require('./enums');
15
15
  const { logLevelMap } = require('./mappings/log_mappings');
16
16
 
17
17
  const { colorize, combine, label, metadata, printf, timestamp } = format;
18
- const { morganFormats: mf, httpRequestHeaders: rqh, httpResponseHeaders: rsh, winstonLogLevelNames: wlln, winstonLogLevels: wll } = enums;
18
+ const { morganFormats: mf, httpRequestHeaders: rqh, httpResponseHeaders: rsh, winstonLogLevelNames: wlln,
19
+ winstonLogLevels: wll } = enums;
19
20
 
20
21
  const COLOR_CONSOLE_WHITE_HEX = '#ECECEC';
21
22
  const DEFAULT_REQUEST_ID_ATTRIBUTE = 'requestId';
@@ -35,6 +36,9 @@ function winstonLogLevelIdToName(levelId) {
35
36
 
36
37
  class ZeddemoreLogger {
37
38
 
39
+ #forceLogLevel = false;
40
+ #logLevel = wll.INFO;
41
+
38
42
  constructor(options) {
39
43
  options = options || { };
40
44
  this.additionalWriteFn = options.additionalWriteFn || null;
@@ -45,17 +49,36 @@ class ZeddemoreLogger {
45
49
  this.displayRequestId = options.hasOwnProperty('displayRequestId') ? options.displayRequestId : true;
46
50
  this.customCallerSettings = options.customCallerSettings || [ ];
47
51
  this.defaultUserLogLevel = options.defaultUserLogLevel || DEFAULT_USER_LOG_LEVEL;
48
- this.forceLogLevel = options.hasOwnProperty('forceLogLevel') ? options.forceLogLevel : false;
49
- this.logLevel = options.hasOwnProperty('logLevel') ? options.logLevel : wll.INFO;
52
+ this.#forceLogLevel = options.hasOwnProperty('forceLogLevel') ? options.forceLogLevel : false;
53
+ this.#logLevel = options.hasOwnProperty('logLevel') ? options.logLevel : wll.INFO;
50
54
  this.morganFormat = options.morganFormat || mf.DEV_ENHANCED;
51
55
  this.timestampFormat = options.timestampFormat || 'YYYY-MM-DD HH:mm:ss';
52
56
  this.requestIdAttribute = options.requestIdAttribute || DEFAULT_REQUEST_ID_ATTRIBUTE;
53
57
  this.userGetFn = options.userGetFn || this.#userGetFn;
54
58
  }
55
59
 
56
- #buildWinstonFormat = (options, additionalFormats = [ ]) =>{
60
+ get forceLogLevel() {
61
+ return this.#forceLogLevel;
62
+ }
63
+
64
+ get logLevel() {
65
+ return this.#logLevel;
66
+ }
67
+
68
+ get logLevelName() {
69
+ return winstonLogLevelIdToName(this.#logLevel);
70
+ }
71
+
72
+ set forceLogLevel(doForce) {
73
+ this.#forceLogLevel = !!doForce;
74
+ }
75
+
76
+ set logLevel(level) {
77
+ this.#logLevel = massageLogLevel(level);
78
+ }
79
+
80
+ #buildWinstonFormat = (options, additionalFormats = [ ]) => {
57
81
  options = options || { };
58
- const colorizedTemplate = this.#colorizedTemplate.bind(this);
59
82
  const formats = [
60
83
  format((info) => {
61
84
  info.id = info.id || options.id || null;
@@ -72,13 +95,13 @@ class ZeddemoreLogger {
72
95
  return combine(...additionalFormats.concat(formats));
73
96
  }
74
97
 
75
- #buildWinstonTransport = (logLevel = this.logLevel) => {
76
- logLevel = logLevel || this.logLevel;
98
+ #buildWinstonTransport = (logLevel = this.#logLevel) => {
99
+ logLevel = logLevel || this.#logLevel;
77
100
  const level = winstonLogLevelIdToName(logLevel);
78
101
  return new transports.Console({ level, name: WINSTON_CONSOLE_TRANSPORT_NAME });
79
102
  }
80
103
 
81
- #buildWinstonTransports = (logLevel = this.logLevel, additionalTransports = [ ]) => {
104
+ #buildWinstonTransports = (logLevel = this.#logLevel, additionalTransports = [ ]) => {
82
105
  return additionalTransports.concat([ this.#buildWinstonTransport(logLevel) ]);
83
106
  }
84
107
 
@@ -91,7 +114,7 @@ class ZeddemoreLogger {
91
114
 
92
115
  #colorizeResponse(message) {
93
116
  const verb = message.split(' ')[ 0 ];
94
- return chalkHttpVerb(verb, message);
117
+ return chalkHttpVerbs(verb, message);
95
118
  }
96
119
 
97
120
  #colorizedTemplate = (info) => {
@@ -177,7 +200,7 @@ class ZeddemoreLogger {
177
200
  function buildStatusColoredToken(res) {
178
201
  const statusCode = areHeadersSent(res) ? res.statusCode : null;
179
202
  if (!statusCode) return '-';
180
- return chalkHttpStatus(statusCode);
203
+ return chalkHttpStatuses(statusCode);
181
204
  }
182
205
  morgan.token('content-length-format', (req, res) => { return buildContentLengthFormatToken(res); });
183
206
  morgan.token('response-time-format', (req, res) => { return buildResponseTimeFormatToken(req, res); });
@@ -217,22 +240,25 @@ class ZeddemoreLogger {
217
240
 
218
241
  requestLoggerMiddleware = (req, res, next) => {
219
242
  const requestValues = parseRequest(req);
220
- const requestId = uuid();//this.#generateUuid();
243
+ const requestId = this.#generateUuid();
221
244
  req[ this.requestIdAttribute ] = requestId;
222
245
  res.header(rqh.REQUEST_ID, requestId);
223
246
  const ipAddress = requestValues.ipAddress;
247
+ res.locals = res.locals || { };
224
248
  res.locals.user = res.locals.user || { };
225
249
  res.locals.user.logger = this.createRequestLogger(requestId, ipAddress);
226
250
  return next();
227
251
  }
228
252
 
229
253
  #userGetFn(req, res) {
230
- return (res.locals ? res.locals.user : null) || (req.locals ? req.locals.user : null) || req.user || null;
254
+ return ((res && res.locals) ? res.locals.user : null)
255
+ || ((req && req.locals) ? req.locals.user : null)
256
+ || (req ? req.user : null);
231
257
  }
232
258
 
233
259
  userLoggerMiddleware = (req, res, next) => {
234
260
  const requestId = this.#getRequestId(req);
235
- const user = res.locals.user || { };
261
+ const user = this.userGetFn(req, res) || { };
236
262
  if (!requestId) return next();
237
263
  const username = user.login || null;
238
264
  let ipAddress = user.ipAddress || null;
@@ -241,8 +267,9 @@ class ZeddemoreLogger {
241
267
  ipAddress = requestValues.ipAddress;
242
268
  }
243
269
  let userLogLevel = !_.isNil(user.logLevel) ? user.logLevel : this.defaultUserLogLevel;
244
- if (userLogLevel < 0) userLogLevel = this.logLevel;
245
- const level = this.forceLogLevel ? this.logLevel : userLogLevel;
270
+ if (userLogLevel < 0) userLogLevel = this.#logLevel;
271
+ const level = this.#forceLogLevel ? this.#logLevel : userLogLevel;
272
+ res.locals = res.locals || { };
246
273
  res.locals.user = res.locals.user || user;
247
274
  res.locals.user.logger = this.createUserLogger(requestId, username, level, ipAddress);
248
275
  return next();
@@ -258,13 +285,13 @@ class ZeddemoreLogger {
258
285
  const logger = options.logger || ZeddemoreLogger.log(options);
259
286
  if (!logger) return;
260
287
  const _method = _request.method || null;
261
- const method = _method ? chalkHttpVerb(_method) : null;
288
+ const method = _method ? chalkHttpVerbs(_method) : null;
262
289
  const _config = _response.config || null;
263
290
  const _headers = _response.headers || null;
264
291
  const _data = _response.data || null;
265
292
  const path = _config ? _config.url : '';
266
293
  const prefix = logOptions.prefix ? `[${ logOptions.prefix }]` : null;
267
- const status = chalkHttpStatus(_response.status);
294
+ const status = chalkHttpStatuses(_response.status);
268
295
  const _duration = axiosError.duration || _response.duration;
269
296
  const duration = _duration ? convertMilliseconds(_duration) : '-';
270
297
  let _contentLength = _headers ? _headers[ rsh.CONTENT_LENGTH ] : null;
@@ -287,13 +314,13 @@ class ZeddemoreLogger {
287
314
  const logger = options.logger || ZeddemoreLogger.log(options);
288
315
  if (!logger) return;
289
316
  const _method = _request.method || null;
290
- const method = _method ? chalkHttpVerb(_method) : null;
317
+ const method = _method ? chalkHttpVerbs(_method) : null;
291
318
  const _headers = axiosResponse.headers || null;
292
319
  const _data = axiosResponse.data || null;
293
320
  const path = _request.path || '';
294
321
  const prefix = logOptions.prefix ? `[${ logOptions.prefix }]` : null;
295
322
  const responseData = (logOptions.response && _.isObject(_data)) ? JSON.stringify(_data) : null;
296
- const status = chalkHttpStatus(axiosResponse.status);
323
+ const status = chalkHttpStatuses(axiosResponse.status);
297
324
  const _duration = axiosResponse.duration;
298
325
  const duration = _duration ? convertMilliseconds(_duration) : '-';
299
326
  let _contentLength = _headers ? _headers[ rsh.CONTENT_LENGTH ] : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zeddemore-logger",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Node.js Express logger using Morgan and Winston for thread and caller info tracking",
5
5
  "repository": {
6
6
  "type": "git",