@trebired/logger 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/LICENSE +21 -0
  3. package/README.md +368 -0
  4. package/dist/constants.d.ts +7 -0
  5. package/dist/constants.d.ts.map +1 -0
  6. package/dist/constants.js +46 -0
  7. package/dist/constants.js.map +1 -0
  8. package/dist/core/create_log.d.ts +4 -0
  9. package/dist/core/create_log.d.ts.map +1 -0
  10. package/dist/core/create_log.js +206 -0
  11. package/dist/core/create_log.js.map +1 -0
  12. package/dist/format/console.d.ts +6 -0
  13. package/dist/format/console.d.ts.map +1 -0
  14. package/dist/format/console.js +62 -0
  15. package/dist/format/console.js.map +1 -0
  16. package/dist/groups.d.ts +7 -0
  17. package/dist/groups.d.ts.map +1 -0
  18. package/dist/groups.js +22 -0
  19. package/dist/groups.js.map +1 -0
  20. package/dist/index.d.ts +7 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +6 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/levels/index.d.ts +7 -0
  25. package/dist/levels/index.d.ts.map +1 -0
  26. package/dist/levels/index.js +47 -0
  27. package/dist/levels/index.js.map +1 -0
  28. package/dist/metadata/process.d.ts +7 -0
  29. package/dist/metadata/process.d.ts.map +1 -0
  30. package/dist/metadata/process.js +93 -0
  31. package/dist/metadata/process.js.map +1 -0
  32. package/dist/middleware/request.d.ts +4 -0
  33. package/dist/middleware/request.d.ts.map +1 -0
  34. package/dist/middleware/request.js +32 -0
  35. package/dist/middleware/request.js.map +1 -0
  36. package/dist/storage/names.d.ts +21 -0
  37. package/dist/storage/names.d.ts.map +1 -0
  38. package/dist/storage/names.js +62 -0
  39. package/dist/storage/names.js.map +1 -0
  40. package/dist/storage/options.d.ts +5 -0
  41. package/dist/storage/options.d.ts.map +1 -0
  42. package/dist/storage/options.js +26 -0
  43. package/dist/storage/options.js.map +1 -0
  44. package/dist/storage/query.d.ts +6 -0
  45. package/dist/storage/query.d.ts.map +1 -0
  46. package/dist/storage/query.js +72 -0
  47. package/dist/storage/query.js.map +1 -0
  48. package/dist/storage/retention.d.ts +5 -0
  49. package/dist/storage/retention.d.ts.map +1 -0
  50. package/dist/storage/retention.js +50 -0
  51. package/dist/storage/retention.js.map +1 -0
  52. package/dist/storage/walk.d.ts +5 -0
  53. package/dist/storage/walk.d.ts.map +1 -0
  54. package/dist/storage/walk.js +59 -0
  55. package/dist/storage/walk.js.map +1 -0
  56. package/dist/storage/write.d.ts +38 -0
  57. package/dist/storage/write.d.ts.map +1 -0
  58. package/dist/storage/write.js +169 -0
  59. package/dist/storage/write.js.map +1 -0
  60. package/dist/stream/index.d.ts +11 -0
  61. package/dist/stream/index.d.ts.map +1 -0
  62. package/dist/stream/index.js +12 -0
  63. package/dist/stream/index.js.map +1 -0
  64. package/dist/types.d.ts +126 -0
  65. package/dist/types.d.ts.map +1 -0
  66. package/dist/types.js +2 -0
  67. package/dist/types.js.map +1 -0
  68. package/dist/utils/datetime.d.ts +16 -0
  69. package/dist/utils/datetime.d.ts.map +1 -0
  70. package/dist/utils/datetime.js +147 -0
  71. package/dist/utils/datetime.js.map +1 -0
  72. package/dist/utils/runtime.d.ts +6 -0
  73. package/dist/utils/runtime.d.ts.map +1 -0
  74. package/dist/utils/runtime.js +30 -0
  75. package/dist/utils/runtime.js.map +1 -0
  76. package/dist/utils/size.d.ts +3 -0
  77. package/dist/utils/size.d.ts.map +1 -0
  78. package/dist/utils/size.js +24 -0
  79. package/dist/utils/size.js.map +1 -0
  80. package/dist/utils/values.d.ts +6 -0
  81. package/dist/utils/values.d.ts.map +1 -0
  82. package/dist/utils/values.js +21 -0
  83. package/dist/utils/values.js.map +1 -0
  84. package/package.json +59 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@trebired/logger` will be documented here.
4
+
5
+ This project follows semantic versioning once published.
6
+
7
+ ## 0.1.0
8
+
9
+ - Added top-level `timeZone` support for saved filenames and console timestamps.
10
+ - Kept `recorded_at` as UTC ISO 8601 for machine-readable log entries.
11
+ - Added `console.locale` support for console timestamp display formatting.
12
+ - Added runtime-default locale handling for console output.
13
+ - Added configurable default log group.
14
+ - Added console display options for timestamp, group, and metadata while always showing level and message.
15
+ - Added publish-ready package metadata, README, MIT license, and contribution guide.
16
+ - Added built `dist` package exports.
17
+ - Added async queued file writing with `flush()`, `close()`, and `getStats()`.
18
+ - Added group-based JSONL storage with ISO-style filenames.
19
+ - Added retention cleanup, max-size rolling, optional gzip compression, redaction, serializers, sampling, request middleware, stream events, and query helpers.
20
+ - Initial public release.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Miroslav M. and Trebired contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,368 @@
1
+ # @trebired/logger
2
+
3
+ Structured backend logging for Bun and Node.js applications that want readable console output and durable local logs without running a separate logging stack.
4
+
5
+ `@trebired/logger` writes JSONL logs into group-based folders, supports custom weighted levels, queues file writes by default, and includes redaction, retention, rolling files, request-scoped loggers, and local query helpers.
6
+
7
+ ## Install
8
+
9
+ Runtime support: Bun 1+ and Node.js 18+.
10
+
11
+ ```sh
12
+ npm install @trebired/logger
13
+ ```
14
+
15
+ ```ts
16
+ import { createLog } from "@trebired/logger";
17
+
18
+ const log = createLog({
19
+ dir: "/var/log/my-app",
20
+ console: true,
21
+ quiet: true,
22
+ });
23
+
24
+ log.info("app.start", "ready", { port: 3000 });
25
+ await log.flush();
26
+ ```
27
+
28
+ ## Why This Logger
29
+
30
+ Most loggers either write to stdout and expect an external collector, or provide a very broad transport system. This package is intentionally opinionated around a simpler operational workflow:
31
+
32
+ - structured JSONL entries
33
+ - one directory tree per log group
34
+ - async queued file writes by default
35
+ - custom weighted log levels
36
+ - local querying by group, level, day, and hour
37
+ - built-in redaction for common sensitive fields
38
+ - retention and file-size rolling without a database
39
+
40
+ The storage layout is meant to stay human-browsable:
41
+
42
+ ```txt
43
+ /var/log/my-app/
44
+ app/
45
+ start/
46
+ 2026-05-03-13-0000-info.jsonl
47
+ billing/
48
+ invoice/
49
+ 2026-05-03-13-0000-audit.jsonl
50
+ ```
51
+
52
+ Each line is a JSON object:
53
+
54
+ ```json
55
+ {"recorded_at":"2026-05-03T13:00:00.000Z","level":"info","group":"app.start","message":"ready","origin":{"source":"app","instance":null},"metadata":{"port":3000}}
56
+ ```
57
+
58
+ ## Core API
59
+
60
+ ```ts
61
+ const log = createLog({
62
+ dir: "/var/log/my-app",
63
+ save: true,
64
+ console: true,
65
+ timeZone: "America/New_York",
66
+ source: "api",
67
+ defaultGroup: "default",
68
+ });
69
+
70
+ log.debug("app.boot", "config loaded");
71
+ log.info("app.boot", "ready");
72
+ log.success("job.import", "finished", { rows: 1200 });
73
+ log.warn("http.request", "slow request", { took_ms: 842 });
74
+ log.fail("job.import", "failed validation");
75
+ log.error("app.runtime", "uncaught error");
76
+ ```
77
+
78
+ `save` defaults to `true` when `dir` is provided. If no `dir` is provided, the logger can still emit console output and live stream events.
79
+
80
+ `defaultGroup` defaults to `"default"`.
81
+
82
+ `@trebired/logger` runs on both Bun and Node.js. It may print one-time package notices for runtime-specific guidance or important future package messages. For example, when it detects Node.js, it recommends Bun for best startup and file I/O performance. Pass `quiet: true` to suppress package notices:
83
+
84
+ ```ts
85
+ const log = createLog({
86
+ quiet: true,
87
+ });
88
+ ```
89
+
90
+ When `quiet` is not `true`, the package also prints a one-time startup greeting using the same console logger style as normal entries.
91
+
92
+ `timeZone` is a top-level logger option because it controls the actual local moment used for saved file names and console timestamps. It defaults to the host timezone, then falls back to `America/New_York`.
93
+
94
+ Console output is configurable. `level` and `message` are always shown; timestamp, group, and metadata can be hidden. `console.locale` only controls display formatting. When `locale` is omitted or invalid, the logger passes `undefined` to `Intl.DateTimeFormat`, so JavaScript uses the runtime or system default locale:
95
+
96
+ ```ts
97
+ const log = createLog({
98
+ timeZone: "America/New_York",
99
+ console: {
100
+ colors: true,
101
+ timestamp: true,
102
+ group: false,
103
+ metadata: false,
104
+ locale: "en-US",
105
+ },
106
+ });
107
+ ```
108
+
109
+ Use `timeZone` for the local hour and `console.locale` for the display style.
110
+
111
+ European dot-date locales such as `cs-CZ` and `de-DE` are formatted consistently as `03.05.2026, 15:59:23`.
112
+
113
+ ## Full API Example
114
+
115
+ ```ts
116
+ import { createLog } from "@trebired/logger";
117
+
118
+ const log = createLog({
119
+ dir: "/var/log/my-app",
120
+ save: true,
121
+ console: {
122
+ enabled: true,
123
+ colors: true,
124
+ timestamp: true,
125
+ group: true,
126
+ metadata: true,
127
+ locale: "en-US",
128
+ },
129
+ quiet: true,
130
+ timeZone: "America/New_York",
131
+ source: "api",
132
+ defaultGroup: "default",
133
+ levels: {
134
+ debug: { weight: 10, label: "DEBUG", color: "#94a3b8" },
135
+ info: { weight: 20, label: "INFO", color: "#38bdf8" },
136
+ success: { weight: 25, label: "SUCCESS", color: "#22c55e", bold: true },
137
+ warn: { weight: 30, label: "WARN", color: "#f59e0b", stream: "stderr" },
138
+ fail: { weight: 40, label: "FAIL", color: "#fb7185", stream: "stderr" },
139
+ error: { weight: 50, label: "ERROR", color: "#ef4444", stream: "stderr", showStack: true, bold: true },
140
+ audit: { weight: 35, label: "AUDIT", color: "#8b5cf6" },
141
+ panic: { weight: 100, label: "PANIC", color: "#dc2626", stream: "stderr", showStack: true, bold: true },
142
+ },
143
+ minLevel: "debug",
144
+ write: {
145
+ mode: "async",
146
+ maxQueue: 10000,
147
+ overflow: "drop-newest",
148
+ },
149
+ retention: {
150
+ enabled: true,
151
+ maxAgeDays: 7,
152
+ maxFileSize: "20mb",
153
+ compressOldFiles: false,
154
+ cleanupIntervalMs: 60_000,
155
+ },
156
+ redact: {
157
+ includeDefaultSensitiveKeys: true,
158
+ paths: ["user.password", /^headers\.authorization$/i],
159
+ replacement: "[REDACTED]",
160
+ },
161
+ serializers: {
162
+ userId: (value) => `user:${String(value)}`,
163
+ error: (value) =>
164
+ value instanceof Error
165
+ ? { name: value.name, message: value.message, stack: value.stack }
166
+ : value,
167
+ },
168
+ sample: (entry) => entry.level !== "debug" || Math.random() < 0.1,
169
+ request: {
170
+ group: "http.request",
171
+ idHeader: "x-request-id",
172
+ attach: true,
173
+ },
174
+ });
175
+
176
+ log.info("app.start", "ready", { port: 3000 });
177
+ log.audit("billing.invoice", "created", { invoiceId: "inv_123" });
178
+ log.error("app.start", "failed", { reason: "missing config" });
179
+
180
+ await log.flush();
181
+ await log.close();
182
+ ```
183
+
184
+ ## Custom Levels
185
+
186
+ Levels are weighted. `minLevel` filters out entries with lower weight.
187
+
188
+ ```ts
189
+ const log = createLog({
190
+ levels: {
191
+ audit: { weight: 35, label: "AUDIT", color: "#8b5cf6" },
192
+ panic: { weight: 100, label: "PANIC", stream: "stderr", bold: true },
193
+ },
194
+ minLevel: "audit",
195
+ });
196
+
197
+ log.audit("billing.invoice", "created", { invoiceId: "inv_123" });
198
+ log.panic("runtime", "unrecoverable");
199
+ ```
200
+
201
+ Built-in levels are `debug`, `info`, `success`, `warn`, `fail`, and `error`. A custom level with the same name overrides the built-in config.
202
+
203
+ Custom level methods are available at runtime. In TypeScript, the logger exposes dynamic level methods through an index signature, so custom names work but are not exhaustively autocompleted from the `levels` object yet.
204
+
205
+ ## Async Writes, Flush, and Close
206
+
207
+ File writes are async queued by default.
208
+
209
+ ```ts
210
+ const log = createLog({
211
+ dir: "/var/log/my-app",
212
+ write: {
213
+ mode: "async",
214
+ maxQueue: 10000,
215
+ overflow: "drop-newest",
216
+ },
217
+ });
218
+
219
+ log.info("queue.example", "queued");
220
+
221
+ await log.flush();
222
+ await log.close();
223
+ ```
224
+
225
+ `getStats()` exposes write health:
226
+
227
+ ```ts
228
+ const stats = log.getStats();
229
+ // { mode, queued, written, dropped, failed, queueLength, closed }
230
+ ```
231
+
232
+ For simple scripts or tests, sync writing is available:
233
+
234
+ ```ts
235
+ const log = createLog({
236
+ dir: "./logs",
237
+ write: { mode: "sync" },
238
+ });
239
+ ```
240
+
241
+ ## Retention and Rolling
242
+
243
+ Defaults:
244
+
245
+ - retention enabled
246
+ - `maxAgeDays: 7`
247
+ - `maxFileSize: "20mb"`
248
+ - `compressOldFiles: false`
249
+
250
+ ```ts
251
+ const log = createLog({
252
+ dir: "/var/log/my-app",
253
+ retention: {
254
+ maxAgeDays: 30,
255
+ maxFileSize: "100mb",
256
+ compressOldFiles: true,
257
+ cleanupIntervalMs: 60 * 60 * 1000,
258
+ },
259
+ });
260
+ ```
261
+
262
+ When a file exceeds the configured size, the logger rolls to the next sequence inside the same group and hour:
263
+
264
+ ```txt
265
+ 2026-05-03-13-0000-info.jsonl
266
+ 2026-05-03-13-0001-info.jsonl
267
+ 2026-05-03-13-0002-info.jsonl
268
+ ```
269
+
270
+ ## Redaction and Serializers
271
+
272
+ Common sensitive keys are redacted by default: password, token, secret, authorization, cookie, api_key, and related variants.
273
+
274
+ ```ts
275
+ const log = createLog({
276
+ redact: {
277
+ paths: ["user.ssn", /^payment\./],
278
+ replacement: "[hidden]",
279
+ },
280
+ serializers: {
281
+ userId: (value) => `user:${value}`,
282
+ },
283
+ });
284
+
285
+ log.info("account.update", "saved", {
286
+ userId: 42,
287
+ password: "secret",
288
+ user: { ssn: "123-45-6789" },
289
+ });
290
+ ```
291
+
292
+ ## Scoped Loggers
293
+
294
+ ```ts
295
+ const jobs = log.group("jobs.queue");
296
+ jobs.info("started", { jobId: "job_1" });
297
+
298
+ const worker = log.withScope("worker", "jobs.queue", 2);
299
+ worker.error("failed", { jobId: "job_1" });
300
+ ```
301
+
302
+ ## Express-Style Middleware
303
+
304
+ `requestLogger()` is compatible with Express-style middleware and attaches `req.log` by default.
305
+
306
+ ```ts
307
+ app.use(log.requestLogger({
308
+ group: "http.request",
309
+ idHeader: "x-request-id",
310
+ }));
311
+
312
+ app.get("/", (req, res) => {
313
+ req.log.info("handled");
314
+ res.end("ok");
315
+ });
316
+ ```
317
+
318
+ ## Query Saved Logs
319
+
320
+ ```ts
321
+ import { getEntriesForDir } from "@trebired/logger";
322
+
323
+ const rows = await getEntriesForDir("/var/log/my-app", {
324
+ level: "error",
325
+ groupKey: "app.runtime",
326
+ limit: 100,
327
+ });
328
+ ```
329
+
330
+ Logger instances also expose `getAll()`:
331
+
332
+ ```ts
333
+ const recent = await log.getAll({ groupKey: "billing.invoice", limit: 50 });
334
+ ```
335
+
336
+ ## Sampling
337
+
338
+ ```ts
339
+ const log = createLog({
340
+ sample: 0.1,
341
+ });
342
+
343
+ const selective = createLog({
344
+ sample: (entry) => entry.level === "error" || entry.group.startsWith("audit."),
345
+ });
346
+ ```
347
+
348
+ ## Live Stream
349
+
350
+ ```ts
351
+ import { logStream } from "@trebired/logger";
352
+
353
+ logStream.on("log", (entry, context) => {
354
+ // context.dir is the active log directory, if one is configured
355
+ });
356
+ ```
357
+
358
+ ## Development
359
+
360
+ ```sh
361
+ bun install
362
+ bun test
363
+ bun run typecheck
364
+ bun run build
365
+ bun run bench
366
+ ```
367
+
368
+ The npm package exports compiled files from `dist`. Publishing runs `typecheck`, tests, and `build` through `prepublishOnly`.
@@ -0,0 +1,7 @@
1
+ import type { LogLevelConfig } from "./types.js";
2
+ declare const TOP_LEVEL = "top-level";
3
+ declare const DEFAULT_LEVELS: Record<string, LogLevelConfig>;
4
+ declare const RESERVED_METADATA_KEYS: Set<string>;
5
+ declare const DEFAULT_SENSITIVE_KEYS: Set<string>;
6
+ export { DEFAULT_LEVELS, DEFAULT_SENSITIVE_KEYS, RESERVED_METADATA_KEYS, TOP_LEVEL };
7
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,QAAA,MAAM,SAAS,cAAc,CAAC;AAE9B,QAAA,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAOjD,CAAC;AAEH,QAAA,MAAM,sBAAsB,aAqB1B,CAAC;AAEH,QAAA,MAAM,sBAAsB,aAY1B,CAAC;AAEH,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,SAAS,EAAE,CAAC"}
@@ -0,0 +1,46 @@
1
+ const TOP_LEVEL = "top-level";
2
+ const DEFAULT_LEVELS = Object.freeze({
3
+ debug: { weight: 10, label: "DEBUG", color: "#b7c063", stream: "stdout", showStack: false, bold: false },
4
+ info: { weight: 20, label: "INFO", color: "#2958ea", stream: "stdout", showStack: false, bold: false },
5
+ success: { weight: 25, label: "SUCCESS", color: "#51b300", stream: "stdout", showStack: false, bold: false },
6
+ warn: { weight: 30, label: "WARN", color: "#f39c12", stream: "stderr", showStack: true, bold: false },
7
+ fail: { weight: 40, label: "FAIL", color: "#b30027", stream: "stderr", showStack: false, bold: false },
8
+ error: { weight: 50, label: "ERROR", color: "#b30000", stream: "stderr", showStack: true, bold: true },
9
+ });
10
+ const RESERVED_METADATA_KEYS = new Set([
11
+ "__recorded_at",
12
+ "configKey",
13
+ "config_key",
14
+ "deployment_type",
15
+ "group",
16
+ "groupKey",
17
+ "group_label",
18
+ "groupLabel",
19
+ "ids",
20
+ "instanceIndex",
21
+ "log_dir",
22
+ "log_root",
23
+ "meta",
24
+ "platform_id",
25
+ "recordedAt",
26
+ "recorded_at",
27
+ "source",
28
+ "timestamp",
29
+ "loggedAt",
30
+ "logged_at",
31
+ ]);
32
+ const DEFAULT_SENSITIVE_KEYS = new Set([
33
+ "api_key",
34
+ "apikey",
35
+ "authorization",
36
+ "cookie",
37
+ "passwd",
38
+ "password",
39
+ "pwd",
40
+ "refresh_token",
41
+ "secret",
42
+ "token",
43
+ "access_token",
44
+ ]);
45
+ export { DEFAULT_LEVELS, DEFAULT_SENSITIVE_KEYS, RESERVED_METADATA_KEYS, TOP_LEVEL };
46
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAEA,MAAM,SAAS,GAAG,WAAW,CAAC;AAE9B,MAAM,cAAc,GAAmC,MAAM,CAAC,MAAM,CAAC;IACnE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE;IACxG,IAAI,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE;IACtG,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE;IAC5G,IAAI,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE;IACrG,IAAI,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE;IACtG,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE;CACvG,CAAC,CAAC;AAEH,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAC;IACrC,eAAe;IACf,WAAW;IACX,YAAY;IACZ,iBAAiB;IACjB,OAAO;IACP,UAAU;IACV,aAAa;IACb,YAAY;IACZ,KAAK;IACL,eAAe;IACf,SAAS;IACT,UAAU;IACV,MAAM;IACN,aAAa;IACb,YAAY;IACZ,aAAa;IACb,QAAQ;IACR,WAAW;IACX,UAAU;IACV,WAAW;CACZ,CAAC,CAAC;AAEH,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAC;IACrC,SAAS;IACT,QAAQ;IACR,eAAe;IACf,QAAQ;IACR,QAAQ;IACR,UAAU;IACV,KAAK;IACL,eAAe;IACf,QAAQ;IACR,OAAO;IACP,cAAc;CACf,CAAC,CAAC;AAEH,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,SAAS,EAAE,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { CreateLogOptions, LogInstance } from "../types.js";
2
+ declare function createLog(options?: CreateLogOptions): LogInstance;
3
+ export { createLog };
4
+ //# sourceMappingURL=create_log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create_log.d.ts","sourceRoot":"","sources":["../../src/core/create_log.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,gBAAgB,EAAY,WAAW,EAAuD,MAAM,aAAa,CAAC;AAuEhI,iBAAS,SAAS,CAAC,OAAO,GAAE,gBAAqB,GAAG,WAAW,CAuI9D;AAED,OAAO,EAAE,SAAS,EAAE,CAAC"}
@@ -0,0 +1,206 @@
1
+ import path from "node:path";
2
+ import { formatConsole, normalizeConsoleOptions, writeConsole } from "../format/console.js";
3
+ import { normGroup } from "../groups.js";
4
+ import { minLevelWeight, normalizeLevel, normalizeLevels } from "../levels/index.js";
5
+ import { prepareMetadata } from "../metadata/process.js";
6
+ import { buildRequestMiddleware } from "../middleware/request.js";
7
+ import { logStream } from "../stream/index.js";
8
+ import { getEntriesForDir } from "../storage/query.js";
9
+ import { normalizeRetentionOptions, normalizeWriteOptions } from "../storage/options.js";
10
+ import { FileWriter } from "../storage/write.js";
11
+ import { normalizeTimeZone } from "../utils/datetime.js";
12
+ import { maybeShowNodeRuntimeNotice } from "../utils/runtime.js";
13
+ import { asObject, toString } from "../utils/values.js";
14
+ let packageGreetingShown = false;
15
+ function safeResolveDir(value) {
16
+ const raw = toString(value);
17
+ return raw ? path.resolve(raw) : "";
18
+ }
19
+ function buildOrigin(source, instance = null) {
20
+ const src = toString(source) || "app";
21
+ const inst = instance == null ? null : String(instance);
22
+ return {
23
+ source: src,
24
+ instance: inst || null,
25
+ };
26
+ }
27
+ function parseCallArguments(argsLike, fallbackGroup) {
28
+ const args = Array.isArray(argsLike) ? argsLike : Array.from(argsLike || []);
29
+ const hasExplicitGroup = typeof args[0] === "string" && typeof args[1] === "string";
30
+ return {
31
+ group: hasExplicitGroup ? args[0] : fallbackGroup,
32
+ message: hasExplicitGroup ? args[1] : String(args[0] ?? ""),
33
+ metadata: hasExplicitGroup ? asObject(args[2]) : asObject(args[1]),
34
+ };
35
+ }
36
+ function shouldKeepSample(entry, sample) {
37
+ if (sample == null)
38
+ return true;
39
+ if (typeof sample === "number") {
40
+ if (!Number.isFinite(sample))
41
+ return true;
42
+ if (sample <= 0)
43
+ return false;
44
+ if (sample >= 1)
45
+ return true;
46
+ return Math.random() < sample;
47
+ }
48
+ if (typeof sample === "function") {
49
+ try {
50
+ return sample(entry) === true;
51
+ }
52
+ catch {
53
+ return false;
54
+ }
55
+ }
56
+ return true;
57
+ }
58
+ function maybeShowPackageGreeting(quiet, consoleOptions, timeZone) {
59
+ if (quiet === true || packageGreetingShown)
60
+ return;
61
+ packageGreetingShown = true;
62
+ const entry = {
63
+ recorded_at: new Date().toISOString(),
64
+ level: "success",
65
+ group: "logger.loader",
66
+ message: "@trebired/logger initialized",
67
+ origin: buildOrigin("@trebired/logger"),
68
+ };
69
+ const levelConfig = {
70
+ weight: 25,
71
+ label: "SUCCESS",
72
+ color: "#22c55e",
73
+ bold: true,
74
+ };
75
+ writeConsole("stdout", formatConsole(entry, levelConfig, { ...consoleOptions, enabled: true }, timeZone));
76
+ }
77
+ function createLog(options = {}) {
78
+ const cfg = options && typeof options === "object" ? options : {};
79
+ maybeShowNodeRuntimeNotice(cfg.quiet);
80
+ const levels = normalizeLevels(cfg.levels);
81
+ const threshold = minLevelWeight(cfg.minLevel, levels);
82
+ const consoleOptions = normalizeConsoleOptions(cfg.console);
83
+ const timeZone = normalizeTimeZone(cfg.timeZone);
84
+ maybeShowPackageGreeting(cfg.quiet, consoleOptions, timeZone);
85
+ const defaultSource = toString(cfg.source) || "app";
86
+ const defaultGroup = toString(cfg.defaultGroup) || "default";
87
+ let loggingEnabled = true;
88
+ let closed = false;
89
+ const writer = new FileWriter({
90
+ dir: safeResolveDir(cfg.dir),
91
+ save: typeof cfg.save === "boolean" ? cfg.save : Boolean(toString(cfg.dir)),
92
+ write: normalizeWriteOptions(cfg.write),
93
+ retention: normalizeRetentionOptions(cfg.retention),
94
+ timeZone,
95
+ onError: (message) => writeConsole("stderr", message),
96
+ });
97
+ function emit(levelInput, groupInput, messageInput, metadataInput, originInput) {
98
+ if (!loggingEnabled || closed)
99
+ return;
100
+ const level = normalizeLevel(levelInput, levels);
101
+ const levelConfig = levels[level] || levels.info;
102
+ if (levelConfig.weight < threshold)
103
+ return;
104
+ const rawMetadata = asObject(metadataInput);
105
+ const recordedAt = toString(rawMetadata.__recorded_at) || new Date().toISOString();
106
+ const metadata = prepareMetadata(rawMetadata, cfg.serializers, cfg.redact);
107
+ const group = normGroup(groupInput || defaultGroup).key;
108
+ const message = typeof messageInput === "string" ? messageInput : String(messageInput ?? "");
109
+ const originSource = originInput && originInput.source ? originInput.source : defaultSource;
110
+ const originInstance = originInput && Object.prototype.hasOwnProperty.call(originInput, "instance") ? originInput.instance : null;
111
+ const entry = {
112
+ recorded_at: recordedAt,
113
+ level,
114
+ group,
115
+ message,
116
+ origin: buildOrigin(originSource, originInstance),
117
+ };
118
+ if (Object.keys(metadata).length)
119
+ entry.metadata = metadata;
120
+ if (!shouldKeepSample(entry, cfg.sample))
121
+ return;
122
+ if (consoleOptions.enabled)
123
+ writeConsole(levelConfig.stream, formatConsole(entry, levelConfig, consoleOptions, timeZone));
124
+ writer.write(entry);
125
+ try {
126
+ logStream.emit("log", entry, { dir: writer.getDir() });
127
+ }
128
+ catch { }
129
+ }
130
+ function logWith(level) {
131
+ return function levelLogger() {
132
+ const parsed = parseCallArguments(arguments, defaultGroup);
133
+ emit(level, parsed.group, parsed.message, parsed.metadata);
134
+ };
135
+ }
136
+ function bindGroup(groupName, originInput) {
137
+ const boundGroup = normGroup(groupName || defaultGroup).key;
138
+ const grouped = {};
139
+ for (const level of Object.keys(levels)) {
140
+ grouped[level] = (message, metadata) => emit(level, boundGroup, message, metadata, originInput);
141
+ }
142
+ grouped.child = (extraMetadata) => {
143
+ const extra = asObject(extraMetadata);
144
+ const child = {};
145
+ for (const level of Object.keys(levels)) {
146
+ child[level] = (message, metadata) => emit(level, boundGroup, message, { ...asObject(metadata), ...extra }, originInput);
147
+ }
148
+ return child;
149
+ };
150
+ return grouped;
151
+ }
152
+ const api = {
153
+ group(groupName) {
154
+ return bindGroup(groupName || defaultGroup);
155
+ },
156
+ withScope(source, groupName, instance) {
157
+ const originInput = {
158
+ source: toString(source) || defaultSource,
159
+ instance: instance == null ? null : String(instance),
160
+ };
161
+ return bindGroup(groupName || defaultGroup, originInput);
162
+ },
163
+ setEnabled(flag) {
164
+ loggingEnabled = Boolean(flag);
165
+ },
166
+ getDir() {
167
+ return writer.getDir();
168
+ },
169
+ setDir(nextDir) {
170
+ writer.setDir(safeResolveDir(nextDir));
171
+ },
172
+ requestLogger: buildRequestMiddleware(null, cfg.request),
173
+ logError(error, metadata, source) {
174
+ const meta = asObject(metadata);
175
+ const groupName = toString(meta.group) || defaultGroup;
176
+ const originSource = toString(source) || defaultSource;
177
+ if (error instanceof Error) {
178
+ emit("error", groupName, error.message, { ...meta, stack: error.stack }, { source: originSource });
179
+ return;
180
+ }
181
+ emit("error", groupName, String(error), meta, { source: originSource });
182
+ },
183
+ async getAll(options) {
184
+ await writer.flush();
185
+ return getEntriesForDir(writer.getDir(), options);
186
+ },
187
+ flush() {
188
+ return writer.flush();
189
+ },
190
+ async close() {
191
+ if (closed)
192
+ return;
193
+ closed = true;
194
+ await writer.close();
195
+ },
196
+ getStats() {
197
+ return writer.getStats();
198
+ },
199
+ };
200
+ api.requestLogger = buildRequestMiddleware(api, cfg.request);
201
+ for (const level of Object.keys(levels))
202
+ api[level] = logWith(level);
203
+ return api;
204
+ }
205
+ export { createLog };
206
+ //# sourceMappingURL=create_log.js.map