@fend/firo 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +180 -155
- package/dist/index.cjs +39 -33
- package/dist/index.d.cts +43 -31
- package/dist/index.d.ts +43 -31
- package/dist/index.js +37 -31
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
[](https://github.com/fend25/firo/actions/workflows/publish.yml)
|
|
8
8
|
[](https://github.com/fend25/firo)
|
|
9
9
|
|
|
10
|
-
**Spruce up your logs!**
|
|
10
|
+
**Spruce up your logs!**
|
|
11
11
|
|
|
12
12
|
The logger for Node.js, Bun and Deno you've been looking for.
|
|
13
13
|
|
|
14
|
-
Beautiful **dev** output - out of the box.
|
|
14
|
+
Beautiful **dev** output - out of the box. Fast, structured NDJSON for **prod**.
|
|
15
15
|
|
|
16
16
|
Think of it as pino, but with brilliant DX.
|
|
17
17
|
|
|
@@ -93,36 +93,6 @@ log.info('Request handled', { status: 200 })
|
|
|
93
93
|
// {"timestamp":"2024-01-15T14:32:01.204Z","level":"info","message":"Request handled","data":{"status":200}}
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
-
## Best practices
|
|
97
|
-
|
|
98
|
-
### AsyncLocalStorage (Traceability)
|
|
99
|
-
|
|
100
|
-
The best way to use **firo** in web frameworks is to store a child logger in `AsyncLocalStorage`. This gives you automatic traceability (e.g. `requestId`) across your entire call stack without passing the logger as an argument.
|
|
101
|
-
|
|
102
|
-
```ts
|
|
103
|
-
import { AsyncLocalStorage } from 'node:util'
|
|
104
|
-
import { createFiro } from '@fend/firo'
|
|
105
|
-
|
|
106
|
-
const logger = createFiro()
|
|
107
|
-
const storage = new AsyncLocalStorage()
|
|
108
|
-
|
|
109
|
-
// Middleware example
|
|
110
|
-
function middleware(req, res, next) {
|
|
111
|
-
const reqLog = logger.child({
|
|
112
|
-
requestId: req.headers['x-request-id'] || 'gen-123',
|
|
113
|
-
method: req.method
|
|
114
|
-
})
|
|
115
|
-
storage.run(reqLog, next)
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Deeply nested function
|
|
119
|
-
function someService() {
|
|
120
|
-
const log = storage.getStore() ?? logger
|
|
121
|
-
log.info('Service action performed')
|
|
122
|
-
// Output: [requestId:gen-123] [method:GET] Service action performed
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
96
|
## Log levels
|
|
127
97
|
|
|
128
98
|
Four levels, in order: `debug` → `info` → `warn` → `error`.
|
|
@@ -139,16 +109,31 @@ Debug lines are dimmed in dev mode to reduce visual noise.
|
|
|
139
109
|
### Filtering
|
|
140
110
|
|
|
141
111
|
```ts
|
|
142
|
-
// Suppress debug in dev, keep everything in prod
|
|
143
|
-
const log = createFiro({
|
|
144
|
-
minLevelInDev: 'info',
|
|
145
|
-
minLevelInProd: 'warn',
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
// Or a single threshold for both modes
|
|
149
112
|
const log = createFiro({ minLevel: 'warn' })
|
|
150
113
|
```
|
|
151
114
|
|
|
115
|
+
## Error signatures
|
|
116
|
+
|
|
117
|
+
`error()` accepts multiple call signatures:
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
// Message only will be automatically wrapped in an Error object to intentionally capture and preserve the stack trace
|
|
121
|
+
// because stack trace with a couple of extra levels of indirection is definitely better than no stack trace at all
|
|
122
|
+
log.error('Something went wrong')
|
|
123
|
+
|
|
124
|
+
// Message + Error object
|
|
125
|
+
log.error('Query failed', new Error('timeout'))
|
|
126
|
+
|
|
127
|
+
// Error object only
|
|
128
|
+
log.error(new Error('Unhandled'))
|
|
129
|
+
|
|
130
|
+
// Error + extra data
|
|
131
|
+
log.error(new Error('DB down'), { query: 'SELECT ...', reqId: 123 })
|
|
132
|
+
|
|
133
|
+
// Anything — will be coerced to Error
|
|
134
|
+
log.error(someUnknownThing)
|
|
135
|
+
```
|
|
136
|
+
|
|
152
137
|
## Context
|
|
153
138
|
|
|
154
139
|
Attach persistent key/value pairs to a logger instance. They appear in every log line.
|
|
@@ -166,29 +151,47 @@ log.info('Started')
|
|
|
166
151
|
|
|
167
152
|
### Context options
|
|
168
153
|
|
|
154
|
+
Three ways to add context:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
// 1. Simple key-value — just the basics
|
|
158
|
+
log.addContext('service', 'auth')
|
|
159
|
+
|
|
160
|
+
// 2. Key + value with options — when you need control
|
|
161
|
+
log.addContext('traceId', { value: 'abc-123-xyz', hideIn: 'dev' })
|
|
162
|
+
log.addContext('region', { value: 'west', color: '38;5;214' })
|
|
163
|
+
|
|
164
|
+
// 3. Object form — everything in one object
|
|
165
|
+
log.addContext({ key: 'userId', value: 'u-789', omitKey: true })
|
|
166
|
+
log.addContext({ key: 'span', value: 'xyz', color: '38;2;255;100;0' })
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Available options (styles 2 and 3):
|
|
170
|
+
|
|
169
171
|
```ts
|
|
170
|
-
// Hide the key, show only the value
|
|
172
|
+
// Hide the key, show only the value: [u-789] instead of [userId:u-789]
|
|
171
173
|
log.addContext({ key: 'userId', value: 'u-789', omitKey: true })
|
|
172
|
-
// renders as [u-789] instead of [userId:u-789]
|
|
173
174
|
|
|
174
175
|
// Pin a specific color by palette index (0–29)
|
|
175
|
-
log.addContext(
|
|
176
|
+
log.addContext('region', { value: 'west', colorIndex: 3 })
|
|
176
177
|
|
|
177
178
|
// Use any ANSI color — 256-color, truecolor, anything
|
|
178
|
-
log.addContext(
|
|
179
|
+
log.addContext('trace', { value: 'abc', color: '38;5;214' }) // 256-color orange
|
|
179
180
|
log.addContext({ key: 'span', value: 'xyz', color: '38;2;255;100;0' }) // truecolor
|
|
180
|
-
```
|
|
181
181
|
|
|
182
|
-
|
|
182
|
+
// Hide in dev — useful for traceIds that clutter the terminal
|
|
183
|
+
log.addContext('traceId', { value: 'abc-123-xyz', hideIn: 'dev' })
|
|
183
184
|
|
|
184
|
-
|
|
185
|
-
log.
|
|
185
|
+
// Hide in prod — dev-only debugging context
|
|
186
|
+
log.addContext('debugTag', { value: 'perf-test', hideIn: 'prod' })
|
|
186
187
|
```
|
|
187
188
|
|
|
188
|
-
###
|
|
189
|
+
### Context API
|
|
189
190
|
|
|
190
191
|
```ts
|
|
191
|
-
|
|
192
|
+
log.getContext() // ContextItem[]
|
|
193
|
+
log.hasInContext('key') // boolean
|
|
194
|
+
log.removeFromContext('env')
|
|
192
195
|
```
|
|
193
196
|
|
|
194
197
|
## Child loggers
|
|
@@ -234,85 +237,75 @@ log.error('Payment failed', err, {
|
|
|
234
237
|
})
|
|
235
238
|
```
|
|
236
239
|
|
|
237
|
-
##
|
|
240
|
+
## Dev formatter options
|
|
238
241
|
|
|
239
|
-
|
|
242
|
+
Fine-tune the dev formatter's timestamp format. For example, to remove seconds and milliseconds:
|
|
240
243
|
|
|
241
244
|
```ts
|
|
242
|
-
|
|
243
|
-
log.error('Something went wrong')
|
|
245
|
+
import { createFiro } from '@fend/firo'
|
|
244
246
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
+
const log = createFiro({
|
|
248
|
+
devFormatterConfig: {
|
|
249
|
+
timeOptions: {
|
|
250
|
+
hour: '2-digit',
|
|
251
|
+
minute: '2-digit',
|
|
252
|
+
second: undefined,
|
|
253
|
+
fractionalSecondDigits: undefined
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
})
|
|
257
|
+
```
|
|
247
258
|
|
|
248
|
-
|
|
249
|
-
log.error(new Error('Unhandled'))
|
|
259
|
+
## Color palette
|
|
250
260
|
|
|
251
|
-
|
|
252
|
-
log.error(someUnknownThing)
|
|
253
|
-
```
|
|
261
|
+
Most loggers give you monochrome walls of text. Firo gives you **30 handpicked colors** that make context badges instantly scannable — you stop reading and start seeing.
|
|
254
262
|
|
|
255
|
-
|
|
263
|
+

|
|
264
|
+
|
|
265
|
+
### How it works
|
|
266
|
+
|
|
267
|
+
By default, firo auto-assigns colors from all 30 palette colors using a hash of the context key. Similar keys like `user-1` and `user-2` land on different colors automatically.
|
|
256
268
|
|
|
257
|
-
|
|
269
|
+
You can also pin a specific color using `FIRO_COLORS` — a named palette with full IDE autocomplete:
|
|
258
270
|
|
|
259
271
|
```ts
|
|
260
|
-
import
|
|
272
|
+
import { createFiro, FIRO_COLORS } from '@fend/firo'
|
|
261
273
|
|
|
262
|
-
const
|
|
263
|
-
// level: 'debug' | 'info' | 'warn' | 'error'
|
|
264
|
-
// context: ContextItemWithOptions[]
|
|
265
|
-
// msg: string | Error | unknown
|
|
266
|
-
// data: Error | unknown
|
|
267
|
-
// opts: LogOptions | undefined
|
|
268
|
-
}
|
|
274
|
+
const log = createFiro()
|
|
269
275
|
|
|
270
|
-
|
|
276
|
+
log.addContext('region', { value: 'west', color: FIRO_COLORS.coral })
|
|
277
|
+
log.addContext('service', { value: 'auth', color: FIRO_COLORS.skyBlue })
|
|
278
|
+
log.addContext('env', { value: 'staging', color: FIRO_COLORS.lavender })
|
|
271
279
|
```
|
|
272
280
|
|
|
273
|
-
|
|
281
|
+
Available colors: `cyan`, `green`, `yellow`, `magenta`, `blue`, `brightCyan`, `brightGreen`, `brightYellow`, `brightMagenta`, `brightBlue`, `orange`, `pink`, `lilac`, `skyBlue`, `mint`, `salmon`, `lemon`, `lavender`, `sage`, `coral`, `teal`, `rose`, `pistachio`, `mauve`, `aqua`, `gold`, `thistle`, `seafoam`, `tangerine`, `periwinkle`.
|
|
274
282
|
|
|
275
|
-
|
|
283
|
+
### Want even more variety?
|
|
276
284
|
|
|
277
|
-
|
|
278
|
-
import { FiroUtils } from '@fend/firo'
|
|
285
|
+
You can also pass any raw ANSI code as a string — 256-color, truecolor, go wild:
|
|
279
286
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
FiroUtils.jsonReplacer // replacer for JSON.stringify (handles bigint)
|
|
284
|
-
FiroUtils.colorize(text, idx) // wrap text in ANSI color by palette index
|
|
285
|
-
FiroUtils.colorizeLevel(level, t) // wrap text in level color (red/yellow/dim)
|
|
287
|
+
```ts
|
|
288
|
+
log.addContext('trace', { value: 'abc', color: '38;5;214' }) // 256-color
|
|
289
|
+
log.addContext('span', { value: 'xyz', color: '38;2;255;105;180' }) // truecolor pink
|
|
286
290
|
```
|
|
287
291
|
|
|
288
|
-
|
|
292
|
+
### Restrict to safe colors
|
|
289
293
|
|
|
290
|
-
|
|
294
|
+
If your terminal doesn't support 256 colors, you can restrict auto-hash to 10 basic terminal-safe colors:
|
|
291
295
|
|
|
292
296
|
```ts
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
const log = createFiro({
|
|
296
|
-
devTransportConfig: {
|
|
297
|
-
timeOptions: {
|
|
298
|
-
hour: '2-digit',
|
|
299
|
-
minute: '2-digit',
|
|
300
|
-
second: undefined,
|
|
301
|
-
fractionalSecondDigits: undefined
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
})
|
|
297
|
+
const log = createFiro({ useSafeColors: true })
|
|
305
298
|
```
|
|
306
299
|
|
|
307
|
-
## Prod
|
|
300
|
+
## Prod formatter options
|
|
308
301
|
|
|
309
|
-
Configure the prod (JSON)
|
|
302
|
+
Configure the prod (JSON) formatter's timestamp format:
|
|
310
303
|
|
|
311
304
|
```ts
|
|
312
305
|
// Epoch ms (faster, same as pino)
|
|
313
306
|
const log = createFiro({
|
|
314
307
|
mode: 'prod',
|
|
315
|
-
|
|
308
|
+
prodFormatterConfig: { timestamp: 'epoch' }
|
|
316
309
|
})
|
|
317
310
|
// {"timestamp":1711100000000,"level":"info","message":"hello"}
|
|
318
311
|
|
|
@@ -323,7 +316,7 @@ const log = createFiro({ mode: 'prod' })
|
|
|
323
316
|
|
|
324
317
|
### Custom destination
|
|
325
318
|
|
|
326
|
-
By default, prod
|
|
319
|
+
By default, prod formatter writes to `process.stdout`. You can redirect output to any object with a `.write(string)` method:
|
|
327
320
|
|
|
328
321
|
```ts
|
|
329
322
|
import { createFiro } from '@fend/firo'
|
|
@@ -332,63 +325,89 @@ import { createWriteStream } from 'node:fs'
|
|
|
332
325
|
// Write to a file
|
|
333
326
|
const log = createFiro({
|
|
334
327
|
mode: 'prod',
|
|
335
|
-
|
|
328
|
+
prodFormatterConfig: { dest: createWriteStream('/var/log/app.log') }
|
|
336
329
|
})
|
|
337
330
|
|
|
338
331
|
// Use SonicBoom for async buffered writes (same as pino)
|
|
339
332
|
import SonicBoom from 'sonic-boom'
|
|
340
333
|
const log = createFiro({
|
|
341
334
|
mode: 'prod',
|
|
342
|
-
|
|
335
|
+
prodFormatterConfig: { dest: new SonicBoom({ fd: 1 }) }
|
|
343
336
|
})
|
|
344
337
|
```
|
|
345
338
|
|
|
346
|
-
##
|
|
339
|
+
## Custom formatter
|
|
347
340
|
|
|
348
|
-
|
|
341
|
+
If for some reason all the options are not enough and you need to take full control of the output, you can provide your own formatter function.
|
|
349
342
|
|
|
350
|
-
|
|
343
|
+
```ts
|
|
344
|
+
import type { FormatterFn } from '@fend/firo'
|
|
351
345
|
|
|
352
|
-
|
|
346
|
+
const myFormatter: FormatterFn = (level, context, msg, data, opts) => {
|
|
347
|
+
// level: 'debug' | 'info' | 'warn' | 'error'
|
|
348
|
+
// context: ContextItemWithOptions[]
|
|
349
|
+
// msg: string | Error | unknown
|
|
350
|
+
// data: Error | unknown
|
|
351
|
+
// opts: LogOptions | undefined
|
|
352
|
+
}
|
|
353
353
|
|
|
354
|
-
|
|
354
|
+
const log = createFiro({ formatter: myFormatter })
|
|
355
|
+
```
|
|
355
356
|
|
|
356
|
-
You
|
|
357
|
+
You don't have to start from scratch — all the helpers we use internally are yours too:
|
|
357
358
|
|
|
358
|
-
|
|
359
|
-
import { createFiro, FIRO_COLORS } from '@fend/firo'
|
|
359
|
+
#### FiroUtils
|
|
360
360
|
|
|
361
|
-
|
|
361
|
+
`FiroUtils` exposes helper functions useful for building custom formatters:
|
|
362
362
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
363
|
+
```ts
|
|
364
|
+
import { FiroUtils } from '@fend/firo'
|
|
365
|
+
|
|
366
|
+
FiroUtils.wrapToError(value) // coerce unknown → Error
|
|
367
|
+
FiroUtils.serializeError(err) // Error → plain object { message, stack, name, cause?, ... }
|
|
368
|
+
FiroUtils.safeStringify(obj) // JSON.stringify with bigint support + fallback
|
|
369
|
+
FiroUtils.jsonReplacer // replacer for JSON.stringify (handles bigint)
|
|
370
|
+
FiroUtils.extractMessage(msg) // extract message string from string | Error | unknown
|
|
371
|
+
FiroUtils.colorize(text, idx, c?) // wrap text in ANSI color by palette index or raw code
|
|
372
|
+
FiroUtils.colorizeLevel(level, t) // wrap text in level color (red/yellow/dim)
|
|
366
373
|
```
|
|
367
374
|
|
|
368
|
-
|
|
375
|
+
## Best practices
|
|
369
376
|
|
|
370
|
-
###
|
|
377
|
+
### AsyncLocalStorage (Traceability)
|
|
371
378
|
|
|
372
|
-
|
|
379
|
+
The best way to use **firo** in web frameworks is to store a child logger in `AsyncLocalStorage`. This gives you automatic traceability (e.g. `requestId`) across your entire call stack without passing the logger as an argument.
|
|
373
380
|
|
|
374
381
|
```ts
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
```
|
|
382
|
+
import { AsyncLocalStorage } from 'node:util'
|
|
383
|
+
import { createFiro } from '@fend/firo'
|
|
378
384
|
|
|
379
|
-
|
|
385
|
+
const logger = createFiro()
|
|
386
|
+
const storage = new AsyncLocalStorage()
|
|
380
387
|
|
|
381
|
-
|
|
388
|
+
// Middleware — traceId is essential in prod logs but noisy in dev terminal
|
|
389
|
+
function middleware(req, res, next) {
|
|
390
|
+
const reqLog = logger.child({
|
|
391
|
+
traceId: { value: req.headers['x-trace-id'] || crypto.randomUUID(), hideIn: 'dev' },
|
|
392
|
+
method: req.method
|
|
393
|
+
})
|
|
394
|
+
storage.run(reqLog, next)
|
|
395
|
+
}
|
|
382
396
|
|
|
383
|
-
|
|
384
|
-
|
|
397
|
+
// Deeply nested function — no logger passing needed
|
|
398
|
+
function someService() {
|
|
399
|
+
const log = storage.getStore() ?? logger
|
|
400
|
+
log.info('Service action performed')
|
|
401
|
+
// dev: [method:GET] Service action performed
|
|
402
|
+
// prod: {"traceId":"a1b2c3","method":"GET","message":"Service action performed"}
|
|
403
|
+
}
|
|
385
404
|
```
|
|
386
405
|
|
|
387
406
|
## Why not pino?
|
|
388
407
|
|
|
389
|
-
**Pino** is Italian for *Pine*. It's a great, sturdy tree, especially in production.
|
|
408
|
+
**Pino** is Italian for *Pine*. It's a great, sturdy tree, especially in production.
|
|
390
409
|
|
|
391
|
-
But sometimes you need to **Spruce** up your development experience.
|
|
410
|
+
But sometimes you need to **Spruce** up your development experience.
|
|
392
411
|
|
|
393
412
|
The problem with pino is development. Its default output is raw JSON — one giant line per log entry, completely unreadable. You reach for `pino-pretty`, and suddenly you're maintaining infrastructure just to see what your app is doing.
|
|
394
413
|
|
|
@@ -400,36 +419,35 @@ The problem with pino is development. Its default output is raw JSON — one gia
|
|
|
400
419
|
- **Visual hierarchy:** Debug lines are dimmed; high-signal logs stay readable.
|
|
401
420
|
- **Zero config:** Beautiful output from the first second.
|
|
402
421
|
|
|
403
|
-
In prod it emits clean NDJSON, same as pino. Your log aggregator won't know the difference.
|
|
422
|
+
In prod it emits clean NDJSON, same as pino. Your log aggregator won't know the difference. And the speed tax? Smaller than you'd think.
|
|
404
423
|
|
|
405
424
|
## Performance
|
|
406
425
|
|
|
407
|
-
|
|
426
|
+
Firo vs [pino](https://github.com/pinojs/pino) — head-to-head, both writing to stdout, same machine, same conditions.
|
|
427
|
+
|
|
428
|
+
| Scenario | pino ops/sec | firo ops/sec | pino ms | firo ms | diff |
|
|
429
|
+
| ------------------------------ | -----------: | -----------: | ------: | ------: | -------: |
|
|
430
|
+
| simple string | 941,986 | 812,970 | 106.2 | 123.0 | +15.82% |
|
|
431
|
+
| string + small obj | 749,782 | 673,332 | 133.4 | 148.5 | +11.32% |
|
|
432
|
+
| string + bigger obj | 582,000 | 523,643 | 171.8 | 191.0 | +11.18% |
|
|
433
|
+
| with 3 context items | 818,123 | 589,433 | 122.2 | 169.7 | +38.87% |
|
|
434
|
+
| child logger (2 ctx) | 807,551 | 592,472 | 123.8 | 168.8 | +36.35% |
|
|
435
|
+
| deep child (7 ctx) + rich data | 408,246 | 314,244 | 245.0 | 318.2 | +29.88% |
|
|
436
|
+
| error with Error obj | 389,665 | 458,247 | 256.6 | 218.2 | -14.96% |
|
|
437
|
+
|
|
438
|
+
<sub>Apple M1, Node.js 25, 10 runs × 100K logs per scenario.</sub>
|
|
439
|
+
|
|
440
|
+
Pino is backed by 10 years of relentless optimization: [SonicBoom](https://github.com/pinojs/sonic-boom) async writer, [fast-json-stringify](https://github.com/fastify/fast-json-stringify) with schema-compiled serialization, pre-serialized child context stored as raw JSON fragments, C++ worker threads. It is an obsessively optimized piece of engineering and fully deserves its reputation as the fastest logger in Node.js.
|
|
408
441
|
|
|
409
|
-
|
|
410
|
-
| ---------------------- | ------- | ------ |
|
|
411
|
-
| simple string | 782,488 | 127.8 |
|
|
412
|
-
| string + small obj | 656,512 | 152.3 |
|
|
413
|
-
| string + bigger obj | 513,087 | 194.9 |
|
|
414
|
-
| with 3 context items | 570,441 | 175.3 |
|
|
415
|
-
| child logger (2 ctx) | 568,977 | 175.8 |
|
|
416
|
-
| error with Error obj | 470,758 | 212.4 |
|
|
442
|
+
Firo uses the most vanilla tools imaginable — `JSON.stringify` and `process.stdout.write`, shipping since 2009. Zero dependencies. Zero tricks. ~30% behind pino on a realistic deep-child scenario with nested payloads. 15% ahead on error serialization.
|
|
417
443
|
|
|
418
|
-
For
|
|
444
|
+
For context, here's where the other loggers stand according to [pino's own benchmarks](https://github.com/pinojs/pino/blob/main/docs/benchmarks.md) (basic "hello world", same machine): winston 174ms, bunyan 228ms, bole 107ms. firo's 123ms puts it comfortably ahead of winston and bunyan, neck and neck with bole — and all of that with a DX that none of them can match.
|
|
419
445
|
|
|
420
|
-
|
|
421
|
-
| ---------------- | ------ |
|
|
422
|
-
| pino | 114.8 |
|
|
423
|
-
| **firo** | **127.8** |
|
|
424
|
-
| bole | 172.7 |
|
|
425
|
-
| pino (NodeStream)| 159.2 |
|
|
426
|
-
| debug | 220.5 |
|
|
427
|
-
| winston | 270.2 |
|
|
428
|
-
| bunyan | 377.4 |
|
|
446
|
+
So yes — if you're looking for a pino alternative with gorgeous DX, structured context, and beautiful dev output, firo is right there performance-wise. Almost a drop-in replacement.*
|
|
429
447
|
|
|
430
|
-
|
|
448
|
+
<sub>* Okay, not exactly drop-in — we put the message first and the data second, like normal humans. `log.info("hello", data)` instead of `log.info(data, "hello")`. We'll let you decide which API sparks more joy.</sub>
|
|
431
449
|
|
|
432
|
-
Run
|
|
450
|
+
Run the benchmark yourself: `pnpm bench`
|
|
433
451
|
|
|
434
452
|
## API reference
|
|
435
453
|
|
|
@@ -452,14 +470,21 @@ Run it yourself: `pnpm bench`
|
|
|
452
470
|
|
|
453
471
|
| Option | Type | Default | Description |
|
|
454
472
|
|---|---|---|---|
|
|
455
|
-
| `mode` | `'dev' \| 'prod'` | `'dev'` | Selects the built-in
|
|
456
|
-
| `minLevel` | `LogLevel` | `'debug'` | Minimum level
|
|
457
|
-
| `
|
|
458
|
-
| `
|
|
459
|
-
| `
|
|
460
|
-
| `
|
|
461
|
-
|
|
462
|
-
|
|
473
|
+
| `mode` | `'dev' \| 'prod'` | `'dev'` | Selects the built-in formatter |
|
|
474
|
+
| `minLevel` | `LogLevel` | `'debug'` | Minimum log level |
|
|
475
|
+
| `formatter` | `FormatterFn` | — | Custom formatter, overrides `mode` |
|
|
476
|
+
| `devFormatterConfig` | `DevFormatterConfig` | — | Options for the built-in dev formatter |
|
|
477
|
+
| `prodFormatterConfig` | `ProdFormatterConfig` | — | Options for the built-in JSON prod formatter |
|
|
478
|
+
| `useSafeColors` | `boolean` | `false` | Restrict auto-hash to 10 terminal-safe colors (set `true` for basic terminals) |
|
|
479
|
+
|
|
480
|
+
### Context options
|
|
481
|
+
|
|
482
|
+
| Option | Type | Default | Description |
|
|
483
|
+
|---|---|---|---|
|
|
484
|
+
| `colorIndex` | `number` | auto | Color palette index (0–29) |
|
|
485
|
+
| `color` | `string` | — | Raw ANSI color code (e.g. `'38;5;214'`). Takes priority over `colorIndex` |
|
|
486
|
+
| `omitKey` | `boolean` | `false` | Hide the key, show only the value as `[value]` |
|
|
487
|
+
| `hideIn` | `'dev' \| 'prod'` | — | Hide this context item in dev or prod mode |
|
|
463
488
|
|
|
464
489
|
## License
|
|
465
490
|
|
package/dist/index.cjs
CHANGED
|
@@ -32,9 +32,9 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
FIRO_COLORS: () => FIRO_COLORS,
|
|
34
34
|
FiroUtils: () => utils_exports,
|
|
35
|
-
|
|
35
|
+
createDevFormatter: () => createDevFormatter,
|
|
36
36
|
createFiro: () => createFiro,
|
|
37
|
-
|
|
37
|
+
createProdFormatter: () => createProdFormatter
|
|
38
38
|
});
|
|
39
39
|
module.exports = __toCommonJS(index_exports);
|
|
40
40
|
|
|
@@ -45,6 +45,7 @@ __export(utils_exports, {
|
|
|
45
45
|
LOG_LEVELS: () => LOG_LEVELS,
|
|
46
46
|
colorize: () => colorize,
|
|
47
47
|
colorizeLevel: () => colorizeLevel,
|
|
48
|
+
extractMessage: () => extractMessage,
|
|
48
49
|
getColorIndex: () => getColorIndex,
|
|
49
50
|
jsonReplacer: () => jsonReplacer,
|
|
50
51
|
safeStringify: () => safeStringify,
|
|
@@ -94,12 +95,12 @@ var FIRO_COLORS = {
|
|
|
94
95
|
};
|
|
95
96
|
var COLORS_LIST = Object.values(FIRO_COLORS);
|
|
96
97
|
var SAFE_COLORS_COUNT = 10;
|
|
97
|
-
var getColorIndex = (str,
|
|
98
|
+
var getColorIndex = (str, useSafeColors = false) => {
|
|
98
99
|
let hash = 0;
|
|
99
100
|
for (let i = 0, len = str.length; i < len; i++) {
|
|
100
101
|
hash = str.charCodeAt(i) + ((hash << 5) - hash);
|
|
101
102
|
}
|
|
102
|
-
const range =
|
|
103
|
+
const range = useSafeColors ? SAFE_COLORS_COUNT : COLORS_LIST.length;
|
|
103
104
|
return Math.abs(hash % range);
|
|
104
105
|
};
|
|
105
106
|
var colorize = (text, colorIndex, color) => {
|
|
@@ -122,13 +123,18 @@ var wrapToError = (obj) => {
|
|
|
122
123
|
};
|
|
123
124
|
var serializeError = (_err) => {
|
|
124
125
|
const err = wrapToError(_err);
|
|
125
|
-
|
|
126
|
+
const result = {
|
|
126
127
|
message: err.message,
|
|
127
128
|
stack: err.stack,
|
|
128
129
|
name: err.name,
|
|
129
130
|
...err
|
|
130
131
|
};
|
|
132
|
+
if (err.cause !== void 0) {
|
|
133
|
+
result.cause = err.cause instanceof Error ? serializeError(err.cause) : err.cause;
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
131
136
|
};
|
|
137
|
+
var extractMessage = (msg) => typeof msg === "string" ? msg : msg instanceof Error ? msg.message : typeof msg === "object" && msg !== null ? safeStringify(msg) : String(msg);
|
|
132
138
|
var colorizeLevel = (level, text) => {
|
|
133
139
|
if (level === "info") return text;
|
|
134
140
|
switch (level) {
|
|
@@ -146,10 +152,10 @@ var colorizeLevel = (level, text) => {
|
|
|
146
152
|
}
|
|
147
153
|
};
|
|
148
154
|
|
|
149
|
-
// src/
|
|
155
|
+
// src/formatter_dev.ts
|
|
150
156
|
var import_node_util2 = require("util");
|
|
151
157
|
var import_node_process = __toESM(require("process"), 1);
|
|
152
|
-
var
|
|
158
|
+
var createDevFormatter = (config = {}) => {
|
|
153
159
|
const locale = config.locale ?? void 0;
|
|
154
160
|
const timeOpts = {
|
|
155
161
|
hour12: false,
|
|
@@ -159,10 +165,10 @@ var createDevTransport = (config = {}) => {
|
|
|
159
165
|
fractionalSecondDigits: 3,
|
|
160
166
|
...config.timeOptions || {}
|
|
161
167
|
};
|
|
162
|
-
const
|
|
168
|
+
const formatter = (level, context, msg, data, opts) => {
|
|
163
169
|
const now = /* @__PURE__ */ new Date();
|
|
164
170
|
const timestamp = now.toLocaleTimeString(locale, timeOpts);
|
|
165
|
-
const contextStr = context.map((ctx) => {
|
|
171
|
+
const contextStr = context.filter((ctx) => ctx.hideIn !== "dev").map((ctx) => {
|
|
166
172
|
const key = ctx.omitKey ? "" : `${ctx.key}:`;
|
|
167
173
|
const content = `${key}${ctx.value}`;
|
|
168
174
|
return colorize(`[${content}]`, ctx.colorIndex, ctx.color);
|
|
@@ -189,25 +195,23 @@ var createDevTransport = (config = {}) => {
|
|
|
189
195
|
if (level === "error") import_node_process.default.stderr.write(finalLine);
|
|
190
196
|
else import_node_process.default.stdout.write(finalLine);
|
|
191
197
|
};
|
|
192
|
-
return
|
|
198
|
+
return formatter;
|
|
193
199
|
};
|
|
194
200
|
|
|
195
|
-
// src/
|
|
201
|
+
// src/formatter_prod.ts
|
|
196
202
|
var import_node_util3 = require("util");
|
|
197
203
|
var import_node_process2 = __toESM(require("process"), 1);
|
|
198
204
|
var buildRecord = (level, context, msg, getTimestamp, data) => {
|
|
199
|
-
const contextObj = context.reduce((acc, item) => {
|
|
200
|
-
acc[item.key] = item.value;
|
|
201
|
-
return acc;
|
|
202
|
-
}, {});
|
|
203
205
|
const logRecord = {
|
|
204
206
|
timestamp: getTimestamp(),
|
|
205
|
-
level
|
|
206
|
-
...contextObj
|
|
207
|
+
level
|
|
207
208
|
};
|
|
209
|
+
for (let i = 0, len = context.length; i < len; i++) {
|
|
210
|
+
if (context[i].hideIn === "prod") continue;
|
|
211
|
+
logRecord[context[i].key] = context[i].value;
|
|
212
|
+
}
|
|
213
|
+
logRecord.message = extractMessage(msg);
|
|
208
214
|
if (level === "error") {
|
|
209
|
-
const message = typeof msg === "string" ? msg : msg instanceof Error ? msg.message : typeof msg === "object" && msg !== null ? safeStringify(msg) : String(msg);
|
|
210
|
-
logRecord.message = message;
|
|
211
215
|
if (data instanceof Error) {
|
|
212
216
|
logRecord.error = serializeError(data);
|
|
213
217
|
} else if (msg instanceof Error) {
|
|
@@ -218,14 +222,16 @@ var buildRecord = (level, context, msg, getTimestamp, data) => {
|
|
|
218
222
|
if (data !== void 0) logRecord.data = data;
|
|
219
223
|
}
|
|
220
224
|
} else {
|
|
221
|
-
|
|
225
|
+
if (msg instanceof Error) {
|
|
226
|
+
logRecord.error = serializeError(msg);
|
|
227
|
+
}
|
|
222
228
|
if (data !== void 0) {
|
|
223
229
|
logRecord.data = data instanceof Error ? serializeError(data) : data;
|
|
224
230
|
}
|
|
225
231
|
}
|
|
226
232
|
return logRecord;
|
|
227
233
|
};
|
|
228
|
-
var
|
|
234
|
+
var createProdFormatter = (config = {}) => {
|
|
229
235
|
const getTimestamp = config.timestamp === "epoch" ? () => Date.now() : () => (/* @__PURE__ */ new Date()).toISOString();
|
|
230
236
|
const dest = config.dest ?? import_node_process2.default.stdout;
|
|
231
237
|
return (level, context, msg, data) => {
|
|
@@ -252,10 +258,10 @@ var createProdTransport = (config = {}) => {
|
|
|
252
258
|
|
|
253
259
|
// src/index.ts
|
|
254
260
|
var createFiro = (config = {}, parentContext = []) => {
|
|
255
|
-
const
|
|
261
|
+
const useSafeColors = config.useSafeColors ?? false;
|
|
256
262
|
const fill = (item) => ({
|
|
257
263
|
...item,
|
|
258
|
-
colorIndex: typeof item.colorIndex === "number" ? item.colorIndex : getColorIndex(item.key,
|
|
264
|
+
colorIndex: typeof item.colorIndex === "number" ? item.colorIndex : getColorIndex(item.key, useSafeColors),
|
|
259
265
|
color: item.color,
|
|
260
266
|
omitKey: item.omitKey ?? false
|
|
261
267
|
});
|
|
@@ -264,8 +270,8 @@ var createFiro = (config = {}, parentContext = []) => {
|
|
|
264
270
|
return [...context2, ...invokeContext.map(fill)];
|
|
265
271
|
};
|
|
266
272
|
const context = [...parentContext.map(fill)];
|
|
267
|
-
const
|
|
268
|
-
const minLevelName = config.
|
|
273
|
+
const formatter = config.formatter ?? (config.mode === "prod" ? createProdFormatter(config.prodFormatterConfig) : createDevFormatter(config.devFormatterConfig));
|
|
274
|
+
const minLevelName = config.minLevel;
|
|
269
275
|
const minLevel = LOG_LEVELS[minLevelName ?? "debug"];
|
|
270
276
|
const getContext = () => context;
|
|
271
277
|
const hasInContext = (key) => context.some((ctx) => ctx.key === key);
|
|
@@ -293,25 +299,25 @@ var createFiro = (config = {}, parentContext = []) => {
|
|
|
293
299
|
const { value: extValue, ...opts } = value;
|
|
294
300
|
return { key, value: extValue, ...opts };
|
|
295
301
|
}
|
|
296
|
-
return { key, value, colorIndex: getColorIndex(key,
|
|
302
|
+
return { key, value, colorIndex: getColorIndex(key, useSafeColors) };
|
|
297
303
|
});
|
|
298
|
-
return createFiro({
|
|
304
|
+
return createFiro({ formatter, minLevel: minLevelName, useSafeColors }, [...context, ...newItems]);
|
|
299
305
|
};
|
|
300
306
|
const debug = (msg, data, opts) => {
|
|
301
307
|
if (minLevel > LOG_LEVELS.debug) return;
|
|
302
|
-
|
|
308
|
+
formatter("debug", appendContextWithInvokeContext(context, opts?.ctx), msg, data, opts);
|
|
303
309
|
};
|
|
304
310
|
const info = (msg, data, opts) => {
|
|
305
311
|
if (minLevel > LOG_LEVELS.info) return;
|
|
306
|
-
|
|
312
|
+
formatter("info", appendContextWithInvokeContext(context, opts?.ctx), msg, data, opts);
|
|
307
313
|
};
|
|
308
314
|
const warn = (msg, data, opts) => {
|
|
309
315
|
if (minLevel > LOG_LEVELS.warn) return;
|
|
310
|
-
|
|
316
|
+
formatter("warn", appendContextWithInvokeContext(context, opts?.ctx), msg, data, opts);
|
|
311
317
|
};
|
|
312
318
|
const error = (msgOrError, err, opts) => {
|
|
313
319
|
if (minLevel > LOG_LEVELS.error) return;
|
|
314
|
-
|
|
320
|
+
formatter("error", appendContextWithInvokeContext(context, opts?.ctx), msgOrError, err, opts);
|
|
315
321
|
};
|
|
316
322
|
const logInstance = ((msg, data, opts) => {
|
|
317
323
|
info(msg, data, opts);
|
|
@@ -332,7 +338,7 @@ var createFiro = (config = {}, parentContext = []) => {
|
|
|
332
338
|
0 && (module.exports = {
|
|
333
339
|
FIRO_COLORS,
|
|
334
340
|
FiroUtils,
|
|
335
|
-
|
|
341
|
+
createDevFormatter,
|
|
336
342
|
createFiro,
|
|
337
|
-
|
|
343
|
+
createProdFormatter
|
|
338
344
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** Available log severity levels. */
|
|
2
2
|
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
3
|
+
/** Numeric severity values for each log level, used for threshold filtering. */
|
|
3
4
|
declare const LOG_LEVELS: {
|
|
4
5
|
readonly debug: 20;
|
|
5
6
|
readonly info: 30;
|
|
@@ -16,6 +17,8 @@ type ContextOptions = {
|
|
|
16
17
|
color?: string;
|
|
17
18
|
/** If true, the key name is hidden, and only the value is printed. */
|
|
18
19
|
omitKey?: boolean;
|
|
20
|
+
/** Hide this context item in 'dev' or 'prod' mode. Useful for keeping traceIds out of dev output. */
|
|
21
|
+
hideIn?: 'dev' | 'prod';
|
|
19
22
|
};
|
|
20
23
|
/** A single key-value context entry. */
|
|
21
24
|
type ContextItem = {
|
|
@@ -31,6 +34,7 @@ type ContextItemWithOptions = ContextItem & {
|
|
|
31
34
|
colorIndex: number;
|
|
32
35
|
omitKey: boolean;
|
|
33
36
|
color?: string;
|
|
37
|
+
hideIn?: 'dev' | 'prod';
|
|
34
38
|
};
|
|
35
39
|
/** Options that can be passed to a single log call. */
|
|
36
40
|
type LogOptions = {
|
|
@@ -40,7 +44,7 @@ type LogOptions = {
|
|
|
40
44
|
ctx?: ContextItem[];
|
|
41
45
|
};
|
|
42
46
|
/** The signature of a function responsible for formatting and emitting log records. */
|
|
43
|
-
type
|
|
47
|
+
type FormatterFn = (level: LogLevel, context: ContextItemWithOptions[], message: string | Error | unknown, data?: Error | unknown, options?: LogOptions) => void;
|
|
44
48
|
/** Named color palette for context badges. Use with `color` option: `{ color: FIRO_COLORS.skyBlue }` */
|
|
45
49
|
declare const FIRO_COLORS: {
|
|
46
50
|
readonly cyan: "36";
|
|
@@ -74,12 +78,21 @@ declare const FIRO_COLORS: {
|
|
|
74
78
|
readonly tangerine: "38;5;208";
|
|
75
79
|
readonly periwinkle: "38;5;147";
|
|
76
80
|
};
|
|
77
|
-
|
|
81
|
+
/** Hash a string to a stable color palette index. Similar strings land on different colors. */
|
|
82
|
+
declare const getColorIndex: (str: string, useSafeColors?: boolean) => number;
|
|
83
|
+
/** Wrap text in an ANSI color escape sequence by palette index or raw ANSI code. */
|
|
78
84
|
declare const colorize: (text: string, colorIndex: number, color?: string) => string;
|
|
85
|
+
/** JSON.stringify replacer that converts BigInt values to strings. */
|
|
79
86
|
declare const jsonReplacer: (_key: string, value: unknown) => unknown;
|
|
87
|
+
/** Safely stringify any value to JSON with BigInt support. Falls back to `util.inspect` on circular references. */
|
|
80
88
|
declare const safeStringify: (obj: unknown) => string;
|
|
89
|
+
/** Coerce any value to an Error instance. If already an Error, returns as-is. */
|
|
81
90
|
declare const wrapToError: (obj: unknown) => Error;
|
|
82
|
-
|
|
91
|
+
/** Serialize an error-like value to a plain object with `message`, `stack`, `name`, and recursively serialized `cause`. */
|
|
92
|
+
declare const serializeError: (_err: unknown) => Record<string, unknown>;
|
|
93
|
+
/** Extract a human-readable message string from any log input. Useful for building custom formatters. */
|
|
94
|
+
declare const extractMessage: (msg: string | Error | unknown) => string;
|
|
95
|
+
/** Wrap text in an ANSI color based on log level: red for error, yellow for warn, dim for debug. */
|
|
83
96
|
declare const colorizeLevel: (level: LogLevel, text: string) => string;
|
|
84
97
|
|
|
85
98
|
type utils_ContextExtension = ContextExtension;
|
|
@@ -88,41 +101,42 @@ type utils_ContextItemWithOptions = ContextItemWithOptions;
|
|
|
88
101
|
type utils_ContextOptions = ContextOptions;
|
|
89
102
|
type utils_ContextValue = ContextValue;
|
|
90
103
|
declare const utils_FIRO_COLORS: typeof FIRO_COLORS;
|
|
104
|
+
type utils_FormatterFn = FormatterFn;
|
|
91
105
|
declare const utils_LOG_LEVELS: typeof LOG_LEVELS;
|
|
92
106
|
type utils_LogLevel = LogLevel;
|
|
93
107
|
type utils_LogOptions = LogOptions;
|
|
94
|
-
type utils_TransportFn = TransportFn;
|
|
95
108
|
declare const utils_colorize: typeof colorize;
|
|
96
109
|
declare const utils_colorizeLevel: typeof colorizeLevel;
|
|
110
|
+
declare const utils_extractMessage: typeof extractMessage;
|
|
97
111
|
declare const utils_getColorIndex: typeof getColorIndex;
|
|
98
112
|
declare const utils_jsonReplacer: typeof jsonReplacer;
|
|
99
113
|
declare const utils_safeStringify: typeof safeStringify;
|
|
100
114
|
declare const utils_serializeError: typeof serializeError;
|
|
101
115
|
declare const utils_wrapToError: typeof wrapToError;
|
|
102
116
|
declare namespace utils {
|
|
103
|
-
export { type utils_ContextExtension as ContextExtension, type utils_ContextItem as ContextItem, type utils_ContextItemWithOptions as ContextItemWithOptions, type utils_ContextOptions as ContextOptions, type utils_ContextValue as ContextValue, utils_FIRO_COLORS as FIRO_COLORS, utils_LOG_LEVELS as LOG_LEVELS, type utils_LogLevel as LogLevel, type utils_LogOptions as LogOptions,
|
|
117
|
+
export { type utils_ContextExtension as ContextExtension, type utils_ContextItem as ContextItem, type utils_ContextItemWithOptions as ContextItemWithOptions, type utils_ContextOptions as ContextOptions, type utils_ContextValue as ContextValue, utils_FIRO_COLORS as FIRO_COLORS, type utils_FormatterFn as FormatterFn, utils_LOG_LEVELS as LOG_LEVELS, type utils_LogLevel as LogLevel, type utils_LogOptions as LogOptions, utils_colorize as colorize, utils_colorizeLevel as colorizeLevel, utils_extractMessage as extractMessage, utils_getColorIndex as getColorIndex, utils_jsonReplacer as jsonReplacer, utils_safeStringify as safeStringify, utils_serializeError as serializeError, utils_wrapToError as wrapToError };
|
|
104
118
|
}
|
|
105
119
|
|
|
106
120
|
/**
|
|
107
|
-
* Configuration options for the development
|
|
121
|
+
* Configuration options for the development formatter.
|
|
108
122
|
*/
|
|
109
|
-
type
|
|
123
|
+
type DevFormatterConfig = {
|
|
110
124
|
/** The locale used for formatting the timestamp. Defaults to the system locale. */
|
|
111
125
|
locale?: string;
|
|
112
126
|
/** Standard Intl.DateTimeFormatOptions to customize the timestamp output. */
|
|
113
127
|
timeOptions?: Intl.DateTimeFormatOptions;
|
|
114
128
|
};
|
|
115
129
|
/**
|
|
116
|
-
* Creates a built-in
|
|
130
|
+
* Creates a built-in formatter optimized for local development.
|
|
117
131
|
* Emits colored, human-readable strings to stdout/stderr.
|
|
118
132
|
*
|
|
119
|
-
* @param config Optional configuration for the
|
|
120
|
-
* @returns A `
|
|
133
|
+
* @param config Optional configuration for the formatter, like timestamp formats.
|
|
134
|
+
* @returns A `FormatterFn` that writes to the console.
|
|
121
135
|
*/
|
|
122
|
-
declare const
|
|
136
|
+
declare const createDevFormatter: (config?: DevFormatterConfig) => FormatterFn;
|
|
123
137
|
|
|
124
138
|
type TimestampFormat = 'iso' | 'epoch';
|
|
125
|
-
type
|
|
139
|
+
type ProdFormatterConfig = {
|
|
126
140
|
/** Timestamp format: 'iso' (default) for ISO 8601 string, 'epoch' for ms since Unix epoch. */
|
|
127
141
|
timestamp?: TimestampFormat;
|
|
128
142
|
/** Output destination. Any object with a `.write(string)` method. Defaults to `process.stdout`. */
|
|
@@ -131,33 +145,29 @@ type ProdTransportConfig = {
|
|
|
131
145
|
};
|
|
132
146
|
};
|
|
133
147
|
/**
|
|
134
|
-
* Creates a built-in
|
|
148
|
+
* Creates a built-in formatter optimized for production.
|
|
135
149
|
* Emits strictly structured NDJSON (Newline Delimited JSON) to stdout.
|
|
136
150
|
*
|
|
137
|
-
* @returns A `
|
|
151
|
+
* @returns A `FormatterFn` that writes JSON to standard output.
|
|
138
152
|
*/
|
|
139
|
-
declare const
|
|
153
|
+
declare const createProdFormatter: (config?: ProdFormatterConfig) => FormatterFn;
|
|
140
154
|
|
|
141
155
|
/**
|
|
142
156
|
* Configuration options for creating a logger instance.
|
|
143
157
|
*/
|
|
144
158
|
type LoggerConfig = {
|
|
145
|
-
/** The minimum log level
|
|
159
|
+
/** The minimum log level. */
|
|
146
160
|
minLevel?: LogLevel;
|
|
147
|
-
/**
|
|
148
|
-
minLevelInDev?: LogLevel;
|
|
149
|
-
/** Minimum log level to emit in 'prod' mode. */
|
|
150
|
-
minLevelInProd?: LogLevel;
|
|
151
|
-
/** Specifies the built-in transport to use. Defaults to 'dev'. */
|
|
161
|
+
/** Selects the built-in formatter. Defaults to 'dev'. */
|
|
152
162
|
mode?: 'dev' | 'prod';
|
|
153
|
-
/** Provide a custom
|
|
154
|
-
|
|
155
|
-
/** Options for fine-tuning the built-in development
|
|
156
|
-
|
|
157
|
-
/** Options for the built-in prod
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
|
|
163
|
+
/** Provide a custom formatter function to override the built-in behaviors. */
|
|
164
|
+
formatter?: FormatterFn;
|
|
165
|
+
/** Options for fine-tuning the built-in development formatter (e.g. timestamp format). */
|
|
166
|
+
devFormatterConfig?: DevFormatterConfig;
|
|
167
|
+
/** Options for the built-in prod formatter (e.g. timestamp format). */
|
|
168
|
+
prodFormatterConfig?: ProdFormatterConfig;
|
|
169
|
+
/** Restrict auto-assigned context badge colors to 10 terminal-safe colors. Defaults to false (all 30 palette colors are used). */
|
|
170
|
+
useSafeColors?: boolean;
|
|
161
171
|
};
|
|
162
172
|
/**
|
|
163
173
|
* The logger instance returned by `createFiro`.
|
|
@@ -174,6 +184,8 @@ interface Firo {
|
|
|
174
184
|
warn: (msg: string, data?: unknown, opts?: LogOptions) => void;
|
|
175
185
|
/** Log an error object directly. */
|
|
176
186
|
error(err: Error | unknown): void;
|
|
187
|
+
/** Log an error object with additional data. */
|
|
188
|
+
error(err: Error, data?: unknown, opts?: LogOptions): void;
|
|
177
189
|
/** Log a message alongside an error or custom data object. */
|
|
178
190
|
error(msg: string, err?: Error | unknown, opts?: LogOptions): void;
|
|
179
191
|
/**
|
|
@@ -196,9 +208,9 @@ interface Firo {
|
|
|
196
208
|
/**
|
|
197
209
|
* Creates a new logger instance with the specified configuration.
|
|
198
210
|
*
|
|
199
|
-
* @param config Optional configuration for log levels,
|
|
211
|
+
* @param config Optional configuration for log levels, format, and formatters.
|
|
200
212
|
* @returns A fully configured `Firo` instance.
|
|
201
213
|
*/
|
|
202
214
|
declare const createFiro: (config?: LoggerConfig, parentContext?: ContextItem[]) => Firo;
|
|
203
215
|
|
|
204
|
-
export { type ContextExtension, type ContextItem, type ContextItemWithOptions, type ContextOptions, type ContextValue, type
|
|
216
|
+
export { type ContextExtension, type ContextItem, type ContextItemWithOptions, type ContextOptions, type ContextValue, type DevFormatterConfig, FIRO_COLORS, type Firo, utils as FiroUtils, type FormatterFn, type LogLevel, type LogOptions, type LoggerConfig, type ProdFormatterConfig, type TimestampFormat, createDevFormatter, createFiro, createProdFormatter };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** Available log severity levels. */
|
|
2
2
|
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
3
|
+
/** Numeric severity values for each log level, used for threshold filtering. */
|
|
3
4
|
declare const LOG_LEVELS: {
|
|
4
5
|
readonly debug: 20;
|
|
5
6
|
readonly info: 30;
|
|
@@ -16,6 +17,8 @@ type ContextOptions = {
|
|
|
16
17
|
color?: string;
|
|
17
18
|
/** If true, the key name is hidden, and only the value is printed. */
|
|
18
19
|
omitKey?: boolean;
|
|
20
|
+
/** Hide this context item in 'dev' or 'prod' mode. Useful for keeping traceIds out of dev output. */
|
|
21
|
+
hideIn?: 'dev' | 'prod';
|
|
19
22
|
};
|
|
20
23
|
/** A single key-value context entry. */
|
|
21
24
|
type ContextItem = {
|
|
@@ -31,6 +34,7 @@ type ContextItemWithOptions = ContextItem & {
|
|
|
31
34
|
colorIndex: number;
|
|
32
35
|
omitKey: boolean;
|
|
33
36
|
color?: string;
|
|
37
|
+
hideIn?: 'dev' | 'prod';
|
|
34
38
|
};
|
|
35
39
|
/** Options that can be passed to a single log call. */
|
|
36
40
|
type LogOptions = {
|
|
@@ -40,7 +44,7 @@ type LogOptions = {
|
|
|
40
44
|
ctx?: ContextItem[];
|
|
41
45
|
};
|
|
42
46
|
/** The signature of a function responsible for formatting and emitting log records. */
|
|
43
|
-
type
|
|
47
|
+
type FormatterFn = (level: LogLevel, context: ContextItemWithOptions[], message: string | Error | unknown, data?: Error | unknown, options?: LogOptions) => void;
|
|
44
48
|
/** Named color palette for context badges. Use with `color` option: `{ color: FIRO_COLORS.skyBlue }` */
|
|
45
49
|
declare const FIRO_COLORS: {
|
|
46
50
|
readonly cyan: "36";
|
|
@@ -74,12 +78,21 @@ declare const FIRO_COLORS: {
|
|
|
74
78
|
readonly tangerine: "38;5;208";
|
|
75
79
|
readonly periwinkle: "38;5;147";
|
|
76
80
|
};
|
|
77
|
-
|
|
81
|
+
/** Hash a string to a stable color palette index. Similar strings land on different colors. */
|
|
82
|
+
declare const getColorIndex: (str: string, useSafeColors?: boolean) => number;
|
|
83
|
+
/** Wrap text in an ANSI color escape sequence by palette index or raw ANSI code. */
|
|
78
84
|
declare const colorize: (text: string, colorIndex: number, color?: string) => string;
|
|
85
|
+
/** JSON.stringify replacer that converts BigInt values to strings. */
|
|
79
86
|
declare const jsonReplacer: (_key: string, value: unknown) => unknown;
|
|
87
|
+
/** Safely stringify any value to JSON with BigInt support. Falls back to `util.inspect` on circular references. */
|
|
80
88
|
declare const safeStringify: (obj: unknown) => string;
|
|
89
|
+
/** Coerce any value to an Error instance. If already an Error, returns as-is. */
|
|
81
90
|
declare const wrapToError: (obj: unknown) => Error;
|
|
82
|
-
|
|
91
|
+
/** Serialize an error-like value to a plain object with `message`, `stack`, `name`, and recursively serialized `cause`. */
|
|
92
|
+
declare const serializeError: (_err: unknown) => Record<string, unknown>;
|
|
93
|
+
/** Extract a human-readable message string from any log input. Useful for building custom formatters. */
|
|
94
|
+
declare const extractMessage: (msg: string | Error | unknown) => string;
|
|
95
|
+
/** Wrap text in an ANSI color based on log level: red for error, yellow for warn, dim for debug. */
|
|
83
96
|
declare const colorizeLevel: (level: LogLevel, text: string) => string;
|
|
84
97
|
|
|
85
98
|
type utils_ContextExtension = ContextExtension;
|
|
@@ -88,41 +101,42 @@ type utils_ContextItemWithOptions = ContextItemWithOptions;
|
|
|
88
101
|
type utils_ContextOptions = ContextOptions;
|
|
89
102
|
type utils_ContextValue = ContextValue;
|
|
90
103
|
declare const utils_FIRO_COLORS: typeof FIRO_COLORS;
|
|
104
|
+
type utils_FormatterFn = FormatterFn;
|
|
91
105
|
declare const utils_LOG_LEVELS: typeof LOG_LEVELS;
|
|
92
106
|
type utils_LogLevel = LogLevel;
|
|
93
107
|
type utils_LogOptions = LogOptions;
|
|
94
|
-
type utils_TransportFn = TransportFn;
|
|
95
108
|
declare const utils_colorize: typeof colorize;
|
|
96
109
|
declare const utils_colorizeLevel: typeof colorizeLevel;
|
|
110
|
+
declare const utils_extractMessage: typeof extractMessage;
|
|
97
111
|
declare const utils_getColorIndex: typeof getColorIndex;
|
|
98
112
|
declare const utils_jsonReplacer: typeof jsonReplacer;
|
|
99
113
|
declare const utils_safeStringify: typeof safeStringify;
|
|
100
114
|
declare const utils_serializeError: typeof serializeError;
|
|
101
115
|
declare const utils_wrapToError: typeof wrapToError;
|
|
102
116
|
declare namespace utils {
|
|
103
|
-
export { type utils_ContextExtension as ContextExtension, type utils_ContextItem as ContextItem, type utils_ContextItemWithOptions as ContextItemWithOptions, type utils_ContextOptions as ContextOptions, type utils_ContextValue as ContextValue, utils_FIRO_COLORS as FIRO_COLORS, utils_LOG_LEVELS as LOG_LEVELS, type utils_LogLevel as LogLevel, type utils_LogOptions as LogOptions,
|
|
117
|
+
export { type utils_ContextExtension as ContextExtension, type utils_ContextItem as ContextItem, type utils_ContextItemWithOptions as ContextItemWithOptions, type utils_ContextOptions as ContextOptions, type utils_ContextValue as ContextValue, utils_FIRO_COLORS as FIRO_COLORS, type utils_FormatterFn as FormatterFn, utils_LOG_LEVELS as LOG_LEVELS, type utils_LogLevel as LogLevel, type utils_LogOptions as LogOptions, utils_colorize as colorize, utils_colorizeLevel as colorizeLevel, utils_extractMessage as extractMessage, utils_getColorIndex as getColorIndex, utils_jsonReplacer as jsonReplacer, utils_safeStringify as safeStringify, utils_serializeError as serializeError, utils_wrapToError as wrapToError };
|
|
104
118
|
}
|
|
105
119
|
|
|
106
120
|
/**
|
|
107
|
-
* Configuration options for the development
|
|
121
|
+
* Configuration options for the development formatter.
|
|
108
122
|
*/
|
|
109
|
-
type
|
|
123
|
+
type DevFormatterConfig = {
|
|
110
124
|
/** The locale used for formatting the timestamp. Defaults to the system locale. */
|
|
111
125
|
locale?: string;
|
|
112
126
|
/** Standard Intl.DateTimeFormatOptions to customize the timestamp output. */
|
|
113
127
|
timeOptions?: Intl.DateTimeFormatOptions;
|
|
114
128
|
};
|
|
115
129
|
/**
|
|
116
|
-
* Creates a built-in
|
|
130
|
+
* Creates a built-in formatter optimized for local development.
|
|
117
131
|
* Emits colored, human-readable strings to stdout/stderr.
|
|
118
132
|
*
|
|
119
|
-
* @param config Optional configuration for the
|
|
120
|
-
* @returns A `
|
|
133
|
+
* @param config Optional configuration for the formatter, like timestamp formats.
|
|
134
|
+
* @returns A `FormatterFn` that writes to the console.
|
|
121
135
|
*/
|
|
122
|
-
declare const
|
|
136
|
+
declare const createDevFormatter: (config?: DevFormatterConfig) => FormatterFn;
|
|
123
137
|
|
|
124
138
|
type TimestampFormat = 'iso' | 'epoch';
|
|
125
|
-
type
|
|
139
|
+
type ProdFormatterConfig = {
|
|
126
140
|
/** Timestamp format: 'iso' (default) for ISO 8601 string, 'epoch' for ms since Unix epoch. */
|
|
127
141
|
timestamp?: TimestampFormat;
|
|
128
142
|
/** Output destination. Any object with a `.write(string)` method. Defaults to `process.stdout`. */
|
|
@@ -131,33 +145,29 @@ type ProdTransportConfig = {
|
|
|
131
145
|
};
|
|
132
146
|
};
|
|
133
147
|
/**
|
|
134
|
-
* Creates a built-in
|
|
148
|
+
* Creates a built-in formatter optimized for production.
|
|
135
149
|
* Emits strictly structured NDJSON (Newline Delimited JSON) to stdout.
|
|
136
150
|
*
|
|
137
|
-
* @returns A `
|
|
151
|
+
* @returns A `FormatterFn` that writes JSON to standard output.
|
|
138
152
|
*/
|
|
139
|
-
declare const
|
|
153
|
+
declare const createProdFormatter: (config?: ProdFormatterConfig) => FormatterFn;
|
|
140
154
|
|
|
141
155
|
/**
|
|
142
156
|
* Configuration options for creating a logger instance.
|
|
143
157
|
*/
|
|
144
158
|
type LoggerConfig = {
|
|
145
|
-
/** The minimum log level
|
|
159
|
+
/** The minimum log level. */
|
|
146
160
|
minLevel?: LogLevel;
|
|
147
|
-
/**
|
|
148
|
-
minLevelInDev?: LogLevel;
|
|
149
|
-
/** Minimum log level to emit in 'prod' mode. */
|
|
150
|
-
minLevelInProd?: LogLevel;
|
|
151
|
-
/** Specifies the built-in transport to use. Defaults to 'dev'. */
|
|
161
|
+
/** Selects the built-in formatter. Defaults to 'dev'. */
|
|
152
162
|
mode?: 'dev' | 'prod';
|
|
153
|
-
/** Provide a custom
|
|
154
|
-
|
|
155
|
-
/** Options for fine-tuning the built-in development
|
|
156
|
-
|
|
157
|
-
/** Options for the built-in prod
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
|
|
163
|
+
/** Provide a custom formatter function to override the built-in behaviors. */
|
|
164
|
+
formatter?: FormatterFn;
|
|
165
|
+
/** Options for fine-tuning the built-in development formatter (e.g. timestamp format). */
|
|
166
|
+
devFormatterConfig?: DevFormatterConfig;
|
|
167
|
+
/** Options for the built-in prod formatter (e.g. timestamp format). */
|
|
168
|
+
prodFormatterConfig?: ProdFormatterConfig;
|
|
169
|
+
/** Restrict auto-assigned context badge colors to 10 terminal-safe colors. Defaults to false (all 30 palette colors are used). */
|
|
170
|
+
useSafeColors?: boolean;
|
|
161
171
|
};
|
|
162
172
|
/**
|
|
163
173
|
* The logger instance returned by `createFiro`.
|
|
@@ -174,6 +184,8 @@ interface Firo {
|
|
|
174
184
|
warn: (msg: string, data?: unknown, opts?: LogOptions) => void;
|
|
175
185
|
/** Log an error object directly. */
|
|
176
186
|
error(err: Error | unknown): void;
|
|
187
|
+
/** Log an error object with additional data. */
|
|
188
|
+
error(err: Error, data?: unknown, opts?: LogOptions): void;
|
|
177
189
|
/** Log a message alongside an error or custom data object. */
|
|
178
190
|
error(msg: string, err?: Error | unknown, opts?: LogOptions): void;
|
|
179
191
|
/**
|
|
@@ -196,9 +208,9 @@ interface Firo {
|
|
|
196
208
|
/**
|
|
197
209
|
* Creates a new logger instance with the specified configuration.
|
|
198
210
|
*
|
|
199
|
-
* @param config Optional configuration for log levels,
|
|
211
|
+
* @param config Optional configuration for log levels, format, and formatters.
|
|
200
212
|
* @returns A fully configured `Firo` instance.
|
|
201
213
|
*/
|
|
202
214
|
declare const createFiro: (config?: LoggerConfig, parentContext?: ContextItem[]) => Firo;
|
|
203
215
|
|
|
204
|
-
export { type ContextExtension, type ContextItem, type ContextItemWithOptions, type ContextOptions, type ContextValue, type
|
|
216
|
+
export { type ContextExtension, type ContextItem, type ContextItemWithOptions, type ContextOptions, type ContextValue, type DevFormatterConfig, FIRO_COLORS, type Firo, utils as FiroUtils, type FormatterFn, type LogLevel, type LogOptions, type LoggerConfig, type ProdFormatterConfig, type TimestampFormat, createDevFormatter, createFiro, createProdFormatter };
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ __export(utils_exports, {
|
|
|
11
11
|
LOG_LEVELS: () => LOG_LEVELS,
|
|
12
12
|
colorize: () => colorize,
|
|
13
13
|
colorizeLevel: () => colorizeLevel,
|
|
14
|
+
extractMessage: () => extractMessage,
|
|
14
15
|
getColorIndex: () => getColorIndex,
|
|
15
16
|
jsonReplacer: () => jsonReplacer,
|
|
16
17
|
safeStringify: () => safeStringify,
|
|
@@ -60,12 +61,12 @@ var FIRO_COLORS = {
|
|
|
60
61
|
};
|
|
61
62
|
var COLORS_LIST = Object.values(FIRO_COLORS);
|
|
62
63
|
var SAFE_COLORS_COUNT = 10;
|
|
63
|
-
var getColorIndex = (str,
|
|
64
|
+
var getColorIndex = (str, useSafeColors = false) => {
|
|
64
65
|
let hash = 0;
|
|
65
66
|
for (let i = 0, len = str.length; i < len; i++) {
|
|
66
67
|
hash = str.charCodeAt(i) + ((hash << 5) - hash);
|
|
67
68
|
}
|
|
68
|
-
const range =
|
|
69
|
+
const range = useSafeColors ? SAFE_COLORS_COUNT : COLORS_LIST.length;
|
|
69
70
|
return Math.abs(hash % range);
|
|
70
71
|
};
|
|
71
72
|
var colorize = (text, colorIndex, color) => {
|
|
@@ -88,13 +89,18 @@ var wrapToError = (obj) => {
|
|
|
88
89
|
};
|
|
89
90
|
var serializeError = (_err) => {
|
|
90
91
|
const err = wrapToError(_err);
|
|
91
|
-
|
|
92
|
+
const result = {
|
|
92
93
|
message: err.message,
|
|
93
94
|
stack: err.stack,
|
|
94
95
|
name: err.name,
|
|
95
96
|
...err
|
|
96
97
|
};
|
|
98
|
+
if (err.cause !== void 0) {
|
|
99
|
+
result.cause = err.cause instanceof Error ? serializeError(err.cause) : err.cause;
|
|
100
|
+
}
|
|
101
|
+
return result;
|
|
97
102
|
};
|
|
103
|
+
var extractMessage = (msg) => typeof msg === "string" ? msg : msg instanceof Error ? msg.message : typeof msg === "object" && msg !== null ? safeStringify(msg) : String(msg);
|
|
98
104
|
var colorizeLevel = (level, text) => {
|
|
99
105
|
if (level === "info") return text;
|
|
100
106
|
switch (level) {
|
|
@@ -112,10 +118,10 @@ var colorizeLevel = (level, text) => {
|
|
|
112
118
|
}
|
|
113
119
|
};
|
|
114
120
|
|
|
115
|
-
// src/
|
|
121
|
+
// src/formatter_dev.ts
|
|
116
122
|
import { inspect as inspect2 } from "util";
|
|
117
123
|
import process from "process";
|
|
118
|
-
var
|
|
124
|
+
var createDevFormatter = (config = {}) => {
|
|
119
125
|
const locale = config.locale ?? void 0;
|
|
120
126
|
const timeOpts = {
|
|
121
127
|
hour12: false,
|
|
@@ -125,10 +131,10 @@ var createDevTransport = (config = {}) => {
|
|
|
125
131
|
fractionalSecondDigits: 3,
|
|
126
132
|
...config.timeOptions || {}
|
|
127
133
|
};
|
|
128
|
-
const
|
|
134
|
+
const formatter = (level, context, msg, data, opts) => {
|
|
129
135
|
const now = /* @__PURE__ */ new Date();
|
|
130
136
|
const timestamp = now.toLocaleTimeString(locale, timeOpts);
|
|
131
|
-
const contextStr = context.map((ctx) => {
|
|
137
|
+
const contextStr = context.filter((ctx) => ctx.hideIn !== "dev").map((ctx) => {
|
|
132
138
|
const key = ctx.omitKey ? "" : `${ctx.key}:`;
|
|
133
139
|
const content = `${key}${ctx.value}`;
|
|
134
140
|
return colorize(`[${content}]`, ctx.colorIndex, ctx.color);
|
|
@@ -155,25 +161,23 @@ var createDevTransport = (config = {}) => {
|
|
|
155
161
|
if (level === "error") process.stderr.write(finalLine);
|
|
156
162
|
else process.stdout.write(finalLine);
|
|
157
163
|
};
|
|
158
|
-
return
|
|
164
|
+
return formatter;
|
|
159
165
|
};
|
|
160
166
|
|
|
161
|
-
// src/
|
|
167
|
+
// src/formatter_prod.ts
|
|
162
168
|
import { inspect as inspect3 } from "util";
|
|
163
169
|
import process2 from "process";
|
|
164
170
|
var buildRecord = (level, context, msg, getTimestamp, data) => {
|
|
165
|
-
const contextObj = context.reduce((acc, item) => {
|
|
166
|
-
acc[item.key] = item.value;
|
|
167
|
-
return acc;
|
|
168
|
-
}, {});
|
|
169
171
|
const logRecord = {
|
|
170
172
|
timestamp: getTimestamp(),
|
|
171
|
-
level
|
|
172
|
-
...contextObj
|
|
173
|
+
level
|
|
173
174
|
};
|
|
175
|
+
for (let i = 0, len = context.length; i < len; i++) {
|
|
176
|
+
if (context[i].hideIn === "prod") continue;
|
|
177
|
+
logRecord[context[i].key] = context[i].value;
|
|
178
|
+
}
|
|
179
|
+
logRecord.message = extractMessage(msg);
|
|
174
180
|
if (level === "error") {
|
|
175
|
-
const message = typeof msg === "string" ? msg : msg instanceof Error ? msg.message : typeof msg === "object" && msg !== null ? safeStringify(msg) : String(msg);
|
|
176
|
-
logRecord.message = message;
|
|
177
181
|
if (data instanceof Error) {
|
|
178
182
|
logRecord.error = serializeError(data);
|
|
179
183
|
} else if (msg instanceof Error) {
|
|
@@ -184,14 +188,16 @@ var buildRecord = (level, context, msg, getTimestamp, data) => {
|
|
|
184
188
|
if (data !== void 0) logRecord.data = data;
|
|
185
189
|
}
|
|
186
190
|
} else {
|
|
187
|
-
|
|
191
|
+
if (msg instanceof Error) {
|
|
192
|
+
logRecord.error = serializeError(msg);
|
|
193
|
+
}
|
|
188
194
|
if (data !== void 0) {
|
|
189
195
|
logRecord.data = data instanceof Error ? serializeError(data) : data;
|
|
190
196
|
}
|
|
191
197
|
}
|
|
192
198
|
return logRecord;
|
|
193
199
|
};
|
|
194
|
-
var
|
|
200
|
+
var createProdFormatter = (config = {}) => {
|
|
195
201
|
const getTimestamp = config.timestamp === "epoch" ? () => Date.now() : () => (/* @__PURE__ */ new Date()).toISOString();
|
|
196
202
|
const dest = config.dest ?? process2.stdout;
|
|
197
203
|
return (level, context, msg, data) => {
|
|
@@ -218,10 +224,10 @@ var createProdTransport = (config = {}) => {
|
|
|
218
224
|
|
|
219
225
|
// src/index.ts
|
|
220
226
|
var createFiro = (config = {}, parentContext = []) => {
|
|
221
|
-
const
|
|
227
|
+
const useSafeColors = config.useSafeColors ?? false;
|
|
222
228
|
const fill = (item) => ({
|
|
223
229
|
...item,
|
|
224
|
-
colorIndex: typeof item.colorIndex === "number" ? item.colorIndex : getColorIndex(item.key,
|
|
230
|
+
colorIndex: typeof item.colorIndex === "number" ? item.colorIndex : getColorIndex(item.key, useSafeColors),
|
|
225
231
|
color: item.color,
|
|
226
232
|
omitKey: item.omitKey ?? false
|
|
227
233
|
});
|
|
@@ -230,8 +236,8 @@ var createFiro = (config = {}, parentContext = []) => {
|
|
|
230
236
|
return [...context2, ...invokeContext.map(fill)];
|
|
231
237
|
};
|
|
232
238
|
const context = [...parentContext.map(fill)];
|
|
233
|
-
const
|
|
234
|
-
const minLevelName = config.
|
|
239
|
+
const formatter = config.formatter ?? (config.mode === "prod" ? createProdFormatter(config.prodFormatterConfig) : createDevFormatter(config.devFormatterConfig));
|
|
240
|
+
const minLevelName = config.minLevel;
|
|
235
241
|
const minLevel = LOG_LEVELS[minLevelName ?? "debug"];
|
|
236
242
|
const getContext = () => context;
|
|
237
243
|
const hasInContext = (key) => context.some((ctx) => ctx.key === key);
|
|
@@ -259,25 +265,25 @@ var createFiro = (config = {}, parentContext = []) => {
|
|
|
259
265
|
const { value: extValue, ...opts } = value;
|
|
260
266
|
return { key, value: extValue, ...opts };
|
|
261
267
|
}
|
|
262
|
-
return { key, value, colorIndex: getColorIndex(key,
|
|
268
|
+
return { key, value, colorIndex: getColorIndex(key, useSafeColors) };
|
|
263
269
|
});
|
|
264
|
-
return createFiro({
|
|
270
|
+
return createFiro({ formatter, minLevel: minLevelName, useSafeColors }, [...context, ...newItems]);
|
|
265
271
|
};
|
|
266
272
|
const debug = (msg, data, opts) => {
|
|
267
273
|
if (minLevel > LOG_LEVELS.debug) return;
|
|
268
|
-
|
|
274
|
+
formatter("debug", appendContextWithInvokeContext(context, opts?.ctx), msg, data, opts);
|
|
269
275
|
};
|
|
270
276
|
const info = (msg, data, opts) => {
|
|
271
277
|
if (minLevel > LOG_LEVELS.info) return;
|
|
272
|
-
|
|
278
|
+
formatter("info", appendContextWithInvokeContext(context, opts?.ctx), msg, data, opts);
|
|
273
279
|
};
|
|
274
280
|
const warn = (msg, data, opts) => {
|
|
275
281
|
if (minLevel > LOG_LEVELS.warn) return;
|
|
276
|
-
|
|
282
|
+
formatter("warn", appendContextWithInvokeContext(context, opts?.ctx), msg, data, opts);
|
|
277
283
|
};
|
|
278
284
|
const error = (msgOrError, err, opts) => {
|
|
279
285
|
if (minLevel > LOG_LEVELS.error) return;
|
|
280
|
-
|
|
286
|
+
formatter("error", appendContextWithInvokeContext(context, opts?.ctx), msgOrError, err, opts);
|
|
281
287
|
};
|
|
282
288
|
const logInstance = ((msg, data, opts) => {
|
|
283
289
|
info(msg, data, opts);
|
|
@@ -297,7 +303,7 @@ var createFiro = (config = {}, parentContext = []) => {
|
|
|
297
303
|
export {
|
|
298
304
|
FIRO_COLORS,
|
|
299
305
|
utils_exports as FiroUtils,
|
|
300
|
-
|
|
306
|
+
createDevFormatter,
|
|
301
307
|
createFiro,
|
|
302
|
-
|
|
308
|
+
createProdFormatter
|
|
303
309
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fend/firo",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Elegant logger for Node.js, Bun and Deno with brilliant DX
|
|
3
|
+
"version": "0.0.7",
|
|
4
|
+
"description": "Elegant logger for Node.js, Bun and Deno with brilliant DX and pino-grade speed",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"firo",
|
|
7
7
|
"logger",
|
|
@@ -42,6 +42,8 @@
|
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/node": "^25.5.0",
|
|
45
|
+
"bumpp": "^11.0.1",
|
|
46
|
+
"pino": "^10.3.1",
|
|
45
47
|
"tsup": "^8.5.1",
|
|
46
48
|
"tsx": "^4.0.0",
|
|
47
49
|
"typescript": "^5.9.3"
|
|
@@ -52,6 +54,7 @@
|
|
|
52
54
|
"check": "tsc --noEmit && node --import tsx --test test/*.test.ts",
|
|
53
55
|
"typecheck": "tsc --noEmit",
|
|
54
56
|
"demo": "tsx demo.ts",
|
|
55
|
-
"bench": "tsx benchmark/prod.ts > /dev/null"
|
|
57
|
+
"bench": "tsx benchmark/prod.ts > /dev/null",
|
|
58
|
+
"bump": "bumpp --files package.json --files jsr.json --execute \"pnpm check && pnpm build && npm publish --dry-run --access public && deno publish --dry-run\" --commit \"v%s\" --no-push"
|
|
56
59
|
}
|
|
57
60
|
}
|