@venizia/ignis-docs 0.2.0 → 0.2.1-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -14
- package/content/best-practices/api-usage-examples.md +39 -19
- package/content/best-practices/architectural-patterns.md +22 -11
- package/content/best-practices/architecture-decisions.md +29 -16
- package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
- package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
- package/content/best-practices/code-style-standards/control-flow.md +33 -1
- package/content/best-practices/code-style-standards/documentation.md +28 -8
- package/content/best-practices/code-style-standards/function-patterns.md +15 -4
- package/content/best-practices/code-style-standards/index.md +7 -2
- package/content/best-practices/code-style-standards/naming-conventions.md +26 -2
- package/content/best-practices/code-style-standards/route-definitions.md +9 -5
- package/content/best-practices/code-style-standards/tooling.md +14 -1
- package/content/best-practices/code-style-standards/type-safety.md +39 -6
- package/content/best-practices/common-pitfalls.md +59 -35
- package/content/best-practices/contribution-workflow.md +6 -2
- package/content/best-practices/data-modeling.md +78 -62
- package/content/best-practices/deployment-strategies.md +56 -19
- package/content/best-practices/error-handling.md +182 -93
- package/content/best-practices/index.md +6 -0
- package/content/best-practices/performance-optimization.md +26 -13
- package/content/best-practices/security-guidelines.md +35 -9
- package/content/best-practices/testing-strategies.md +7 -2
- package/content/best-practices/troubleshooting-tips.md +27 -17
- package/content/extensions/components/api-reference.md +107 -322
- package/content/extensions/components/authentication/api.md +454 -603
- package/content/extensions/components/authentication/errors.md +121 -498
- package/content/extensions/components/authentication/index.md +88 -801
- package/content/extensions/components/authentication/usage.md +207 -956
- package/content/extensions/components/authorization/api.md +736 -656
- package/content/extensions/components/authorization/errors.md +168 -206
- package/content/extensions/components/authorization/index.md +82 -797
- package/content/extensions/components/authorization/usage.md +194 -527
- package/content/extensions/components/health-check.md +71 -243
- package/content/extensions/components/mail/api.md +504 -287
- package/content/extensions/components/mail/errors.md +73 -61
- package/content/extensions/components/mail/index.md +96 -467
- package/content/extensions/components/mail/usage.md +130 -172
- package/content/extensions/components/request-tracker.md +66 -173
- package/content/extensions/components/socket-io/api.md +195 -13
- package/content/extensions/components/socket-io/errors.md +3 -3
- package/content/extensions/components/socket-io/index.md +50 -337
- package/content/extensions/components/socket-io/usage.md +143 -26
- package/content/extensions/components/static-asset/api.md +410 -142
- package/content/extensions/components/static-asset/errors.md +110 -53
- package/content/extensions/components/static-asset/index.md +79 -608
- package/content/extensions/components/static-asset/usage.md +180 -300
- package/content/extensions/components/websocket/api.md +275 -399
- package/content/extensions/components/websocket/errors.md +47 -56
- package/content/extensions/components/websocket/index.md +74 -407
- package/content/extensions/components/websocket/usage.md +110 -341
- package/content/extensions/helpers/cron/index.md +51 -160
- package/content/extensions/helpers/crypto/index.md +62 -483
- package/content/extensions/helpers/crypto/reference.md +456 -0
- package/content/extensions/helpers/env/index.md +60 -178
- package/content/extensions/helpers/error/index.md +221 -207
- package/content/extensions/helpers/inversion/index.md +65 -556
- package/content/extensions/helpers/inversion/reference.md +522 -0
- package/content/extensions/helpers/kafka/admin.md +20 -1
- package/content/extensions/helpers/kafka/compile-binary.md +41 -35
- package/content/extensions/helpers/kafka/consumer.md +54 -20
- package/content/extensions/helpers/kafka/examples.md +21 -16
- package/content/extensions/helpers/kafka/index.md +80 -610
- package/content/extensions/helpers/kafka/producer.md +134 -5
- package/content/extensions/helpers/kafka/schema-registry.md +45 -70
- package/content/extensions/helpers/logger/hf-logger.md +193 -0
- package/content/extensions/helpers/logger/index.md +64 -563
- package/content/extensions/helpers/logger/pino.md +85 -0
- package/content/extensions/helpers/logger/reference.md +746 -0
- package/content/extensions/helpers/network/api.md +241 -195
- package/content/extensions/helpers/network/index.md +72 -530
- package/content/extensions/helpers/queue/index.md +72 -900
- package/content/extensions/helpers/queue/reference.md +467 -0
- package/content/extensions/helpers/redis/index.md +73 -645
- package/content/extensions/helpers/redis/reference.md +727 -0
- package/content/extensions/helpers/secrets/index.md +66 -0
- package/content/extensions/helpers/socket-io/api.md +305 -203
- package/content/extensions/helpers/socket-io/index.md +66 -432
- package/content/extensions/helpers/storage/api.md +564 -462
- package/content/extensions/helpers/storage/index.md +77 -573
- package/content/extensions/helpers/types/index.md +66 -499
- package/content/extensions/helpers/types/reference.md +650 -0
- package/content/extensions/helpers/uid/index.md +58 -227
- package/content/extensions/helpers/websocket/api.md +329 -216
- package/content/extensions/helpers/websocket/index.md +65 -503
- package/content/extensions/helpers/worker-thread/index.md +58 -396
- package/content/extensions/helpers/worker-thread/reference.md +428 -0
- package/content/guides/core-concepts/persistent/models.md +1 -1
- package/content/guides/core-concepts/persistent/search-typesense.md +3 -3
- package/content/guides/core-concepts/persistent/transactions.md +1 -1
- package/content/guides/core-concepts/secrets-vault.md +177 -0
- package/content/guides/core-concepts/services.md +1 -1
- package/content/guides/migrations/redis-helpers-migration.md +1 -1
- package/content/guides/migrations/unified-connectors-migration.md +2 -2
- package/content/guides/tutorials/ecommerce-api.md +3 -8
- package/content/references/base/application.md +1 -1
- package/content/references/base/connectors.md +79 -136
- package/content/references/base/datasources-reference.md +599 -0
- package/content/references/base/datasources.md +84 -444
- package/content/references/base/dependency-injection.md +17 -39
- package/content/references/base/filter-system/application-usage.md +69 -121
- package/content/references/base/filter-system/array-operators.md +12 -0
- package/content/references/base/filter-system/comparison-operators.md +12 -0
- package/content/references/base/filter-system/default-filter.md +136 -348
- package/content/references/base/filter-system/fields-order-pagination.md +38 -16
- package/content/references/base/filter-system/index.md +106 -257
- package/content/references/base/filter-system/json-filtering.md +12 -2
- package/content/references/base/filter-system/list-operators.md +16 -2
- package/content/references/base/filter-system/logical-operators.md +13 -0
- package/content/references/base/filter-system/null-operators.md +13 -0
- package/content/references/base/filter-system/pattern-matching.md +12 -0
- package/content/references/base/filter-system/quick-reference.md +11 -2
- package/content/references/base/filter-system/range-operators.md +12 -0
- package/content/references/base/filter-system/tips.md +70 -133
- package/content/references/base/filter-system/use-cases.md +156 -233
- package/content/references/base/middlewares.md +35 -21
- package/content/references/base/models-reference.md +886 -0
- package/content/references/base/models.md +80 -1452
- package/content/references/base/repositories/advanced.md +156 -192
- package/content/references/base/repositories/index.md +77 -650
- package/content/references/base/repositories/mixins.md +22 -18
- package/content/references/base/repositories/relations.md +123 -171
- package/content/references/base/repositories/soft-deletable.md +58 -56
- package/content/references/base/secrets.md +263 -0
- package/content/references/base/services.md +2 -2
- package/content/references/configuration/environment-variables.md +48 -4
- package/content/references/configuration/index.md +49 -31
- package/content/references/quick-reference.md +3 -16
- package/content/references/utilities/crypto.md +35 -76
- package/content/references/utilities/date.md +33 -73
- package/content/references/utilities/index.md +1 -1
- package/content/references/utilities/jsx-reference.md +298 -0
- package/content/references/utilities/jsx.md +82 -525
- package/content/references/utilities/module.md +29 -62
- package/content/references/utilities/parse.md +34 -64
- package/content/references/utilities/performance.md +33 -58
- package/content/references/utilities/promise.md +28 -62
- package/content/references/utilities/request.md +57 -218
- package/content/references/utilities/schema.md +43 -137
- package/content/references/utilities/statuses-reference.md +361 -0
- package/content/references/utilities/statuses.md +63 -667
- package/package.json +8 -8
|
@@ -0,0 +1,746 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Logger - Full Reference
|
|
3
|
+
description: Complete reference for Logger, LoggerFactory, HfLogger, transports, formatters, and every APP_ENV_LOGGER_* environment variable
|
|
4
|
+
difficulty: intermediate
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Logger - Full Reference
|
|
8
|
+
|
|
9
|
+
Exhaustive reference for `Logger`, `LoggerFactory`, `HfLogger`/`HfLogFlusher`, the Winston formatter and transport internals, and every environment variable. For a readable introduction and the common tasks, start with the [Logger overview](/extensions/helpers/logger/).
|
|
10
|
+
|
|
11
|
+
The default provider is **Winston** (with `winston-daily-rotate-file` for file rotation). All provider packages are OPTIONAL peers - an application loads exactly ONE provider (single-provider loading, below).
|
|
12
|
+
|
|
13
|
+
**Files:**
|
|
14
|
+
|
|
15
|
+
- [`packages/helpers/src/modules/logger/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/common/types.ts) - `ILogger`, `TLogLevel`, `TLoggerFormat`
|
|
16
|
+
- [`packages/helpers/src/modules/logger/common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/common/constants.ts) - `LogLevels`, `LoggerFormats`
|
|
17
|
+
- [`packages/helpers/src/modules/logger/base/abstract.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/base/abstract.ts) - `AbstractLogger`
|
|
18
|
+
- [`packages/helpers/src/modules/logger/base/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/base/base.ts) - `BaseLogger`
|
|
19
|
+
- [`packages/helpers/src/modules/logger/formatting/deep-splat.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/formatting/deep-splat.ts) - `formatLogMessage`, `%s` inspection widening
|
|
20
|
+
- [`packages/helpers/src/modules/logger/winston/logger.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/winston/logger.ts) - `WinstonLogger`, `Logger` alias
|
|
21
|
+
- [`packages/helpers/src/modules/logger/winston/define.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/winston/define.ts) - `defineCustomLogger`, formatters
|
|
22
|
+
- [`packages/helpers/src/modules/logger/winston/formatters/deep-splat.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/winston/formatters/deep-splat.ts) - `deepSplat`
|
|
23
|
+
- [`packages/helpers/src/modules/logger/winston/transports/dgram.transport.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/winston/transports/dgram.transport.ts) - `DgramTransport`
|
|
24
|
+
- [`packages/helpers/src/modules/logger/hf/logger.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/hf/logger.ts) - `HfLogger`
|
|
25
|
+
- [`packages/helpers/src/modules/logger/hf/flusher.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/hf/flusher.ts) - `HfLogFlusher`
|
|
26
|
+
- [`packages/helpers/src/modules/logger/factory.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/factory.ts) - `LoggerFactory`, `ApplicationLogger`
|
|
27
|
+
|
|
28
|
+
## Architecture & ILogger
|
|
29
|
+
|
|
30
|
+
The module follows IGNIS's house format: `common/` (the contract), `base/` (provider-independent plumbing), `winston/` (the built-in provider), `hf/` (the separate high-frequency logger), and a top-level `factory.ts` as the single acquisition path.
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
ILogger (interface) common/types.ts
|
|
34
|
+
└─ AbstractLogger (abstract) base/abstract.ts - the contract as a class, for `instanceof`
|
|
35
|
+
└─ BaseLogger (abstract) base/base.ts - scope, prefix, DEBUG gate, .for(), one write() sink
|
|
36
|
+
├─ WinstonLogger winston/logger.ts - the built-in provider (default)
|
|
37
|
+
└─ PinoLogger pino/logger.ts - sub-path @venizia/ignis-helpers/pino
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
- **Consumers type against `ILogger`, never a concrete class.** `LoggerFactory.getLogger()` and `BaseHelper.logger` both return `ILogger`. Which provider produced the instance stays invisible behind the interface.
|
|
41
|
+
- **Provider registration.** `LoggerFactory.use({ provider })` selects the application's provider (default: `WinstonLogger`). The factory hands out stable delegating wrappers and re-points ALL of them when `use()` is called - even module-level loggers captured at import time follow the registration; the per-call cost is one property read (measured ~0ns).
|
|
42
|
+
- **Single-provider loading.** Exactly ONE provider is ever loaded. Delegates resolve lazily at the first log call, so an app that registers pino at its entrypoint never loads winston. The winston default loads only when `use()` was never called by the first log line - it requires the winston peers installed (`bun add winston winston-transport winston-daily-rotate-file`). Compiled binaries (`bun build --compile`) must ALWAYS register a provider explicitly: only a class reference carries a provider into a bundle.
|
|
43
|
+
- **Both providers are sub-path only**: `WinstonLogger` at `@venizia/ignis-helpers/winston`, `PinoLogger` at `@venizia/ignis-helpers/pino` ([guide](/extensions/helpers/logger/pino)). The root barrel is provider-free; importing it loads neither.
|
|
44
|
+
|
|
45
|
+
**Which names follow `use()`:**
|
|
46
|
+
|
|
47
|
+
| Name | Is | Follows `use()`? |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| `LoggerFactory.getLogger()` / `BaseHelper.logger` | delegating wrapper | YES |
|
|
50
|
+
| `ApplicationLogger.get()` | facade over the factory; type = `ILogger` | YES |
|
|
51
|
+
| `Logger` / `Logger.get(scope, customWinston?)` | concrete `WinstonLogger` (instanceof, custom winston instances) | NO - names winston deliberately |
|
|
52
|
+
| `WinstonLogger` / `PinoLogger` | concrete providers | are the targets |
|
|
53
|
+
|
|
54
|
+
## Quick Reference
|
|
55
|
+
|
|
56
|
+
| Class | Extends | Use Case |
|
|
57
|
+
|-------|---------|----------|
|
|
58
|
+
| `Logger` | `BaseLogger` -> `AbstractLogger` (`ILogger`) | General-purpose scoped logger with caching (permanent alias of `WinstonLogger`) |
|
|
59
|
+
| `LoggerFactory` | - | Provider registration (`use`) + `ILogger` acquisition from scope arrays |
|
|
60
|
+
| `PinoLogger` | `BaseLogger` (`ILogger`) | Throughput provider - NDJSON, sub-path only ([guide](/extensions/helpers/logger/pino)) |
|
|
61
|
+
| `HfLogger` | `AbstractLogger` (`ILogger`) | Ring-buffer logger for hot paths - bytes path ~59ns, string no-args path ~66ns |
|
|
62
|
+
| `HfLogFlusher` | - | Background flusher for `HfLogger` entries |
|
|
63
|
+
| `DgramTransport` | `winston-transport.Transport` | Custom Winston transport that sends logs over UDP |
|
|
64
|
+
|
|
65
|
+
### Import paths
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
// Core classes - provider-neutral, root barrel
|
|
69
|
+
import { LoggerFactory, ApplicationLogger } from '@venizia/ignis-helpers';
|
|
70
|
+
import type { ILogger } from '@venizia/ignis-helpers';
|
|
71
|
+
|
|
72
|
+
// Abstract tiers - implementing ILogger yourself, or instanceof checks
|
|
73
|
+
import { AbstractLogger, BaseLogger } from '@venizia/ignis-helpers';
|
|
74
|
+
|
|
75
|
+
// High-frequency logger
|
|
76
|
+
import { HfLogger, HfLogFlusher } from '@venizia/ignis-helpers';
|
|
77
|
+
|
|
78
|
+
// Constants & types
|
|
79
|
+
import { LogLevels, LoggerFormats } from '@venizia/ignis-helpers';
|
|
80
|
+
import type { TLogLevel, TLoggerFormat } from '@venizia/ignis-helpers';
|
|
81
|
+
|
|
82
|
+
// Level resolution - provider-neutral
|
|
83
|
+
import { resolveLoggerLevel } from '@venizia/ignis-helpers';
|
|
84
|
+
|
|
85
|
+
// Winston provider + its utilities - SUB-PATH only (winston is an optional peer)
|
|
86
|
+
import {
|
|
87
|
+
Logger,
|
|
88
|
+
WinstonLogger,
|
|
89
|
+
defineCustomLogger,
|
|
90
|
+
defineLogFormatter,
|
|
91
|
+
defineJsonLoggerFormatter,
|
|
92
|
+
definePrettyLoggerFormatter,
|
|
93
|
+
applicationLogFormatter,
|
|
94
|
+
resolveDefaultTransportOptions,
|
|
95
|
+
} from '@venizia/ignis-helpers/winston';
|
|
96
|
+
import type { IFileTransportOptions, ICustomLoggerOptions } from '@venizia/ignis-helpers/winston';
|
|
97
|
+
|
|
98
|
+
// Pino provider - SUB-PATH only (optional peers: pino, pino-pretty, pino-roll)
|
|
99
|
+
import { PinoLogger, setPinoBackingLogger } from '@venizia/ignis-helpers/pino';
|
|
100
|
+
import type { ILoggerProvider } from '@venizia/ignis-helpers';
|
|
101
|
+
|
|
102
|
+
// UDP transport (winston)
|
|
103
|
+
import { DgramTransport } from '@venizia/ignis-helpers/winston';
|
|
104
|
+
import type { IDgramTransportOptions } from '@venizia/ignis-helpers/winston';
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The root barrel is provider-free: importing `@venizia/ignis-helpers` loads NO provider. Winston names resolve only through `@venizia/ignis-helpers/winston`, pino names only through `@venizia/ignis-helpers/pino`.
|
|
108
|
+
|
|
109
|
+
## Creating an Instance
|
|
110
|
+
|
|
111
|
+
`Source ->` [`winston/logger.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/winston/logger.ts), [`factory.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/factory.ts)
|
|
112
|
+
|
|
113
|
+
### Using LoggerFactory (recommended)
|
|
114
|
+
|
|
115
|
+
`LoggerFactory.getLogger` accepts an array of scope strings, joins them with `-`, and returns a cached provider-following wrapper (see the name/role table above).
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import { LoggerFactory } from '@venizia/ignis-helpers';
|
|
119
|
+
|
|
120
|
+
const logger = LoggerFactory.getLogger(['MyService']);
|
|
121
|
+
logger.info('Service initialized');
|
|
122
|
+
// Output: [MyService] Service initialized
|
|
123
|
+
|
|
124
|
+
const scopedLogger = LoggerFactory.getLogger(['Payment', 'Stripe']);
|
|
125
|
+
scopedLogger.info('Charge created');
|
|
126
|
+
// Output: [Payment-Stripe] Charge created
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Using Logger.get() directly
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import { Logger } from '@venizia/ignis-helpers/winston';
|
|
133
|
+
|
|
134
|
+
const logger = Logger.get('MyService');
|
|
135
|
+
logger.info('Direct logger access');
|
|
136
|
+
// Output: [MyService] Direct logger access
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Pass a custom Winston logger instance as the second parameter to use your own transport configuration:
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
import { Logger, defineCustomLogger, applicationLogFormatter } from '@venizia/ignis-helpers/winston';
|
|
143
|
+
|
|
144
|
+
const customWinstonLogger = defineCustomLogger({
|
|
145
|
+
formatter: applicationLogFormatter,
|
|
146
|
+
transports: {
|
|
147
|
+
info: { file: { prefix: 'custom', folder: './logs' } },
|
|
148
|
+
error: { file: { prefix: 'custom-error', folder: './logs' } },
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const logger = Logger.get('MyService', customWinstonLogger);
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
A custom-backed `Logger` is a fresh wrapper on every call - a scope-keyed cache cannot tell two different Winston instances apart, and the wrapper is cheap enough not to need one. `.for()` on a custom-backed logger keeps the same Winston instance.
|
|
156
|
+
|
|
157
|
+
### Logger caching
|
|
158
|
+
|
|
159
|
+
Without a custom logger, both `Logger.get` and `LoggerFactory.getLogger` cache internally - the same scope always returns the same `Logger` instance:
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
const logger1 = Logger.get('MyService');
|
|
163
|
+
const logger2 = Logger.get('MyService');
|
|
164
|
+
// logger1 === logger2 (same instance)
|
|
165
|
+
|
|
166
|
+
const custom1 = Logger.get('MyService', customWinstonLogger);
|
|
167
|
+
const custom2 = Logger.get('MyService', customWinstonLogger);
|
|
168
|
+
// custom1 !== custom2 (fresh wrapper each call, same backing Winston instance)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### ApplicationLogger - the provider-following facade
|
|
172
|
+
|
|
173
|
+
`ApplicationLogger` is "the APPLICATION's logger": `ApplicationLogger.get(scope)` always returns the provider registered via `LoggerFactory.use()` (winston unless the app registered another). Its TYPE is `ILogger`. It is no longer a class alias of `WinstonLogger` - `instanceof ApplicationLogger` is now a compile error (use `instanceof AbstractLogger` to test any provider instance); the concrete alias remains available as `Logger`.
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
import { ApplicationLogger } from '@venizia/ignis-helpers';
|
|
177
|
+
|
|
178
|
+
const logger = ApplicationLogger.get('MyService'); // ILogger, follows LoggerFactory.use()
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
The old scope-less `applicationLogger` instance was REMOVED - use `ApplicationLogger.get('YourScope')`; apps that need a raw winston instance build one with `defineCustomLogger` (sub-path `/winston`).
|
|
182
|
+
|
|
183
|
+
## Log Levels
|
|
184
|
+
|
|
185
|
+
`Source ->` [`common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/common/constants.ts), [`base/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/base/base.ts), [`winston/define.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/winston/define.ts)
|
|
186
|
+
|
|
187
|
+
Five levels, each with a direct method on `ILogger`: `debug`, `info`, `warn`, `error`, `emerg`. The generic `log(level, ...)` remains for dynamic level selection.
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
logger.info('User created');
|
|
191
|
+
logger.warn('Rate limit approaching');
|
|
192
|
+
logger.error('Failed to process payment');
|
|
193
|
+
logger.emerg('System out of memory');
|
|
194
|
+
logger.debug('Query took 12ms'); // Requires DEBUG=true
|
|
195
|
+
logger.log('warn', 'Threshold exceeded'); // Generic method for any level
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### What each level means
|
|
199
|
+
|
|
200
|
+
The level set follows the npm/winston convention. Lower priority number = more severe; the level floor (`APP_ENV_LOGGER_LEVEL`) admits everything at or above its severity.
|
|
201
|
+
|
|
202
|
+
| Level | Priority | Meaning | Use it for |
|
|
203
|
+
|-------|----------|---------|------------|
|
|
204
|
+
| `error` | 0 | An operation failed | Caught failures the line exists to diagnose - always pair an `Error` with `%s` |
|
|
205
|
+
| `emerg` | 0 | The process is in a fatal state | Out-of-memory, unrecoverable corruption, imminent shutdown |
|
|
206
|
+
| `warn` | 1 | Something is off but handled | Retries, fallbacks taken, deprecated usage |
|
|
207
|
+
| `info` | 2 | A business event happened | "Order created", lifecycle milestones, boot phases |
|
|
208
|
+
| `debug` | 3 | Developer diagnostics | Values and timings useful only while developing - ALSO gated by `DEBUG` env |
|
|
209
|
+
|
|
210
|
+
> [!NOTE]
|
|
211
|
+
> Two gates apply to `debug`, one to everything else. Every level passes the floor (`APP_ENV_LOGGER_LEVEL`, default `debug` - which admits ALL five levels); `debug()` additionally requires the `DEBUG` env gate. The vocabulary was deliberately trimmed to these five (2026-07-18): `alert`/`http`/`verbose`/`silly` had zero call sites and no consuming infrastructure - `http` returns as an access-line level if the request-correlation feature lands.
|
|
212
|
+
|
|
213
|
+
`LogLevels` defines all available levels and provides validation:
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
import { LogLevels } from '@venizia/ignis-helpers';
|
|
217
|
+
import type { TLogLevel } from '@venizia/ignis-helpers';
|
|
218
|
+
|
|
219
|
+
LogLevels.ERROR; // 'error'
|
|
220
|
+
LogLevels.EMERG; // 'emerg'
|
|
221
|
+
LogLevels.WARN; // 'warn'
|
|
222
|
+
LogLevels.INFO; // 'info'
|
|
223
|
+
LogLevels.DEBUG; // 'debug'
|
|
224
|
+
|
|
225
|
+
LogLevels.isValid('info'); // true
|
|
226
|
+
LogLevels.isValid('unknown'); // false
|
|
227
|
+
|
|
228
|
+
const level: TLogLevel = 'info';
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Winston level priority
|
|
232
|
+
|
|
233
|
+
`defineCustomLogger` configures Winston with these numeric priorities by default:
|
|
234
|
+
|
|
235
|
+
| Level | Priority | Color |
|
|
236
|
+
|-------|----------|-------|
|
|
237
|
+
| `error` | 0 | red |
|
|
238
|
+
| `emerg` | 0 | red |
|
|
239
|
+
| `warn` | 1 | yellow |
|
|
240
|
+
| `info` | 2 | green |
|
|
241
|
+
| `debug` | 3 | blue |
|
|
242
|
+
|
|
243
|
+
Lower numeric values have higher priority. `error` and `emerg` share priority `0`.
|
|
244
|
+
|
|
245
|
+
## Method-Scoped Logging
|
|
246
|
+
|
|
247
|
+
`.for()` creates a sub-scoped logger for a specific method, appending the method name to the scope with a `-` separator, backed by the same provider instance as the parent. Default-backed results are cached.
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
class UserService {
|
|
251
|
+
private logger = LoggerFactory.getLogger(['UserService']);
|
|
252
|
+
|
|
253
|
+
async createUser(data: CreateUserDto) {
|
|
254
|
+
this.logger.for('createUser').info('Creating user: %j', data);
|
|
255
|
+
// Output: [UserService-createUser] Creating user: {...}
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
const user = await this.userRepo.create({ data });
|
|
259
|
+
this.logger.for('createUser').info('User created: %s', user.id);
|
|
260
|
+
return user;
|
|
261
|
+
} catch (error) {
|
|
262
|
+
this.logger.for('createUser').error('Failed to create user: %s', error);
|
|
263
|
+
throw error;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Message Formatting
|
|
270
|
+
|
|
271
|
+
`Source ->` [`winston/formatters/deep-splat.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/winston/formatters/deep-splat.ts), [`formatting/deep-splat.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/formatting/deep-splat.ts)
|
|
272
|
+
|
|
273
|
+
### Logging errors: `%s`, never `%j`
|
|
274
|
+
|
|
275
|
+
- **`message` and `stack` are non-enumerable** on a native `Error`.
|
|
276
|
+
- **`%j` formats via `JSON.stringify`**, which only visits enumerable own properties, so `logger.error('Failed: %j', error)` silently drops both `message` and `stack` - the two fields the log line exists to capture.
|
|
277
|
+
- **Always pair an `Error` argument with `%s`**; reserve `%j`/`%o` for plain data objects.
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
// Good - %s prints message + stack
|
|
281
|
+
logger.error('Failed to create user: %s', error);
|
|
282
|
+
|
|
283
|
+
// Bad - %j drops message and stack (non-enumerable on Error)
|
|
284
|
+
logger.error('Failed to create user: %j', error);
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Object inspection depth for `%s`
|
|
288
|
+
|
|
289
|
+
- **Node hard-codes `depth: 0` for `%s`** in `util.format` - an object passed to `%s` collapses to `[Object]`, hiding the nested `extra` or `cause` a wrapped error carries.
|
|
290
|
+
- **`deepSplat` widens that depth.** The formatter (`formatLogMessage`) pre-inspects any object bound to a `%s` placeholder before handing the message to Winston, so nested fields print instead of collapsing.
|
|
291
|
+
- **Applies per-placeholder.** Only arguments matched to a `%s` token are affected, so `%j` still gets `JSON.stringify` semantics.
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
logger.error('Failed: %s', error); // nested `error.cause` is now visible, not `[Object]`
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
The inspection depth defaults to `5` and is configurable via `APP_ENV_LOGGER_INSPECT_DEPTH`:
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
APP_ENV_LOGGER_INSPECT_DEPTH=8
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
The value must be a non-negative integer. An absent, empty, negative, or unparseable value falls back to the default of `5` - there is no "unlimited" setting.
|
|
304
|
+
|
|
305
|
+
### Log formats
|
|
306
|
+
|
|
307
|
+
Output format is controlled by `APP_ENV_LOGGER_FORMAT` (default: `text`). `LoggerFormats` provides constants and validation:
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
import { LoggerFormats } from '@venizia/ignis-helpers';
|
|
311
|
+
import type { TLoggerFormat } from '@venizia/ignis-helpers';
|
|
312
|
+
|
|
313
|
+
LoggerFormats.JSON; // 'json'
|
|
314
|
+
LoggerFormats.TEXT; // 'text'
|
|
315
|
+
LoggerFormats.isValid('json'); // true
|
|
316
|
+
|
|
317
|
+
const fmt: TLoggerFormat = 'text';
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
**JSON format** (`APP_ENV_LOGGER_FORMAT=json`):
|
|
321
|
+
|
|
322
|
+
```json
|
|
323
|
+
{"level":"info","message":"[UserService] User created","timestamp":"2024-01-11T10:30:00.000Z","label":"APP"}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
**Pretty text format** (`APP_ENV_LOGGER_FORMAT=text`, default):
|
|
327
|
+
|
|
328
|
+
```
|
|
329
|
+
2024-01-11T10:30:00.000Z [APP] info: [UserService] User created
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
> [!NOTE]
|
|
333
|
+
> The label shown in log output (e.g. `APP`) comes from `APP_ENV_APPLICATION_NAME` (defaults to `'APP'`). Set this env var to customize the label for your application.
|
|
334
|
+
|
|
335
|
+
### Custom formatters
|
|
336
|
+
|
|
337
|
+
Build formatters directly using the exported helper functions:
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
import {
|
|
341
|
+
defineLogFormatter,
|
|
342
|
+
defineJsonLoggerFormatter,
|
|
343
|
+
definePrettyLoggerFormatter,
|
|
344
|
+
} from '@venizia/ignis-helpers';
|
|
345
|
+
|
|
346
|
+
// Auto-detect from APP_ENV_LOGGER_FORMAT (or override with format option)
|
|
347
|
+
const formatter = defineLogFormatter({ label: 'my-app' });
|
|
348
|
+
const jsonFmt = defineLogFormatter({ label: 'my-app', format: 'json' });
|
|
349
|
+
|
|
350
|
+
// Or use specific formatters directly
|
|
351
|
+
const jsonFormatter = defineJsonLoggerFormatter({ label: 'my-app' });
|
|
352
|
+
const prettyFormatter = definePrettyLoggerFormatter({ label: 'my-app' });
|
|
353
|
+
const plainFormatter = definePrettyLoggerFormatter({ label: 'my-app', colorize: false });
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
`defineLogFormatter` throws an `ApplicationError` if `format` (or `APP_ENV_LOGGER_FORMAT`) is not `'json'` or `'text'`.
|
|
357
|
+
|
|
358
|
+
## Transports
|
|
359
|
+
|
|
360
|
+
`Source ->` [`winston/define.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/winston/define.ts)
|
|
361
|
+
|
|
362
|
+
Every logger created by `defineCustomLogger` always includes a **Console** transport, which inherits the logger-level floor (`APP_ENV_LOGGER_LEVEL`, default `debug`). File and UDP transports are optional and are registered per transport group (`info`, `error`).
|
|
363
|
+
|
|
364
|
+
Formatting is split in two stages: a shared preparation format on the logger (label, timestamp, error normalization, deep splat) and a per-transport assembly format. In `text` mode the console assembly colorizes while the file assembly does not - log FILES never contain ANSI color codes. In `json` mode every transport assembles with plain `format.json()`. Passing `formatter` disables the split: that one format produces the final line for every transport.
|
|
365
|
+
|
|
366
|
+
### File rotation (DailyRotateFile)
|
|
367
|
+
|
|
368
|
+
Configure file rotation through environment variables or programmatically via `IFileTransportOptions`.
|
|
369
|
+
|
|
370
|
+
**Environment variables:**
|
|
371
|
+
|
|
372
|
+
| Variable | Default | Description |
|
|
373
|
+
|----------|---------|-------------|
|
|
374
|
+
| `APP_ENV_LOGGER_FOLDER_PATH` | _(unset)_ | Log files directory; file logging is OFF when unset |
|
|
375
|
+
| `APP_ENV_LOGGER_FILE_FREQUENCY` | `1h` | Rotation frequency |
|
|
376
|
+
| `APP_ENV_LOGGER_FILE_MAX_SIZE` | `100m` | Max file size before rotation |
|
|
377
|
+
| `APP_ENV_LOGGER_FILE_MAX_FILES` | `5d` | Retention period |
|
|
378
|
+
| `APP_ENV_LOGGER_FILE_DATE_PATTERN` | `YYYYMMDD_HH` | Date pattern in filename |
|
|
379
|
+
|
|
380
|
+
**Programmatic configuration:**
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
import { defineCustomLogger, applicationLogFormatter } from '@venizia/ignis-helpers/winston';
|
|
384
|
+
|
|
385
|
+
const customLogger = defineCustomLogger({
|
|
386
|
+
formatter: applicationLogFormatter,
|
|
387
|
+
transports: {
|
|
388
|
+
info: {
|
|
389
|
+
file: {
|
|
390
|
+
prefix: 'my-app',
|
|
391
|
+
folder: './logs',
|
|
392
|
+
frequency: '24h',
|
|
393
|
+
maxSize: '500m',
|
|
394
|
+
maxFiles: '30d',
|
|
395
|
+
datePattern: 'YYYYMMDD',
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
error: {
|
|
399
|
+
file: {
|
|
400
|
+
prefix: 'my-app-error',
|
|
401
|
+
folder: './logs',
|
|
402
|
+
maxFiles: '90d',
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
},
|
|
406
|
+
});
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Generated filename pattern: `{folder}/{prefix}-info-{DATE}.log` or `{folder}/{prefix}-error-{DATE}.log`. An `error`-level file transport is also registered as a Winston exception handler.
|
|
410
|
+
|
|
411
|
+
#### IFileTransportOptions
|
|
412
|
+
|
|
413
|
+
```typescript
|
|
414
|
+
interface IFileTransportOptions {
|
|
415
|
+
prefix: string; // Filename prefix (required)
|
|
416
|
+
folder: string; // Output directory (required)
|
|
417
|
+
frequency?: string; // Rotation frequency (default: '1h')
|
|
418
|
+
maxSize?: string; // Max file size (default: '100m')
|
|
419
|
+
maxFiles?: string; // Retention period (default: '5d')
|
|
420
|
+
datePattern?: string; // Date pattern in filename (default: 'YYYYMMDD_HH')
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### UDP transport (DgramTransport)
|
|
425
|
+
|
|
426
|
+
`Source ->` [`winston/transports/dgram.transport.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/winston/transports/dgram.transport.ts)
|
|
427
|
+
|
|
428
|
+
`DgramTransport` is a custom Winston transport that sends log entries over UDP. It filters by level - only messages whose level is in the configured `levels` set are forwarded.
|
|
429
|
+
|
|
430
|
+
```typescript
|
|
431
|
+
import { DgramTransport } from '@venizia/ignis-helpers/winston';
|
|
432
|
+
|
|
433
|
+
const transport = new DgramTransport({
|
|
434
|
+
label: 'my-app',
|
|
435
|
+
host: '127.0.0.1',
|
|
436
|
+
port: 5000,
|
|
437
|
+
levels: ['error', 'warn', 'info'],
|
|
438
|
+
socketOptions: { type: 'udp4' },
|
|
439
|
+
});
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
**Static factory with validation** - `fromPartial` returns `null` if any required field is missing, rather than throwing:
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
const transport = DgramTransport.fromPartial({
|
|
446
|
+
label: 'my-app',
|
|
447
|
+
host: '127.0.0.1',
|
|
448
|
+
port: 5000,
|
|
449
|
+
levels: ['error', 'warn'],
|
|
450
|
+
socketOptions: { type: 'udp4' },
|
|
451
|
+
});
|
|
452
|
+
// Returns null if label, host, port, levels (non-empty), or socketOptions is missing
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
On a socket error the transport closes and nulls its client; the next `log()` call re-establishes the socket before sending. A failed `send` is logged to the console and the socket is dropped for reconnection - it is never re-emitted as an `'error'` event, so one lost UDP log line can never crash the process.
|
|
456
|
+
|
|
457
|
+
**Environment variables for the default application logger:**
|
|
458
|
+
|
|
459
|
+
| Variable | Description |
|
|
460
|
+
|----------|-------------|
|
|
461
|
+
| `APP_ENV_LOGGER_DGRAM_HOST` | UDP log aggregator host |
|
|
462
|
+
| `APP_ENV_LOGGER_DGRAM_PORT` | UDP log aggregator port |
|
|
463
|
+
| `APP_ENV_LOGGER_DGRAM_LABEL` | Label to identify log source |
|
|
464
|
+
| `APP_ENV_LOGGER_DGRAM_LEVELS` | Comma-separated levels to send via UDP |
|
|
465
|
+
|
|
466
|
+
#### IDgramTransportOptions
|
|
467
|
+
|
|
468
|
+
```typescript
|
|
469
|
+
interface IDgramTransportOptions extends Transport.TransportStreamOptions {
|
|
470
|
+
label: string; // Label to identify log source
|
|
471
|
+
host: string; // UDP host
|
|
472
|
+
port: number; // UDP port
|
|
473
|
+
levels: Array<string>; // Levels to forward over UDP
|
|
474
|
+
socketOptions: dgram.SocketOptions; // Node.js dgram socket options
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### ICustomLoggerOptions
|
|
479
|
+
|
|
480
|
+
```typescript
|
|
481
|
+
interface ICustomLoggerOptions {
|
|
482
|
+
levels?: { [name: string | symbol]: number };
|
|
483
|
+
colors?: { [name: string | symbol]: string };
|
|
484
|
+
// Full override: applied once for every transport, exactly as it produces the line
|
|
485
|
+
formatter?: ReturnType<typeof winston.format.combine>;
|
|
486
|
+
format?: TLoggerFormat; // 'json' | 'text'; defaults to APP_ENV_LOGGER_FORMAT
|
|
487
|
+
level?: TLogLevel; // logger-level floor; defaults to APP_ENV_LOGGER_LEVEL, then 'debug'
|
|
488
|
+
transports: {
|
|
489
|
+
info: {
|
|
490
|
+
file?: IFileTransportOptions;
|
|
491
|
+
dgram?: Partial<IDgramTransportOptions>;
|
|
492
|
+
};
|
|
493
|
+
error: {
|
|
494
|
+
file?: IFileTransportOptions;
|
|
495
|
+
dgram?: Partial<IDgramTransportOptions>;
|
|
496
|
+
};
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
- **Both `info` and `error` transport groups support optional `file` (DailyRotateFile) and `dgram` (UDP) transports.**
|
|
502
|
+
- **A console transport is always included**, regardless of what is configured.
|
|
503
|
+
- **Error file transports double as Winston exception handlers.**
|
|
504
|
+
|
|
505
|
+
## Debug Logging Behavior
|
|
506
|
+
|
|
507
|
+
`Source ->` [`base/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/base/base.ts)
|
|
508
|
+
|
|
509
|
+
Debug logs require **both** conditions to be met:
|
|
510
|
+
|
|
511
|
+
1. `DEBUG=true` environment variable is set (parsed via `toBoolean`)
|
|
512
|
+
2. `NODE_ENV` is either unset **or** is present in the `Environment.COMMON_ENVS` set
|
|
513
|
+
|
|
514
|
+
`COMMON_ENVS` includes: `local`, `debug`, `development`, `dev`, `sit`, `uat`, `alpha`, `beta`, `staging`, `production`. Extend this set with `APP_ENV_EXTRA_LOG_ENVS`:
|
|
515
|
+
|
|
516
|
+
```bash
|
|
517
|
+
DEBUG=true
|
|
518
|
+
NODE_ENV=development
|
|
519
|
+
APP_ENV_EXTRA_LOG_ENVS=qa,preview # Comma-separated additional environments
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
> [!IMPORTANT]
|
|
523
|
+
> The debug flag check is pre-computed at module load time. Changing `DEBUG` or `NODE_ENV` at runtime has no effect - the values are captured once when the module is first imported.
|
|
524
|
+
|
|
525
|
+
## High-Frequency Logger (HfLogger)
|
|
526
|
+
|
|
527
|
+
`Source ->` [`hf/logger.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/hf/logger.ts), [`hf/flusher.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/hf/flusher.ts)
|
|
528
|
+
|
|
529
|
+
For performance-critical applications (e.g. HFT systems, game servers), `HfLogger` provides ring-buffer logging with a 59.4ns bytes-path enqueue and a 66.0ns string no-args enqueue (Bun 1.3.14, 1M-iteration median). It extends `AbstractLogger` and implements `ILogger`, so it can be used anywhere an `ILogger` is expected - but it is still entirely separate from the Winston-backed `Logger` pipeline: no formatters, transports, or `APP_ENV_LOGGER_*` env vars apply to it. Read the [HfLogger guide](/extensions/helpers/logger/hf-logger) before using it - it carries hard usage rules (pre-encoded fixed message vocabulary, single-thread only, flush-interval sizing) and documented limitations.
|
|
530
|
+
|
|
531
|
+
```typescript
|
|
532
|
+
import { HfLogger, HfLogFlusher } from '@venizia/ignis-helpers';
|
|
533
|
+
|
|
534
|
+
// At initialization time (once):
|
|
535
|
+
const logger = HfLogger.get('OrderEngine');
|
|
536
|
+
const MSG_ORDER_SENT = HfLogger.encodeMessage('Order sent');
|
|
537
|
+
const MSG_ORDER_FILLED = HfLogger.encodeMessage('Order filled');
|
|
538
|
+
|
|
539
|
+
// Start background flusher
|
|
540
|
+
const flusher = new HfLogFlusher();
|
|
541
|
+
flusher.start(100); // Flush every 100ms
|
|
542
|
+
|
|
543
|
+
// In hot path (bytes path, ~59ns, no allocation):
|
|
544
|
+
logger.log('info', MSG_ORDER_SENT);
|
|
545
|
+
logger.log('info', MSG_ORDER_FILLED);
|
|
546
|
+
|
|
547
|
+
// ILogger surface also works (string no-args path, ~66ns on a cache hit):
|
|
548
|
+
logger.info('Order sent');
|
|
549
|
+
|
|
550
|
+
// Shutdown:
|
|
551
|
+
await flusher.flush();
|
|
552
|
+
flusher.stop();
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
### HfLogger API
|
|
556
|
+
|
|
557
|
+
`HfLogger` implements the full `ILogger` contract plus its own static/bytes surface:
|
|
558
|
+
|
|
559
|
+
| Method | Signature | Description |
|
|
560
|
+
|--------|-----------|--------------|
|
|
561
|
+
| `HfLogger.get` | `(scope: string) => HfLogger` | Get or create a cached logger instance (allocates the ring lazily on first call) |
|
|
562
|
+
| `HfLogger.encodeMessage` | `(msg: string) => Uint8Array` | Pre-encode a message string to bytes; FIFO-bounded cache, cap `MESSAGE_CACHE_CAP = 4096` |
|
|
563
|
+
| `logger.debug/info/warn/error/emerg` | `(message: string, ...args: AnyType[]) => void` | `ILogger` methods. No args: cache-lookup encode + bytes-path write. With args: `formatLogMessage` (deep inspection + redaction) then an uncached encode - the slow path |
|
|
564
|
+
| `logger.log` | `(level: TLogLevel, message: string, ...args: AnyType[]) => void`<br>`(level: TLogLevel, messageBytes: Uint8Array) => void` | Overloaded: string form follows the `debug`/`info`/... cost model above; `Uint8Array` form is the legacy bytes hot path, unchanged signature |
|
|
565
|
+
| `logger.for` | `(methodName: string) => ILogger` | Returns `HfLogger.get(`${scope}-${methodName}`)`, same dash composition as `BaseLogger` |
|
|
566
|
+
|
|
567
|
+
Supported levels (`TLogLevel`, full set): `debug` (0), `info` (1), `warn` (2), `error` (3), `emerg` (4).
|
|
568
|
+
|
|
569
|
+
### HfLogFlusher API
|
|
570
|
+
|
|
571
|
+
| Method | Signature | Description |
|
|
572
|
+
|--------|-----------|--------------|
|
|
573
|
+
| `new HfLogFlusher` | `(options?: IHfLogFlusherOptions) => HfLogFlusher` | `sink?: THfSink` (full custom delivery), `filePath?: string` (default sink appends here instead of stdout), `batchSize?: number` (entries rendered per batch before yielding, default `1024`; invalid values fall back with a `console.warn`) |
|
|
574
|
+
| `flusher.flush` | `() => Promise<void>` | Drain the full backlog in bounded batches, yielding (`setImmediate`) between batches; re-entrant calls return the in-progress promise |
|
|
575
|
+
| `flusher.start` | `(intervalMs?: number) => void` | Start a background `setInterval` flush loop (default `100`ms), unref'd so it never blocks process exit; idempotent - calling again restarts cleanly |
|
|
576
|
+
| `flusher.stop` | `() => void` | Clear the interval started by `start()` |
|
|
577
|
+
|
|
578
|
+
`THfSinkBatch` passed to a custom `sink`: `{ lines: Array<string>; dropped: number }` - `dropped` is the exact count of entries overwritten by the ring before the flusher could read them since the previous batch (see "Lap accounting" in the guide). The default sink (no `filePath`) writes `process.stdout.write(...)` once per batch; with `filePath`, it appends via `fs.appendFileSync` once per batch. A sink that throws is logged via `console.error` and does not abort the drain.
|
|
579
|
+
|
|
580
|
+
### Line format
|
|
581
|
+
|
|
582
|
+
The default sink renders each entry as:
|
|
583
|
+
|
|
584
|
+
```
|
|
585
|
+
<ISO timestamp> [<level name>] <scope> <message>
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
For example: `2026-07-18T09:41:03.128Z [info] OrderEngine Order sent`. When a batch has `dropped > 0`, the default sink emits a `warn` marker line ahead of it: `<ISO timestamp> [warn] HfLogFlusher ring lapped - <N> entries overwritten before they could be read`.
|
|
589
|
+
|
|
590
|
+
### Ring buffer entry format
|
|
591
|
+
|
|
592
|
+
Each entry occupies exactly 256 bytes in a 64K-entry (16MB) `ArrayBuffer`, allocated lazily on the first `HfLogger.get()` call (not at module import) and shared module-wide (not per-`HfLogger` instance):
|
|
593
|
+
|
|
594
|
+
| Offset | Size | Field |
|
|
595
|
+
|--------|------|-------|
|
|
596
|
+
| 0-7 | 8 bytes | Timestamp (`float64` epoch milliseconds, sub-millisecond precision) |
|
|
597
|
+
| 8 | 1 byte | Level (`0`=debug, `1`=info, `2`=warn, `3`=error, `4`=emerg) |
|
|
598
|
+
| 9 | 1 byte | Scope length (0-32) |
|
|
599
|
+
| 10-41 | 32 bytes | Scope bytes |
|
|
600
|
+
| 42 | 1 byte | Message length (0-213) |
|
|
601
|
+
| 43-255 | 213 bytes | Message bytes |
|
|
602
|
+
|
|
603
|
+
The explicit length bytes are what make reads exact: the flusher decodes only the bytes a field actually holds, so there is no NUL padding and no stale tail from a previous, longer entry in a reused slot. The buffer wraps around at 65,536 entries using bitwise AND masking (`writeIndex & (BUFFER_SIZE - 1)`); when the producer writes faster than the flusher drains, unflushed entries are overwritten, and the overwritten count is reported via `dropped` on the next sink batch rather than silently emitted.
|
|
604
|
+
|
|
605
|
+
> [!WARNING]
|
|
606
|
+
> Pre-encode messages at initialization time using `HfLogger.encodeMessage()` or by calling a no-args `ILogger` method once per distinct message. Calling either with dynamic, per-event strings puts UTF-8 encoding on the hot path and can evict other cached messages once the FIFO-bounded cache (4096 entries) fills.
|
|
607
|
+
|
|
608
|
+
## Environment Variables
|
|
609
|
+
|
|
610
|
+
### Core configuration
|
|
611
|
+
|
|
612
|
+
| Variable | Default | Description |
|
|
613
|
+
|----------|---------|-------------|
|
|
614
|
+
| `APP_ENV_APPLICATION_NAME` | `APP` | Label prefix shown in all log output |
|
|
615
|
+
| `DEBUG` | `false` | Enable debug-level logging |
|
|
616
|
+
| `NODE_ENV` | _(unset)_ | Must be in `COMMON_ENVS` or unset for debug to activate |
|
|
617
|
+
| `APP_ENV_EXTRA_LOG_ENVS` | _(empty)_ | Comma-separated additional environments to allow debug |
|
|
618
|
+
| `APP_ENV_LOGGER_FORMAT` | `text` | Output format (`json` or `text`) |
|
|
619
|
+
| `APP_ENV_LOGGER_LEVEL` | `debug` | Logger-level floor; transports without their own level inherit it. Invalid values fall back to `debug` with a console warning |
|
|
620
|
+
| `APP_ENV_LOGGER_FOLDER_PATH` | _(unset)_ | Log files directory; file logging is OFF when unset |
|
|
621
|
+
| `APP_ENV_LOGGER_INSPECT_DEPTH` | `5` | Object inspection depth for `%s` placeholders. Non-negative integer only; invalid or absent falls back to `5` |
|
|
622
|
+
| `APP_ENV_LOGGER_DO_REDACT` | `true` | Secret redaction in log arguments. ONLY the literal `false` disables it - raw values (passwords, tokens, connection URLs) then reach the sinks. Any other value, unset included, keeps redaction ON. Never disable in production |
|
|
623
|
+
|
|
624
|
+
### File rotation
|
|
625
|
+
|
|
626
|
+
| Variable | Default | Description |
|
|
627
|
+
|----------|---------|-------------|
|
|
628
|
+
| `APP_ENV_LOGGER_FILE_FREQUENCY` | `1h` | Rotation frequency |
|
|
629
|
+
| `APP_ENV_LOGGER_FILE_MAX_SIZE` | `100m` | Max file size before rotation |
|
|
630
|
+
| `APP_ENV_LOGGER_FILE_MAX_FILES` | `5d` | Retention period |
|
|
631
|
+
| `APP_ENV_LOGGER_FILE_DATE_PATTERN` | `YYYYMMDD_HH` | Date pattern in filename |
|
|
632
|
+
|
|
633
|
+
### UDP transport
|
|
634
|
+
|
|
635
|
+
| Variable | Description |
|
|
636
|
+
|----------|-------------|
|
|
637
|
+
| `APP_ENV_LOGGER_DGRAM_HOST` | UDP log aggregator host |
|
|
638
|
+
| `APP_ENV_LOGGER_DGRAM_PORT` | UDP log aggregator port |
|
|
639
|
+
| `APP_ENV_LOGGER_DGRAM_LABEL` | Label to identify log source |
|
|
640
|
+
| `APP_ENV_LOGGER_DGRAM_LEVELS` | Comma-separated levels to send via UDP |
|
|
641
|
+
|
|
642
|
+
### Example `.env`
|
|
643
|
+
|
|
644
|
+
```bash
|
|
645
|
+
# Application
|
|
646
|
+
APP_ENV_APPLICATION_NAME=my-service
|
|
647
|
+
|
|
648
|
+
# Core
|
|
649
|
+
DEBUG=true
|
|
650
|
+
APP_ENV_LOGGER_FORMAT=json
|
|
651
|
+
APP_ENV_LOGGER_FOLDER_PATH=./app_data/logs
|
|
652
|
+
APP_ENV_LOGGER_INSPECT_DEPTH=5
|
|
653
|
+
|
|
654
|
+
# File rotation
|
|
655
|
+
APP_ENV_LOGGER_FILE_FREQUENCY=24h
|
|
656
|
+
APP_ENV_LOGGER_FILE_MAX_SIZE=500m
|
|
657
|
+
APP_ENV_LOGGER_FILE_MAX_FILES=30d
|
|
658
|
+
|
|
659
|
+
# UDP transport
|
|
660
|
+
APP_ENV_LOGGER_DGRAM_HOST=127.0.0.1
|
|
661
|
+
APP_ENV_LOGGER_DGRAM_PORT=5000
|
|
662
|
+
APP_ENV_LOGGER_DGRAM_LABEL=my-app
|
|
663
|
+
APP_ENV_LOGGER_DGRAM_LEVELS=error,warn,info
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
## API Summary
|
|
667
|
+
|
|
668
|
+
| Export | Kind | Description |
|
|
669
|
+
|--------|------|-------------|
|
|
670
|
+
| `ILogger` | interface | The logging contract every consumer types against - one direct method per level (`debug`, `info`, `warn`, `error`, `emerg`), plus `log` and `for` |
|
|
671
|
+
| `AbstractLogger` | abstract class | `ILogger` as a class - the `instanceof` check that works for EVERY provider |
|
|
672
|
+
| `BaseLogger` | abstract class | Provider-independent plumbing shared by every implementation: scope, prefix, the `DEBUG` gate, `.for()`, one abstract `write()` sink |
|
|
673
|
+
| `WinstonLogger` | class | The Winston-backed provider (the default); `Logger` is its permanent concrete alias |
|
|
674
|
+
| `Logger` | class | Concrete winston alias - `Logger.get(scope, customWinstonLogger?)`, `instanceof Logger`; deliberately does NOT follow `use()` |
|
|
675
|
+
| `PinoLogger` | class (sub-path `/pino`) | The throughput provider - register with `LoggerFactory.use` ([guide](/extensions/helpers/logger/pino)) |
|
|
676
|
+
| `ApplicationLogger` | const facade + type (`ILogger`) | `ApplicationLogger.get(scope)` always returns the REGISTERED provider's logger |
|
|
677
|
+
| `LoggerFactory` | class | Provider registration (`use({ provider })`) + `ILogger` acquisition from scope arrays |
|
|
678
|
+
| `ILoggerProvider` | interface | Static-side contract a provider class satisfies (`get(scope): ILogger`) |
|
|
679
|
+
| `HfLogger` | class | `ILogger`-conformant ring-buffer logger for hot paths |
|
|
680
|
+
| `HfLogFlusher` | class | Background flusher for `HfLogger` |
|
|
681
|
+
| `LogLevels` | class (constants) | Log level constants (`ERROR`, `EMERG`, `WARN`, `INFO`, `DEBUG`) with `isValid()` |
|
|
682
|
+
| `LoggerFormats` | class (constants) | Format constants (`JSON`, `TEXT`) with `isValid()` |
|
|
683
|
+
| `defineCustomLogger` | `(opts: ICustomLoggerOptions) => winston.Logger` | Create a fully configured Winston logger |
|
|
684
|
+
| `defineLogFormatter` | `(opts: { label: string; format?: TLoggerFormat }) => winston.Logform.Format` | Create a formatter (auto-detects format from env) |
|
|
685
|
+
| `defineJsonLoggerFormatter` | `(opts: { label: string }) => winston.Logform.Format` | Create a JSON formatter |
|
|
686
|
+
| `definePrettyLoggerFormatter` | `(opts: { label: string; colorize?: boolean }) => winston.Logform.Format` | Create a pretty text formatter; `colorize: false` for files/aggregators |
|
|
687
|
+
| `applicationLogFormatter` | `winston.Logform.Format` | Pre-built formatter using `APP_ENV_APPLICATION_NAME` label |
|
|
688
|
+
| `resolveLoggerLevel` | `(opts: { configured?: string }) => TLogLevel` | Validate a level string; invalid or absent falls back to `debug` |
|
|
689
|
+
| `resolveDefaultTransportOptions` | `() => ICustomLoggerOptions['transports']` | Default transports from `APP_ENV_LOGGER_*`, resolved at call time |
|
|
690
|
+
| `DgramTransport` | class | Custom Winston transport for UDP logging |
|
|
691
|
+
| `TLogLevel` | type | Union of all log level string literals |
|
|
692
|
+
| `TLoggerFormat` | type | Union of `'json' \| 'text'` |
|
|
693
|
+
| `IFileTransportOptions` | interface | Options for daily-rotating file transport |
|
|
694
|
+
| `ICustomLoggerOptions` | interface | Options for `defineCustomLogger` |
|
|
695
|
+
| `IDgramTransportOptions` | interface | Options for `DgramTransport` |
|
|
696
|
+
|
|
697
|
+
## Troubleshooting
|
|
698
|
+
|
|
699
|
+
### Debug logs not appearing
|
|
700
|
+
|
|
701
|
+
**Cause:** Debug logging requires both `DEBUG=true` AND a `NODE_ENV` that is either unset or present in the `COMMON_ENVS` set. These values are pre-computed at module load time.
|
|
702
|
+
|
|
703
|
+
**Fix:**
|
|
704
|
+
1. Verify `DEBUG=true` is set in your environment.
|
|
705
|
+
2. Verify `NODE_ENV` is set to one of: `local`, `debug`, `development`, `dev`, `sit`, `uat`, `alpha`, `beta`, `staging`, `production` - or is unset entirely.
|
|
706
|
+
3. If you use a custom environment name (e.g. `qa`), add it to `APP_ENV_EXTRA_LOG_ENVS=qa`.
|
|
707
|
+
|
|
708
|
+
```bash
|
|
709
|
+
DEBUG=true NODE_ENV=development bun run server:dev
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
### "[defineLogger] Invalid logger format | format: {format} | valids: json,text"
|
|
713
|
+
|
|
714
|
+
**Cause:** The `format` option passed to `defineLogFormatter` (or the `APP_ENV_LOGGER_FORMAT` environment variable) is not `json` or `text`.
|
|
715
|
+
|
|
716
|
+
**Fix:** Set `APP_ENV_LOGGER_FORMAT` to either `json` or `text`:
|
|
717
|
+
|
|
718
|
+
```bash
|
|
719
|
+
APP_ENV_LOGGER_FORMAT=text
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
### UDP transport not sending logs
|
|
723
|
+
|
|
724
|
+
**Cause:** `DgramTransport.fromPartial()` returns `null` if any required option is missing (`label`, `host`, `port`, `levels` with at least one entry, or `socketOptions`). The transport is silently not registered.
|
|
725
|
+
|
|
726
|
+
**Fix:**
|
|
727
|
+
1. Ensure **all four** dgram env vars are set: `APP_ENV_LOGGER_DGRAM_HOST`, `APP_ENV_LOGGER_DGRAM_PORT`, `APP_ENV_LOGGER_DGRAM_LABEL`, and `APP_ENV_LOGGER_DGRAM_LEVELS`.
|
|
728
|
+
2. `APP_ENV_LOGGER_DGRAM_LEVELS` must contain at least one level (e.g. `error,warn,info`). An empty value results in no transport.
|
|
729
|
+
3. Verify the UDP aggregator is reachable from your host (firewall, port binding).
|
|
730
|
+
|
|
731
|
+
### Log label shows "APP" instead of application name
|
|
732
|
+
|
|
733
|
+
**Cause:** The default label comes from `Defaults.APPLICATION_NAME`, which reads `APP_ENV_APPLICATION_NAME`. If the env var is not set, it falls back to `'APP'`.
|
|
734
|
+
|
|
735
|
+
**Fix:** Set `APP_ENV_APPLICATION_NAME` in your environment:
|
|
736
|
+
|
|
737
|
+
```bash
|
|
738
|
+
APP_ENV_APPLICATION_NAME=my-service
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
## See also
|
|
742
|
+
|
|
743
|
+
- [Logger overview](/extensions/helpers/logger/) - introduction and the most common tasks
|
|
744
|
+
- [Request Tracker Component](/extensions/components/request-tracker) - request logging
|
|
745
|
+
- [Winston documentation](https://github.com/winstonjs/winston) - underlying logging library
|
|
746
|
+
- [winston-daily-rotate-file](https://github.com/winstonjs/winston-daily-rotate-file) - file rotation transport
|