@emartech/json-logger 7.2.3 → 8.0.0

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/README.md CHANGED
@@ -9,9 +9,21 @@ It has the same namespace based enabling/disabling mechanism as [debug].
9
9
  npm install @emartech/json-logger
10
10
  ```
11
11
 
12
+ ### Usage
12
13
 
14
+ Since 8.0.0, by default ECS fields will be used when logging.
13
15
 
14
- ### Usage
16
+ If for reason you still need the old format, you need to override the `outputFormat` config.
17
+ `configure` will apply this setting globally, for all instances of the logger.
18
+
19
+ ```javascript
20
+ const { createLogger } = require('@emartech/json-logger');
21
+
22
+ createLogger.configure({
23
+ outputFormat: 'legacy'
24
+ });
25
+
26
+ ```
15
27
 
16
28
  #### Script
17
29
 
@@ -22,13 +34,15 @@ const mongoLogger = createLogger('mongo');
22
34
  const redisLogger = createLogger('redis');
23
35
 
24
36
  redisLogger.info('connected', { domain: 'yahoo' });
25
- // {"name":"redis","action":"connected","level":30,"time":"2016-08-15T08:50:23.566Z","domain":"yahoo"}
37
+ // ECS format: {"event":{"action":"connected"},"log":{"logger":"redis","level":30},"@timestamp":"2016-08-15T08:50:23.566Z","domain":"yahoo"}
38
+ // Legacy format: {"name":"redis","action":"connected","level":30,"time":"2016-08-15T08:50:23.566Z","domain":"yahoo"}
26
39
 
27
40
  mongoLogger.info('connected', { domain: 'google' });
28
41
  // no output, because 'mongo' is not within namespaces (process.env.DEBUG)
29
42
 
30
43
  redisLogger.fromError('query', new Error('Unauthorized'), { problem: 'missmatch' });
31
- // {"name":"redis","action":"query","level":50,"time":"2016-08-15T08:50:23.569Z","error_name":"Error","error_stack":"Error: Unauthorized\n at Object.<anonymous> (/home/blacksonic/workspace/bunyan-debug/example.js:15:32)\n at Module._compile (module.js:541:32)\n at Object.Module._extensions..js (module.js:550:10)\n at Module.load (module.js:458:32)\n at tryModuleLoad (module.js:417:12)\n at Function.Module._load (module.js:409:3)\n at Module.runMain (module.js:575:10)\n at run (bootstrap_node.js:352:7)\n at startup (bootstrap_node.js:144:9)\n at bootstrap_node.js:467:3","error_message":"Unauthorized","problem":"missmatch"}
44
+ // ECS format: {"event":{"action":"query"},"log":{"logger":"redis","level":50},"@timestamp":"2016-08-15T08:50:23.569Z","error":{"type":"Error","message":"Unauthorized","stack_trace":"..."},"problem":"mismatch"}
45
+ // Legacy format: {"name":"redis","action":"query","level":50,"time":"2016-08-15T08:50:23.569Z","error_name":"Error","error_stack":"Error: Unauthorized\n at Object.<anonymous> (/home/blacksonic/workspace/bunyan-debug/example.js:15:32)\n at Module._compile (module.js:541:32)\n at Object.Module._extensions..js (module.js:550:10)\n at Module.load (module.js:458:32)\n at tryModuleLoad (module.js:417:12)\n at Function.Module._load (module.js:409:3)\n at Module.runMain (module.js:575:10)\n at run (bootstrap_node.js:352:7)\n at startup (bootstrap_node.js:144:9)\n at bootstrap_node.js:467:3","error_message":"Unauthorized","problem":"missmatch"}
32
46
  ```
33
47
 
34
48
  #### Class
@@ -103,10 +117,12 @@ const { createLogger } = require('@emartech/json-logger');
103
117
  const redisLogger = createLogger('redis');
104
118
 
105
119
  redisLogger.info('connected', { domain: 'yahoo' });
106
- // {"name":"redis","action":"connected","level":30,"time":"2016-08-15T08:50:23.566Z","domain":"yahoo"}
120
+ // ECS format: {"event":{"action":"connected"},"log":{"logger":"redis","level":30},"@timestamp":"2016-08-15T08:50:23.566Z","domain":"yahoo"}
121
+ // Legacy format: {"name":"redis","action":"connected","level":30,"time":"2016-08-15T08:50:23.566Z","domain":"yahoo"}
107
122
 
108
123
  redisLogger.info('connected');
