@darksheep/logger 1.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/CHANGELOG.md +31 -0
- package/README.md +191 -0
- package/package.json +44 -0
- package/src/create-logger.js +36 -0
- package/src/formatter.js +14 -0
- package/src/formatters/formatter-console.js +243 -0
- package/src/formatters/formatter-json.js +7 -0
- package/src/index.js +25 -0
- package/src/logger.js +369 -0
- package/src/replacer.js +68 -0
- package/src/replacers/buffers.js +18 -0
- package/src/replacers/error.js +40 -0
- package/src/replacers/http-client-request.js +18 -0
- package/src/replacers/http-incoming-message.js +26 -0
- package/src/replacers/http-server-response.js +16 -0
- package/src/replacers/index.js +8 -0
- package/src/replacers/long-strings.js +11 -0
- package/src/replacers/net-socket.js +21 -0
- package/src/replacers/secrets.js +50 -0
- package/src/stdout-write.js +7 -0
- package/src/utilities/colour.js +152 -0
- package/src/utilities/environment.js +61 -0
- package/src/utilities/json-path.js +17 -0
- package/src/utilities/last-callsite.js +11 -0
- package/src/utilities/log-filters.js +22 -0
- package/src/utilities/log-types.js +46 -0
- package/src/utilities/parse-filters.js +47 -0
- package/src/utilities/parse-log-level.js +32 -0
- package/src/utilities/stacktrace.js +57 -0
- package/types/assert.zod.d.ts +18 -0
- package/types/create-logger.d.ts +5 -0
- package/types/formatter.d.ts +6 -0
- package/types/formatters/formatter-console.d.ts +9 -0
- package/types/formatters/formatter-json.d.ts +5 -0
- package/types/index.d.ts +11 -0
- package/types/logger.d.ts +184 -0
- package/types/logger.test.d.ts +1 -0
- package/types/replacer.d.ts +45 -0
- package/types/replacer.test.d.ts +1 -0
- package/types/replacers/buffers.d.ts +2 -0
- package/types/replacers/buffers.test.d.ts +1 -0
- package/types/replacers/error.d.ts +37 -0
- package/types/replacers/error.test.d.ts +1 -0
- package/types/replacers/http-client-request.d.ts +3 -0
- package/types/replacers/http-client-request.test.d.ts +1 -0
- package/types/replacers/http-incoming-message.d.ts +15 -0
- package/types/replacers/http-server-response.d.ts +3 -0
- package/types/replacers/index.d.ts +8 -0
- package/types/replacers/long-strings.d.ts +2 -0
- package/types/replacers/net-socket.d.ts +3 -0
- package/types/replacers/secrets.d.ts +4 -0
- package/types/stdout-write.d.ts +5 -0
- package/types/utilities/colour.d.ts +76 -0
- package/types/utilities/environment.d.ts +25 -0
- package/types/utilities/json-path.d.ts +5 -0
- package/types/utilities/last-callsite.d.ts +9 -0
- package/types/utilities/log-filters.d.ts +12 -0
- package/types/utilities/log-types.d.ts +126 -0
- package/types/utilities/parse-filters.d.ts +9 -0
- package/types/utilities/parse-log-level.d.ts +7 -0
- package/types/utilities/stacktrace.d.ts +59 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0 (2024-07-02)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### ⚠ BREAKING CHANGES
|
|
7
|
+
|
|
8
|
+
* Drop node@^18
|
|
9
|
+
|
|
10
|
+
### 💡 Features
|
|
11
|
+
|
|
12
|
+
* Bump to latest node lts versions ([#196](https://github.com/DarkSheepSoftware/node-packages/issues/196)) ([60a95cc](https://github.com/DarkSheepSoftware/node-packages/commit/60a95cc41b22c39428d35814c0d5e85830052a14))
|
|
13
|
+
* Drop node@^18 ([5f809a6](https://github.com/DarkSheepSoftware/node-packages/commit/5f809a6b2737db425cde55d58c22375ebcded687))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### 📦 Dependencies
|
|
17
|
+
|
|
18
|
+
* **dev:** update dependency @types/node to v20.14.1 ([#207](https://github.com/DarkSheepSoftware/node-packages/issues/207)) ([5ad7a75](https://github.com/DarkSheepSoftware/node-packages/commit/5ad7a75b29cf1fe8710a70f4179027ff7f156611))
|
|
19
|
+
* **dev:** update dependency eslint to v9 ([#139](https://github.com/DarkSheepSoftware/node-packages/issues/139)) ([a01a119](https://github.com/DarkSheepSoftware/node-packages/commit/a01a119642704615386e37f2092801e08cf2f1b7))
|
|
20
|
+
* **dev:** update dependency eslint to v9 ([#252](https://github.com/DarkSheepSoftware/node-packages/issues/252)) ([7473b18](https://github.com/DarkSheepSoftware/node-packages/commit/7473b18e59c132f41e03cd6237842205bc23d08e))
|
|
21
|
+
* **dev:** update dependency typescript to ~5.5.0 ([#242](https://github.com/DarkSheepSoftware/node-packages/issues/242)) ([68413b5](https://github.com/DarkSheepSoftware/node-packages/commit/68413b57ef85df879c33243566618a2ecb700051))
|
|
22
|
+
* **pkg:** update dependency deepmerge-ts to v6 ([#189](https://github.com/DarkSheepSoftware/node-packages/issues/189)) ([23a4575](https://github.com/DarkSheepSoftware/node-packages/commit/23a4575e4405034d3562a90d6c706cab07d62ce5))
|
|
23
|
+
* **pkg:** update dependency deepmerge-ts to v7 ([#191](https://github.com/DarkSheepSoftware/node-packages/issues/191)) ([717aca5](https://github.com/DarkSheepSoftware/node-packages/commit/717aca519c6114b35be756dcae225e92dafc7ff4))
|
|
24
|
+
* **pkg:** update dependency deepmerge-ts to v7.0.1 ([#192](https://github.com/DarkSheepSoftware/node-packages/issues/192)) ([bb7557a](https://github.com/DarkSheepSoftware/node-packages/commit/bb7557a533b033e54b4498b524ea90195878c8f7))
|
|
25
|
+
* **pkg:** update dependency deepmerge-ts to v7.0.2 ([#210](https://github.com/DarkSheepSoftware/node-packages/issues/210)) ([109f763](https://github.com/DarkSheepSoftware/node-packages/commit/109f763411f7b825cf4cd9c1d92741e737a0bdd6))
|
|
26
|
+
* **pkg:** update dependency deepmerge-ts to v7.0.3 ([#217](https://github.com/DarkSheepSoftware/node-packages/issues/217)) ([ea218ab](https://github.com/DarkSheepSoftware/node-packages/commit/ea218ab9496532721d99d402de7200488a8bfd16))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### 🧹 Miscellaneous Chores
|
|
30
|
+
|
|
31
|
+
* Prepare "Logger" for publishing ([b14b4af](https://github.com/DarkSheepSoftware/node-packages/commit/b14b4af7278eca60156169155a98519985771270))
|
package/README.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# @darksheep/logger
|
|
2
|
+
Log configuration for NodeJS repos
|
|
3
|
+
|
|
4
|
+
This repo contains environment aware logging configurations for our NodeJS modules that use Winston.
|
|
5
|
+
|
|
6
|
+
This can be added using `yarn add @darksheep/logger` how ever you require a valid npm token for github packages!
|
|
7
|
+
|
|
8
|
+
### Quick start
|
|
9
|
+
#### Get something logging:
|
|
10
|
+
```js
|
|
11
|
+
const { createLogger } = require('@darksheep/logger');
|
|
12
|
+
const logger = createLogger('some-identifier');
|
|
13
|
+
|
|
14
|
+
logger.info('something');
|
|
15
|
+
```
|
|
16
|
+
For ESM
|
|
17
|
+
```js
|
|
18
|
+
import { createLogger } from '@darksheep/logger';
|
|
19
|
+
const logger = createLogger('some-identifier');
|
|
20
|
+
|
|
21
|
+
logger.info('something');
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Utilities
|
|
25
|
+
#### `.context(context: Object, filterLevel?: string)`
|
|
26
|
+
This will make a new logger instance combining the old context with the context provided.
|
|
27
|
+
In the event that `filterLevel` is provided the context may be dropped using the same logic as normal logs.
|
|
28
|
+
|
|
29
|
+
#### `.channel(channel: string)`
|
|
30
|
+
This will make a new logger with the new specifyed channel. for example:
|
|
31
|
+
```js
|
|
32
|
+
logger.info('something')
|
|
33
|
+
// INFO: something
|
|
34
|
+
logger.channel('somewhere').info('test');
|
|
35
|
+
// somewhere.INFO: test
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
#### `.trail(?string)`
|
|
39
|
+
This will make a new logger for following a specific request though routers, for example:
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
const app = express()
|
|
43
|
+
|
|
44
|
+
app.use((request, response, next) => {
|
|
45
|
+
request.logger = logger.trail();
|
|
46
|
+
return next();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
app.get('/', (request, response) => {
|
|
50
|
+
request.logger.warning('I logged a thing');
|
|
51
|
+
response.send('I sent a thing');
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Environment variables:
|
|
56
|
+
| Environment variable | Description |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| `NODE_ENV` | The deployment location |
|
|
59
|
+
| `LOG_LEVEL` | Overrides the log level set by the detected environment |
|
|
60
|
+
| `LOG_FILTERS` | This allows you to allow/block specific channels |
|
|
61
|
+
| `LOG_SECRETS` | This allows you to hide or remove secrets in an object |
|
|
62
|
+
| `LOG_CALLSITES` | Include the callsite of the log |
|
|
63
|
+
|
|
64
|
+
#### `NODE_ENV`
|
|
65
|
+
|
|
66
|
+
| Value | Formatter | Level |
|
|
67
|
+
| --- | --- | --- |
|
|
68
|
+
| `development` | `console` | `debug` |
|
|
69
|
+
| `test` | `json` | `warning` |
|
|
70
|
+
| `production` | `json` | `info` |
|
|
71
|
+
|
|
72
|
+
#### `LOG_LEVEL`
|
|
73
|
+
|
|
74
|
+
Allowed values:
|
|
75
|
+
- `critical` or `crit`
|
|
76
|
+
- `error`
|
|
77
|
+
- `warning` or `warn`
|
|
78
|
+
- `notice`
|
|
79
|
+
- `info`
|
|
80
|
+
- `debug` or `verbose`
|
|
81
|
+
- `silly`
|
|
82
|
+
|
|
83
|
+
#### `LOG_FILTERS`
|
|
84
|
+
|
|
85
|
+
This allows you to filter specific channels from the logs.
|
|
86
|
+
Filters are separated by commas, and negative filters are marked using a leading `-`.
|
|
87
|
+
|
|
88
|
+
Special characters:
|
|
89
|
+
| Character | Meaning |
|
|
90
|
+
| :-: | :-- |
|
|
91
|
+
| `.` | Channel separator |
|
|
92
|
+
| `+` | One level channel wildcard |
|
|
93
|
+
| `*` | Multi level channel wildcard |
|
|
94
|
+
|
|
95
|
+
The following filter means that all calls to the `router` channel will be removed.
|
|
96
|
+
|
|
97
|
+
```shell
|
|
98
|
+
LOG_FILTERS="-router"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The following filter means that ONLY `router` gets logged
|
|
102
|
+
|
|
103
|
+
```shell
|
|
104
|
+
LOG_FILTERS="router"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The following filter means that ONLY `router` gets logged, except for the `router.ok` message.
|
|
108
|
+
|
|
109
|
+
```shell
|
|
110
|
+
LOG_FILTERS="router,-router.ok"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
#### `LOG_SECRETS`
|
|
114
|
+
|
|
115
|
+
Secrets also use a filter, if you use a standard filter the secret key is displayed, if you use a negate the key and value are deleted.
|
|
116
|
+
|
|
117
|
+
The following means that `password` is deleted, and `token` is obscured.
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
// LOG_SECRETS="-password,token"
|
|
121
|
+
|
|
122
|
+
// Given the following context
|
|
123
|
+
const context = {
|
|
124
|
+
password: '1234',
|
|
125
|
+
token: 'asdf',
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Then the following will be logged
|
|
129
|
+
const output = {
|
|
130
|
+
token: '[secret #/token]'
|
|
131
|
+
};
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Wild cards are also permitted.
|
|
135
|
+
|
|
136
|
+
For example, using a positive (obscure) wildcard
|
|
137
|
+
|
|
138
|
+
```js
|
|
139
|
+
// LOG_SECRETS="*.token"
|
|
140
|
+
|
|
141
|
+
// Given the following context
|
|
142
|
+
const context = {
|
|
143
|
+
user: {
|
|
144
|
+
id: '1234',
|
|
145
|
+
token: 'asdf',
|
|
146
|
+
},
|
|
147
|
+
api: {
|
|
148
|
+
name: 'fred',
|
|
149
|
+
token: 'asdf',
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// Then the following will be logged
|
|
154
|
+
const output = {
|
|
155
|
+
user: {
|
|
156
|
+
id: '1234',
|
|
157
|
+
token: '[secret #/user/token]'
|
|
158
|
+
},
|
|
159
|
+
api: {
|
|
160
|
+
name: 'fred',
|
|
161
|
+
token: '[secret #/api/token]'
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
For example, using a negative (delete) wildcard:
|
|
167
|
+
```js
|
|
168
|
+
// LOG_SECRETS="-*.token"
|
|
169
|
+
|
|
170
|
+
// Given the following context
|
|
171
|
+
const context = {
|
|
172
|
+
user: {
|
|
173
|
+
id: '1234',
|
|
174
|
+
token: 'asdf',
|
|
175
|
+
},
|
|
176
|
+
api: {
|
|
177
|
+
name: 'fred',
|
|
178
|
+
token: 'asdf',
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// Then the following will be logged
|
|
183
|
+
const output = {
|
|
184
|
+
user: { id: '1234' },
|
|
185
|
+
api: { name: 'fred' }
|
|
186
|
+
};
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
#### `LOG_CALLSITES`
|
|
190
|
+
|
|
191
|
+
This is simply a boolean to indicate whether or not the location of the place calling the log should be added to the context
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@darksheep/logger",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Logging your stuff to where ever you want it",
|
|
5
|
+
"license": "UNLICENCED",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./types/index.d.ts",
|
|
10
|
+
"import": "./src/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"src",
|
|
15
|
+
"types",
|
|
16
|
+
"!*.test.js",
|
|
17
|
+
"!assert.zod.js"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"lint": "eslint .",
|
|
21
|
+
"lint:fix": "eslint . --fix",
|
|
22
|
+
"prepack": "tsc",
|
|
23
|
+
"test": "node --test src/**/*.test.js",
|
|
24
|
+
"test:watch": "node --watch --test src/**/*.test.js"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"deepmerge-ts": "7.0.3",
|
|
28
|
+
"stacktrace-parser": "0.1.10",
|
|
29
|
+
"traverse": "0.6.9"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@darksheep/eslint": "~4.4.0",
|
|
33
|
+
"@types/eslint": "~8.56.0",
|
|
34
|
+
"@types/node": "~20.14.0",
|
|
35
|
+
"@types/traverse": "~0.6.33",
|
|
36
|
+
"eslint": "~9.6.0",
|
|
37
|
+
"nock": "~13.5.0",
|
|
38
|
+
"typescript": "~5.5.0",
|
|
39
|
+
"zod": "~3.23.0"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": "^20.13.1 || ^22.2.0"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Logger } from './logger.js';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
SecretDelete,
|
|
5
|
+
SecretObscure,
|
|
6
|
+
BufferReplacer,
|
|
7
|
+
ErrorReplacer,
|
|
8
|
+
HttpClientRequestReplacer,
|
|
9
|
+
HttpIncomingMessageReplacer,
|
|
10
|
+
HttpServerResponseReplacer,
|
|
11
|
+
LongStringReplacer,
|
|
12
|
+
NetSocketReplacer,
|
|
13
|
+
} from './replacers/index.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {string} [channel] The logging channel
|
|
17
|
+
* @returns {import('./logger.js').Logger}
|
|
18
|
+
*/
|
|
19
|
+
export function createLogger(channel) {
|
|
20
|
+
return new Logger(
|
|
21
|
+
typeof channel === 'string' ? { channel } : {},
|
|
22
|
+
{ replacers: [
|
|
23
|
+
// Always run the secret replacer first.
|
|
24
|
+
SecretDelete,
|
|
25
|
+
SecretObscure,
|
|
26
|
+
|
|
27
|
+
ErrorReplacer,
|
|
28
|
+
HttpClientRequestReplacer,
|
|
29
|
+
HttpIncomingMessageReplacer,
|
|
30
|
+
HttpServerResponseReplacer,
|
|
31
|
+
LongStringReplacer,
|
|
32
|
+
NetSocketReplacer,
|
|
33
|
+
BufferReplacer,
|
|
34
|
+
] },
|
|
35
|
+
);
|
|
36
|
+
}
|
package/src/formatter.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { environment } from './utilities/environment.js';
|
|
2
|
+
import { formatterJson } from './formatters/formatter-json.js';
|
|
3
|
+
import { formatterConsole } from './formatters/formatter-console.js';
|
|
4
|
+
|
|
5
|
+
const isConsole = (
|
|
6
|
+
process.stdout.isTTY === true &&
|
|
7
|
+
environment.isDevelopment === true
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {(logEntry: import('./utilities/log-types.js').LogEntry) => string} Formatter
|
|
12
|
+
*/
|
|
13
|
+
/** @type {Formatter} */
|
|
14
|
+
export const formatter = isConsole === true ? formatterConsole : formatterJson;
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { inspect } from 'node:util';
|
|
2
|
+
import { LogNames } from '../utilities/log-types.js';
|
|
3
|
+
import { colourString, shouldColour } from '../utilities/colour.js';
|
|
4
|
+
|
|
5
|
+
/** @typedef {import('../utilities/log-types.js').LogContext} LogContext */
|
|
6
|
+
/** @typedef {import('../utilities/log-types.js').LogEntry} LogEntry */
|
|
7
|
+
/** @typedef {import('../utilities/log-types.js').LogLevel} LogLevel */
|
|
8
|
+
/** @typedef {import('../utilities/log-types.js').Callsite} Callsite */
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {string} [channel] The channel string
|
|
12
|
+
* @param {LogLevel} [level] The LogLevel number
|
|
13
|
+
* @returns {string}
|
|
14
|
+
*/
|
|
15
|
+
function flattenChannelLevel(channel, level) {
|
|
16
|
+
let output = '';
|
|
17
|
+
|
|
18
|
+
if (channel == null && level == null) {
|
|
19
|
+
return output;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (channel != null) {
|
|
23
|
+
output += channel;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (channel != null && level != null) {
|
|
27
|
+
output += '.';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (level != null) {
|
|
31
|
+
output += `${LogNames[level].toUpperCase()}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
output += ': ';
|
|
35
|
+
|
|
36
|
+
return output;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {string} _match The whole match
|
|
41
|
+
* @param {string} type The module type
|
|
42
|
+
* @param {string} name The module name
|
|
43
|
+
* @returns {string}
|
|
44
|
+
*/
|
|
45
|
+
function underlineName(_match, type, name) {
|
|
46
|
+
const moduleName = colourString(name, { effects: [ 'underline' ] });
|
|
47
|
+
|
|
48
|
+
return `${type}${moduleName}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @param {Callsite} callsite The callsite to underline
|
|
53
|
+
* @returns {string}
|
|
54
|
+
*/
|
|
55
|
+
function formatFileSegment(callsite) {
|
|
56
|
+
let { file } = callsite;
|
|
57
|
+
|
|
58
|
+
file = file
|
|
59
|
+
.replace(/^((?:node:)?internal\/)([^/]+)/g, underlineName)
|
|
60
|
+
.replace(/^(\w+:)([a-z]\w+)$/g, underlineName)
|
|
61
|
+
.replace(/(node_modules\/)(@[^/]+\/[^/]+|[^/]+)/g, underlineName);
|
|
62
|
+
|
|
63
|
+
if (typeof callsite.line === 'number') {
|
|
64
|
+
file += `:${callsite.line}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (typeof callsite.column === 'number') {
|
|
68
|
+
file += `:${callsite.column}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return file;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @param {Callsite} callsite The node module callsite to format
|
|
76
|
+
* @returns {string}
|
|
77
|
+
*/
|
|
78
|
+
function stackLineModule(callsite) {
|
|
79
|
+
const file = colourString(
|
|
80
|
+
formatFileSegment(callsite),
|
|
81
|
+
{ effects: [ 'faint' ] },
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
return colourString(
|
|
85
|
+
`at ${callsite.methodName} (${file})`,
|
|
86
|
+
{ foreground: 'yellow' },
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @param {Callsite} callsite The internal callsite to format
|
|
92
|
+
* @returns {string}
|
|
93
|
+
*/
|
|
94
|
+
function stackLineInternal(callsite) {
|
|
95
|
+
const file = formatFileSegment(callsite);
|
|
96
|
+
return colourString(
|
|
97
|
+
`at ${callsite.methodName} (${file})`,
|
|
98
|
+
{ foreground: 'black', foregroundMode: 'bright' },
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @param {Callsite} callsite The callsite to format
|
|
104
|
+
* @returns {string}
|
|
105
|
+
*/
|
|
106
|
+
function stackLine(callsite) {
|
|
107
|
+
if (callsite.file.startsWith('node_modules')) {
|
|
108
|
+
return stackLineModule(callsite);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (
|
|
112
|
+
callsite.file === '<anonymous>' ||
|
|
113
|
+
callsite.file.startsWith('node:') ||
|
|
114
|
+
callsite.file.startsWith('internal')
|
|
115
|
+
) {
|
|
116
|
+
return stackLineInternal(callsite);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let file = colourString(callsite.file, {
|
|
120
|
+
foreground: 'green',
|
|
121
|
+
foregroundMode: 'bright',
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
file += `:${callsite.line}`;
|
|
125
|
+
if (typeof callsite.column === 'number') {
|
|
126
|
+
file += colourString(
|
|
127
|
+
`:${callsite.column}`,
|
|
128
|
+
{ foreground: 'black', foregroundMode: 'bright' },
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return `at ${callsite.methodName} (${file})`;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const fixEmpties = /^\{\s*\}$/;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @param {LogContext} entry The entry we're going to convert
|
|
139
|
+
* @returns {string}
|
|
140
|
+
*/
|
|
141
|
+
function splatify(entry) {
|
|
142
|
+
const object = { ...entry };
|
|
143
|
+
|
|
144
|
+
delete object.timestamp;
|
|
145
|
+
delete object.channel;
|
|
146
|
+
delete object.level;
|
|
147
|
+
delete object.type;
|
|
148
|
+
delete object.message;
|
|
149
|
+
delete object.stack;
|
|
150
|
+
delete object.cause;
|
|
151
|
+
|
|
152
|
+
return inspect(object, {
|
|
153
|
+
colors: shouldColour(),
|
|
154
|
+
getters: true,
|
|
155
|
+
depth: null,
|
|
156
|
+
})
|
|
157
|
+
.replace(fixEmpties, '')
|
|
158
|
+
.trim();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @param {Callsite[] | string | undefined} stack The stack trace to format
|
|
163
|
+
* @param {number} indent The indent to render from
|
|
164
|
+
* @returns {string}
|
|
165
|
+
*/
|
|
166
|
+
function formatStack(stack, indent) {
|
|
167
|
+
const pad = ' '.repeat(indent + 2);
|
|
168
|
+
if (Array.isArray(stack)) {
|
|
169
|
+
return stack
|
|
170
|
+
.map(stackLine)
|
|
171
|
+
.map((line) => `${pad}${line.trim()}`)
|
|
172
|
+
.join('\n');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return (stack ?? '')
|
|
176
|
+
.split(/\r?\n|\r/)
|
|
177
|
+
.map((line) => `${pad}${line.trim()}`)
|
|
178
|
+
.join('\n');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* @param {Error} error The error to render
|
|
183
|
+
* @param {number} [indent] The indent to render from
|
|
184
|
+
* @returns {string}
|
|
185
|
+
*/
|
|
186
|
+
function formatError(error, indent = 0) {
|
|
187
|
+
const pad = ' '.repeat(indent);
|
|
188
|
+
let output = pad;
|
|
189
|
+
|
|
190
|
+
output +=
|
|
191
|
+
indent > 0
|
|
192
|
+
? colourString(
|
|
193
|
+
error.message,
|
|
194
|
+
{ foreground: 'red', effects: [ 'bold' ] },
|
|
195
|
+
)
|
|
196
|
+
: `${error.message}`;
|
|
197
|
+
|
|
198
|
+
const stack = formatStack(error.stack, indent);
|
|
199
|
+
if (stack.trim().length > 0) {
|
|
200
|
+
output += `\n${stack}`;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (error?.cause != null) {
|
|
204
|
+
const cause = formatError(
|
|
205
|
+
/** @type {Error} */ (error?.cause),
|
|
206
|
+
indent + 2,
|
|
207
|
+
);
|
|
208
|
+
output += `\n${cause}`;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return output;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* @param {LogEntry} logEntry the Log entry which we're going to convert to a splatted string
|
|
216
|
+
* @returns {string}
|
|
217
|
+
*/
|
|
218
|
+
export function formatterConsole(logEntry) {
|
|
219
|
+
const {
|
|
220
|
+
timestamp,
|
|
221
|
+
channel,
|
|
222
|
+
level,
|
|
223
|
+
message,
|
|
224
|
+
stack,
|
|
225
|
+
cause,
|
|
226
|
+
...entry
|
|
227
|
+
} = logEntry;
|
|
228
|
+
|
|
229
|
+
let output = '';
|
|
230
|
+
|
|
231
|
+
if (timestamp instanceof Date) {
|
|
232
|
+
output += `[${timestamp.toISOString()}] `;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
output += flattenChannelLevel(channel, level);
|
|
236
|
+
|
|
237
|
+
output += formatError(/** @type {Error} */ ({ message, stack, cause }));
|
|
238
|
+
|
|
239
|
+
const context = splatify(entry)
|
|
240
|
+
.replace(/\r?\n|\r/g, '\n ');
|
|
241
|
+
|
|
242
|
+
return `${output}\n ${context}`.trim();
|
|
243
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createLogger } from './create-logger.js';
|
|
2
|
+
|
|
3
|
+
/** @typedef {import('./utilities/log-types.js').LogContext} LogContext */
|
|
4
|
+
/** @typedef {import('./utilities/log-types.js').LogEntry} LogEntry */
|
|
5
|
+
/** @typedef {import('./utilities/log-types.js').LogLevel} LogLevel */
|
|
6
|
+
/** @typedef {import('./formatter.js').Formatter} Formatter */
|
|
7
|
+
/** @typedef {import('./replacer.js').Replacer} Replacer */
|
|
8
|
+
|
|
9
|
+
export { Logger } from './logger.js';
|
|
10
|
+
export const logger = createLogger();
|
|
11
|
+
|
|
12
|
+
export { environment } from './utilities/environment.js';
|
|
13
|
+
export { createLogger } from './create-logger.js';
|
|
14
|
+
export { LogLevels } from './utilities/log-types.js';
|
|
15
|
+
export {
|
|
16
|
+
SecretDelete,
|
|
17
|
+
SecretObscure,
|
|
18
|
+
BufferReplacer,
|
|
19
|
+
ErrorReplacer,
|
|
20
|
+
HttpClientRequestReplacer,
|
|
21
|
+
HttpIncomingMessageReplacer,
|
|
22
|
+
HttpServerResponseReplacer,
|
|
23
|
+
LongStringReplacer,
|
|
24
|
+
NetSocketReplacer,
|
|
25
|
+
} from './replacers/index.js';
|