@venizia/ignis-docs 0.2.1-0 → 0.2.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.
- package/content/best-practices/architectural-patterns.md +3 -3
- package/content/best-practices/code-style-standards/naming-conventions.md +1 -1
- package/content/best-practices/contribution-workflow.md +2 -2
- package/content/best-practices/error-handling.md +94 -89
- package/content/best-practices/security-guidelines.md +5 -5
- package/content/extensions/components/api-reference.md +22 -21
- package/content/extensions/components/authentication/api.md +64 -28
- package/content/extensions/components/authentication/errors.md +19 -5
- package/content/extensions/components/authentication/index.md +19 -18
- package/content/extensions/components/authentication/usage.md +55 -30
- package/content/extensions/components/authorization/api.md +351 -84
- package/content/extensions/components/authorization/errors.md +51 -17
- package/content/extensions/components/authorization/getting-started.md +227 -0
- package/content/extensions/components/authorization/index.md +24 -16
- package/content/extensions/components/authorization/usage.md +45 -21
- package/content/extensions/components/health-check.md +15 -9
- package/content/extensions/components/index.md +24 -90
- package/content/extensions/components/mail/api.md +105 -54
- package/content/extensions/components/mail/errors.md +12 -10
- package/content/extensions/components/mail/index.md +32 -13
- package/content/extensions/components/mail/usage.md +26 -18
- package/content/extensions/components/request-tracker.md +18 -14
- package/content/extensions/components/socket-io/api.md +377 -882
- package/content/extensions/components/socket-io/errors.md +49 -51
- package/content/extensions/components/socket-io/index.md +72 -88
- package/content/extensions/components/socket-io/usage.md +107 -117
- package/content/extensions/components/static-asset/api.md +83 -31
- package/content/extensions/components/static-asset/errors.md +18 -7
- package/content/extensions/components/static-asset/index.md +18 -11
- package/content/extensions/components/static-asset/usage.md +11 -6
- package/content/extensions/components/template/index.md +3 -3
- package/content/extensions/components/websocket/api.md +58 -27
- package/content/extensions/components/websocket/errors.md +3 -3
- package/content/extensions/components/websocket/index.md +8 -7
- package/content/extensions/components/websocket/usage.md +21 -8
- package/content/extensions/helpers/cron/index.md +8 -7
- package/content/extensions/helpers/crypto/index.md +16 -8
- package/content/extensions/helpers/crypto/reference.md +96 -24
- package/content/extensions/helpers/env/index.md +14 -10
- package/content/extensions/helpers/error/index.md +99 -23
- package/content/extensions/helpers/index.md +61 -47
- package/content/extensions/helpers/inversion/index.md +23 -6
- package/content/extensions/helpers/inversion/reference.md +30 -22
- package/content/extensions/helpers/kafka/admin.md +3 -2
- package/content/extensions/helpers/kafka/compile-binary.md +69 -44
- package/content/extensions/helpers/kafka/consumer.md +24 -21
- package/content/extensions/helpers/kafka/examples.md +22 -234
- package/content/extensions/helpers/kafka/index.md +30 -62
- package/content/extensions/helpers/kafka/producer.md +31 -26
- package/content/extensions/helpers/kafka/schema-registry.md +19 -14
- package/content/extensions/helpers/logger/hf-logger.md +49 -22
- package/content/extensions/helpers/logger/index.md +37 -12
- package/content/extensions/helpers/logger/pino.md +31 -11
- package/content/extensions/helpers/logger/reference.md +243 -52
- package/content/extensions/helpers/network/api.md +65 -30
- package/content/extensions/helpers/network/index.md +32 -11
- package/content/extensions/helpers/queue/index.md +17 -6
- package/content/extensions/helpers/queue/reference.md +52 -25
- package/content/extensions/helpers/redis/index.md +29 -11
- package/content/extensions/helpers/redis/reference.md +85 -28
- package/content/extensions/helpers/secrets/index.md +82 -12
- package/content/extensions/helpers/socket-io/api.md +40 -21
- package/content/extensions/helpers/socket-io/index.md +26 -10
- package/content/extensions/helpers/storage/api.md +42 -24
- package/content/extensions/helpers/storage/index.md +10 -7
- package/content/extensions/helpers/types/index.md +20 -7
- package/content/extensions/helpers/types/reference.md +53 -14
- package/content/extensions/helpers/uid/index.md +166 -10
- package/content/extensions/helpers/websocket/api.md +52 -13
- package/content/extensions/helpers/websocket/index.md +7 -4
- package/content/extensions/helpers/worker-thread/index.md +9 -5
- package/content/extensions/helpers/worker-thread/reference.md +20 -20
- package/content/extensions/index.md +38 -39
- package/content/extensions/src-details/mcp-server.md +96 -548
- package/content/guides/core-concepts/persistent/datasources.md +2 -2
- package/content/guides/core-concepts/persistent/index.md +5 -1
- package/content/guides/core-concepts/persistent/pglite.md +218 -0
- package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
- package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
- package/content/guides/core-concepts/persistent/search-typesense.md +26 -14
- package/content/guides/core-concepts/persistent/sqlite.md +296 -0
- package/content/guides/core-concepts/persistent/transactions.md +12 -11
- package/content/guides/get-started/5-minute-quickstart.md +59 -206
- package/content/guides/get-started/philosophy.md +135 -670
- package/content/guides/get-started/setup.md +53 -74
- package/content/guides/migrations/redis-helpers-migration.md +4 -3
- package/content/guides/migrations/scoped-rbac-migration.md +5 -5
- package/content/guides/migrations/unified-connectors-migration.md +7 -6
- package/content/guides/tutorials/realtime-chat.md +1 -1
- package/content/references/base/application.md +63 -21
- package/content/references/base/components.md +3 -3
- package/content/references/base/connectors.md +14 -14
- package/content/references/base/controllers.md +12 -12
- package/content/references/base/datasources-reference.md +32 -31
- package/content/references/base/datasources.md +6 -6
- package/content/references/base/dependency-injection.md +12 -11
- package/content/references/base/filter-system/application-usage.md +54 -30
- package/content/references/base/filter-system/array-operators.md +24 -46
- package/content/references/base/filter-system/comparison-operators.md +47 -67
- package/content/references/base/filter-system/default-filter.md +59 -53
- package/content/references/base/filter-system/fields-order-pagination.md +92 -146
- package/content/references/base/filter-system/index.md +25 -13
- package/content/references/base/filter-system/json-filtering.md +45 -184
- package/content/references/base/filter-system/list-operators.md +23 -53
- package/content/references/base/filter-system/logical-operators.md +63 -121
- package/content/references/base/filter-system/null-operators.md +34 -104
- package/content/references/base/filter-system/pattern-matching.md +40 -55
- package/content/references/base/filter-system/quick-reference.md +86 -198
- package/content/references/base/filter-system/range-operators.md +18 -46
- package/content/references/base/filter-system/tips.md +6 -6
- package/content/references/base/filter-system/use-cases.md +33 -15
- package/content/references/base/grpc-controllers.md +53 -17
- package/content/references/base/index.md +5 -3
- package/content/references/base/middlewares.md +11 -10
- package/content/references/base/models-reference.md +17 -17
- package/content/references/base/models.md +4 -3
- package/content/references/base/providers.md +8 -8
- package/content/references/base/repositories/advanced.md +224 -326
- package/content/references/base/repositories/index.md +19 -6
- package/content/references/base/repositories/mixins.md +5 -5
- package/content/references/base/repositories/relations.md +160 -293
- package/content/references/base/repositories/soft-deletable.md +16 -6
- package/content/references/base/secrets.md +17 -13
- package/content/references/base/services.md +6 -4
- package/content/references/configuration/environment-variables.md +31 -23
- package/content/references/configuration/index.md +6 -4
- package/content/references/index.md +1 -1
- package/content/references/utilities/duration.md +85 -0
- package/content/references/utilities/index.md +5 -1
- package/content/references/utilities/jsx-reference.md +11 -11
- package/content/references/utilities/jsx.md +2 -2
- package/content/references/utilities/module.md +78 -25
- package/content/references/utilities/request.md +2 -1
- package/content/references/utilities/retry.md +139 -0
- package/content/references/utilities/schema.md +2 -2
- package/content/references/utilities/statuses-reference.md +4 -4
- package/content/references/utilities/statuses.md +5 -5
- package/dist/mcp-server/index.js +0 -0
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
- package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
- package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
- package/package.json +17 -16
|
@@ -6,21 +6,32 @@ difficulty: intermediate
|
|
|
6
6
|
|
|
7
7
|
# Pino Provider
|
|
8
8
|
|
|
9
|
-
`PinoLogger` is the second logger provider behind the `ILogger` contract - the throughput option.
|
|
9
|
+
`PinoLogger` is the second logger provider behind the `ILogger` contract - the throughput option. Winston remains the DEFAULT. Nothing changes for apps that never register pino.
|
|
10
|
+
|
|
11
|
+
| Provider | Output | Speed (measured) |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| Winston (built-in) | Colorized console, daily-rotating info/error files, UDP shipping | ~1.2-1.6us/line |
|
|
14
|
+
| Pino | Newline-delimited JSON | ~0.5-0.6us/line |
|
|
10
15
|
|
|
11
16
|
## Registration - one line, order-independent
|
|
12
17
|
|
|
13
18
|
```typescript
|
|
14
|
-
// entrypoint (
|
|
19
|
+
// entrypoint (for example, src/index.ts)
|
|
15
20
|
import { LoggerFactory } from '@venizia/ignis-helpers';
|
|
16
21
|
import { PinoLogger } from '@venizia/ignis-helpers/pino';
|
|
17
22
|
|
|
18
23
|
LoggerFactory.use({ provider: PinoLogger });
|
|
19
24
|
```
|
|
20
25
|
|
|
21
|
-
The two providers register symmetrically - `WinstonLogger` lives at `@venizia/ignis-helpers/winston` the same way. Both are sub-path only with optional peers
|
|
26
|
+
The two providers register symmetrically - `WinstonLogger` lives at `@venizia/ignis-helpers/winston` the same way. Both are sub-path only, with optional peers. Exactly ONE provider is ever loaded - registering pino here means winston is never loaded, nor bundled.
|
|
27
|
+
|
|
28
|
+
From that moment, every factory-issued logger runs on pino:
|
|
29
|
+
|
|
30
|
+
- `BaseHelper.logger` in every controller, service, repository, and helper
|
|
31
|
+
- `ApplicationLogger.get(...)`
|
|
32
|
+
- Even a module-level `const logger = LoggerFactory.getLogger([...])`, captured at import time before this line ran - thanks to swap-on-use delegation
|
|
22
33
|
|
|
23
|
-
|
|
34
|
+
Import order doesn't matter. The factory re-points every wrapper it has ever issued when `use()` is called.
|
|
24
35
|
|
|
25
36
|
> [!IMPORTANT]
|
|
26
37
|
> `Logger.get(...)` (the concrete `WinstonLogger` alias) and `defineCustomLogger` deliberately do NOT follow the registration - they name winston explicitly. See the name/role table in the [Full reference](/extensions/helpers/logger/reference).
|
|
@@ -32,16 +43,20 @@ The provider lives at the sub-path `@venizia/ignis-helpers/pino` only - importin
|
|
|
32
43
|
| Peer | Needed when |
|
|
33
44
|
|------|-------------|
|
|
34
45
|
| `pino` | always (the sub-path values-imports it) |
|
|
35
|
-
| `pino-pretty` | `APP_ENV_LOGGER_FORMAT=text` (
|
|
46
|
+
| `pino-pretty` | `APP_ENV_LOGGER_FORMAT=text` (pretty dev output, colorized when the environment allows it) |
|
|
36
47
|
| `pino-roll` | `APP_ENV_LOGGER_FOLDER_PATH` is set (file rotation) |
|
|
37
48
|
|
|
38
49
|
A missing peer fails with the standard install-hint error BEFORE any worker thread spawns.
|
|
39
50
|
|
|
40
51
|
## Output modes
|
|
41
52
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
53
|
+
| Trigger | Output |
|
|
54
|
+
|---|---|
|
|
55
|
+
| `APP_ENV_LOGGER_FORMAT=json`, or unset in production practice | NDJSON to stdout - the k8s/docker collector pattern |
|
|
56
|
+
| `APP_ENV_LOGGER_FORMAT=text` | Pretty lines via a `pino-pretty` worker-thread transport - dev only. Color follows the [Color](./reference#color) rules, and `pino-pretty` still drops it when stdout is not a terminal |
|
|
57
|
+
| `APP_ENV_LOGGER_FOLDER_PATH` is set | A rotating file via `pino-roll`, honoring the same env vars winston uses (table below) |
|
|
58
|
+
|
|
59
|
+
**`pino-roll` file rotation - env var mapping:**
|
|
45
60
|
|
|
46
61
|
| Env | pino-roll meaning |
|
|
47
62
|
|-----|-------------------|
|
|
@@ -50,11 +65,16 @@ A missing peer fails with the standard install-hint error BEFORE any worker thre
|
|
|
50
65
|
| `APP_ENV_LOGGER_FILE_MAX_FILES` | retention -> file count: `'5d'` -> 120 files (hourly) / 5 (daily); a bare integer -> that count |
|
|
51
66
|
| `APP_ENV_LOGGER_FILE_DATE_PATTERN` | NOT supported (pino-roll has no date pattern) |
|
|
52
67
|
|
|
53
|
-
`APP_ENV_LOGGER_LEVEL` sets the floor exactly as with winston
|
|
68
|
+
`APP_ENV_LOGGER_LEVEL` sets the floor exactly as with winston. `emerg` is the single custom pino level, above `error`. The default `debug` floor admits every level - identical to the winston provider.
|
|
54
69
|
|
|
55
70
|
## What stays identical, what differs
|
|
56
71
|
|
|
57
|
-
Identical by construction:
|
|
72
|
+
Identical by construction:
|
|
73
|
+
|
|
74
|
+
- the `[Scope] ` message prefix
|
|
75
|
+
- args formatting through `formatLogMessage` - deep inspection plus secret redaction; a `token` field renders `[REDACTED]` on pino exactly as on winston
|
|
76
|
+
- the level vocabulary and floor semantics
|
|
77
|
+
- the `DEBUG` gate on `debug()`
|
|
58
78
|
|
|
59
79
|
Different on purpose (pino stays pino-native - every parity shim would cost the speed you came for):
|
|
60
80
|
|
|
@@ -71,7 +91,7 @@ If your operations depend on the left column, stay on winston - it is not deprec
|
|
|
71
91
|
|
|
72
92
|
## Advanced: injecting a backing instance
|
|
73
93
|
|
|
74
|
-
`setPinoBackingLogger({ instance })` replaces the env-driven singleton with a pino instance you configured yourself
|
|
94
|
+
`setPinoBackingLogger({ instance })` replaces the env-driven singleton with a pino instance you configured yourself. Tests use this with an in-memory destination; apps can use it for exotic transports. The previous instance's transport is flushed and closed on replacement. `buildPinoOptions()` and `resolveDestinationPlan()` are exported for building compatible options.
|
|
75
95
|
|
|
76
96
|
## See also
|
|
77
97
|
|
|
@@ -8,7 +8,7 @@ difficulty: intermediate
|
|
|
8
8
|
|
|
9
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
10
|
|
|
11
|
-
The default provider is **Winston
|
|
11
|
+
The default provider is **Winston**, paired with `winston-daily-rotate-file` for file rotation. All provider packages are OPTIONAL peers. An application loads exactly ONE provider - see [single-provider loading](#architecture-and-ilogger) below.
|
|
12
12
|
|
|
13
13
|
**Files:**
|
|
14
14
|
|
|
@@ -25,9 +25,31 @@ The default provider is **Winston** (with `winston-daily-rotate-file` for file r
|
|
|
25
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
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
27
|
|
|
28
|
-
##
|
|
28
|
+
## Find what you need
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
| You want to | Go to |
|
|
31
|
+
|---|---|
|
|
32
|
+
| See how provider registration and delegation work | [Architecture and ILogger](#architecture-and-ilogger) |
|
|
33
|
+
| Import the right class from the right sub-path | [Import paths](#import-paths) |
|
|
34
|
+
| Get or cache a scoped logger instance | [Creating an Instance](#creating-an-instance) |
|
|
35
|
+
| Know what each log level means and when to use it | [What each level means](#what-each-level-means) |
|
|
36
|
+
| Keep nested fields visible when logging with `%s` | [Message Formatting](#message-formatting) |
|
|
37
|
+
| Turn on file rotation or UDP shipping | [Transports](#transports) |
|
|
38
|
+
| Fix `debug()` logs that aren't showing | [Debug Logging Behavior](#debug-logging-behavior) |
|
|
39
|
+
| Log on a hot path doing 100k+ events/sec | [High-Frequency Logger](#high-frequency-logger) |
|
|
40
|
+
| Look up one `APP_ENV_LOGGER_*` variable | [Environment Variables](#environment-variables) |
|
|
41
|
+
| Find one exported symbol fast | [API Summary](#api-summary) |
|
|
42
|
+
| Fix a specific error message | [Troubleshooting](#troubleshooting) |
|
|
43
|
+
|
|
44
|
+
## Architecture and ILogger
|
|
45
|
+
|
|
46
|
+
The module follows IGNIS's house format - one folder per concern:
|
|
47
|
+
|
|
48
|
+
- `common/` - the contract
|
|
49
|
+
- `base/` - provider-independent plumbing
|
|
50
|
+
- `winston/` - the built-in provider
|
|
51
|
+
- `hf/` - the separate high-frequency logger
|
|
52
|
+
- `factory.ts` - the single acquisition path
|
|
31
53
|
|
|
32
54
|
```
|
|
33
55
|
ILogger (interface) common/types.ts
|
|
@@ -38,9 +60,12 @@ ILogger (interface) common/types.ts
|
|
|
38
60
|
```
|
|
39
61
|
|
|
40
62
|
- **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
|
|
42
|
-
-
|
|
43
|
-
- **
|
|
63
|
+
- **Provider registration.** `LoggerFactory.use({ provider })` selects the application's provider (default: `WinstonLogger`). The factory hands out stable delegating wrappers.
|
|
64
|
+
- **`use()` re-points every wrapper, even ones captured at import time.** The per-call cost after that: one property read (measured ~0ns).
|
|
65
|
+
- **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.
|
|
66
|
+
- **The winston default loads only when `use()` was never called first.** It requires the winston peers installed: `bun add winston winston-transport winston-daily-rotate-file`.
|
|
67
|
+
- **Compiled binaries (`bun build --compile`) must ALWAYS register a provider explicitly.** Only a class reference carries a provider into a bundle.
|
|
68
|
+
- **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
69
|
|
|
45
70
|
**Which names follow `use()`:**
|
|
46
71
|
|
|
@@ -79,6 +104,10 @@ import { HfLogger, HfLogFlusher } from '@venizia/ignis-helpers';
|
|
|
79
104
|
import { LogLevels, LoggerFormats } from '@venizia/ignis-helpers';
|
|
80
105
|
import type { TLogLevel, TLoggerFormat } from '@venizia/ignis-helpers';
|
|
81
106
|
|
|
107
|
+
// Error rendering - a readable block instead of a raw object dump
|
|
108
|
+
import { ErrorPrettier, formatLogMessage } from '@venizia/ignis-helpers';
|
|
109
|
+
import type { IErrorSummary } from '@venizia/ignis-helpers';
|
|
110
|
+
|
|
82
111
|
// Level resolution - provider-neutral
|
|
83
112
|
import { resolveLoggerLevel } from '@venizia/ignis-helpers';
|
|
84
113
|
|
|
@@ -152,7 +181,7 @@ const customWinstonLogger = defineCustomLogger({
|
|
|
152
181
|
const logger = Logger.get('MyService', customWinstonLogger);
|
|
153
182
|
```
|
|
154
183
|
|
|
155
|
-
A custom-backed `Logger` is a fresh wrapper on every call
|
|
184
|
+
A custom-backed `Logger` is a fresh wrapper on every call. A scope-keyed cache can't 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
185
|
|
|
157
186
|
### Logger caching
|
|
158
187
|
|
|
@@ -170,7 +199,9 @@ const custom2 = Logger.get('MyService', customWinstonLogger);
|
|
|
170
199
|
|
|
171
200
|
### ApplicationLogger - the provider-following facade
|
|
172
201
|
|
|
173
|
-
`ApplicationLogger` is "the APPLICATION's logger"
|
|
202
|
+
`ApplicationLogger` is "the APPLICATION's logger." `ApplicationLogger.get(scope)` always returns whatever provider `LoggerFactory.use()` registered - winston, unless the app registered something else. Its type is `ILogger`.
|
|
203
|
+
|
|
204
|
+
It is no longer a class alias of `WinstonLogger`. `instanceof ApplicationLogger` is now a compile error - use `instanceof AbstractLogger` to test any provider instance instead. The concrete winston alias still exists, named `Logger`.
|
|
174
205
|
|
|
175
206
|
```typescript
|
|
176
207
|
import { ApplicationLogger } from '@venizia/ignis-helpers';
|
|
@@ -178,7 +209,8 @@ import { ApplicationLogger } from '@venizia/ignis-helpers';
|
|
|
178
209
|
const logger = ApplicationLogger.get('MyService'); // ILogger, follows LoggerFactory.use()
|
|
179
210
|
```
|
|
180
211
|
|
|
181
|
-
|
|
212
|
+
> [!WARNING]
|
|
213
|
+
> The old scope-less `applicationLogger` instance was REMOVED. Use `ApplicationLogger.get('YourScope')` instead. An app that needs a raw winston instance builds one with `defineCustomLogger` (sub-path `/winston`).
|
|
182
214
|
|
|
183
215
|
## Log Levels
|
|
184
216
|
|
|
@@ -197,18 +229,30 @@ logger.log('warn', 'Threshold exceeded'); // Generic method for any level
|
|
|
197
229
|
|
|
198
230
|
### What each level means
|
|
199
231
|
|
|
200
|
-
|
|
232
|
+
Severity runs `emerg` > `error` > `warn` > `info` > `debug` in every provider. The level floor (`APP_ENV_LOGGER_LEVEL`) admits everything at or above its severity.
|
|
233
|
+
|
|
234
|
+
| Level | Meaning | Use it for |
|
|
235
|
+
|-------|---------|------------|
|
|
236
|
+
| `emerg` | The process is in a fatal state | Out-of-memory, unrecoverable corruption, imminent shutdown |
|
|
237
|
+
| `error` | An operation failed | Caught failures the line exists to diagnose - always pair an `Error` with `%s` |
|
|
238
|
+
| `warn` | Something is off but handled | Retries, fallbacks taken, deprecated usage |
|
|
239
|
+
| `info` | A business event happened | "Order created", lifecycle milestones, boot phases |
|
|
240
|
+
| `debug` | Developer diagnostics | Values and timings useful only while developing - ALSO gated by `DEBUG` env |
|
|
241
|
+
|
|
242
|
+
Each provider numbers those levels internally, and the numbers disagree. Read them only when debugging a provider, never as a cross-provider ranking.
|
|
243
|
+
|
|
244
|
+
| Provider | Numbering | `emerg` / `error` / `warn` / `info` / `debug` |
|
|
245
|
+
|---|---|---|
|
|
246
|
+
| Winston | lower is more severe | `0` / `0` / `1` / `2` / `3` |
|
|
247
|
+
| Pino | higher is more severe | `70` / `50` / `40` / `30` / `20` |
|
|
248
|
+
| HfLogger | higher is more severe | `4` / `3` / `2` / `1` / `0` |
|
|
201
249
|
|
|
202
|
-
|
|
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 |
|
|
250
|
+
Winston gives `emerg` and `error` the same number, so a winston transport cannot admit `emerg` while rejecting `error`.
|
|
209
251
|
|
|
210
252
|
> [!NOTE]
|
|
211
|
-
> Two gates apply to `debug
|
|
253
|
+
> Two gates apply to `debug`; only one applies to everything else. Every level passes the floor (`APP_ENV_LOGGER_LEVEL`, default `debug` - which admits all five levels). `debug()` also requires the `DEBUG` env gate.
|
|
254
|
+
>
|
|
255
|
+
> The vocabulary was deliberately trimmed to these five (2026-07-18). `alert`/`http`/`verbose`/`silly` had zero call sites and no consuming infrastructure. `http` may return as an access-line level if the request-correlation feature lands.
|
|
212
256
|
|
|
213
257
|
`LogLevels` defines all available levels and provides validation:
|
|
214
258
|
|
|
@@ -244,7 +288,7 @@ Lower numeric values have higher priority. `error` and `emerg` share priority `0
|
|
|
244
288
|
|
|
245
289
|
## Method-Scoped Logging
|
|
246
290
|
|
|
247
|
-
`.for()` creates a sub-scoped logger for a specific method
|
|
291
|
+
`.for()` creates a sub-scoped logger for a specific method. It appends the method name to the scope with a `-` separator, backed by the same provider instance as the parent. Default-backed results are cached.
|
|
248
292
|
|
|
249
293
|
```typescript
|
|
250
294
|
class UserService {
|
|
@@ -272,23 +316,25 @@ class UserService {
|
|
|
272
316
|
|
|
273
317
|
### Logging errors: `%s`, never `%j`
|
|
274
318
|
|
|
275
|
-
-
|
|
276
|
-
- **`%j`
|
|
319
|
+
- **`%s` routes an Error through `ErrorPrettier`**, which projects it down to identity, cause and frames.
|
|
320
|
+
- **`%j` keeps every enumerable own property**, so a `pg` error carries its whole query along and a `jose` error its whole payload. That projection is the reason the rule exists.
|
|
277
321
|
- **Always pair an `Error` argument with `%s`**; reserve `%j`/`%o` for plain data objects.
|
|
278
322
|
|
|
279
323
|
```typescript
|
|
280
324
|
// Good - %s prints message + stack
|
|
281
325
|
logger.error('Failed to create user: %s', error);
|
|
282
326
|
|
|
283
|
-
// Bad - %j
|
|
327
|
+
// Bad - %j dumps every own property the error happens to carry
|
|
284
328
|
logger.error('Failed to create user: %j', error);
|
|
285
329
|
```
|
|
286
330
|
|
|
287
|
-
|
|
331
|
+
`message` and `stack` are non-enumerable, so `JSON.stringify` alone would render an Error as `{}`. The formatter projects both in first, which makes a mistaken `%j` merely noisy rather than empty.
|
|
332
|
+
|
|
333
|
+
### Object inspection depth for `%s` and `%j`
|
|
288
334
|
|
|
289
|
-
- **Node hard-codes `depth: 0` for `%s`** in `util.format
|
|
290
|
-
- **`deepSplat` widens that depth.** The formatter (`formatLogMessage`) pre-inspects any object bound to a `%s` placeholder before handing the message to Winston
|
|
291
|
-
-
|
|
335
|
+
- **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.
|
|
336
|
+
- **`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.
|
|
337
|
+
- **`%j` is capped at the same depth**, and keeps JSON semantics. Below the cap it prints `"[Object]"`.
|
|
292
338
|
|
|
293
339
|
```typescript
|
|
294
340
|
logger.error('Failed: %s', error); // nested `error.cause` is now visible, not `[Object]`
|
|
@@ -302,6 +348,92 @@ APP_ENV_LOGGER_INSPECT_DEPTH=8
|
|
|
302
348
|
|
|
303
349
|
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
350
|
|
|
351
|
+
### `%j` projects the argument first
|
|
352
|
+
|
|
353
|
+
`JSON.stringify` renders the WHOLE argument as `[Circular]` when a single cycle sits anywhere inside it. One live handle in the payload - a transaction, a connector, a request context - therefore erased every other field:
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
logger.debug('Updating user | Args: %j', { id, data, transaction });
|
|
357
|
+
// Before: Updating user | Args: [Circular]
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
The formatter now projects a `%j` argument before `util.format` sees it. Three consequences:
|
|
361
|
+
|
|
362
|
+
| Concern | Behavior |
|
|
363
|
+
|---|---|
|
|
364
|
+
| Cycles | Collapse to `"[Circular]"` on the offending branch only; sibling fields survive |
|
|
365
|
+
| Secret-looking keys | Redacted, exactly as under `%s` |
|
|
366
|
+
| Depth | Capped by `APP_ENV_LOGGER_INSPECT_DEPTH`, so a live connector cannot flood one line |
|
|
367
|
+
|
|
368
|
+
A payload holding a transaction still prints its own fields, but the handle itself is noise. Keep live objects out of the logged arguments.
|
|
369
|
+
|
|
370
|
+
### ErrorPrettier - a readable block instead of an object dump
|
|
371
|
+
|
|
372
|
+
`Source ->` [`formatting/error-prettier.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/formatting/error-prettier.ts)
|
|
373
|
+
|
|
374
|
+
Widening the depth makes a nested `cause` visible, but it also prints everything else. A `pg`/`drizzle` failure carries the statement in `message`, in `stack` and in `query`, so one failure floods the log with the same SQL several times. `ErrorPrettier` projects the error down to what a reader needs, then renders it as a block.
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
import { ErrorPrettier } from '@venizia/ignis-helpers';
|
|
378
|
+
|
|
379
|
+
logger.error('Order recalculation failed | %s', ErrorPrettier.format({ error }));
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
- **Keeps** `name`, the full untruncated `message`, `code`, an `ApplicationError`'s `normalized.args` and `normalized.code`, the `pg` diagnostics (`hint`, `detail`, `table`, `constraint`), the root stack frames, and a flattened `cause` chain.
|
|
383
|
+
- **Drops** `query`, `params`, the stack header that repeats the message, and the `getError` frame that names no call site.
|
|
384
|
+
- **Returns a string**, so `%s` prints it verbatim and the message keeps its real newlines instead of `\n` escapes.
|
|
385
|
+
- **Bounded.** The `cause` chain is cut at 5 levels and is cycle-safe; frames stop at 10.
|
|
386
|
+
|
|
387
|
+
An `ApplicationError` message keeps its `%{placeholder}` tokens - i18n resolves them downstream, not here. So the block prints the values on their own `args:` line, right under the message:
|
|
388
|
+
|
|
389
|
+
```
|
|
390
|
+
- message: Field %{field} is fixed at creation and cannot be changed.
|
|
391
|
+
- args: { field: 'ticketType' }
|
|
392
|
+
- code: server.core.inventory.ticket.update.immutable_field
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Args come from the root error only, are redacted like `extra`, and an empty map prints no line.
|
|
396
|
+
|
|
397
|
+
#### `ErrorPrettier.format(opts)`
|
|
398
|
+
|
|
399
|
+
| Option | Type | Default | Meaning |
|
|
400
|
+
|---|---|---|---|
|
|
401
|
+
| `error` | `unknown` | - | The thrown value. A string or plain object works too |
|
|
402
|
+
| `messageCode` | `string` | - | Renders the `code:` line. Without it the error's own `normalized.code` is used |
|
|
403
|
+
| `extra` | `Record<string, unknown>` | - | Caller context. Redacted before printing |
|
|
404
|
+
| `includeStack` | `boolean` | `true` | Set `false` to drop frames entirely |
|
|
405
|
+
| `maxStackFrames` | `number` | `10` | Frame budget, forwarded to `summarize` |
|
|
406
|
+
| `format` | `TLoggerFormat` | `APP_ENV_LOGGER_FORMAT`, else `text` | `text` renders the block; `json` renders one line |
|
|
407
|
+
|
|
408
|
+
#### One line for a log monitor
|
|
409
|
+
|
|
410
|
+
A multi-line block becomes one record per line in Loki or CloudWatch, and the error loses its
|
|
411
|
+
context. Set `APP_ENV_LOGGER_FORMAT=json` and the same projection renders as a single line:
|
|
412
|
+
|
|
413
|
+
```json
|
|
414
|
+
{"message":"Field %{field} is fixed at creation and cannot be changed.","args":{"field":"ticketType"},"code":"server.core.inventory.ticket.update.immutable_field","stack":["at TicketService.update (...)"]}
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Absent fields are omitted rather than set to `null`. `stack` is an **array of frames** here, not the
|
|
418
|
+
newline-joined string `text` prints, so a monitor can count and slice it. `args` and `extra` are
|
|
419
|
+
redacted exactly as in `text`.
|
|
420
|
+
|
|
421
|
+
#### `ErrorPrettier.summarize(opts)`
|
|
422
|
+
|
|
423
|
+
Returns the same projection as a typed `IErrorSummary` object rather than a string - for a JSON sink or a log aggregator. `IErrorSummary.args` carries the root error's `normalized.args` unredacted; `format()` redacts on render.
|
|
424
|
+
|
|
425
|
+
`code` and `messageCode` are separate fields on purpose. `code` is the error's own - a driver's `23505`, a gRPC `14` - and renders inside the `name:` line. `messageCode` is an `ApplicationError`'s `normalized.code`, the identifier an application filters on, and renders on the `code:` line. `MessageCode.DEFAULT` never surfaces, since every codeless error carries it.
|
|
426
|
+
|
|
427
|
+
| Option | Type | Default | Meaning |
|
|
428
|
+
|---|---|---|---|
|
|
429
|
+
| `error` | `unknown` | - | The thrown value |
|
|
430
|
+
| `includeStack` | `boolean` | `true` | Skips frame extraction entirely when `false` |
|
|
431
|
+
| `maxCauseDepth` | `number` | `5` | Bounds a pathological or cyclic `cause` chain |
|
|
432
|
+
| `maxStackFrames` | `number` | `10` | The throw site is near the top; the tail is framework plumbing |
|
|
433
|
+
|
|
434
|
+
> [!TIP]
|
|
435
|
+
> `AppErrorMiddleware` already renders every thrown error this way. Reach for `ErrorPrettier` when you log an error yourself.
|
|
436
|
+
|
|
305
437
|
### Log formats
|
|
306
438
|
|
|
307
439
|
Output format is controlled by `APP_ENV_LOGGER_FORMAT` (default: `text`). `LoggerFormats` provides constants and validation:
|
|
@@ -330,7 +462,7 @@ const fmt: TLoggerFormat = 'text';
|
|
|
330
462
|
```
|
|
331
463
|
|
|
332
464
|
> [!NOTE]
|
|
333
|
-
> The label shown in log output (
|
|
465
|
+
> The label shown in log output (for example, `APP`) comes from `APP_ENV_APPLICATION_NAME` (defaults to `'APP'`). Set this env var to customize the label for your application.
|
|
334
466
|
|
|
335
467
|
### Custom formatters
|
|
336
468
|
|
|
@@ -353,19 +485,26 @@ const prettyFormatter = definePrettyLoggerFormatter({ label: 'my-app' });
|
|
|
353
485
|
const plainFormatter = definePrettyLoggerFormatter({ label: 'my-app', colorize: false });
|
|
354
486
|
```
|
|
355
487
|
|
|
488
|
+
Without an explicit `colorize`, `definePrettyLoggerFormatter` follows the [Color](#color) rules.
|
|
489
|
+
|
|
356
490
|
`defineLogFormatter` throws an `ApplicationError` if `format` (or `APP_ENV_LOGGER_FORMAT`) is not `'json'` or `'text'`.
|
|
357
491
|
|
|
358
492
|
## Transports
|
|
359
493
|
|
|
360
494
|
`Source ->` [`winston/define.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/winston/define.ts)
|
|
361
495
|
|
|
362
|
-
Every logger created by `defineCustomLogger` always includes a **Console** transport
|
|
496
|
+
Every logger created by `defineCustomLogger` always includes a **Console** transport. It inherits the logger-level floor (`APP_ENV_LOGGER_LEVEL`, default `debug`). File and UDP transports are optional, registered per transport group (`info`, `error`).
|
|
497
|
+
|
|
498
|
+
Formatting happens in two stages:
|
|
499
|
+
|
|
500
|
+
- a shared preparation format on the logger (label, timestamp, error normalization, deep splat)
|
|
501
|
+
- a per-transport assembly format
|
|
363
502
|
|
|
364
|
-
|
|
503
|
+
In `text` mode the console assembly colorizes; the file assembly does not - log FILES never carry 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
504
|
|
|
366
|
-
### File rotation
|
|
505
|
+
### File rotation transport
|
|
367
506
|
|
|
368
|
-
Configure
|
|
507
|
+
Winston implements this transport with `DailyRotateFile`. Configure it through environment variables, or programmatically via `IFileTransportOptions`.
|
|
369
508
|
|
|
370
509
|
**Environment variables:**
|
|
371
510
|
|
|
@@ -452,7 +591,9 @@ const transport = DgramTransport.fromPartial({
|
|
|
452
591
|
// Returns null if label, host, port, levels (non-empty), or socketOptions is missing
|
|
453
592
|
```
|
|
454
593
|
|
|
455
|
-
On a socket error the transport closes and nulls its client
|
|
594
|
+
On a socket error, the transport closes and nulls its client. The next `log()` call re-establishes the socket before sending.
|
|
595
|
+
|
|
596
|
+
A failed `send` is logged to the console, and the socket is dropped for reconnection. It's never re-emitted as an `'error'` event - so one lost UDP log line can never crash the process.
|
|
456
597
|
|
|
457
598
|
**Environment variables for the default application logger:**
|
|
458
599
|
|
|
@@ -485,6 +626,7 @@ interface ICustomLoggerOptions {
|
|
|
485
626
|
formatter?: ReturnType<typeof winston.format.combine>;
|
|
486
627
|
format?: TLoggerFormat; // 'json' | 'text'; defaults to APP_ENV_LOGGER_FORMAT
|
|
487
628
|
level?: TLogLevel; // logger-level floor; defaults to APP_ENV_LOGGER_LEVEL, then 'debug'
|
|
629
|
+
colorize?: boolean; // console ANSI color; defaults to the Color rules above
|
|
488
630
|
transports: {
|
|
489
631
|
info: {
|
|
490
632
|
file?: IFileTransportOptions;
|
|
@@ -498,7 +640,7 @@ interface ICustomLoggerOptions {
|
|
|
498
640
|
}
|
|
499
641
|
```
|
|
500
642
|
|
|
501
|
-
- **Both `info` and `error` transport groups support optional `file` (DailyRotateFile) and `dgram` (UDP)
|
|
643
|
+
- **Both `info` and `error` transport groups support two optional transports:** `file` (DailyRotateFile) and `dgram` (UDP).
|
|
502
644
|
- **A console transport is always included**, regardless of what is configured.
|
|
503
645
|
- **Error file transports double as Winston exception handlers.**
|
|
504
646
|
|
|
@@ -522,11 +664,15 @@ APP_ENV_EXTRA_LOG_ENVS=qa,preview # Comma-separated additional environments
|
|
|
522
664
|
> [!IMPORTANT]
|
|
523
665
|
> 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
666
|
|
|
525
|
-
## High-Frequency Logger
|
|
667
|
+
## High-Frequency Logger
|
|
526
668
|
|
|
527
669
|
`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
670
|
|
|
529
|
-
For performance-critical applications (
|
|
671
|
+
For performance-critical applications (for example, HFT systems or game servers), `HfLogger` provides ring-buffer logging. It measures a 59.4ns bytes-path enqueue and a 66.0ns string no-args enqueue (Bun 1.3.14, 1M-iteration median).
|
|
672
|
+
|
|
673
|
+
It extends `AbstractLogger` and implements `ILogger`, so it works anywhere an `ILogger` is expected. But it stays entirely separate from the Winston-backed `Logger` pipeline - no formatters, transports, or `APP_ENV_LOGGER_*` env vars apply to it.
|
|
674
|
+
|
|
675
|
+
Read the [HfLogger guide](/extensions/helpers/logger/hf-logger) before using it. It carries hard usage rules - a pre-encoded fixed message vocabulary, single-thread only, flush-interval sizing - and documented limitations.
|
|
530
676
|
|
|
531
677
|
```typescript
|
|
532
678
|
import { HfLogger, HfLogFlusher } from '@venizia/ignis-helpers';
|
|
@@ -559,9 +705,9 @@ flusher.stop();
|
|
|
559
705
|
| Method | Signature | Description |
|
|
560
706
|
|--------|-----------|--------------|
|
|
561
707
|
| `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
|
|
563
|
-
| `logger.debug/info/warn/error/emerg` | `(message: string, ...args: AnyType[]) => void` | `ILogger` methods. No args: cache-lookup encode
|
|
564
|
-
| `logger.log` | `(level: TLogLevel, message: string, ...args: AnyType[]) => void`<br>`(level: TLogLevel, messageBytes: Uint8Array) => void` | Overloaded
|
|
708
|
+
| `HfLogger.encodeMessage` | `(msg: string) => Uint8Array` | Pre-encode a message string to bytes. FIFO-bounded cache, capped at `MESSAGE_CACHE_CAP = 4096` |
|
|
709
|
+
| `logger.debug/info/warn/error/emerg` | `(message: string, ...args: AnyType[]) => void` | `ILogger` methods. No args: cache-lookup encode, then the bytes-path write. With args: `formatLogMessage` (deep inspection, redaction), then an uncached encode - the slow path |
|
|
710
|
+
| `logger.log` | `(level: TLogLevel, message: string, ...args: AnyType[]) => void`<br>`(level: TLogLevel, messageBytes: Uint8Array) => void` | Overloaded. The string form follows the `debug`/`info`/... cost model above. The `Uint8Array` form is the legacy bytes hot path, unchanged |
|
|
565
711
|
| `logger.for` | `(methodName: string) => ILogger` | Returns `HfLogger.get(`${scope}-${methodName}`)`, same dash composition as `BaseLogger` |
|
|
566
712
|
|
|
567
713
|
Supported levels (`TLogLevel`, full set): `debug` (0), `info` (1), `warn` (2), `error` (3), `emerg` (4).
|
|
@@ -570,12 +716,22 @@ Supported levels (`TLogLevel`, full set): `debug` (0), `info` (1), `warn` (2), `
|
|
|
570
716
|
|
|
571
717
|
| Method | Signature | Description |
|
|
572
718
|
|--------|-----------|--------------|
|
|
573
|
-
| `new HfLogFlusher` | `(options?: IHfLogFlusherOptions) => HfLogFlusher` |
|
|
574
|
-
| `flusher.flush` | `() => Promise<void>` | Drain the full backlog in bounded batches, yielding (`setImmediate`) between batches
|
|
575
|
-
| `flusher.start` | `(intervalMs?: number) => void` | Start a background `setInterval` flush loop (default `100`ms), unref'd so it never blocks process exit
|
|
719
|
+
| `new HfLogFlusher` | `(options?: IHfLogFlusherOptions) => HfLogFlusher` | Create a flusher; see `IHfLogFlusherOptions` below |
|
|
720
|
+
| `flusher.flush` | `() => Promise<void>` | Drain the full backlog in bounded batches, yielding (`setImmediate`) between batches. Re-entrant calls return the in-progress promise |
|
|
721
|
+
| `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
722
|
| `flusher.stop` | `() => void` | Clear the interval started by `start()` |
|
|
577
723
|
|
|
578
|
-
|
|
724
|
+
#### IHfLogFlusherOptions
|
|
725
|
+
|
|
726
|
+
| Option | Type | Default | Meaning |
|
|
727
|
+
|--------|------|---------|---------|
|
|
728
|
+
| `sink` | `THfSink` | the built-in stdout/file sink | Full custom delivery. Overrides `filePath` |
|
|
729
|
+
| `filePath` | `string` | _(unset)_ | The default sink appends here instead of writing to stdout |
|
|
730
|
+
| `batchSize` | `number` | `1024` | Entries rendered per batch before yielding. An invalid value falls back to the default with a `console.warn` |
|
|
731
|
+
|
|
732
|
+
A custom `sink` receives `THfSinkBatch`: `{ lines: Array<string>; dropped: number }`. `dropped` is the exact count of entries the ring overwrote before the flusher could read them, since the previous batch. See "Lap accounting" in the [HfLogger guide](/extensions/helpers/logger/hf-logger).
|
|
733
|
+
|
|
734
|
+
The default sink writes `process.stdout.write(...)` once per batch, or `fs.appendFileSync` once per batch when `filePath` is set. A sink that throws is logged via `console.error` and does not abort the drain.
|
|
579
735
|
|
|
580
736
|
### Line format
|
|
581
737
|
|
|
@@ -585,11 +741,17 @@ The default sink renders each entry as:
|
|
|
585
741
|
<ISO timestamp> [<level name>] <scope> <message>
|
|
586
742
|
```
|
|
587
743
|
|
|
588
|
-
For example: `2026-07-18T09:41:03.128Z [info] OrderEngine Order sent`.
|
|
744
|
+
For example: `2026-07-18T09:41:03.128Z [info] OrderEngine Order sent`.
|
|
745
|
+
|
|
746
|
+
When a batch has `dropped > 0`, the default sink emits a `warn` marker line ahead of it:
|
|
747
|
+
|
|
748
|
+
```
|
|
749
|
+
<ISO timestamp> [warn] HfLogFlusher ring lapped - <N> entries overwritten before they could be read
|
|
750
|
+
```
|
|
589
751
|
|
|
590
752
|
### Ring buffer entry format
|
|
591
753
|
|
|
592
|
-
Each entry occupies exactly 256 bytes
|
|
754
|
+
Each entry occupies exactly 256 bytes, inside a 64K-entry (16MB) `ArrayBuffer`. That buffer allocates lazily on the first `HfLogger.get()` call - not at module import - and is shared module-wide, not per-`HfLogger` instance:
|
|
593
755
|
|
|
594
756
|
| Offset | Size | Field |
|
|
595
757
|
|--------|------|-------|
|
|
@@ -600,10 +762,12 @@ Each entry occupies exactly 256 bytes in a 64K-entry (16MB) `ArrayBuffer`, alloc
|
|
|
600
762
|
| 42 | 1 byte | Message length (0-213) |
|
|
601
763
|
| 43-255 | 213 bytes | Message bytes |
|
|
602
764
|
|
|
603
|
-
The explicit length bytes are what make reads exact
|
|
765
|
+
The explicit length bytes are what make reads exact. The flusher decodes only the bytes a field actually holds. That leaves no NUL padding and no stale tail from a longer entry that used to occupy the slot.
|
|
766
|
+
|
|
767
|
+
The buffer wraps at 65,536 entries, using bitwise AND masking (`writeIndex & (BUFFER_SIZE - 1)`). When the producer writes faster than the flusher drains, unflushed entries get overwritten. That loss is never silent - the overwritten count is reported via `dropped` on the next sink batch.
|
|
604
768
|
|
|
605
769
|
> [!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
|
|
770
|
+
> 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. It can also evict other cached messages once the FIFO-bounded cache (4096 entries) fills.
|
|
607
771
|
|
|
608
772
|
## Environment Variables
|
|
609
773
|
|
|
@@ -616,10 +780,35 @@ The explicit length bytes are what make reads exact: the flusher decodes only th
|
|
|
616
780
|
| `NODE_ENV` | _(unset)_ | Must be in `COMMON_ENVS` or unset for debug to activate |
|
|
617
781
|
| `APP_ENV_EXTRA_LOG_ENVS` | _(empty)_ | Comma-separated additional environments to allow debug |
|
|
618
782
|
| `APP_ENV_LOGGER_FORMAT` | `text` | Output format (`json` or `text`) |
|
|
619
|
-
| `APP_ENV_LOGGER_LEVEL` | `debug` | Logger-level floor
|
|
620
|
-
| `APP_ENV_LOGGER_FOLDER_PATH` | _(unset)_ | Log files directory
|
|
621
|
-
| `APP_ENV_LOGGER_INSPECT_DEPTH` | `5` | Object inspection depth for `%s` placeholders. Non-negative integer only
|
|
622
|
-
| `APP_ENV_LOGGER_DO_REDACT` | `true` | Secret redaction in log arguments.
|
|
783
|
+
| `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 |
|
|
784
|
+
| `APP_ENV_LOGGER_FOLDER_PATH` | _(unset)_ | Log files directory. File logging is OFF when unset |
|
|
785
|
+
| `APP_ENV_LOGGER_INSPECT_DEPTH` | `5` | Object inspection depth for `%s` placeholders. Non-negative integer only - invalid or absent falls back to `5` |
|
|
786
|
+
| `APP_ENV_LOGGER_DO_REDACT` | `true` | Secret redaction in log arguments. See the warning below before touching this |
|
|
787
|
+
| `APP_ENV_LOGGER_COLOR` | _(unset)_ | ANSI color on console log lines. Unset means auto - see [Color](#color) |
|
|
788
|
+
|
|
789
|
+
> [!WARNING]
|
|
790
|
+
> Only the literal string `false` disables `APP_ENV_LOGGER_DO_REDACT`. Any other value - including unset - keeps redaction ON. Once disabled, raw secrets (passwords, tokens, connection URLs) reach the log sinks. Never disable this in production.
|
|
791
|
+
|
|
792
|
+
### Color
|
|
793
|
+
|
|
794
|
+
Color is a terminal affordance. In a deployed environment the same bytes land in a file or an aggregator as escape noise, so IGNIS turns color off outside a development `NODE_ENV`.
|
|
795
|
+
|
|
796
|
+
The first rule that matches wins:
|
|
797
|
+
|
|
798
|
+
| Rule | Result |
|
|
799
|
+
|------|--------|
|
|
800
|
+
| `APP_ENV_LOGGER_COLOR` is set | That value. `false` or `0` is off, anything else is on |
|
|
801
|
+
| `NO_COLOR` is set and non-empty | Off ([no-color.org](https://no-color.org)) |
|
|
802
|
+
| `NODE_ENV` is `local`, `debug`, `development`, `dev` or `sit` - or unset | On |
|
|
803
|
+
| Anything else, including `production`, `staging`, `uat` and unrecognized names | Off |
|
|
804
|
+
|
|
805
|
+
To keep color in a production terminal, set it back explicitly:
|
|
806
|
+
|
|
807
|
+
```bash
|
|
808
|
+
APP_ENV_LOGGER_COLOR=true
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
The file and UDP transports never colorize, in any environment. Under the pino provider the rule is a veto only: when it allows color, `pino-pretty` still suppresses it if stdout is not a terminal.
|
|
623
812
|
|
|
624
813
|
### File rotation
|
|
625
814
|
|
|
@@ -678,6 +867,8 @@ APP_ENV_LOGGER_DGRAM_LEVELS=error,warn,info
|
|
|
678
867
|
| `ILoggerProvider` | interface | Static-side contract a provider class satisfies (`get(scope): ILogger`) |
|
|
679
868
|
| `HfLogger` | class | `ILogger`-conformant ring-buffer logger for hot paths |
|
|
680
869
|
| `HfLogFlusher` | class | Background flusher for `HfLogger` |
|
|
870
|
+
| `ErrorPrettier` | class (statics) | `format({ error })` renders a thrown value as a readable block; `summarize({ error })` returns it as `IErrorSummary` |
|
|
871
|
+
| `IErrorSummary` | interface | The projection `summarize` returns - `name`, `message`, `code`, `stack` (frames only), the `pg` diagnostics, and a nested `cause` |
|
|
681
872
|
| `LogLevels` | class (constants) | Log level constants (`ERROR`, `EMERG`, `WARN`, `INFO`, `DEBUG`) with `isValid()` |
|
|
682
873
|
| `LoggerFormats` | class (constants) | Format constants (`JSON`, `TEXT`) with `isValid()` |
|
|
683
874
|
| `defineCustomLogger` | `(opts: ICustomLoggerOptions) => winston.Logger` | Create a fully configured Winston logger |
|
|
@@ -703,7 +894,7 @@ APP_ENV_LOGGER_DGRAM_LEVELS=error,warn,info
|
|
|
703
894
|
**Fix:**
|
|
704
895
|
1. Verify `DEBUG=true` is set in your environment.
|
|
705
896
|
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 (
|
|
897
|
+
3. If you use a custom environment name (for example, `qa`), add it to `APP_ENV_EXTRA_LOG_ENVS=qa`.
|
|
707
898
|
|
|
708
899
|
```bash
|
|
709
900
|
DEBUG=true NODE_ENV=development bun run server:dev
|
|
@@ -725,7 +916,7 @@ APP_ENV_LOGGER_FORMAT=text
|
|
|
725
916
|
|
|
726
917
|
**Fix:**
|
|
727
918
|
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 (
|
|
919
|
+
2. `APP_ENV_LOGGER_DGRAM_LEVELS` must contain at least one level (for example, `error,warn,info`). An empty value results in no transport.
|
|
729
920
|
3. Verify the UDP aggregator is reachable from your host (firewall, port binding).
|
|
730
921
|
|
|
731
922
|
### Log label shows "APP" instead of application name
|