109
- // {"name":"redis","action":"connected","level":30,"time":"2016-08-15T08:50:23.566Z"}
124
+ // ECS format: {"event":{"action":"connected"},"log":{"logger":"redis","level":30},"@timestamp":"2016-08-15T08:50:23.566Z"}
125
+ // Legacy format: {"name":"redis","action":"connected","level":30,"time":"2016-08-15T08:50:23.566Z"}
110
126
  ```
111
127
 
112
128
  By default displays the namespace of the instance (`name`), the current time in ISO8601 format (`time`),
@@ -144,7 +160,8 @@ const { createLogger } = require('@emartech/json-logger');
144
160
  const redisLogger = createLogger('redis');
145
161
 
146
162
  redisLogger.fromError('query', new Error('Unauthorized'), { problem: 'missmatch' });
147
- // {"name":"redis","action":"query","level":50,"time":"2016-08-15T08:50:23.569Z","error_name":"Error","error_stack":"Error: Unauthorized\n at Object.<anonymous> (/home/blacksonic/workspace/bunyan-debug/example.js:15:32)\n at Module._compile (module.js:541:32)\n at Object.Module._extensions..js (module.js:550:10)\n at Module.load (module.js:458:32)\n at tryModuleLoad (module.js:417:12)\n at Function.Module._load (module.js:409:3)\n at Module.runMain (module.js:575:10)\n at run (bootstrap_node.js:352:7)\n at startup (bootstrap_node.js:144:9)\n at bootstrap_node.js:467:3","error_message":"Unauthorized","problem":"missmatch"}
163
+ // ECS format: {"event":{"action":"query"},"log":{"logger":"redis","level":50},"@timestamp":"2016-08-15T08:50:23.569Z","error":{"type":"Error","message":"Unauthorized","stack_trace":"..."},"problem":"mismatch"}
164
+ // Legacy format: {"name":"redis","action":"query","level":50,"time":"2016-08-15T08:50:23.569Z","error_name":"Error","error_stack":"Error: Unauthorized\n at Object.<anonymous> (/home/blacksonic/workspace/bunyan-debug/example.js:15:32)\n at Module._compile (module.js:541:32)\n at Object.Module._extensions..js (module.js:550:10)\n at Module.load (module.js:458:32)\n at tryModuleLoad (module.js:417:12)\n at Function.Module._load (module.js:409:3)\n at Module.runMain (module.js:575:10)\n at run (bootstrap_node.js:352:7)\n at startup (bootstrap_node.js:144:9)\n at bootstrap_node.js:467:3","error_message":"Unauthorized","problem":"missmatch"}
148
165
  ```
149
166
 
150
167
  ##### JsonLogger.prototype.warnFromError(action, data)
@@ -166,7 +183,8 @@ const timer = redisLogger.timer();
166
183
  // heavy task
167
184
 
168
185
  timer.info('completed');
169
- // {"name":"redis","action":"completed","level":30,"time":"2016-08-15T08:50:23.566Z","duration": 1500}
186
+ // Legacy format: {"name":"redis","action":"completed","level":30,"time":"2016-08-15T08:50:23.566Z","duration": 1500}
187
+ // ECS format: {"event":{"action":"completed","duration":"1500"},"log":{"logger":"redis","level":30},"@timestamp":"2016-08-15T08:50:23.566Z"}
170
188
  ```
171
189
 
172
190
  ##### JsonLogger.configure(options)
@@ -182,7 +200,8 @@ const { createLogger } = require('@emartech/json-logger');
182
200
  createLogger.configure({
183
201
  formatter: JSON.stringify,
184
202
  output: console.log,
185
- transformers: []
203
+ transformers: [],
204
+ outputFormat: 'ecs'
186
205
  });
187
206
 
188
207
  ```
@@ -225,7 +244,7 @@ app.use(clsAdapter.getKoaMiddleware());
225
244
 
226
245
  app.use(async () => {
227
246
  logger.info('connected');
228
- // {"name":"redis","action":"connected","level":30,"time":"2016-08-15T08:50:23.566Z","request_id":"d5caaa0e-b04e-4d94-bc88-3ed3b62dc94a"}
247
+ // Legacy format: {"name":"redis","action":"connected","level":30,"time":"2016-08-15T08:50:23.566Z","request_id":"d5caaa0e-b04e-4d94-bc88-3ed3b62dc94a"}
229
248
  })
