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 +4 -2
- package/lib/enums.js +22 -1
- package/lib/math.js +3 -3
- package/lib/zeddemore-logger.js +81 -32
- package/package.json +1 -1
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
|
|
6
|
-
const spaced = options.
|
|
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
|
|
9
|
-
const decimals = options.decimals
|
|
10
|
-
const spaced = options.
|
|
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' ]
|
package/lib/zeddemore-logger.js
CHANGED
|
@@ -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,
|
|
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.
|
|
70
|
-
this.disableRouteLogging = options.
|
|
71
|
-
this.disableMorganLogging = options.
|
|
72
|
-
this.displayCaller = options.
|
|
73
|
-
this.displayIpAddress = options.
|
|
74
|
-
this.displayRequestId = options.
|
|
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.
|
|
78
|
-
this.#logLevel = options.
|
|
79
|
-
this.morganFormat = options.morganFormat
|
|
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.
|
|
82
|
-
this.suppressSuccessfulRequestApiCallLogging = options.
|
|
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'), {
|
|
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
|
|
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
|
|
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(
|
|
259
|
-
morgan.token(
|
|
260
|
-
morgan.token(
|
|
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.
|
|
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.
|
|
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.
|
|
320
|
-
const showPackages = options.
|
|
321
|
-
const showRoutes = options.
|
|
322
|
-
const showInfo = options.
|
|
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.
|
|
344
|
-
const morganFormat = options.morganFormat || this
|
|
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);
|