axe 9.0.0 → 10.0.1

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.
Files changed (5) hide show
  1. package/README.md +724 -179
  2. package/dist/axe.js +1153 -7063
  3. package/dist/axe.min.js +1 -1
  4. package/lib/index.js +261 -166
  5. package/package.json +21 -16
package/README.md CHANGED
@@ -5,165 +5,490 @@
5
5
  [![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
6
6
  [![made with lass](https://img.shields.io/badge/made_with-lass-95CC28.svg)](https://github.com/lassjs/lass)
7
7
  [![license](https://img.shields.io/github/license/cabinjs/axe.svg)](LICENSE)
8
+ [![npm downloads](https://img.shields.io/npm/dt/axe.svg)](https://npm.im/axe)
8
9
 
9
- > Logging add-on to send logs over HTTP to your server in Node and Browser environments. Works with any logger! Chop up your logs consistently! Made for [Cabin][].
10
+ > Axe is a logger-agnostic wrapper that normalizes logs regardless of argument style. Great for large development teams, old and new projects, and works with Pino, Bunyan, Winston, console, and more. It is lightweight, performant, highly-configurable, and automatically adds OS, CPU, and Git information to your logs. It supports hooks (useful for masking sensitive data) and dot-notation remapping, omitting, and picking of log metadata properties. Made for [Forward Email][forward-email], [Lad][], and [Cabin][].
10
11
 
11
12
 
12
13
  ## Table of Contents
13
14
 
15
+ * [Foreword](#foreword)
16
+ * [Application Metadata and Information](#application-metadata-and-information)
14
17
  * [Install](#install)
15
18
  * [Node](#node)
16
19
  * [Browser](#browser)
17
- * [Approach](#approach)
18
- * [Application Information](#application-information)
19
20
  * [Usage](#usage)
21
+ * [Options](#options)
22
+ * [Supported Platforms](#supported-platforms)
20
23
  * [Node](#node-1)
21
24
  * [Browser](#browser-1)
22
25
  * [Custom logger](#custom-logger)
23
- * [Custom endpoint](#custom-endpoint)
24
- * [Suppress logs](#suppress-logs)
26
+ * [Silent Logging](#silent-logging)
25
27
  * [Stack Traces and Error Handling](#stack-traces-and-error-handling)
26
- * [Options](#options)
27
- * [DEPRECATED](#deprecated)
28
- * [Aliases](#aliases)
29
- * [Methods](#methods)
30
- * [Send Logs To Slack](#send-logs-to-slack)
28
+ * [Hooks](#hooks)
29
+ * [Remapping](#remapping)
30
+ * [Omitting](#omitting)
31
+ * [Picking](#picking)
32
+ * [Aliases](#aliases)
33
+ * [Methods](#methods)
34
+ * [Examples](#examples)
35
+ * [Send Logs to HTTP Endpoint](#send-logs-to-http-endpoint)
36
+ * [Send Logs to Slack](#send-logs-to-slack)
37
+ * [Suppress Logger Data](#suppress-logger-data)
31
38
  * [Contributors](#contributors)
32
- * [Trademark Notice](#trademark-notice)
33
39
  * [License](#license)
34
40
 
35
41
 
36
- ## Install
42
+ ## Foreword
37
43
 
38
- ### Node
44
+ Axe was built to provide consistency among development teams when it comes to logging. You not only have to worry about your development team using the same approach to writing logs and debugging applications, but you also have to consider that open-source maintainers implement logging differently in their packages.
39
45
 
40
- [npm][]:
46
+ There is no industry standard as to logging style, and developers mix and match arguments without consistency. For example, one developer may use the approach of `console.log('someVariable', someVariable)` and another developer will simply write `console.log(someVariable)`. Even if both developers wrote in the style of `console.log('someVariable', someVariable)`, there still could be an underlying third-party package that logs differently, or uses an entirely different approach. Furthermore, by default there is no consistency of logs with stdout or using any third-party hosted logging dashboard solution. It will also be almost impossible to spot logging outliers as it would be too time intensive.
41
47
 
42
- ```sh
43
- npm install axe
44
- ```
48
+ No matter how your team or underlying packages style arguments when invoked with logger methods, Axe will clean it up and normalize it for you. This is especially helpful as you can see outliers much more easily in your logging dashboards, and pinpoint where in your application you need to do a better job of logging at. Axe makes your logs consistent and organized.
45
49
 
46
- ### Browser
50
+ Axe is highly configurable and has built-in functionality to remap, omit, and pick metadata fields with dot-notation support. Instead of using [slow functions](https://medium.com/nerd-for-tech/replacing-lodash-omit-using-object-restructuring-and-the-spread-syntax-d7af1607a390) like `lodash`'s `omit`, we use a more performant approach.
47
51
 
48
- See [Browser](#browser-1) usage below for more information.
49
-
50
-
51
- ## Approach
52
-
53
- We adhere to the [Log4j][log4j] standard. This means that you can use any [custom logger](#custom-logger) (or the default `console`), but we strictly support the following log levels:
52
+ Axe adheres to the [Log4j][log4j] log levels, which have been established for 21+ years (since 2001). This means that you can use any [custom logger](#custom-logger) (or the default `console`), but we strictly support the following log levels:
54
53
 
55
54
  * `trace`
56
55
  * `debug`
57
56
  * `info`
58
57
  * `warn`
59
58
  * `error`
60
- * `fatal` (uses `error`)
59
+ * `fatal`
60
+
61
+ Axe normalizes invocation of logger methods to be called with *only* two arguments: a String or Error as the first argument and an Object as the second argument. These two arguments are referred to as "message" and "meta" respectively. For example, if you're simply logging a message and some other information:
62
+
63
+ ```js
64
+ logger.info('Hello world', { beep: 'boop', foo: true });
65
+ // Hello world { beep: 'boop', foo: true }
66
+ ```
67
+
68
+ Or if you're logging a user, or a variable in general:
69
+
70
+ ```js
71
+ logger.info('user', { user: { id: '1' } });
72
+ // user { user: { id: '1' } }
73
+ ```
74
+
75
+ ```js
76
+ logger.info('someVariable', { someVariable: true });
77
+ // someVariable { someVariable: true }
78
+ ```
79
+
80
+ You might write logs with three arguments `(level, message, meta)` using the `log` method of Axe's returned `logger` instance:
81
+
82
+ ```js
83
+ logger.log('info', 'Hello world', { beep: 'boop', foo: true });
84
+ // Hello world { beep: 'boop', foo: true }
85
+ ```
61
86
 
62
- We highly recommend that you follow this approach when logging `(message, meta)`:
87
+ Logging errors is just the same as you might do now:
63
88
 
64
89
  ```js
65
- const message = 'Hello world';
66
- const meta = { beep: 'boop', foo: true };
67
- axe.info(message, meta);
90
+ logger.error(new Error('Oops!'));
91
+
92
+ // Error: Oops!
93
+ // at REPL3:1:14
94
+ // at Script.runInThisContext (node:vm:129:12)
95
+ // at REPLServer.defaultEval (node:repl:566:29)
96
+ // at bound (node:domain:421:15)
97
+ // at REPLServer.runBound [as eval] (node:domain:432:12)
98
+ // at REPLServer.onLine (node:repl:893:10)
99
+ // at REPLServer.emit (node:events:539:35)
100
+ // at REPLServer.emit (node:domain:475:12)
101
+ // at REPLServer.Interface._onLine (node:readline:487:10)
102
+ // at REPLServer.Interface._line (node:readline:864:8)
68
103
  ```
69
104
 
70
- You can also make logs with three arguments `(level, message, meta)`:
105
+ You might log errors like this:
71
106
 
72
107
  ```js
73
- const level = 'info';
74
- const message = 'Hello world';
75
- const meta = { beep: 'boop', foo: true };
76
- axe.log(level, message, meta);
108
+ logger.error(new Error('Oops!'), new Error('Another Error!'));
109
+
110
+ // Error: Oops!
111
+ // at Object.<anonymous> (/Users/user/Projects/axe/test.js:5:14)
112
+ // at Module._compile (node:internal/modules/cjs/loader:1105:14)
113
+ // at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
114
+ // at Module.load (node:internal/modules/cjs/loader:981:32)
115
+ // at Function.Module._load (node:internal/modules/cjs/loader:822:12)
116
+ // at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
117
+ // at node:internal/main/run_main_module:17:47
118
+ //
119
+ // Error: Another Error!
120
+ // at Object.<anonymous> (/Users/user/Projects/axe/test.js:5:34)
121
+ // at Module._compile (node:internal/modules/cjs/loader:1105:14)
122
+ // at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
123
+ // at Module.load (node:internal/modules/cjs/loader:981:32)
124
+ // at Function.Module._load (node:internal/modules/cjs/loader:822:12)
125
+ // at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
126
+ // at node:internal/main/run_main_module:17:47
77
127
  ```
78
128
 
79
- You should also log errors like this:
129
+ Or even multiple errors:
80
130
 
81
131
  ```js
82
- const err = new Error('Oops!');
83
- axe.error(err);
132
+ logger.error(new Error('Oops!'), new Error('Another Error!'), new Error('Woah!'));
133
+
134
+ // Error: Oops!
135
+ // at Object.<anonymous> (/Users/user/Projects/axe/test.js:6:3)
136
+ // at Module._compile (node:internal/modules/cjs/loader:1105:14)
137
+ // at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
138
+ // at Module.load (node:internal/modules/cjs/loader:981:32)
139
+ // at Function.Module._load (node:internal/modules/cjs/loader:822:12)
140
+ // at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
141
+ // at node:internal/main/run_main_module:17:47
142
+ //
143
+ // Error: Another Error!
144
+ // at Object.<anonymous> (/Users/user/Projects/axe/test.js:7:3)
145
+ // at Module._compile (node:internal/modules/cjs/loader:1105:14)
146
+ // at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
147
+ // at Module.load (node:internal/modules/cjs/loader:981:32)
148
+ // at Function.Module._load (node:internal/modules/cjs/loader:822:12)
149
+ // at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
150
+ // at node:internal/main/run_main_module:17:47
151
+ //
152
+ // Error: Woah!
153
+ // at Object.<anonymous> (/Users/user/Projects/axe/test.js:8:3)
154
+ // at Module._compile (node:internal/modules/cjs/loader:1105:14)
155
+ // at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
156
+ // at Module.load (node:internal/modules/cjs/loader:981:32)
157
+ // at Function.Module._load (node:internal/modules/cjs/loader:822:12)
158
+ // at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
159
+ // at node:internal/main/run_main_module:17:47
84
160
  ```
85
161
 
86
- **To recap:** The first argument `message` should be a String, and the second `meta` should be an optional Object.
162
+ As you can see, Axe combines multiple errors into one for an easy to read stack trace.
87
163
 
88
- If you simply use `axe.log`, then the log level used will be `info`, but it will still use the logger's native `log` method (as opposed to using `info`).
164
+ If you simply use `logger.log`, then the log level used will be `info`, but it will still use the logger's native `log` method (as opposed to using `info`). If you invoke `logger.log` (or any other logging method, e.g. `logger.info`, `logger.warn`, or `logger.error`), then it will consistently invoke the internal logger with these two arguments.
89
165
 
90
- If you invoke `axe.log` (or any other logging method, e.g. `info`), then it will return a consistent value no matter the edge case.
166
+ ```js
167
+ logger.log('hello world');
168
+ // hello world
169
+ ```
91
170
 
92
- For example, if you log `axe.log('hello world')`, it will output with `console.log` (or your custom logger's `log` method) and `return` the Object:
171
+ ```js
172
+ logger.info('hello world');
173
+ // hello world
174
+ ```
93
175
 
94
176
  ```js
95
- { message: 'hello world', meta: { level: 'info' } }
177
+ logger.warn('uh oh!', { amount_spent: 50 });
178
+ // uh oh! { amount_spent: 50 }
96
179
  ```
97
180
 
98
- And if you were to log `axe.info('hello world')`, it will output with `console.info` (or your custom logger's `info` method) and `return` the Object:
181
+ As you can see - this is exactly what you'd want your logger output to look like. Axe doesn't change anything out of the ordinary. Now here is where Axe is handy - **it will automatically normalize argument style for you:**
182
+
183
+ ```js
184
+ logger.warn({ hello: 'world' }, 'uh oh');
185
+ // uh oh { hello: 'world' }
186
+ ```
187
+
188
+ ```js
189
+ logger.warn('uh oh', 'foo bar', 'beep boop');
190
+ // uh oh foo bar beep boop
191
+ ```
192
+
193
+ ```js
194
+ logger.warn('hello', new Error('uh oh!'));
195
+
196
+ // Error: uh oh!
197
+ // at Object.<anonymous> (/Users/user/Projects/axe/test.js:5:22)
198
+ // at Module._compile (node:internal/modules/cjs/loader:1105:14)
199
+ // at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
200
+ // at Module.load (node:internal/modules/cjs/loader:981:32)
201
+ // at Function.Module._load (node:internal/modules/cjs/loader:822:12)
202
+ // at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
203
+ // at node:internal/main/run_main_module:17:47
204
+ ```
99
205
 
100
206
  ```js
101
- { message: 'hello world', meta: { level: 'info' } }
207
+ logger.warn(new Error('uh oh!'), 'hello');
208
+
209
+ // Error: uh oh!
210
+ // at Object.<anonymous> (/Users/user/Projects/axe/test.js:9:13)
211
+ // at Module._compile (node:internal/modules/cjs/loader:1105:14)
212
+ // at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
213
+ // at Module.load (node:internal/modules/cjs/loader:981:32)
214
+ // at Function.Module._load (node:internal/modules/cjs/loader:822:12)
215
+ // at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
216
+ // at node:internal/main/run_main_module:17:47
102
217
  ```
103
218
 
104
- Lastly if you were to log `axe.warn('uh oh!', { amount_spent: 50 })`, it will output with `console.warn` (or your custom logger's `warn` method) and `return` the Object:
219
+ Axe has support for format specifiers, and you can even use format specifiers in the browser (uses [format-util][] has limited number of format specifiers) and Node (uses the built-in [util.format][] method supports all format specifiers). This feature is built-in thanks to smart detection using [format-specifiers][].
105
220
 
106
221
  ```js
107
- { message: 'uh oh!', meta: { amount_spent: 50, level: 'warn' } }
222
+ logger.info('favorite color is %s', 'blue');
223
+ // favorite color is blue
108
224
  ```
109
225
 
110
- These returned values will be automatically sent to the endpoint (by default to your [Cabin][] account associated with your API key).
226
+ As you can see, Axe makes your logs consistent in both Node and browser environments.
227
+
228
+ Axe's goal is to allow you to log in any style, but make your log output more readable, organized, and clean.
229
+
230
+ The **most impactful feature of Axe** is that it **makes logger output human-friendly and readable** when there are multiple errors.
231
+
232
+ Normally `console` output (and most other loggers) by default will output the following unreadable stack trace:
233
+
234
+ ```sh
235
+ > console.log(new Error('hello'), new Error('world'));
236
+ Error: hello
237
+ at REPL6:1:13
238
+ at Script.runInThisContext (node:vm:129:12)
239
+ at REPLServer.defaultEval (node:repl:566:29)
240
+ at bound (node:domain:421:15)
241
+ at REPLServer.runBound [as eval] (node:domain:432:12)
242
+ at REPLServer.onLine (node:repl:893:10)
243
+ at REPLServer.emit (node:events:539:35)
244
+ at REPLServer.emit (node:domain:475:12)
245
+ at REPLServer.Interface._onLine (node:readline:487:10)
246
+ at REPLServer.Interface._line (node:readline:864:8) Error: world
247
+ at REPL6:1:33
248
+ at Script.runInThisContext (node:vm:129:12)
249
+ at REPLServer.defaultEval (node:repl:566:29)
250
+ at bound (node:domain:421:15)
251
+ at REPLServer.runBound [as eval] (node:domain:432:12)
252
+ at REPLServer.onLine (node:repl:893:10)
253
+ at REPLServer.emit (node:events:539:35)
254
+ at REPLServer.emit (node:domain:475:12)
255
+ at REPLServer.Interface._onLine (node:readline:487:10)
256
+ at REPLServer.Interface._line (node:readline:864:8)
257
+ ```
258
+
259
+ However with Axe, errors and stack traces are much more readable (we use [maybe-combine-errors][] under the hood):
260
+
261
+ ```sh
262
+ > logger.log(new Error('hello'), new Error('world'));
263
+ Error: hello
264
+ at REPL7:1:12
265
+ at Script.runInThisContext (node:vm:129:12)
266
+ at REPLServer.defaultEval (node:repl:566:29)
267
+ at bound (node:domain:421:15)
268
+ at REPLServer.runBound [as eval] (node:domain:432:12)
269
+ at REPLServer.onLine (node:repl:893:10)
270
+ at REPLServer.emit (node:events:539:35)
271
+ at REPLServer.emit (node:domain:475:12)
272
+ at REPLServer.Interface._onLine (node:readline:487:10)
273
+ at REPLServer.Interface._line (node:readline:864:8)
274
+
275
+ Error: world
276
+ at REPL7:1:32
277
+ at Script.runInThisContext (node:vm:129:12)
278
+ at REPLServer.defaultEval (node:repl:566:29)
279
+ at bound (node:domain:421:15)
280
+ at REPLServer.runBound [as eval] (node:domain:432:12)
281
+ at REPLServer.onLine (node:repl:893:10)
282
+ at REPLServer.emit (node:events:539:35)
283
+ at REPLServer.emit (node:domain:475:12)
284
+ at REPLServer.Interface._onLine (node:readline:487:10)
285
+ at REPLServer.Interface._line (node:readline:864:8)
286
+ ```
287
+
288
+ Lastly, Axe works in both server-side and client-side environments (with Node and the browser).
289
+
111
290
 
112
- You can also use format specifiers in the browser (uses [format-util][] – has limited number of format specifiers) and Node (uses the built-in [util.format][] method – supports all format specifiers). This feature is built-in thanks to smart detection using [format-specifiers][].
291
+ ## Application Metadata and Information
113
292
 
114
- **This consistency among server and browser environments is the beauty of Axe – and when used in combination with [Cabin][], your logs will be beautiful with HTTP request information, user metadata, IP address, User-Agent, and more!**
293
+ If you've read the [Foreword](#foreword), you'll know that Axe invokes logger methods with two normalized arguments, `message` (String or Error) and `meta` (Object).
115
294
 
295
+ Axe will automatically add the following metadata and information to the `meta` Object argument passed to logger methods:
116
296
 
117
- ## Application Information
297
+ | Property | Type | Description |
298
+ | ------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
299
+ | `meta.args` | Array | The original arguments passed to the logger method when invoked. **Note that this is hidden by default via `meta.omittedFields` option**. |
300
+ | `meta.level` | String | The log level invoked (e.g. `"info"`). |
301
+ | `meta.err` | Object | Parsed error information using [parse-err][]. |
302
+ | `meta.original_err` | Object | If and only if `meta.err` already existed, this field is preserved as `meta.original_err` on the metadata object. |
303
+ | `meta.original_meta` | Object | If and only if `meta` already existed as an argument and was not an Object (e.g. an Array), this field is preserved as `meta.original_meta` on the metadata object. |
304
+ | `meta.app` | Object | Application information parsed using [parse-app-info][]. **This is not added in Browser environments.** See below nested properties. |
305
+ | `meta.app.name` | String | Name of the app from `package.json`. |
306
+ | `meta.app.version` | String | Version of the app `package.json`. |
307
+ | `meta.app.node` | String | Version if node.js running the app. |
308
+ | `meta.app.hash` | String | The latest Git commit hash; not available when not in a Git repository or if there is no Git commit hash. |
309
+ | `meta.app.tag` | String | The latest Git tag; not available when not in a Git repository or if there is no Git tag. |
310
+ | `meta.app.environment` | String | The value of `process.env.NODE_ENV`. |
311
+ | `meta.app.hostname` | String | Name of the computer. |
312
+ | `meta.app.pid` | Number | Process ID as in `process.pid`. |
313
+ | `meta.app.cluster` | Object | Node [cluster](https://nodejs.org/api/cluster.html) information. |
314
+ | `meta.app.os` | Object | Node [os](https://nodejs.org/api/os.html) information. |
315
+ | `meta.app.worker_threads` | Object | Node [worker_threads](https://nodejs.org/api/worker_threads.html) information. |
118
316
 
119
- By default a `meta.app` property is populated in all logs for you using [parse-app-info][].
317
+ :warning: **Note that by default,** **<u>Axe will not output this additional information for you</u>** (since we set the `meta.omittedFields` option to `[ 'level', 'err', 'app', 'args' ]` by default).
120
318
 
121
- At a glance, here are the properties that are automatically populated for you:
319
+ Axe will omit from metadata all properties via the default Array from `meta.omittedFields` option (see [Options](#options) below for more insight).
122
320
 
123
- | Property | Description |
124
- | ----------- | ----------------------------------- |
125
- | environment | The value of NODE_ENV |
126
- | hostname | Name of the computer |
127
- | name | Name of the app from `package.json` |
128
- | node | Version if node.js running the app |
129
- | pid | Process ID as in `process.pid` |
130
- | version | Version of the app `package.json` |
321
+ If the argument "meta" is an empty object, then it will not be passed as an argument to logger methods \*ndash; because you don't want to see an empty `{}` polluting your log metadata. Axe keeps your log output tidy.
131
322
 
132
- Additional properties when the app is in a git repository
323
+ If you set `meta.omittedFields` to an empty Array, or alternatively use the environment variable `AXE_OMIT_META_FIELDS=""`, then application information will be visible:
133
324
 
134
- | Property | Description |
135
- | -------- | ------------------------------------------------------------------ |
136
- | hash | git hash of latest commit if the app |
137
- | tag | the latest git tag. Property is not available when there is no tag |
325
+ ```js
326
+ const Axe = require('axe');
327
+
328
+ const logger = new Axe({
329
+ meta: {
330
+ omittedFields: []
331
+ // NOTE: the default is `[ 'level', 'err', 'app', 'args' ]`
332
+ }
333
+ });
334
+
335
+ // hello world {
336
+ // args: [ 'info', 'hello world' ],
337
+ // level: 'info',
338
+ // app: {
339
+ // name: 'axe',
340
+ // version: '10.0.0',
341
+ // node: 'v16.15.1',
342
+ // hash: '5ecd389b2523a8e810416f6c4e3ffa0ba6573dc2',
343
+ // tag: 'v10.0.0',
344
+ // environment: 'development',
345
+ // hostname: 'users-MacBook-Air.local',
346
+ // pid: 3477,
347
+ // cluster: { isMaster: true, isWorker: false, schedulingPolicy: 2 },
348
+ // os: {
349
+ // arch: 'arm64',
350
+ // cpus: [Array],
351
+ // endianness: 'LE',
352
+ // freemem: 271433728,
353
+ // priority: 0,
354
+ // homedir: '/Users/user',
355
+ // hostname: 'users-MacBook-Air.local',
356
+ // loadavg: [Array],
357
+ // network_interfaces: [Object],
358
+ // platform: 'darwin',
359
+ // release: '21.3.0',
360
+ // tmpdir: '/var/folders/rl/gz_3j8fx4s98k2kb0hknfygm0000gn/T',
361
+ // totalmem: 17179869184,
362
+ // type: 'Darwin',
363
+ // uptime: 708340,
364
+ // user: [Object],
365
+ // version: 'Darwin Kernel Version 21.3.0: Wed Dec 8 00:40:46 PST 2021; root:xnu-8019.80.11.111.1~1/RELEASE_ARM64_T8101'
366
+ // },
367
+ // worker_threads: {
368
+ // isMainThread: true,
369
+ // resourceLimits: {},
370
+ // threadId: 0,
371
+ // workerData: null
372
+ // }
373
+ // }
374
+ // }
375
+ ```
376
+
377
+ We recommend that you set `meta.omittedFields` to an empty Array in production environments for verbosity.
378
+
379
+ Note that you can also combine `meta.omittedFields` with `meta.pickedFields` and `meta.remappedFields` (in case you want to output specific properties from `meta.app` and exclude others – see [Options](#options) for more insight).
380
+
381
+
382
+ ## Install
383
+
384
+ ### Node
385
+
386
+ [npm][]:
387
+
388
+ ```sh
389
+ npm install axe
390
+ ```
391
+
392
+ ### Browser
393
+
394
+ See [Browser](#browser-1) usage below for more information.
138
395
 
139
396
 
140
397
  ## Usage
141
398
 
142
- We highly recommend to simply use [Cabin][] as this package is built-in!
399
+ ### Options
400
+
401
+ | Property | Type | Default Value | Description | |
402
+ | ----------------------- | ----------------- | --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - |
403
+ | `showStack` | Boolean | `true` | Attempts to parse a boolean value from `process.env.AXE_SHOW_STACK`). **If this value is `true`, then if `message` is an instance of an Error, it will be invoked as the first argument to logger methods. If this is `false`, then only the `err.message` will be invoked as the first argument to logger methods.** Basically if `true` it will call `logger.method(err)` and if `false` it will call `logger.method(err.message)`. If you pass `err` as the first argument to a logger method, then it will show the stack trace via `err.stack` typically. | |
404
+ | `meta` | Object | See below | Stores all meta config information (see the following nested properties below). | |
405
+ | `meta.show` | Boolean | `true` | Attempts to parse a boolean value from `process.env.AXE_SHOW_META` – meaning you can pass a flag `AXE_SHOW_META=true node app.js` when needed for debugging), whether or not to output metadata to logger methods. If set to `false`, then fields will not be omitted nor picked; the entire meta object will be hidden from logger output. | |
406
+ | `meta.remappedFields` | Object | `{}` | Attempts to parse an Object mapping from `process.env.AXE_REMAPPED_META_FIELDS` (`,` and `:` delimited, e.g. `REMAPPED_META_FIELDS=foo:bar,beep.boop:beepBoop` to remap `meta.foo` to `meta.bar` and `meta.beep.boop` to `meta.beepBoop`). Note that this will clean up empty objects by default unless you set the option `meta.cleanupRemapping` to `false`). Supports dot-notation. | |
407
+ | `meta.omittedFields` | Array | `['level','err','app', 'args']` | Attempts to parse an array value from `process.env.AXE_OMIT_META_FIELDS` (`,` delimited) - meaning you can pass a flag `OMIT_META_FIELDS=user,id node app.js`), determining which fields to omit in the metadata passed to logger methods. Supports dot-notation. | |
408
+ | `meta.pickedFields` | Array | `[]` | Attempts to parse an array value from `process.env.AXE_PICK_META_FIELDS` (`,` delimited) - meaning you can pass a flag, e.g. `PICK_META_FIELDS=request.headers,response.headers node app.js` which would pick from `meta.request` and `meta.response` *only* `meta.request.headers` and `meta.response.headers`), **This takes precedence after fields are omitted, which means this acts as a whitelist.** Supports dot-notation. | |
409
+ | `meta.cleanupRemapping` | Boolean | `true` | Whether or not to cleanup empty objects after remapping operations are completed) | |
410
+ | `silent` | Boolean | `false` | Whether or not to invoke logger methods. Pre and post hooks will still run even if this option is set to `false`. | |
411
+ | `logger` | Object | `console` | Defaults to `console` with [console-polyfill][] added automatically, though **you can bring your own logger**. See [custom logger](#custom-logger) – you can pass an instance of `pino`, `signale`, `winston`, `bunyan`, etc. | |
412
+ | `name` | String or Boolean | `false` | The default name for the logger (defaults to `false`, which does not set `logger.name`). If you wish to pass a name such as `os.hostname()`, then set `name: os.hostname()` – this is useful if you are using a logger like `pino` which prefixes log output with the name set here. | |
413
+ | `level` | String | `"info"` | The default level of logging to invoke `logger` methods for (defaults to `info`, which includes all logs including info and higher in severity (e.g. `info`, `warn`, `error`, `fatal`) | |
414
+ | `levels` | Array | `['info','warn','error','fatal']` | An Array of logging levels to support. You usually shouldn't change this unless you want to prevent logger methods from being invoked or prevent hooks from being run for a certain log level. If an invalid log level is attempted to be invoked, and if it is not in this Array, then no hooks and no logger methods will be invoked. | |
415
+ | `appInfo` | Boolean | `true` | Attempts to parse a boolean value from `process.env.AXE_APP_INFO`) - whether or not to parse application information (using [parse-app-info][]). | |
416
+
417
+ ### Supported Platforms
418
+
419
+ * Node: v14+
420
+ * Browsers (see [.browserslistrc](.browserslistrc)):
421
+
422
+ ```sh
423
+ npx browserslist
424
+ ```
425
+
426
+ ```sh
427
+ and_chr 102
428
+ and_ff 101
429
+ and_qq 10.4
430
+ and_uc 12.12
431
+ android 101
432
+ chrome 103
433
+ chrome 102
434
+ chrome 101
435
+ chrome 100
436
+ edge 103
437
+ edge 102
438
+ edge 101
439
+ firefox 101
440
+ firefox 100
441
+ firefox 91
442
+ ios_saf 15.5
443
+ ios_saf 15.4
444
+ ios_saf 15.2-15.3
445
+ ios_saf 15.0-15.1
446
+ ios_saf 14.5-14.8
447
+ ios_saf 14.0-14.4
448
+ ios_saf 12.2-12.5
449
+ kaios 2.5
450
+ op_mini all
451
+ op_mob 64
452
+ opera 86
453
+ opera 85
454
+ safari 15.5
455
+ safari 15.4
456
+ samsung 17.0
457
+ samsung 16.0
458
+ ```
143
459
 
144
460
  ### Node
145
461
 
146
462
  ```js
147
463
  const Axe = require('axe');
148
464
 
149
- const axe = new Axe({ key: 'YOUR-CABIN-API-KEY' });
465
+ const logger = new Axe();
150
466
 
151
- axe.info('hello world');
467
+ logger.info('hello world');
152
468
  ```
153
469
 
154
470
  ### Browser
155
471
 
472
+ This package requires Promise support, therefore you will need to polyfill if you are using an unsupported browser (namely Opera mini).
473
+
474
+ **We no longer support IE as of Axe v10.0.0+.**
475
+
156
476
  #### VanillaJS
157
477
 
158
- **The browser-ready bundle is only 36 KB (minified and gzipped)**.
478
+ **The browser-ready bundle is only 18 KB when minified and 6 KB when gzipped**.
159
479
 
160
480
  ```html
161
- <script src="https://polyfill.io/v3/polyfill.min.js?features=WeakRef,BigInt"></script>
481
+ <script src="https://polyfill.io/v3/polyfill.min.js?features=Promise"></script>
162
482
  <script src="https://unpkg.com/axe"></script>
163
483
  <script type="text/javascript">
164
- (function() {
165
- var Axe = new Axe({ key: 'YOUR-CABIN-API-KEY' });
166
- axe.info('hello world');
484
+ (function () {
485
+ // make a new logger instance
486
+ const logger = new Axe();
487
+ logger.info('hello world');
488
+
489
+ // or you can override console everywhere
490
+ console = new Axe();
491
+ console.info('hello world');
167
492
  });
168
493
  </script>
169
494
  ```
@@ -173,11 +498,10 @@ axe.info('hello world');
173
498
  We recommend using <https://polyfill.io> (specifically with the bundle mentioned in [VanillaJS](#vanillajs) above):
174
499
 
175
500
  ```html
176
- <script src="https://polyfill.io/v3/polyfill.min.js?features=WeakRef,BigInt"></script>
501
+ <script src="https://polyfill.io/v3/polyfill.min.js?features=Promise"></script>
177
502
  ```
178
503
 
179
- * WeakRef is not supported in Opera 85, iOS Safari 12.2-12.5
180
- * BigInt is not supported in iOS Safari 12.2-12.5
504
+ * Promise is not supported in op\_mini all
181
505
 
182
506
  #### Bundler
183
507
 
@@ -204,9 +528,9 @@ Loggers supported include, but are not limited to:
204
528
  const signale = require('signale');
205
529
  const Axe = require('axe');
206
530
 
207
- const axe = new Axe({ logger: signale, key: 'YOUR-CABIN-API-KEY' });
531
+ const logger = new Axe({ logger: signale });
208
532
 
209
- axe.info('hello world');
533
+ logger.info('hello world');
210
534
  ```
211
535
 
212
536
  In [Lad][], we have an approach similar to the following, where non-production environments use [consola][], and production environments use [pino][].
@@ -239,79 +563,260 @@ const logger = new Axe({
239
563
  logger.info('hello world');
240
564
  ```
241
565
 
242
- ### Custom endpoint
566
+ ### Silent Logging
567
+
568
+ Silent logging is useful when you need to disable logging in certain environments for privacy reasons or to simply clean up output on stdout.
569
+
570
+ For example when you're running tests you can set `logger.config.silent = true`.
571
+
572
+ ```js
573
+ const Axe = require('axe');
574
+
575
+ const logger = new Axe({ silent: true });
576
+
577
+ logger.info('hello world');
578
+ ```
579
+
580
+ ### Stack Traces and Error Handling
581
+
582
+ Please see Cabin's documentation for [stack traces and error handling](https://github.com/cabinjs/cabin#stack-traces-and-error-handling) for more information.
583
+
584
+ > If you're not using `cabin`, you can simply replace instances of the word `cabin` with `axe` in the documentation examples linked above.
585
+
586
+ ### Hooks
587
+
588
+ You can add synchronous "pre" hooks and/or asynchronous/synchronous "post" hooks with Axe. Both pre and post hooks accept four arguments (`level`, `err`, `message`, and `meta`). Pre hooks are required to be synchronous.
243
589
 
244
- By default we built-in support such that if you provide your [Cabin][] API key, then your logs will be uploaded automatically for you in both server and browser environments.
590
+ Both pre and post hooks execute serially and while pre hooks are blocking, post-hooks will run in the background after logger methods are invoked (you can have a post hook that's a Promise or async function).
245
591
 
246
- If you decide to [self-host your own Cabin API][cabin-api] (or roll your own logging service) then you can specify your own endpoint under `config.endpoint`.
592
+ Pre hooks require an Array to be returned of `[ err, message, meta ]`.
247
593
 
248
- See [Options](#options) below for more information.
594
+ Pre hooks allow you to manipulate the arguments `err`, `message`, and `meta` that are passed to the internal logger methods. This is useful for masking sensitive data or doing additional custom logic before writing logs.
249
595
 
250
- ### Suppress logs
596
+ Post hooks are useful if you want to send logging information to a third-party, store them into a database, or do any sort of custom processing.
251
597
 
252
- This is useful when you want need logging turned off in certain environments.
598
+ You should properly handle any errors in your pre hooks, otherwise they will be thrown and logger methods will not be invoked.
253
599
 
254
- For example when you're running tests you can set `axe.config.silent = true`.
600
+ We will catch errors for post hooks by default and log them as errors with your logger methods' `logger.error` method).
601
+
602
+ Hooks can be defined in the options passed to an instance of Axe, e.g. `new Axe({ hooks: { pre: [ fn ], post: [ fn ] } });` and/or with the method `logger.pre(level, fn)` or `logger.post(level, fn)`. Here are a few examples below:
255
603
 
256
604
  ```js
257
605
  const Axe = require('axe');
258
606
 
259
- const axe = new Axe({ silent: true, key: 'YOUR-CABIN-API-KEY' });
607
+ const logger = new Axe({
608
+ hooks: {
609
+ pre: [
610
+ function (level, err, message, meta) {
611
+ message = message.replace(/world/gi, 'planet earth');
612
+ return [err, message, meta];
613
+ }
614
+ ]
615
+ }
616
+ });
260
617
 
261
- axe.info('hello world');
618
+ logger.info('hello world');
619
+
620
+ // hello planet earth
262
621
  ```
263
622
 
264
- ### Stack Traces and Error Handling
623
+ ```js
624
+ const Axe = require('axe');
265
625
 
266
- Please see Cabin's documentation for [stack traces and error handling](https://github.com/cabinjs/cabin#stack-traces-and-error-handling) for more information.
626
+ const logger = new Axe();
267
627
 
268
- > If you're not using `cabin`, you can simply replace instances of the word `cabin` with `axe` in the documentation examples linked above.
628
+ logger.pre('error', (err, message, meta) => {
629
+ if (err instanceof Error) err.is_beep_boop = true;
630
+ return [err, message, meta];
631
+ });
632
+
633
+ logger.error(new Error('oops'));
634
+
635
+ // Error: oops
636
+ // at Object.<anonymous> (/Users/user/Projects/axe/test.js:39:14)
637
+ // at Module._compile (node:internal/modules/cjs/loader:1105:14)
638
+ // at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
639
+ // at Module.load (node:internal/modules/cjs/loader:981:32)
640
+ // at Function.Module._load (node:internal/modules/cjs/loader:822:12)
641
+ // at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
642
+ // at node:internal/main/run_main_module:17:47 {
643
+ // is_beep_boop: true
644
+ // }
645
+ ```
646
+
647
+ For more examples of hooks, see our below sections on [Send Logs to HTTP Endpoint](#send-logs-to-http-endpoint), [Send Logs to Slack](#send-logs-to-slack)), and [Suppress Logger Data](#suppress-logger-data) below.
648
+
649
+ ### Remapping
650
+
651
+ If you would like to remap fields, such as `response.headers` to `responseHeaders`, then you can use environment variables or pass an object with configuration mapping.
652
+
653
+ ```js
654
+ const logger = new Axe({
655
+ meta: {
656
+ remappedFields: {
657
+ 'response.headers': 'responseHeaders'
658
+ }
659
+ }
660
+ });
661
+
662
+ logger.info('foo bar', {
663
+ response: {
664
+ headers: {
665
+ 'X-Hello-World': true
666
+ }
667
+ }
668
+ });
669
+
670
+ // foo bar { responseHeaders: { 'X-Hello-World': true } }
671
+ ```
672
+
673
+ ### Omitting
674
+
675
+ If you would like to omit fields, such as `response.headers` from a response, so you are only left with the status code:
269
676
 
677
+ ```js
678
+ const logger = new Axe({
679
+ meta: {
680
+ omittedFields: ['level', 'err', 'app', 'args', 'response.headers']
681
+ }
682
+ });
270
683
 
271
- ## Options
684
+ logger.info('foo bar', {
685
+ response: {
686
+ status: 200,
687
+ headers: {
688
+ 'X-Hello-World': true
689
+ }
690
+ }
691
+ });
272
692
 
273
- * `key` (String) - defaults to an empty string, so BasicAuth is not used – **this is your Cabin API key**, which you can get for free at [Cabin][] (note you could provide your own API key here if you are self-hosting or rolling your own logging service)
274
- * `endpoint` (String) - defaults to `https://api.cabinjs.com`
275
- * `headers` (Object) - HTTP headers to send along with log to the `endpoint`
276
- * `timeout` (Number) - defaults to `5000`, number of milliseconds to wait for a response
277
- * `retry` (Number) - defaults to `3`, number of attempts to retry sending log over HTTP
278
- * `showStack` (Boolean) - defaults to `true` (attempts to parse a boolean value from `process.env.SHOW_STACK`) - whether or not to output a stack trace
279
- * `meta` (Object) - stores all meta config information
280
- * `show` (Boolean) - defaults to `true` (attempts to parse a boolean value from `process.env.SHOW_META` – meaning you can pass a flag `SHOW_META=true node app.js` when needed for debugging), whether or not to output metadata to logger methods.
281
- * `showApp` (Boolean) - defaults to `false` (attempts to parse a boolean value from `process.env.SHOW_META_APP` – meaning you can pass a flag `SHOW_META_APP=true node app.js` when needed for debugging), whether or not to output `appInfo` in the metadata to logger methods.
282
- * `omittedFields` (Array) - defaults to `[]` (attempts to parse an array value from `process.env.OMIT_META_FIELDS` (`,` delimited) - meaning you can pass a flag `OMIT_META_FIELDS=user,id node app.js`), determining which fields to omit in the metadata passed to logger methods.
283
- * `silent` (Boolean) - defaults to `false`, whether or not to suppress log output to console
284
- * `logger` (Object) - defaults to `console` (with [console-polyfill][] added automatically), but you may wish to use a [custom logger](#custom-logger)
285
- * `name` (String) - the default name for the logger (defaults to `false`, which does not set `logger.name`). If you wish to pass a name such as `os.hostname()`, then set `name: os.hostname()` – this is useful if you are using a logger like `pino` which prefixes log output with the name set here.
286
- * `level` (String) - the default level of logging to capture (defaults to `info`, which includes all logs including info and higher in severity (e.g. `info`, `warn`, `error`, `fatal`)
287
- * `capture` (Boolean) - defaults to `false`, whether or not to `POST` logs to the `endpoint` (takes into consideration the `config.level` to only send valid capture levels)
288
- * `callback` (Function) - defaults to `false`, but if it is a `Function`, then it will be called with `callback(level, message, meta)` – this is super useful for [sending messages to Slack when errors occur (see below)](#send-logs-to-slack). Note that if you specify `{ callback: false }` in the meta object when logging, it will prevent the callback function from being invoked (e.g. `axe.error(new Error('Slack callback failed'), { callback: false })` ‐ see below example). The `callback` property is always purged from `meta` object for sanity.
289
- * `appInfo` (Boolean) - defaults to `true` (attempts to parse a boolean value from `process.env.APP_INFO`) - whether or not to parse application information (using [parse-app-info][]).
693
+ // foo bar { response: { status: 200 } }
694
+ ```
695
+
696
+ ### Picking
697
+
698
+ If you would like to pick certain fields, such as `response.status` from a response:
290
699
 
291
- ### DEPRECATED
700
+ ```js
701
+ const logger = new Axe({
702
+ meta: {
703
+ pickedFields: [ 'response.status' ]
704
+ }
705
+ });
292
706
 
293
- * `showMeta` (Boolean) - defaults to `true` (attempts to parse a boolean value from `process.env.SHOW_META` – meaning you can pass a flag `SHOW_META=true node app.js` when needed for debugging), whether or not to output metadata to logger methods.
294
- * This will be automatically assigned to `meta.show` when passed as part of config.
707
+ logger.info('foo bar', {
708
+ response: {
709
+ status: 200,
710
+ headers: {
711
+ 'X-Hello-World': true
712
+ }
713
+ }
714
+ });
295
715
 
716
+ // foo bar { response: { status: 200 } }
717
+ ```
296
718
 
297
- ## Aliases
719
+ ### Aliases
298
720
 
299
721
  We have provided helper/safety aliases for `logger.warn` and `logger.error` of `logger.warning` and `logger.err` respectively.
300
722
 
723
+ ### Methods
724
+
725
+ A few extra methods are available, which were inspired by [Slack's logger][slack-logger] and added for compatibility:
726
+
727
+ * `logger.setLevel(level)` - sets the log `level` (String) severity to invoke `logger` methods for (must be valid enumerable level)
728
+ * `logger.getNormalizedLevel(level)` - gets the normalized log `level` (String) severity (normalizes to known logger levels, e.g. "warning" => "warn", "err" => "error", "log" => "info")
729
+ * `logger.setName(name)` - sets the `name` (String) property (some loggers like `pino` will prefix logs with the name set here)
301
730
 
302
- ## Methods
303
731
 
304
- Two extra methods are available, which were inspired by [Slack's logger][slack-logger] and added for compatibility:
732
+ ## Examples
305
733
 
306
- * `axe.setLevel(level)` - sets the log `level` (String) severity to capture (must be valid enumerable level)
307
- * `axe.getNormalizedLevel(level)` - gets the normalized log `level` (String) severity (normalizes to known logger levels, e.g. "warning" => "warn", "err" => "error", "log" => "info")
308
- * `axe.setName(name)` - sets the `name` (String) property (some loggers like `pino` will prefix logs with the name set here)
309
- * `axe.setCallback(callback)` - sets the `callback` (Function) property (see `callback` option above and [Slack example below](#send-logs-to-slack)
734
+ ### Send Logs to HTTP Endpoint
310
735
 
736
+ This is an example of using hooks to send a POST request to an HTTP endpoint with logs of the "fatal" and "error" levels that occur in your application:
311
737
 
312
- ## Send Logs To Slack
738
+ We recommend [superagent](https://github.com/visionmedia/superagent), however there are plenty of alternatives such as [axios](https://github.com/axios/axios) and [ky](https://github.com/sindresorhus/ky).
313
739
 
314
- This is just an example of using the `callback` option to send a message to Slack with errors that occur in your application:
740
+ 1. You will need to install the `superagent`, `cuid`, and `fast-safe-stringify` packages:
741
+
742
+ ```sh
743
+ npm install superagent cuid fast-safe-stringify
744
+ ```
745
+
746
+ 2. Implementation example is provided below:
747
+
748
+ ```js
749
+ const os = require('os');
750
+
751
+ const Axe = require('axe');
752
+ const superagent = require('superagent');
753
+ const cuid = require('cuid');
754
+ const safeStringify = require('fast-safe-stringify');
755
+
756
+ // create our application logger that uses hooks
757
+ const logger = new Axe({
758
+ logger: console, // optional (e.g. pino, signale, consola),
759
+ level: 'info', // optional (defaults to info)
760
+ name: process.env.HOSTNAME || os.hostname() // optional
761
+ });
762
+
763
+ async function hook(next, message, meta) {
764
+ //
765
+ // return early if we wish to ignore this
766
+ // (this prevents recursion; see end of this fn)
767
+ //
768
+ if (meta.ignore_emit) return next();
769
+
770
+ try {
771
+ //
772
+ // set the body used in the HTTP request to be consistent object with
773
+ // two properties sent in the payload of `message` and `meta`
774
+ // (and also remove circular references)
775
+ //
776
+ const body = safeStringify({ message, meta });
777
+
778
+ //
779
+ // send to Cabin or your own custom endpoint here
780
+ // https://cabinjs.com
781
+ //
782
+ const request = superagent
783
+ .post('https://api.cabinjs.com')
784
+ .set('X-Request-Id', cuid()) // normalize server/browser request id formatting
785
+ .timeout(5000);
786
+
787
+ request.set('User-Agent', `axe/${logger.version}`);
788
+
789
+ // add basic auth header (e.g. if you use Cabin)
790
+ // if (config.key) request.auth('INSERT-YOUR-KEY');
791
+
792
+ // set any additional headers if necessary
793
+ // request.set({ ... });
794
+
795
+ const response = await request
796
+ .type('application/json')
797
+ .retry(3)
798
+ .send(body);
799
+
800
+ logger.info('log sent over HTTP', { response });
801
+ } catch (err) {
802
+ logger.fatal(err, { ignore_emit: true });
803
+ }
804
+
805
+ // move along to the next hook
806
+ next();
807
+ }
808
+
809
+ // bind custom hooks for "fatal" and "error" log levels
810
+ logger.post('error', hook);
811
+ logger.post('fatal', hook);
812
+
813
+ // test out the HTTP integration
814
+ logger.error(new Error('Uh oh something went wrong!'));
815
+ ```
816
+
817
+ ### Send Logs to Slack
818
+
819
+ This is an example of using hooks to send a message to Slack with logs of the "fatal" and "error" levels that occur in your application:
315
820
 
316
821
  1. You will need to install the `@slack/web-api` package locally:
317
822
 
@@ -327,58 +832,37 @@ This is just an example of using the `callback` option to send a message to Slac
327
832
 
328
833
  ```js
329
834
  const os = require('os');
835
+
330
836
  const Axe = require('axe');
331
837
  const { WebClient } = require('@slack/web-api');
332
- const signale = require('signale');
333
- const pino = require('pino')({
334
- customLevels: {
335
- log: 30
336
- },
337
- hooks: {
338
- // <https://github.com/pinojs/pino/blob/master/docs/api.md#logmethod>
339
- logMethod(inputArgs, method) {
340
- return method.call(this, {
341
- // <https://github.com/pinojs/pino/issues/854>
342
- // message: inputArgs[0],
343
- msg: inputArgs[0],
344
- meta: inputArgs[1]
345
- });
346
- }
347
- }
348
- });
349
-
350
- const isProduction = process.env.NODE_ENV === 'production';
351
-
352
- const config = {
353
- logger: isProduction ? pino : signale,
354
- level: isProduction ? 'warn' : 'info',
355
- name: process.env.HOSTNAME || os.hostname()
356
- };
357
838
 
358
- // custom logger for Slack that inherits our Axe config
359
- // (with the exception of a `callback` function for logging to Slack)
360
- const slackLogger = new Axe(config);
839
+ // create our application logger that uses hooks
840
+ const logger = new Axe({
841
+ logger: console, // optional (e.g. pino, signale, consola)
842
+ level: 'info', // optional (defaults to info)
843
+ name: process.env.HOSTNAME || os.hostname() // optional
844
+ });
361
845
 
362
846
  // create an instance of the Slack Web Client API for posting messages
363
847
  const web = new WebClient('INSERT-YOUR-TOKEN', {
364
848
  // <https://slack.dev/node-slack-sdk/web-api#logging>
365
- logger: slackLogger,
366
- logLevel: config.level
849
+ logger,
850
+ logLevel: logger.config.level
367
851
  });
368
852
 
369
- // create our application logger that uses a custom callback function
370
- const axe = new Axe({ ...config });
853
+ async function hook(next, message, meta) {
854
+ //
855
+ // return early if we wish to ignore this
856
+ // (this prevents recursion; see end of this fn)
857
+ //
858
+ if (meta.ignore_emit) return next();
371
859
 
372
- axe.setCallback(async (level, message, meta) => {
860
+ // otherwise post a message to the slack channel
373
861
  try {
374
- // if it was not an error then return early
375
- if (!['error','fatal'].includes(level)) return;
376
-
377
- // otherwise post a message to the slack channel
378
862
  const result = await web.chat.postMessage({
379
- channel: 'general',
380
- username: 'Cabin',
381
- icon_emoji: ':evergreen_tree:',
863
+ channel: 'monitoring',
864
+ username: 'Axe',
865
+ icon_emoji: ':axe:',
382
866
  attachments: [
383
867
  {
384
868
  title: meta.err && meta.err.message ? meta.err.message : message,
@@ -411,29 +895,86 @@ This is just an example of using the `callback` option to send a message to Slac
411
895
  });
412
896
 
413
897
  // finally log the result from slack
414
- axe.info('web.chat.postMessage', { result, callback: false });
898
+ logger.info('slack message sent', { result });
415
899
  } catch (err) {
416
- axe.error(err, { callback: false });
900
+ logger.fatal(err, { ignore_emit: true });
417
901
  }
418
- });
419
902
 
420
- axe.error(new Error('Uh oh something went wrong!'));
903
+ // move along to the next hook
904
+ next();
905
+ }
906
+
907
+ // bind custom hooks for "fatal" and "error" log levels
908
+ logger.post('error', hook);
909
+ logger.post('fatal', hook);
910
+
911
+ // test out the slack integration
912
+ logger.error(new Error('Uh oh something went wrong!'));
421
913
  ```
422
914
 
915
+ ### Suppress Logger Data
423
916
 
424
- ## Contributors
917
+ This is an example of using a custom hook to manipulate logger arguments to suppress sensitive data.
425
918
 
426
- | Name | Website |
427
- | ---------------- | ------------------------- |
428
- | **Nick Baugh** | <http://niftylettuce.com> |
429
- | **Alexis Tyler** | <https://wvvw.me/> |
919
+ ```js
920
+ const Axe = require('.');
921
+
922
+ const logger = new Axe();
923
+
924
+ for (const level of logger.config.levels) {
925
+ const fn = logger.config.logger[level];
926
+ logger.config.logger[level] = function (message, meta) {
927
+ // replace any messages "beep" -> "boop"
928
+ if (typeof message === 'string') message = message.replace(/beep/g, 'boop');
929
+
930
+ // mask the property "beep" in the meta object "data"
931
+ if (meta?.data?.beep)
932
+ meta.data.beep = Array.from({ length: meta.data.beep.length })
933
+ .fill('*')
934
+ .join('');
430
935
 
936
+ return Reflect.apply(fn, this, [message, meta]);
937
+ };
938
+ }
431
939
 
432
- ## Trademark Notice
940
+ logger.warn('hello world beep');
433
941
 
434
- Axe, Lad, Lass, and their respective logos are trademarks of Niftylettuce LLC.
435
- These trademarks may not be reproduced, distributed, transmitted, or otherwise used, except with the prior written permission of Niftylettuce LLC.
436
- If you are seeking permission to use these trademarks, then please [contact us](mailto:niftylettuce@gmail.com).
942
+ // hello world boop
943
+
944
+ logger.info('start', {
945
+ data: {
946
+ foo: 'bar',
947
+ beep: 'boop' // <--- we're suppressing "beep" -> "****"
948
+ }
949
+ });
950
+
951
+ // start { data: { foo: 'bar', beep: '****' } }
952
+
953
+ logger.error(new Error('oops!'), {
954
+ data: {
955
+ beep: 'beep-boop-beep' // this becomes "**************"
956
+ }
957
+ });
958
+
959
+ // Error: oops!
960
+ // at Object.<anonymous> (/Users/user/Projects/axe/test.js:30:14)
961
+ // at Module._compile (node:internal/modules/cjs/loader:1105:14)
962
+ // at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
963
+ // at Module.load (node:internal/modules/cjs/loader:981:32)
964
+ // at Function.Module._load (node:internal/modules/cjs/loader:822:12)
965
+ // at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
966
+ // at node:internal/main/run_main_module:17:47 { data: { beep: '**************' } }
967
+ ```
968
+
969
+
970
+ ## Contributors
971
+
972
+ | Name | Website |
973
+ | ------------------ | --------------------------------- |
974
+ | **Nick Baugh** | <http://niftylettuce.com> |
975
+ | **Alexis Tyler** | <https://wvvw.me/> |
976
+ | **shadowgate15** | <https://github.com/shadowgate15> |
977
+ | **Spencer Snyder** | <https://spencersnyder.io> |
437
978
 
438
979
 
439
980
  ## License
@@ -457,8 +998,6 @@ If you are seeking permission to use these trademarks, then please [contact us](
457
998
 
458
999
  [signale]: https://github.com/klauscfhq/signale
459
1000
 
460
- [high-console]: https://github.com/tusharf5/high-console
461
-
462
1001
  [pino]: https://github.com/pinojs/pino
463
1002
 
464
1003
  [winston]: https://github.com/winstonjs/winston
@@ -467,11 +1006,9 @@ If you are seeking permission to use these trademarks, then please [contact us](
467
1006
 
468
1007
  [console-polyfill]: https://github.com/paulmillr/console-polyfill
469
1008
 
470
- [cabin-api]: https://github.com/cabinjs/api.cabinjs.com
471
-
472
1009
  [consola]: https://github.com/nuxt/consola
473
1010
 
474
- [log4j]: https://en.wikipedia.org/wiki/Log4
1011
+ [log4j]: https://en.wikipedia.org/wiki/Log4j#Log4j_log_levels
475
1012
 
476
1013
  [parse-app-info]: https://github.com/cabinjs/parse-app-info
477
1014
 
@@ -481,4 +1018,12 @@ If you are seeking permission to use these trademarks, then please [contact us](
481
1018
 
482
1019
  [util.format]: https://nodejs.org/api/util.html#util_util_format_format_args
483
1020
 
484
- [format-specifiers]: https://github.com/niftylettuce/format-specifiers
1021
+ [format-specifiers]: https://github.com/cabinjs/format-specifiers
1022
+
1023
+ [forward-email]: https://forwardemail.net
1024
+
1025
+ [high-console]: https://github.com/tusharf5/high-console
1026
+
1027
+ [maybe-combine-errors]: https://github.com/vweevers/maybe-combine-errors
1028
+
1029
+ [parse-err]: https://github.com/cabinjs/parse-err