@fend/firo 0.0.4 → 0.0.6
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 +114 -30
- package/dist/index.cjs +78 -58
- package/dist/index.d.cts +64 -5
- package/dist/index.d.ts +64 -5
- package/dist/index.js +81 -56
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -54,9 +54,9 @@ deno add jsr:@fend/firo
|
|
|
54
54
|
## Quick start
|
|
55
55
|
|
|
56
56
|
```ts
|
|
57
|
-
import {
|
|
57
|
+
import { createFiro } from '@fend/firo'
|
|
58
58
|
|
|
59
|
-
const log =
|
|
59
|
+
const log = createFiro()
|
|
60
60
|
|
|
61
61
|
// log() is shorthand for log.info()
|
|
62
62
|
log('Server started')
|
|
@@ -79,7 +79,7 @@ Dev output:
|
|
|
79
79
|
Colored, human-readable. Errors go to `stderr`, everything else to `stdout`.
|
|
80
80
|
|
|
81
81
|
```ts
|
|
82
|
-
const log =
|
|
82
|
+
const log = createFiro({ mode: 'dev' })
|
|
83
83
|
```
|
|
84
84
|
|
|
85
85
|
### Prod
|
|
@@ -87,7 +87,7 @@ const log = createLogger({ mode: 'dev' })
|
|
|
87
87
|
Structured NDJSON. Everything goes to `stdout` — let your infrastructure route it.
|
|
88
88
|
|
|
89
89
|
```ts
|
|
90
|
-
const log =
|
|
90
|
+
const log = createFiro({ mode: 'prod' })
|
|
91
91
|
|
|
92
92
|
log.info('Request handled', { status: 200 })
|
|
93
93
|
// {"timestamp":"2024-01-15T14:32:01.204Z","level":"info","message":"Request handled","data":{"status":200}}
|
|
@@ -101,9 +101,9 @@ The best way to use **firo** in web frameworks is to store a child logger in `As
|
|
|
101
101
|
|
|
102
102
|
```ts
|
|
103
103
|
import { AsyncLocalStorage } from 'node:util'
|
|
104
|
-
import {
|
|
104
|
+
import { createFiro } from '@fend/firo'
|
|
105
105
|
|
|
106
|
-
const logger =
|
|
106
|
+
const logger = createFiro()
|
|
107
107
|
const storage = new AsyncLocalStorage()
|
|
108
108
|
|
|
109
109
|
// Middleware example
|
|
@@ -140,13 +140,13 @@ Debug lines are dimmed in dev mode to reduce visual noise.
|
|
|
140
140
|
|
|
141
141
|
```ts
|
|
142
142
|
// Suppress debug in dev, keep everything in prod
|
|
143
|
-
const log =
|
|
143
|
+
const log = createFiro({
|
|
144
144
|
minLevelInDev: 'info',
|
|
145
145
|
minLevelInProd: 'warn',
|
|
146
146
|
})
|
|
147
147
|
|
|
148
148
|
// Or a single threshold for both modes
|
|
149
|
-
const log =
|
|
149
|
+
const log = createFiro({ minLevel: 'warn' })
|
|
150
150
|
```
|
|
151
151
|
|
|
152
152
|
## Context
|
|
@@ -154,7 +154,7 @@ const log = createLogger({ minLevel: 'warn' })
|
|
|
154
154
|
Attach persistent key/value pairs to a logger instance. They appear in every log line.
|
|
155
155
|
|
|
156
156
|
```ts
|
|
157
|
-
const log =
|
|
157
|
+
const log = createFiro()
|
|
158
158
|
|
|
159
159
|
log.addContext('service', 'auth')
|
|
160
160
|
log.addContext('env', 'production')
|
|
@@ -171,7 +171,7 @@ log.info('Started')
|
|
|
171
171
|
log.addContext({ key: 'userId', value: 'u-789', omitKey: true })
|
|
172
172
|
// renders as [u-789] instead of [userId:u-789]
|
|
173
173
|
|
|
174
|
-
// Pin a specific color (0–
|
|
174
|
+
// Pin a specific color by palette index (0–29)
|
|
175
175
|
log.addContext({ key: 'region', value: 'west', colorIndex: 3 })
|
|
176
176
|
|
|
177
177
|
// Use any ANSI color — 256-color, truecolor, anything
|
|
@@ -196,7 +196,7 @@ const ctx = log.getContext() // ContextItem[]
|
|
|
196
196
|
Create a scoped logger that inherits the parent's context at the moment of creation. Parent and child are fully isolated — mutations on one do not affect the other.
|
|
197
197
|
|
|
198
198
|
```ts
|
|
199
|
-
const log =
|
|
199
|
+
const log = createFiro()
|
|
200
200
|
log.addContext('service', 'api')
|
|
201
201
|
|
|
202
202
|
const reqLog = log.child({ requestId: 'req-123', method: 'POST' })
|
|
@@ -239,7 +239,8 @@ log.error('Payment failed', err, {
|
|
|
239
239
|
`error()` accepts multiple call signatures:
|
|
240
240
|
|
|
241
241
|
```ts
|
|
242
|
-
// Message only
|
|
242
|
+
// Message only will be automatically wrapped in an Error object to intentionally capture and preserve the stack trace
|
|
243
|
+
// because stack trace with a couple of extra levels of indirection is definitely better than no stack trace at all
|
|
243
244
|
log.error('Something went wrong')
|
|
244
245
|
|
|
245
246
|
// Message + Error object
|
|
@@ -248,6 +249,9 @@ log.error('Query failed', new Error('timeout'))
|
|
|
248
249
|
// Error object only
|
|
249
250
|
log.error(new Error('Unhandled'))
|
|
250
251
|
|
|
252
|
+
// Error + extra data
|
|
253
|
+
log.error(new Error('DB down'), { query: 'SELECT ...', reqId: 123 })
|
|
254
|
+
|
|
251
255
|
// Anything — will be coerced to Error
|
|
252
256
|
log.error(someUnknownThing)
|
|
253
257
|
```
|
|
@@ -267,7 +271,23 @@ const myTransport: TransportFn = (level, context, msg, data, opts) => {
|
|
|
267
271
|
// opts: LogOptions | undefined
|
|
268
272
|
}
|
|
269
273
|
|
|
270
|
-
const log =
|
|
274
|
+
const log = createFiro({ transport: myTransport })
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### FiroUtils
|
|
278
|
+
|
|
279
|
+
`FiroUtils` exposes helper functions useful for building custom transports:
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
import { FiroUtils } from '@fend/firo'
|
|
283
|
+
|
|
284
|
+
FiroUtils.wrapToError(value) // coerce unknown → Error
|
|
285
|
+
FiroUtils.serializeError(err) // Error → plain object { message, stack, name, cause?, ... }
|
|
286
|
+
FiroUtils.safeStringify(obj) // JSON.stringify with bigint support + fallback
|
|
287
|
+
FiroUtils.jsonReplacer // replacer for JSON.stringify (handles bigint)
|
|
288
|
+
FiroUtils.extractMessage(msg) // extract message string from string | Error | unknown
|
|
289
|
+
FiroUtils.colorize(text, idx) // wrap text in ANSI color by palette index
|
|
290
|
+
FiroUtils.colorizeLevel(level, t) // wrap text in level color (red/yellow/dim)
|
|
271
291
|
```
|
|
272
292
|
|
|
273
293
|
## Dev transport options
|
|
@@ -275,9 +295,9 @@ const log = createLogger({ transport: myTransport })
|
|
|
275
295
|
Fine-tune the dev transport's timestamp format. For example, to remove seconds and milliseconds:
|
|
276
296
|
|
|
277
297
|
```ts
|
|
278
|
-
import {
|
|
298
|
+
import { createFiro } from '@fend/firo'
|
|
279
299
|
|
|
280
|
-
const log =
|
|
300
|
+
const log = createFiro({
|
|
281
301
|
devTransportConfig: {
|
|
282
302
|
timeOptions: {
|
|
283
303
|
hour: '2-digit',
|
|
@@ -289,6 +309,45 @@ const log = createLogger({
|
|
|
289
309
|
})
|
|
290
310
|
```
|
|
291
311
|
|
|
312
|
+
## Prod transport options
|
|
313
|
+
|
|
314
|
+
Configure the prod (JSON) transport's timestamp format:
|
|
315
|
+
|
|
316
|
+
```ts
|
|
317
|
+
// Epoch ms (faster, same as pino)
|
|
318
|
+
const log = createFiro({
|
|
319
|
+
mode: 'prod',
|
|
320
|
+
prodTransportConfig: { timestamp: 'epoch' }
|
|
321
|
+
})
|
|
322
|
+
// {"timestamp":1711100000000,"level":"info","message":"hello"}
|
|
323
|
+
|
|
324
|
+
// ISO 8601 (default, human-readable)
|
|
325
|
+
const log = createFiro({ mode: 'prod' })
|
|
326
|
+
// {"timestamp":"2024-01-15T14:32:01.204Z","level":"info","message":"hello"}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Custom destination
|
|
330
|
+
|
|
331
|
+
By default, prod transport writes to `process.stdout`. You can redirect output to any object with a `.write(string)` method:
|
|
332
|
+
|
|
333
|
+
```ts
|
|
334
|
+
import { createFiro } from '@fend/firo'
|
|
335
|
+
import { createWriteStream } from 'node:fs'
|
|
336
|
+
|
|
337
|
+
// Write to a file
|
|
338
|
+
const log = createFiro({
|
|
339
|
+
mode: 'prod',
|
|
340
|
+
prodTransportConfig: { dest: createWriteStream('/var/log/app.log') }
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
// Use SonicBoom for async buffered writes (same as pino)
|
|
344
|
+
import SonicBoom from 'sonic-boom'
|
|
345
|
+
const log = createFiro({
|
|
346
|
+
mode: 'prod',
|
|
347
|
+
prodTransportConfig: { dest: new SonicBoom({ fd: 1 }) }
|
|
348
|
+
})
|
|
349
|
+
```
|
|
350
|
+
|
|
292
351
|
## Color palette
|
|
293
352
|
|
|
294
353
|
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.
|
|
@@ -297,14 +356,14 @@ Most loggers give you monochrome walls of text. firo gives you **30 handpicked c
|
|
|
297
356
|
|
|
298
357
|
### How it works
|
|
299
358
|
|
|
300
|
-
By default, firo auto-assigns colors from
|
|
359
|
+
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.
|
|
301
360
|
|
|
302
|
-
|
|
361
|
+
You can also pin a specific color using `FIRO_COLORS` — a named palette with full IDE autocomplete:
|
|
303
362
|
|
|
304
363
|
```ts
|
|
305
|
-
import {
|
|
364
|
+
import { createFiro, FIRO_COLORS } from '@fend/firo'
|
|
306
365
|
|
|
307
|
-
const log =
|
|
366
|
+
const log = createFiro()
|
|
308
367
|
|
|
309
368
|
log.addContext('region', { value: 'west', color: FIRO_COLORS.coral })
|
|
310
369
|
log.addContext('service', { value: 'auth', color: FIRO_COLORS.skyBlue })
|
|
@@ -322,18 +381,12 @@ log.addContext('trace', { value: 'abc', color: '38;5;214' }) // 256-colo
|
|
|
322
381
|
log.addContext('span', { value: 'xyz', color: '38;2;255;105;180' }) // truecolor pink
|
|
323
382
|
```
|
|
324
383
|
|
|
325
|
-
###
|
|
384
|
+
### Restrict to safe colors
|
|
326
385
|
|
|
327
|
-
|
|
386
|
+
If your terminal doesn't support 256 colors, you can restrict auto-hash to 10 basic terminal-safe colors:
|
|
328
387
|
|
|
329
388
|
```ts
|
|
330
|
-
const log =
|
|
331
|
-
|
|
332
|
-
// Now every context key auto-gets one of 30 distinct colors
|
|
333
|
-
log.addContext('service', 'api')
|
|
334
|
-
log.addContext('region', 'west')
|
|
335
|
-
log.addContext('pod', 'web-3')
|
|
336
|
-
// Each badge is a different, beautiful color — no configuration needed
|
|
389
|
+
const log = createFiro({ useAllColors: false })
|
|
337
390
|
```
|
|
338
391
|
|
|
339
392
|
## Why not pino?
|
|
@@ -354,6 +407,35 @@ The problem with pino is development. Its default output is raw JSON — one gia
|
|
|
354
407
|
|
|
355
408
|
In prod it emits clean NDJSON, same as pino. Your log aggregator won't know the difference.
|
|
356
409
|
|
|
410
|
+
## Performance
|
|
411
|
+
|
|
412
|
+
Prod mode (NDJSON) with `timestamp: 'epoch'`, Apple M1, Node.js 25. Average time per 100K log lines (lower is better):
|
|
413
|
+
|
|
414
|
+
| Scenario | ops/sec | ms |
|
|
415
|
+
| ---------------------- | ------- | ------ |
|
|
416
|
+
| simple string | 782,488 | 127.8 |
|
|
417
|
+
| string + small obj | 656,512 | 152.3 |
|
|
418
|
+
| string + bigger obj | 513,087 | 194.9 |
|
|
419
|
+
| with 3 context items | 570,441 | 175.3 |
|
|
420
|
+
| child logger (2 ctx) | 568,977 | 175.8 |
|
|
421
|
+
| error with Error obj | 470,758 | 212.4 |
|
|
422
|
+
|
|
423
|
+
For comparison, here's [pino's own benchmark](https://github.com/pinojs/pino/blob/main/docs/benchmarks.md) (100K `"hello world"` logs):
|
|
424
|
+
|
|
425
|
+
| Logger | ms |
|
|
426
|
+
| ---------------- | ------ |
|
|
427
|
+
| pino | 114.8 |
|
|
428
|
+
| **firo** | **127.8** |
|
|
429
|
+
| bole | 172.7 |
|
|
430
|
+
| pino (NodeStream)| 159.2 |
|
|
431
|
+
| debug | 220.5 |
|
|
432
|
+
| winston | 270.2 |
|
|
433
|
+
| bunyan | 377.4 |
|
|
434
|
+
|
|
435
|
+
Pino is faster thanks to [SonicBoom](https://github.com/pinojs/sonic-boom) — an optimized async writer with buffering and [fast-json-stringify](https://github.com/fastify/fast-json-stringify) for schema-compiled serialization. firo uses plain `JSON.stringify` + `process.stdout.write` and lands within 8% of pino — a difference of ~130 nanoseconds per log line.
|
|
436
|
+
|
|
437
|
+
Run it yourself: `pnpm bench`
|
|
438
|
+
|
|
357
439
|
## API reference
|
|
358
440
|
|
|
359
441
|
### Logger methods
|
|
@@ -369,8 +451,9 @@ In prod it emits clean NDJSON, same as pino. Your log aggregator won't know the
|
|
|
369
451
|
| `addContext(item)` | Add a context entry (object form) |
|
|
370
452
|
| `removeFromContext(key)` | Remove a context entry by key |
|
|
371
453
|
| `getContext()` | Return the current context array |
|
|
454
|
+
| `hasInContext(key)` | Check if a context key exists |
|
|
372
455
|
|
|
373
|
-
### `
|
|
456
|
+
### `createFiro(config?)`
|
|
374
457
|
|
|
375
458
|
| Option | Type | Default | Description |
|
|
376
459
|
|---|---|---|---|
|
|
@@ -380,7 +463,8 @@ In prod it emits clean NDJSON, same as pino. Your log aggregator won't know the
|
|
|
380
463
|
| `minLevelInProd` | `LogLevel` | — | Overrides `minLevel` in prod mode |
|
|
381
464
|
| `transport` | `TransportFn` | — | Custom transport, overrides `mode` |
|
|
382
465
|
| `devTransportConfig` | `DevTransportConfig` | — | Options for the built-in dev transport |
|
|
383
|
-
| `
|
|
466
|
+
| `prodTransportConfig` | `ProdTransportConfig` | — | Options for the built-in JSON prod transport |
|
|
467
|
+
| `useAllColors` | `boolean` | `true` | Use all 30 palette colors for auto-hash (set `false` for 10 safe colors) |
|
|
384
468
|
|
|
385
469
|
## License
|
|
386
470
|
|
package/dist/index.cjs
CHANGED
|
@@ -31,13 +31,28 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
FIRO_COLORS: () => FIRO_COLORS,
|
|
34
|
+
FiroUtils: () => utils_exports,
|
|
34
35
|
createDevTransport: () => createDevTransport,
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
createFiro: () => createFiro,
|
|
37
|
+
createProdTransport: () => createProdTransport
|
|
37
38
|
});
|
|
38
39
|
module.exports = __toCommonJS(index_exports);
|
|
39
40
|
|
|
40
41
|
// src/utils.ts
|
|
42
|
+
var utils_exports = {};
|
|
43
|
+
__export(utils_exports, {
|
|
44
|
+
FIRO_COLORS: () => FIRO_COLORS,
|
|
45
|
+
LOG_LEVELS: () => LOG_LEVELS,
|
|
46
|
+
colorize: () => colorize,
|
|
47
|
+
colorizeLevel: () => colorizeLevel,
|
|
48
|
+
extractMessage: () => extractMessage,
|
|
49
|
+
getColorIndex: () => getColorIndex,
|
|
50
|
+
jsonReplacer: () => jsonReplacer,
|
|
51
|
+
safeStringify: () => safeStringify,
|
|
52
|
+
serializeError: () => serializeError,
|
|
53
|
+
wrapToError: () => wrapToError
|
|
54
|
+
});
|
|
55
|
+
var import_node_util = require("util");
|
|
41
56
|
var LOG_LEVELS = {
|
|
42
57
|
debug: 20,
|
|
43
58
|
info: 30,
|
|
@@ -92,6 +107,34 @@ var colorize = (text, colorIndex, color) => {
|
|
|
92
107
|
const code = color ?? (COLORS_LIST[colorIndex] || COLORS_LIST[colorIndex % SAFE_COLORS_COUNT]);
|
|
93
108
|
return `\x1B[${code}m${text}\x1B[0m`;
|
|
94
109
|
};
|
|
110
|
+
var jsonReplacer = (_key, value) => typeof value === "bigint" ? value.toString() : value;
|
|
111
|
+
var safeStringify = (obj) => {
|
|
112
|
+
try {
|
|
113
|
+
return JSON.stringify(obj, jsonReplacer);
|
|
114
|
+
} catch {
|
|
115
|
+
return (0, import_node_util.inspect)(obj);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
var wrapToError = (obj) => {
|
|
119
|
+
if (obj instanceof Error) return obj;
|
|
120
|
+
return new Error(
|
|
121
|
+
typeof obj === "object" && obj !== null ? safeStringify(obj) : String(obj)
|
|
122
|
+
);
|
|
123
|
+
};
|
|
124
|
+
var serializeError = (_err) => {
|
|
125
|
+
const err = wrapToError(_err);
|
|
126
|
+
const result = {
|
|
127
|
+
message: err.message,
|
|
128
|
+
stack: err.stack,
|
|
129
|
+
name: err.name,
|
|
130
|
+
...err
|
|
131
|
+
};
|
|
132
|
+
if (err.cause !== void 0) {
|
|
133
|
+
result.cause = err.cause instanceof Error ? serializeError(err.cause) : err.cause;
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
136
|
+
};
|
|
137
|
+
var extractMessage = (msg) => typeof msg === "string" ? msg : msg instanceof Error ? msg.message : typeof msg === "object" && msg !== null ? safeStringify(msg) : String(msg);
|
|
95
138
|
var colorizeLevel = (level, text) => {
|
|
96
139
|
if (level === "info") return text;
|
|
97
140
|
switch (level) {
|
|
@@ -109,8 +152,8 @@ var colorizeLevel = (level, text) => {
|
|
|
109
152
|
}
|
|
110
153
|
};
|
|
111
154
|
|
|
112
|
-
// src/
|
|
113
|
-
var
|
|
155
|
+
// src/transport_dev.ts
|
|
156
|
+
var import_node_util2 = require("util");
|
|
114
157
|
var import_node_process = __toESM(require("process"), 1);
|
|
115
158
|
var createDevTransport = (config = {}) => {
|
|
116
159
|
const locale = config.locale ?? void 0;
|
|
@@ -138,9 +181,9 @@ var createDevTransport = (config = {}) => {
|
|
|
138
181
|
let dataStr = "";
|
|
139
182
|
if (data !== void 0) {
|
|
140
183
|
const inspectOptions = opts?.pretty ? { compact: false, colors: true, depth: null } : { compact: true, breakLength: Infinity, colors: true, depth: null };
|
|
141
|
-
dataStr = (0,
|
|
184
|
+
dataStr = (0, import_node_util2.inspect)(data, inspectOptions);
|
|
142
185
|
}
|
|
143
|
-
const msgStr = typeof msg === "object" && msg !== null ? (0,
|
|
186
|
+
const msgStr = typeof msg === "object" && msg !== null ? (0, import_node_util2.inspect)(msg, { colors: true, compact: true, breakLength: Infinity }) : String(msg);
|
|
144
187
|
const parts = [
|
|
145
188
|
`[${timestamp}]`,
|
|
146
189
|
// Normal (not dimmed)
|
|
@@ -154,42 +197,20 @@ var createDevTransport = (config = {}) => {
|
|
|
154
197
|
};
|
|
155
198
|
return transport;
|
|
156
199
|
};
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
return (0, import_node_util.inspect)(obj);
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
var wrapToError = (obj) => {
|
|
166
|
-
if (obj instanceof Error) return obj;
|
|
167
|
-
return new Error(
|
|
168
|
-
typeof obj === "object" && obj !== null ? safeStringify(obj) : String(obj)
|
|
169
|
-
);
|
|
170
|
-
};
|
|
171
|
-
var serializeError = (_err) => {
|
|
172
|
-
const err = wrapToError(_err);
|
|
173
|
-
return {
|
|
174
|
-
message: err.message,
|
|
175
|
-
stack: err.stack,
|
|
176
|
-
name: err.name,
|
|
177
|
-
...err
|
|
178
|
-
};
|
|
179
|
-
};
|
|
180
|
-
var buildRecord = (level, context, msg, data) => {
|
|
181
|
-
const contextObj = context.reduce((acc, item) => {
|
|
182
|
-
acc[item.key] = item.value;
|
|
183
|
-
return acc;
|
|
184
|
-
}, {});
|
|
200
|
+
|
|
201
|
+
// src/transport_prod.ts
|
|
202
|
+
var import_node_util3 = require("util");
|
|
203
|
+
var import_node_process2 = __toESM(require("process"), 1);
|
|
204
|
+
var buildRecord = (level, context, msg, getTimestamp, data) => {
|
|
185
205
|
const logRecord = {
|
|
186
|
-
timestamp: (
|
|
187
|
-
level
|
|
188
|
-
...contextObj
|
|
206
|
+
timestamp: getTimestamp(),
|
|
207
|
+
level
|
|
189
208
|
};
|
|
209
|
+
for (let i = 0, len = context.length; i < len; i++) {
|
|
210
|
+
logRecord[context[i].key] = context[i].value;
|
|
211
|
+
}
|
|
212
|
+
logRecord.message = extractMessage(msg);
|
|
190
213
|
if (level === "error") {
|
|
191
|
-
const message = typeof msg === "string" ? msg : msg instanceof Error ? msg.message : typeof msg === "object" && msg !== null ? safeStringify(msg) : String(msg);
|
|
192
|
-
logRecord.message = message;
|
|
193
214
|
if (data instanceof Error) {
|
|
194
215
|
logRecord.error = serializeError(data);
|
|
195
216
|
} else if (msg instanceof Error) {
|
|
@@ -200,21 +221,25 @@ var buildRecord = (level, context, msg, data) => {
|
|
|
200
221
|
if (data !== void 0) logRecord.data = data;
|
|
201
222
|
}
|
|
202
223
|
} else {
|
|
203
|
-
|
|
224
|
+
if (msg instanceof Error) {
|
|
225
|
+
logRecord.error = serializeError(msg);
|
|
226
|
+
}
|
|
204
227
|
if (data !== void 0) {
|
|
205
228
|
logRecord.data = data instanceof Error ? serializeError(data) : data;
|
|
206
229
|
}
|
|
207
230
|
}
|
|
208
231
|
return logRecord;
|
|
209
232
|
};
|
|
210
|
-
var
|
|
233
|
+
var createProdTransport = (config = {}) => {
|
|
234
|
+
const getTimestamp = config.timestamp === "epoch" ? () => Date.now() : () => (/* @__PURE__ */ new Date()).toISOString();
|
|
235
|
+
const dest = config.dest ?? import_node_process2.default.stdout;
|
|
211
236
|
return (level, context, msg, data) => {
|
|
212
|
-
const record = buildRecord(level, context, msg, data);
|
|
237
|
+
const record = buildRecord(level, context, msg, getTimestamp, data);
|
|
213
238
|
let line;
|
|
214
239
|
try {
|
|
215
240
|
line = JSON.stringify(record, jsonReplacer) + "\n";
|
|
216
241
|
} catch {
|
|
217
|
-
if (record.data) record.data = (0,
|
|
242
|
+
if (record.data) record.data = (0, import_node_util3.inspect)(record.data);
|
|
218
243
|
try {
|
|
219
244
|
line = JSON.stringify(record, jsonReplacer) + "\n";
|
|
220
245
|
} catch {
|
|
@@ -226,28 +251,25 @@ var createJsonTransport = () => {
|
|
|
226
251
|
}) + "\n";
|
|
227
252
|
}
|
|
228
253
|
}
|
|
229
|
-
|
|
254
|
+
dest.write(line);
|
|
230
255
|
};
|
|
231
256
|
};
|
|
232
257
|
|
|
233
258
|
// src/index.ts
|
|
234
|
-
var
|
|
235
|
-
|
|
259
|
+
var createFiro = (config = {}, parentContext = []) => {
|
|
260
|
+
const useAllColors = config.useAllColors ?? true;
|
|
261
|
+
const fill = (item) => ({
|
|
236
262
|
...item,
|
|
237
263
|
colorIndex: typeof item.colorIndex === "number" ? item.colorIndex : getColorIndex(item.key, useAllColors),
|
|
238
264
|
color: item.color,
|
|
239
265
|
omitKey: item.omitKey ?? false
|
|
240
|
-
};
|
|
241
|
-
};
|
|
242
|
-
var createLoggerInternal = (config, parentContext) => {
|
|
243
|
-
const useAllColors = config.useAllColors ?? false;
|
|
244
|
-
const fill = (item) => fillContextItem(item, useAllColors);
|
|
266
|
+
});
|
|
245
267
|
const appendContextWithInvokeContext = (context2, invokeContext) => {
|
|
246
268
|
if (!invokeContext || invokeContext.length === 0) return context2;
|
|
247
269
|
return [...context2, ...invokeContext.map(fill)];
|
|
248
270
|
};
|
|
249
271
|
const context = [...parentContext.map(fill)];
|
|
250
|
-
const transport = config.transport ?? (config.mode === "prod" ?
|
|
272
|
+
const transport = config.transport ?? (config.mode === "prod" ? createProdTransport(config.prodTransportConfig) : createDevTransport(config.devTransportConfig));
|
|
251
273
|
const minLevelName = config.mode === "prod" ? config.minLevelInProd ?? config.minLevel : config.minLevelInDev ?? config.minLevel;
|
|
252
274
|
const minLevel = LOG_LEVELS[minLevelName ?? "debug"];
|
|
253
275
|
const getContext = () => context;
|
|
@@ -278,7 +300,7 @@ var createLoggerInternal = (config, parentContext) => {
|
|
|
278
300
|
}
|
|
279
301
|
return { key, value, colorIndex: getColorIndex(key, useAllColors) };
|
|
280
302
|
});
|
|
281
|
-
return
|
|
303
|
+
return createFiro({ transport, minLevel: minLevelName, useAllColors }, [...context, ...newItems]);
|
|
282
304
|
};
|
|
283
305
|
const debug = (msg, data, opts) => {
|
|
284
306
|
if (minLevel > LOG_LEVELS.debug) return;
|
|
@@ -311,13 +333,11 @@ var createLoggerInternal = (config, parentContext) => {
|
|
|
311
333
|
removeFromContext: removeKeyFromContext
|
|
312
334
|
});
|
|
313
335
|
};
|
|
314
|
-
var createLogger = (config = {}) => {
|
|
315
|
-
return createLoggerInternal(config, []);
|
|
316
|
-
};
|
|
317
336
|
// Annotate the CommonJS export names for ESM import in node:
|
|
318
337
|
0 && (module.exports = {
|
|
319
338
|
FIRO_COLORS,
|
|
339
|
+
FiroUtils,
|
|
320
340
|
createDevTransport,
|
|
321
|
-
|
|
322
|
-
|
|
341
|
+
createFiro,
|
|
342
|
+
createProdTransport
|
|
323
343
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
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. */
|
|
4
|
+
declare const LOG_LEVELS: {
|
|
5
|
+
readonly debug: 20;
|
|
6
|
+
readonly info: 30;
|
|
7
|
+
readonly warn: 40;
|
|
8
|
+
readonly error: 50;
|
|
9
|
+
};
|
|
3
10
|
/** Primitive types allowed as context values. */
|
|
4
11
|
type ContextValue = string | number | boolean | null | undefined;
|
|
5
12
|
/** Options to customize how a context item is rendered. */
|
|
@@ -68,6 +75,44 @@ declare const FIRO_COLORS: {
|
|
|
68
75
|
readonly tangerine: "38;5;208";
|
|
69
76
|
readonly periwinkle: "38;5;147";
|
|
70
77
|
};
|
|
78
|
+
/** Hash a string to a stable color palette index. Similar strings land on different colors. */
|
|
79
|
+
declare const getColorIndex: (str: string, useAllColors?: boolean) => number;
|
|
80
|
+
/** Wrap text in an ANSI color escape sequence by palette index or raw ANSI code. */
|
|
81
|
+
declare const colorize: (text: string, colorIndex: number, color?: string) => string;
|
|
82
|
+
/** JSON.stringify replacer that converts BigInt values to strings. */
|
|
83
|
+
declare const jsonReplacer: (_key: string, value: unknown) => unknown;
|
|
84
|
+
/** Safely stringify any value to JSON with BigInt support. Falls back to `util.inspect` on circular references. */
|
|
85
|
+
declare const safeStringify: (obj: unknown) => string;
|
|
86
|
+
/** Coerce any value to an Error instance. If already an Error, returns as-is. */
|
|
87
|
+
declare const wrapToError: (obj: unknown) => Error;
|
|
88
|
+
/** Serialize an error-like value to a plain object with `message`, `stack`, `name`, and recursively serialized `cause`. */
|
|
89
|
+
declare const serializeError: (_err: unknown) => Record<string, unknown>;
|
|
90
|
+
/** Extract a human-readable message string from any log input. Useful for building custom transports. */
|
|
91
|
+
declare const extractMessage: (msg: string | Error | unknown) => string;
|
|
92
|
+
/** Wrap text in an ANSI color based on log level: red for error, yellow for warn, dim for debug. */
|
|
93
|
+
declare const colorizeLevel: (level: LogLevel, text: string) => string;
|
|
94
|
+
|
|
95
|
+
type utils_ContextExtension = ContextExtension;
|
|
96
|
+
type utils_ContextItem = ContextItem;
|
|
97
|
+
type utils_ContextItemWithOptions = ContextItemWithOptions;
|
|
98
|
+
type utils_ContextOptions = ContextOptions;
|
|
99
|
+
type utils_ContextValue = ContextValue;
|
|
100
|
+
declare const utils_FIRO_COLORS: typeof FIRO_COLORS;
|
|
101
|
+
declare const utils_LOG_LEVELS: typeof LOG_LEVELS;
|
|
102
|
+
type utils_LogLevel = LogLevel;
|
|
103
|
+
type utils_LogOptions = LogOptions;
|
|
104
|
+
type utils_TransportFn = TransportFn;
|
|
105
|
+
declare const utils_colorize: typeof colorize;
|
|
106
|
+
declare const utils_colorizeLevel: typeof colorizeLevel;
|
|
107
|
+
declare const utils_extractMessage: typeof extractMessage;
|
|
108
|
+
declare const utils_getColorIndex: typeof getColorIndex;
|
|
109
|
+
declare const utils_jsonReplacer: typeof jsonReplacer;
|
|
110
|
+
declare const utils_safeStringify: typeof safeStringify;
|
|
111
|
+
declare const utils_serializeError: typeof serializeError;
|
|
112
|
+
declare const utils_wrapToError: typeof wrapToError;
|
|
113
|
+
declare namespace utils {
|
|
114
|
+
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, type utils_TransportFn as TransportFn, 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 };
|
|
115
|
+
}
|
|
71
116
|
|
|
72
117
|
/**
|
|
73
118
|
* Configuration options for the development transport.
|
|
@@ -86,13 +131,23 @@ type DevTransportConfig = {
|
|
|
86
131
|
* @returns A `TransportFn` that writes to the console.
|
|
87
132
|
*/
|
|
88
133
|
declare const createDevTransport: (config?: DevTransportConfig) => TransportFn;
|
|
134
|
+
|
|
135
|
+
type TimestampFormat = 'iso' | 'epoch';
|
|
136
|
+
type ProdTransportConfig = {
|
|
137
|
+
/** Timestamp format: 'iso' (default) for ISO 8601 string, 'epoch' for ms since Unix epoch. */
|
|
138
|
+
timestamp?: TimestampFormat;
|
|
139
|
+
/** Output destination. Any object with a `.write(string)` method. Defaults to `process.stdout`. */
|
|
140
|
+
dest?: {
|
|
141
|
+
write(s: string): unknown;
|
|
142
|
+
};
|
|
143
|
+
};
|
|
89
144
|
/**
|
|
90
145
|
* Creates a built-in transport optimized for production.
|
|
91
146
|
* Emits strictly structured NDJSON (Newline Delimited JSON) to stdout.
|
|
92
147
|
*
|
|
93
148
|
* @returns A `TransportFn` that writes JSON to standard output.
|
|
94
149
|
*/
|
|
95
|
-
declare const
|
|
150
|
+
declare const createProdTransport: (config?: ProdTransportConfig) => TransportFn;
|
|
96
151
|
|
|
97
152
|
/**
|
|
98
153
|
* Configuration options for creating a logger instance.
|
|
@@ -110,11 +165,13 @@ type LoggerConfig = {
|
|
|
110
165
|
transport?: TransportFn;
|
|
111
166
|
/** Options for fine-tuning the built-in development transport (e.g. timestamp format). */
|
|
112
167
|
devTransportConfig?: DevTransportConfig;
|
|
113
|
-
/**
|
|
168
|
+
/** Options for the built-in prod transport (e.g. timestamp format). */
|
|
169
|
+
prodTransportConfig?: ProdTransportConfig;
|
|
170
|
+
/** Use the full extended color palette (30 colors including 256-color) for auto-assigned context badges. Defaults to true. Set to false to restrict to 10 terminal-safe colors. */
|
|
114
171
|
useAllColors?: boolean;
|
|
115
172
|
};
|
|
116
173
|
/**
|
|
117
|
-
* The logger instance returned by `
|
|
174
|
+
* The logger instance returned by `createFiro`.
|
|
118
175
|
* It is a callable object: calling `log(msg)` is shorthand for `log.info(msg)`.
|
|
119
176
|
*/
|
|
120
177
|
interface Firo {
|
|
@@ -128,6 +185,8 @@ interface Firo {
|
|
|
128
185
|
warn: (msg: string, data?: unknown, opts?: LogOptions) => void;
|
|
129
186
|
/** Log an error object directly. */
|
|
130
187
|
error(err: Error | unknown): void;
|
|
188
|
+
/** Log an error object with additional data. */
|
|
189
|
+
error(err: Error, data?: unknown, opts?: LogOptions): void;
|
|
131
190
|
/** Log a message alongside an error or custom data object. */
|
|
132
191
|
error(msg: string, err?: Error | unknown, opts?: LogOptions): void;
|
|
133
192
|
/**
|
|
@@ -153,6 +212,6 @@ interface Firo {
|
|
|
153
212
|
* @param config Optional configuration for log levels, mode, and transports.
|
|
154
213
|
* @returns A fully configured `Firo` instance.
|
|
155
214
|
*/
|
|
156
|
-
declare const
|
|
215
|
+
declare const createFiro: (config?: LoggerConfig, parentContext?: ContextItem[]) => Firo;
|
|
157
216
|
|
|
158
|
-
export { type ContextExtension, type ContextItem, type ContextItemWithOptions, type ContextOptions, type ContextValue, type DevTransportConfig, FIRO_COLORS, type Firo, type LogLevel, type LogOptions, type LoggerConfig, type TransportFn, createDevTransport,
|
|
217
|
+
export { type ContextExtension, type ContextItem, type ContextItemWithOptions, type ContextOptions, type ContextValue, type DevTransportConfig, FIRO_COLORS, type Firo, utils as FiroUtils, type LogLevel, type LogOptions, type LoggerConfig, type ProdTransportConfig, type TimestampFormat, type TransportFn, createDevTransport, createFiro, createProdTransport };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
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. */
|
|
4
|
+
declare const LOG_LEVELS: {
|
|
5
|
+
readonly debug: 20;
|
|
6
|
+
readonly info: 30;
|
|
7
|
+
readonly warn: 40;
|
|
8
|
+
readonly error: 50;
|
|
9
|
+
};
|
|
3
10
|
/** Primitive types allowed as context values. */
|
|
4
11
|
type ContextValue = string | number | boolean | null | undefined;
|
|
5
12
|
/** Options to customize how a context item is rendered. */
|
|
@@ -68,6 +75,44 @@ declare const FIRO_COLORS: {
|
|
|
68
75
|
readonly tangerine: "38;5;208";
|
|
69
76
|
readonly periwinkle: "38;5;147";
|
|
70
77
|
};
|
|
78
|
+
/** Hash a string to a stable color palette index. Similar strings land on different colors. */
|
|
79
|
+
declare const getColorIndex: (str: string, useAllColors?: boolean) => number;
|
|
80
|
+
/** Wrap text in an ANSI color escape sequence by palette index or raw ANSI code. */
|
|
81
|
+
declare const colorize: (text: string, colorIndex: number, color?: string) => string;
|
|
82
|
+
/** JSON.stringify replacer that converts BigInt values to strings. */
|
|
83
|
+
declare const jsonReplacer: (_key: string, value: unknown) => unknown;
|
|
84
|
+
/** Safely stringify any value to JSON with BigInt support. Falls back to `util.inspect` on circular references. */
|
|
85
|
+
declare const safeStringify: (obj: unknown) => string;
|
|
86
|
+
/** Coerce any value to an Error instance. If already an Error, returns as-is. */
|
|
87
|
+
declare const wrapToError: (obj: unknown) => Error;
|
|
88
|
+
/** Serialize an error-like value to a plain object with `message`, `stack`, `name`, and recursively serialized `cause`. */
|
|
89
|
+
declare const serializeError: (_err: unknown) => Record<string, unknown>;
|
|
90
|
+
/** Extract a human-readable message string from any log input. Useful for building custom transports. */
|
|
91
|
+
declare const extractMessage: (msg: string | Error | unknown) => string;
|
|
92
|
+
/** Wrap text in an ANSI color based on log level: red for error, yellow for warn, dim for debug. */
|
|
93
|
+
declare const colorizeLevel: (level: LogLevel, text: string) => string;
|
|
94
|
+
|
|
95
|
+
type utils_ContextExtension = ContextExtension;
|
|
96
|
+
type utils_ContextItem = ContextItem;
|
|
97
|
+
type utils_ContextItemWithOptions = ContextItemWithOptions;
|
|
98
|
+
type utils_ContextOptions = ContextOptions;
|
|
99
|
+
type utils_ContextValue = ContextValue;
|
|
100
|
+
declare const utils_FIRO_COLORS: typeof FIRO_COLORS;
|
|
101
|
+
declare const utils_LOG_LEVELS: typeof LOG_LEVELS;
|
|
102
|
+
type utils_LogLevel = LogLevel;
|
|
103
|
+
type utils_LogOptions = LogOptions;
|
|
104
|
+
type utils_TransportFn = TransportFn;
|
|
105
|
+
declare const utils_colorize: typeof colorize;
|
|
106
|
+
declare const utils_colorizeLevel: typeof colorizeLevel;
|
|
107
|
+
declare const utils_extractMessage: typeof extractMessage;
|
|
108
|
+
declare const utils_getColorIndex: typeof getColorIndex;
|
|
109
|
+
declare const utils_jsonReplacer: typeof jsonReplacer;
|
|
110
|
+
declare const utils_safeStringify: typeof safeStringify;
|
|
111
|
+
declare const utils_serializeError: typeof serializeError;
|
|
112
|
+
declare const utils_wrapToError: typeof wrapToError;
|
|
113
|
+
declare namespace utils {
|
|
114
|
+
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, type utils_TransportFn as TransportFn, 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 };
|
|
115
|
+
}
|
|
71
116
|
|
|
72
117
|
/**
|
|
73
118
|
* Configuration options for the development transport.
|
|
@@ -86,13 +131,23 @@ type DevTransportConfig = {
|
|
|
86
131
|
* @returns A `TransportFn` that writes to the console.
|
|
87
132
|
*/
|
|
88
133
|
declare const createDevTransport: (config?: DevTransportConfig) => TransportFn;
|
|
134
|
+
|
|
135
|
+
type TimestampFormat = 'iso' | 'epoch';
|
|
136
|
+
type ProdTransportConfig = {
|
|
137
|
+
/** Timestamp format: 'iso' (default) for ISO 8601 string, 'epoch' for ms since Unix epoch. */
|
|
138
|
+
timestamp?: TimestampFormat;
|
|
139
|
+
/** Output destination. Any object with a `.write(string)` method. Defaults to `process.stdout`. */
|
|
140
|
+
dest?: {
|
|
141
|
+
write(s: string): unknown;
|
|
142
|
+
};
|
|
143
|
+
};
|
|
89
144
|
/**
|
|
90
145
|
* Creates a built-in transport optimized for production.
|
|
91
146
|
* Emits strictly structured NDJSON (Newline Delimited JSON) to stdout.
|
|
92
147
|
*
|
|
93
148
|
* @returns A `TransportFn` that writes JSON to standard output.
|
|
94
149
|
*/
|
|
95
|
-
declare const
|
|
150
|
+
declare const createProdTransport: (config?: ProdTransportConfig) => TransportFn;
|
|
96
151
|
|
|
97
152
|
/**
|
|
98
153
|
* Configuration options for creating a logger instance.
|
|
@@ -110,11 +165,13 @@ type LoggerConfig = {
|
|
|
110
165
|
transport?: TransportFn;
|
|
111
166
|
/** Options for fine-tuning the built-in development transport (e.g. timestamp format). */
|
|
112
167
|
devTransportConfig?: DevTransportConfig;
|
|
113
|
-
/**
|
|
168
|
+
/** Options for the built-in prod transport (e.g. timestamp format). */
|
|
169
|
+
prodTransportConfig?: ProdTransportConfig;
|
|
170
|
+
/** Use the full extended color palette (30 colors including 256-color) for auto-assigned context badges. Defaults to true. Set to false to restrict to 10 terminal-safe colors. */
|
|
114
171
|
useAllColors?: boolean;
|
|
115
172
|
};
|
|
116
173
|
/**
|
|
117
|
-
* The logger instance returned by `
|
|
174
|
+
* The logger instance returned by `createFiro`.
|
|
118
175
|
* It is a callable object: calling `log(msg)` is shorthand for `log.info(msg)`.
|
|
119
176
|
*/
|
|
120
177
|
interface Firo {
|
|
@@ -128,6 +185,8 @@ interface Firo {
|
|
|
128
185
|
warn: (msg: string, data?: unknown, opts?: LogOptions) => void;
|
|
129
186
|
/** Log an error object directly. */
|
|
130
187
|
error(err: Error | unknown): void;
|
|
188
|
+
/** Log an error object with additional data. */
|
|
189
|
+
error(err: Error, data?: unknown, opts?: LogOptions): void;
|
|
131
190
|
/** Log a message alongside an error or custom data object. */
|
|
132
191
|
error(msg: string, err?: Error | unknown, opts?: LogOptions): void;
|
|
133
192
|
/**
|
|
@@ -153,6 +212,6 @@ interface Firo {
|
|
|
153
212
|
* @param config Optional configuration for log levels, mode, and transports.
|
|
154
213
|
* @returns A fully configured `Firo` instance.
|
|
155
214
|
*/
|
|
156
|
-
declare const
|
|
215
|
+
declare const createFiro: (config?: LoggerConfig, parentContext?: ContextItem[]) => Firo;
|
|
157
216
|
|
|
158
|
-
export { type ContextExtension, type ContextItem, type ContextItemWithOptions, type ContextOptions, type ContextValue, type DevTransportConfig, FIRO_COLORS, type Firo, type LogLevel, type LogOptions, type LoggerConfig, type TransportFn, createDevTransport,
|
|
217
|
+
export { type ContextExtension, type ContextItem, type ContextItemWithOptions, type ContextOptions, type ContextValue, type DevTransportConfig, FIRO_COLORS, type Firo, utils as FiroUtils, type LogLevel, type LogOptions, type LoggerConfig, type ProdTransportConfig, type TimestampFormat, type TransportFn, createDevTransport, createFiro, createProdTransport };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,24 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
+
};
|
|
6
|
+
|
|
1
7
|
// src/utils.ts
|
|
8
|
+
var utils_exports = {};
|
|
9
|
+
__export(utils_exports, {
|
|
10
|
+
FIRO_COLORS: () => FIRO_COLORS,
|
|
11
|
+
LOG_LEVELS: () => LOG_LEVELS,
|
|
12
|
+
colorize: () => colorize,
|
|
13
|
+
colorizeLevel: () => colorizeLevel,
|
|
14
|
+
extractMessage: () => extractMessage,
|
|
15
|
+
getColorIndex: () => getColorIndex,
|
|
16
|
+
jsonReplacer: () => jsonReplacer,
|
|
17
|
+
safeStringify: () => safeStringify,
|
|
18
|
+
serializeError: () => serializeError,
|
|
19
|
+
wrapToError: () => wrapToError
|
|
20
|
+
});
|
|
21
|
+
import { inspect } from "util";
|
|
2
22
|
var LOG_LEVELS = {
|
|
3
23
|
debug: 20,
|
|
4
24
|
info: 30,
|
|
@@ -53,6 +73,34 @@ var colorize = (text, colorIndex, color) => {
|
|
|
53
73
|
const code = color ?? (COLORS_LIST[colorIndex] || COLORS_LIST[colorIndex % SAFE_COLORS_COUNT]);
|
|
54
74
|
return `\x1B[${code}m${text}\x1B[0m`;
|
|
55
75
|
};
|
|
76
|
+
var jsonReplacer = (_key, value) => typeof value === "bigint" ? value.toString() : value;
|
|
77
|
+
var safeStringify = (obj) => {
|
|
78
|
+
try {
|
|
79
|
+
return JSON.stringify(obj, jsonReplacer);
|
|
80
|
+
} catch {
|
|
81
|
+
return inspect(obj);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
var wrapToError = (obj) => {
|
|
85
|
+
if (obj instanceof Error) return obj;
|
|
86
|
+
return new Error(
|
|
87
|
+
typeof obj === "object" && obj !== null ? safeStringify(obj) : String(obj)
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
|
+
var serializeError = (_err) => {
|
|
91
|
+
const err = wrapToError(_err);
|
|
92
|
+
const result = {
|
|
93
|
+
message: err.message,
|
|
94
|
+
stack: err.stack,
|
|
95
|
+
name: err.name,
|
|
96
|
+
...err
|
|
97
|
+
};
|
|
98
|
+
if (err.cause !== void 0) {
|
|
99
|
+
result.cause = err.cause instanceof Error ? serializeError(err.cause) : err.cause;
|
|
100
|
+
}
|
|
101
|
+
return result;
|
|
102
|
+
};
|
|
103
|
+
var extractMessage = (msg) => typeof msg === "string" ? msg : msg instanceof Error ? msg.message : typeof msg === "object" && msg !== null ? safeStringify(msg) : String(msg);
|
|
56
104
|
var colorizeLevel = (level, text) => {
|
|
57
105
|
if (level === "info") return text;
|
|
58
106
|
switch (level) {
|
|
@@ -70,8 +118,8 @@ var colorizeLevel = (level, text) => {
|
|
|
70
118
|
}
|
|
71
119
|
};
|
|
72
120
|
|
|
73
|
-
// src/
|
|
74
|
-
import { inspect } from "util";
|
|
121
|
+
// src/transport_dev.ts
|
|
122
|
+
import { inspect as inspect2 } from "util";
|
|
75
123
|
import process from "process";
|
|
76
124
|
var createDevTransport = (config = {}) => {
|
|
77
125
|
const locale = config.locale ?? void 0;
|
|
@@ -99,9 +147,9 @@ var createDevTransport = (config = {}) => {
|
|
|
99
147
|
let dataStr = "";
|
|
100
148
|
if (data !== void 0) {
|
|
101
149
|
const inspectOptions = opts?.pretty ? { compact: false, colors: true, depth: null } : { compact: true, breakLength: Infinity, colors: true, depth: null };
|
|
102
|
-
dataStr =
|
|
150
|
+
dataStr = inspect2(data, inspectOptions);
|
|
103
151
|
}
|
|
104
|
-
const msgStr = typeof msg === "object" && msg !== null ?
|
|
152
|
+
const msgStr = typeof msg === "object" && msg !== null ? inspect2(msg, { colors: true, compact: true, breakLength: Infinity }) : String(msg);
|
|
105
153
|
const parts = [
|
|
106
154
|
`[${timestamp}]`,
|
|
107
155
|
// Normal (not dimmed)
|
|
@@ -115,42 +163,20 @@ var createDevTransport = (config = {}) => {
|
|
|
115
163
|
};
|
|
116
164
|
return transport;
|
|
117
165
|
};
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
return inspect(obj);
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
var wrapToError = (obj) => {
|
|
127
|
-
if (obj instanceof Error) return obj;
|
|
128
|
-
return new Error(
|
|
129
|
-
typeof obj === "object" && obj !== null ? safeStringify(obj) : String(obj)
|
|
130
|
-
);
|
|
131
|
-
};
|
|
132
|
-
var serializeError = (_err) => {
|
|
133
|
-
const err = wrapToError(_err);
|
|
134
|
-
return {
|
|
135
|
-
message: err.message,
|
|
136
|
-
stack: err.stack,
|
|
137
|
-
name: err.name,
|
|
138
|
-
...err
|
|
139
|
-
};
|
|
140
|
-
};
|
|
141
|
-
var buildRecord = (level, context, msg, data) => {
|
|
142
|
-
const contextObj = context.reduce((acc, item) => {
|
|
143
|
-
acc[item.key] = item.value;
|
|
144
|
-
return acc;
|
|
145
|
-
}, {});
|
|
166
|
+
|
|
167
|
+
// src/transport_prod.ts
|
|
168
|
+
import { inspect as inspect3 } from "util";
|
|
169
|
+
import process2 from "process";
|
|
170
|
+
var buildRecord = (level, context, msg, getTimestamp, data) => {
|
|
146
171
|
const logRecord = {
|
|
147
|
-
timestamp: (
|
|
148
|
-
level
|
|
149
|
-
...contextObj
|
|
172
|
+
timestamp: getTimestamp(),
|
|
173
|
+
level
|
|
150
174
|
};
|
|
175
|
+
for (let i = 0, len = context.length; i < len; i++) {
|
|
176
|
+
logRecord[context[i].key] = context[i].value;
|
|
177
|
+
}
|
|
178
|
+
logRecord.message = extractMessage(msg);
|
|
151
179
|
if (level === "error") {
|
|
152
|
-
const message = typeof msg === "string" ? msg : msg instanceof Error ? msg.message : typeof msg === "object" && msg !== null ? safeStringify(msg) : String(msg);
|
|
153
|
-
logRecord.message = message;
|
|
154
180
|
if (data instanceof Error) {
|
|
155
181
|
logRecord.error = serializeError(data);
|
|
156
182
|
} else if (msg instanceof Error) {
|
|
@@ -161,21 +187,25 @@ var buildRecord = (level, context, msg, data) => {
|
|
|
161
187
|
if (data !== void 0) logRecord.data = data;
|
|
162
188
|
}
|
|
163
189
|
} else {
|
|
164
|
-
|
|
190
|
+
if (msg instanceof Error) {
|
|
191
|
+
logRecord.error = serializeError(msg);
|
|
192
|
+
}
|
|
165
193
|
if (data !== void 0) {
|
|
166
194
|
logRecord.data = data instanceof Error ? serializeError(data) : data;
|
|
167
195
|
}
|
|
168
196
|
}
|
|
169
197
|
return logRecord;
|
|
170
198
|
};
|
|
171
|
-
var
|
|
199
|
+
var createProdTransport = (config = {}) => {
|
|
200
|
+
const getTimestamp = config.timestamp === "epoch" ? () => Date.now() : () => (/* @__PURE__ */ new Date()).toISOString();
|
|
201
|
+
const dest = config.dest ?? process2.stdout;
|
|
172
202
|
return (level, context, msg, data) => {
|
|
173
|
-
const record = buildRecord(level, context, msg, data);
|
|
203
|
+
const record = buildRecord(level, context, msg, getTimestamp, data);
|
|
174
204
|
let line;
|
|
175
205
|
try {
|
|
176
206
|
line = JSON.stringify(record, jsonReplacer) + "\n";
|
|
177
207
|
} catch {
|
|
178
|
-
if (record.data) record.data =
|
|
208
|
+
if (record.data) record.data = inspect3(record.data);
|
|
179
209
|
try {
|
|
180
210
|
line = JSON.stringify(record, jsonReplacer) + "\n";
|
|
181
211
|
} catch {
|
|
@@ -187,28 +217,25 @@ var createJsonTransport = () => {
|
|
|
187
217
|
}) + "\n";
|
|
188
218
|
}
|
|
189
219
|
}
|
|
190
|
-
|
|
220
|
+
dest.write(line);
|
|
191
221
|
};
|
|
192
222
|
};
|
|
193
223
|
|
|
194
224
|
// src/index.ts
|
|
195
|
-
var
|
|
196
|
-
|
|
225
|
+
var createFiro = (config = {}, parentContext = []) => {
|
|
226
|
+
const useAllColors = config.useAllColors ?? true;
|
|
227
|
+
const fill = (item) => ({
|
|
197
228
|
...item,
|
|
198
229
|
colorIndex: typeof item.colorIndex === "number" ? item.colorIndex : getColorIndex(item.key, useAllColors),
|
|
199
230
|
color: item.color,
|
|
200
231
|
omitKey: item.omitKey ?? false
|
|
201
|
-
};
|
|
202
|
-
};
|
|
203
|
-
var createLoggerInternal = (config, parentContext) => {
|
|
204
|
-
const useAllColors = config.useAllColors ?? false;
|
|
205
|
-
const fill = (item) => fillContextItem(item, useAllColors);
|
|
232
|
+
});
|
|
206
233
|
const appendContextWithInvokeContext = (context2, invokeContext) => {
|
|
207
234
|
if (!invokeContext || invokeContext.length === 0) return context2;
|
|
208
235
|
return [...context2, ...invokeContext.map(fill)];
|
|
209
236
|
};
|
|
210
237
|
const context = [...parentContext.map(fill)];
|
|
211
|
-
const transport = config.transport ?? (config.mode === "prod" ?
|
|
238
|
+
const transport = config.transport ?? (config.mode === "prod" ? createProdTransport(config.prodTransportConfig) : createDevTransport(config.devTransportConfig));
|
|
212
239
|
const minLevelName = config.mode === "prod" ? config.minLevelInProd ?? config.minLevel : config.minLevelInDev ?? config.minLevel;
|
|
213
240
|
const minLevel = LOG_LEVELS[minLevelName ?? "debug"];
|
|
214
241
|
const getContext = () => context;
|
|
@@ -239,7 +266,7 @@ var createLoggerInternal = (config, parentContext) => {
|
|
|
239
266
|
}
|
|
240
267
|
return { key, value, colorIndex: getColorIndex(key, useAllColors) };
|
|
241
268
|
});
|
|
242
|
-
return
|
|
269
|
+
return createFiro({ transport, minLevel: minLevelName, useAllColors }, [...context, ...newItems]);
|
|
243
270
|
};
|
|
244
271
|
const debug = (msg, data, opts) => {
|
|
245
272
|
if (minLevel > LOG_LEVELS.debug) return;
|
|
@@ -272,12 +299,10 @@ var createLoggerInternal = (config, parentContext) => {
|
|
|
272
299
|
removeFromContext: removeKeyFromContext
|
|
273
300
|
});
|
|
274
301
|
};
|
|
275
|
-
var createLogger = (config = {}) => {
|
|
276
|
-
return createLoggerInternal(config, []);
|
|
277
|
-
};
|
|
278
302
|
export {
|
|
279
303
|
FIRO_COLORS,
|
|
304
|
+
utils_exports as FiroUtils,
|
|
280
305
|
createDevTransport,
|
|
281
|
-
|
|
282
|
-
|
|
306
|
+
createFiro,
|
|
307
|
+
createProdTransport
|
|
283
308
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fend/firo",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "Elegant logger for Node.js, Bun and Deno with brilliant DX.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"firo",
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/node": "^25.5.0",
|
|
45
|
+
"bumpp": "^11.0.1",
|
|
45
46
|
"tsup": "^8.5.1",
|
|
46
47
|
"tsx": "^4.0.0",
|
|
47
48
|
"typescript": "^5.9.3"
|
|
@@ -51,6 +52,8 @@
|
|
|
51
52
|
"test": "node --import tsx --test test/*.test.ts",
|
|
52
53
|
"check": "tsc --noEmit && node --import tsx --test test/*.test.ts",
|
|
53
54
|
"typecheck": "tsc --noEmit",
|
|
54
|
-
"demo": "tsx demo.ts"
|
|
55
|
+
"demo": "tsx demo.ts",
|
|
56
|
+
"bench": "tsx benchmark/prod.ts > /dev/null",
|
|
57
|
+
"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"
|
|
55
58
|
}
|
|
56
59
|
}
|