230
249
  ```
231
250
 
@@ -3,6 +3,7 @@ export interface LoggerConfig {
3
3
  formatter: Function;
4
4
  output: Function;
5
5
  transformers: Function[];
6
+ outputFormat: String;
6
7
  }
7
8
  export declare class Logger {
8
9
  private readonly namespace;
@@ -23,8 +24,10 @@ export declare class Logger {
23
24
  warnFromError(action: string, error: unknown, data?: unknown): void;
24
25
  timer(): Timer;
25
26
  private log;
27
+ private getBaseLogFields;
26
28
  private shortenStackTrace;
27
29
  private shortenData;
28
30
  private getErrorDetails;
31
+ private getBaseErrorDetails;
29
32
  private getAxiosErrorDetails;
30
33
  }
@@ -7,7 +7,8 @@ const DATA_LIMIT = 3000;
7
7
  const timer_1 = require("../timer/timer");
8
8
  const json_1 = require("../formatter/json");
9
9
  const console_1 = require("../output/console");
10
- const allowedKeys = ['output', 'formatter', 'transformers'];
10
+ const lodash_1 = require("lodash");
11
+ const allowedKeys = ['output', 'formatter', 'transformers', 'outputFormat'];
11
12
  class Logger {
12
13
  constructor(namespace, enabled) {
13
14
  this.namespace = namespace;
@@ -20,7 +21,7 @@ class Logger {
20
21
  static validate(options) {
21
22
  Object.keys(options).forEach((key) => {
22
23
  if (!allowedKeys.includes(key)) {
23
- throw new Error('Only the following keys are allowed: formatter, output');
24
+ throw new Error('Only the following keys are allowed: ' + allowedKeys);
24
25
  }
25
26
  });
26
27
  }
@@ -46,7 +47,7 @@ class Logger {
46
47
  this.log('fatal', action, data);
47
48
  }
48
49
  customError(severity, action, error, data = {}) {
49
- this.log(severity, action, Object.assign(this.getErrorDetails(error), data));
50
+ this.log(severity, action, (0, lodash_1.merge)(this.getErrorDetails(error), data));
50
51
  }
51
52
  fromError(action, error, data = {}) {
52
53
  this.customError('error', action, error, data);
@@ -61,17 +62,32 @@ class Logger {
61
62
  if (!this.enabled) {
62
63
  return;
63
64
  }
64
- let dataToLog = Object.assign({
65
- name: this.namespace,
66
- action: action,
67
- level: config_1.config.levels[level].number,
68
- time: new Date().toISOString(),
69
- }, data);
65
+ let dataToLog = (0, lodash_1.merge)(this.getBaseLogFields(level, action), data);
70
66
  Logger.config.transformers.forEach((transform) => {
71
67
  dataToLog = transform(dataToLog);
72
68
  });
73
69
  Logger.config.output(Logger.config.formatter(dataToLog));
74
70
  }
71
+ getBaseLogFields(level, action) {
72
+ if (Logger.config.outputFormat === 'legacy') {
73
+ return {
74
+ name: this.namespace,
75
+ action: action,
76
+ level: config_1.config.levels[level].number,
77
+ time: new Date().toISOString(),
78
+ };
79
+ }
80
+ return {
81
+ '@timestamp': new Date().toISOString(),
82
+ event: {
83
+ action: action,
84
+ },
85
+ log: {
86
+ logger: this.namespace,
87
+ level: config_1.config.levels[level].number,
88
+ },
89
+ };
90
+ }
75
91
  shortenStackTrace(stack) {
76
92
  if (!stack) {
77
93
  return;
@@ -89,24 +105,54 @@ class Logger {
89
105
  if (!(error instanceof Object)) {
90
106
  return {};
91
107
  }
92
- const baseDetails = {
93
- error_name: error.name,
94
- error_stack: this.shortenStackTrace(error.stack || ''),
95
- error_message: error.message,
96
- error_data: this.shortenData(error.data),
108
+ return (0, lodash_1.merge)(this.getBaseErrorDetails(error), this.getAxiosErrorDetails(error));
109
+ }
110
+ getBaseErrorDetails(error) {
111
+ if (Logger.config.outputFormat === 'legacy') {
112
+ return {
113
+ error_name: error.name,
114
+ error_stack: this.shortenStackTrace(error.stack || ''),
115
+ error_message: error.message,
116
+ error_data: this.shortenData(error.data),
117
+ };
118
+ }
119
+ return {
120
+ error: {
121
+ type: error.name,
122
+ message: error.message,
123
+ context: this.shortenData(error.data),
124
+ stack_trace: this.shortenStackTrace(error.stack || ''),
125
+ },
97
126
  };
98
- return Object.assign(baseDetails, this.getAxiosErrorDetails(error));
99
127
  }
100
128
  getAxiosErrorDetails(error) {
101
129
  if (!error.isAxiosError) {
102
130
  return {};
103
131
  }
132
+ if (Logger.config.outputFormat === 'legacy') {
133
+ return {
134
+ request_method: error.config.method,
135
+ request_url: error.config.url,
136
+ response_status: error.response ? error.response.status : undefined,
137
+ response_status_text: error.response ? error.response.statusText : undefined,
138
+ response_data: error.response ? this.shortenData(error.response.data) : undefined,
139
+ };
140
+ }
104
141
  return {
105
- request_method: error.config.method,
106
- request_url: error.config.url,
107
- response_status: error.response ? error.response.status : undefined,
108
- response_status_text: error.response ? error.response.statusText : undefined,
109
- response_data: error.response ? this.shortenData(error.response.data) : undefined,
142
+ url: {
143
+ full: error.config.url,
144
+ },
145
+ http: {
146
+ request: {
147
+ method: error.config.method,
148
+ },
149
+ response: {
150
+ status_code: error.response ? error.response.status : undefined,
151
+ body: {
152
+ content: error.response ? this.shortenData(error.response.data) : undefined,
153
+ },
154
+ },
155
+ },
110
156
  };
111
157
  }
112
158
  }
@@ -115,4 +161,5 @@ Logger.config = {
115
161
  formatter: json_1.jsonFormatter,
116
162
  output: console_1.consoleOutput,
117
163
  transformers: [],
164
+ outputFormat: 'ecs',
118
165
  };
@@ -11,5 +11,6 @@ export declare class Timer {
11
11
  fatal(action: string, data?: unknown): void;
12
12
  fromError(action: string, error: unknown, data?: unknown): void;
13
13
  warnFromError(action: string, error: unknown, data?: unknown): void;
14
+ private getData;
14
15
  private duration;
15
16
  }
@@ -1,34 +1,42 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Timer = void 0;
4
+ const logger_1 = require("../logger/logger");
5
+ const lodash_1 = require("lodash");
4
6
  class Timer {
5
7
  constructor(logger) {
6
8
  this.logger = logger;
7
9
  this.start = new Date().getTime();
8
10
  }
9
11
  trace(action, data = {}) {
10
- this.logger.trace(action, Object.assign({ duration: this.duration() }, data));
12
+ this.logger.trace(action, this.getData(data));
11
13
  }
12
14
  debug(action, data = {}) {
13
- this.logger.debug(action, Object.assign({ duration: this.duration() }, data));
15
+ this.logger.debug(action, this.getData(data));
14
16
  }
15
17
  info(action, data = {}) {
16
- this.logger.info(action, Object.assign({ duration: this.duration() }, data));
18
+ this.logger.info(action, this.getData(data));
17
19
  }
18
20
  warn(action, data = {}) {
19
- this.logger.warn(action, Object.assign({ duration: this.duration() }, data));
21
+ this.logger.warn(action, this.getData(data));
20
22
  }
21
23
  error(action, data = {}) {
22
- this.logger.error(action, Object.assign({ duration: this.duration() }, data));
24
+ this.logger.error(action, this.getData(data));
23
25
  }
24
26
  fatal(action, data = {}) {
25
- this.logger.fatal(action, Object.assign({ duration: this.duration() }, data));
27
+ this.logger.fatal(action, this.getData(data));
26
28
  }
27
29
  fromError(action, error, data = {}) {
28
- this.logger.fromError(action, error, Object.assign({ duration: this.duration() }, data));
30
+ this.logger.fromError(action, error, this.getData(data));
29
31
  }
30
32
  warnFromError(action, error, data = {}) {
31
- this.logger.warnFromError(action, error, Object.assign({ duration: this.duration() }, data));
33
+ this.logger.warnFromError(action, error, this.getData(data));
34
+ }
35
+ getData(data) {
36
+ if (logger_1.Logger.config.outputFormat === 'legacy') {
37
+ return Object.assign({ duration: this.duration() }, data);
38
+ }
39
+ return (0, lodash_1.merge)({ event: { duration: this.duration() } }, data);
32
40
  }
33
41
  duration() {
34
42
  const end = new Date().getTime();
package/package.json CHANGED
@@ -28,6 +28,7 @@
28
28
  ],
29
29
  "devDependencies": {
30
30
  "@types/chai": "4.3.3",
31
+ "@types/lodash": "4.14.198",
31
32
  "@types/mocha": "10.0.0",
32
33
  "@types/node": "18.7.23",
33
34
  "@types/sinon": "10.0.13",
@@ -60,5 +61,8 @@
60
61
  "url": "https://github.com/emartech/json-logger-js/issues"
61
62
  },
62
63
  "homepage": "https://github.com/emartech/json-logger-js#readme",
63
- "version": "7.2.3"
64
+ "dependencies": {
65
+ "lodash": "4.17.21"
66
+ },
67
+ "version": "8.0.0"
64
68
  }
package/repo-info.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "is_in_production": true,
3
+ "is_scannable": true,
4
+ "is_critical": false,
5
+ "contact": "guild-javascript@emarsys.com",
6
+ "hosted": null
7
+ }