@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.
Files changed (145) hide show
  1. package/content/best-practices/architectural-patterns.md +3 -3
  2. package/content/best-practices/code-style-standards/naming-conventions.md +1 -1
  3. package/content/best-practices/contribution-workflow.md +2 -2
  4. package/content/best-practices/error-handling.md +94 -89
  5. package/content/best-practices/security-guidelines.md +5 -5
  6. package/content/extensions/components/api-reference.md +22 -21
  7. package/content/extensions/components/authentication/api.md +64 -28
  8. package/content/extensions/components/authentication/errors.md +19 -5
  9. package/content/extensions/components/authentication/index.md +19 -18
  10. package/content/extensions/components/authentication/usage.md +55 -30
  11. package/content/extensions/components/authorization/api.md +351 -84
  12. package/content/extensions/components/authorization/errors.md +51 -17
  13. package/content/extensions/components/authorization/getting-started.md +227 -0
  14. package/content/extensions/components/authorization/index.md +24 -16
  15. package/content/extensions/components/authorization/usage.md +45 -21
  16. package/content/extensions/components/health-check.md +15 -9
  17. package/content/extensions/components/index.md +24 -90
  18. package/content/extensions/components/mail/api.md +105 -54
  19. package/content/extensions/components/mail/errors.md +12 -10
  20. package/content/extensions/components/mail/index.md +32 -13
  21. package/content/extensions/components/mail/usage.md +26 -18
  22. package/content/extensions/components/request-tracker.md +18 -14
  23. package/content/extensions/components/socket-io/api.md +377 -882
  24. package/content/extensions/components/socket-io/errors.md +49 -51
  25. package/content/extensions/components/socket-io/index.md +72 -88
  26. package/content/extensions/components/socket-io/usage.md +107 -117
  27. package/content/extensions/components/static-asset/api.md +83 -31
  28. package/content/extensions/components/static-asset/errors.md +18 -7
  29. package/content/extensions/components/static-asset/index.md +18 -11
  30. package/content/extensions/components/static-asset/usage.md +11 -6
  31. package/content/extensions/components/template/index.md +3 -3
  32. package/content/extensions/components/websocket/api.md +58 -27
  33. package/content/extensions/components/websocket/errors.md +3 -3
  34. package/content/extensions/components/websocket/index.md +8 -7
  35. package/content/extensions/components/websocket/usage.md +21 -8
  36. package/content/extensions/helpers/cron/index.md +8 -7
  37. package/content/extensions/helpers/crypto/index.md +16 -8
  38. package/content/extensions/helpers/crypto/reference.md +96 -24
  39. package/content/extensions/helpers/env/index.md +14 -10
  40. package/content/extensions/helpers/error/index.md +99 -23
  41. package/content/extensions/helpers/index.md +61 -47
  42. package/content/extensions/helpers/inversion/index.md +23 -6
  43. package/content/extensions/helpers/inversion/reference.md +30 -22
  44. package/content/extensions/helpers/kafka/admin.md +3 -2
  45. package/content/extensions/helpers/kafka/compile-binary.md +69 -44
  46. package/content/extensions/helpers/kafka/consumer.md +24 -21
  47. package/content/extensions/helpers/kafka/examples.md +22 -234
  48. package/content/extensions/helpers/kafka/index.md +30 -62
  49. package/content/extensions/helpers/kafka/producer.md +31 -26
  50. package/content/extensions/helpers/kafka/schema-registry.md +19 -14
  51. package/content/extensions/helpers/logger/hf-logger.md +49 -22
  52. package/content/extensions/helpers/logger/index.md +37 -12
  53. package/content/extensions/helpers/logger/pino.md +31 -11
  54. package/content/extensions/helpers/logger/reference.md +243 -52
  55. package/content/extensions/helpers/network/api.md +65 -30
  56. package/content/extensions/helpers/network/index.md +32 -11
  57. package/content/extensions/helpers/queue/index.md +17 -6
  58. package/content/extensions/helpers/queue/reference.md +52 -25
  59. package/content/extensions/helpers/redis/index.md +29 -11
  60. package/content/extensions/helpers/redis/reference.md +85 -28
  61. package/content/extensions/helpers/secrets/index.md +82 -12
  62. package/content/extensions/helpers/socket-io/api.md +40 -21
  63. package/content/extensions/helpers/socket-io/index.md +26 -10
  64. package/content/extensions/helpers/storage/api.md +42 -24
  65. package/content/extensions/helpers/storage/index.md +10 -7
  66. package/content/extensions/helpers/types/index.md +20 -7
  67. package/content/extensions/helpers/types/reference.md +53 -14
  68. package/content/extensions/helpers/uid/index.md +166 -10
  69. package/content/extensions/helpers/websocket/api.md +52 -13
  70. package/content/extensions/helpers/websocket/index.md +7 -4
  71. package/content/extensions/helpers/worker-thread/index.md +9 -5
  72. package/content/extensions/helpers/worker-thread/reference.md +20 -20
  73. package/content/extensions/index.md +38 -39
  74. package/content/extensions/src-details/mcp-server.md +96 -548
  75. package/content/guides/core-concepts/persistent/datasources.md +2 -2
  76. package/content/guides/core-concepts/persistent/index.md +5 -1
  77. package/content/guides/core-concepts/persistent/pglite.md +218 -0
  78. package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
  79. package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
  80. package/content/guides/core-concepts/persistent/search-typesense.md +26 -14
  81. package/content/guides/core-concepts/persistent/sqlite.md +296 -0
  82. package/content/guides/core-concepts/persistent/transactions.md +12 -11
  83. package/content/guides/get-started/5-minute-quickstart.md +59 -206
  84. package/content/guides/get-started/philosophy.md +135 -670
  85. package/content/guides/get-started/setup.md +53 -74
  86. package/content/guides/migrations/redis-helpers-migration.md +4 -3
  87. package/content/guides/migrations/scoped-rbac-migration.md +5 -5
  88. package/content/guides/migrations/unified-connectors-migration.md +7 -6
  89. package/content/guides/tutorials/realtime-chat.md +1 -1
  90. package/content/references/base/application.md +63 -21
  91. package/content/references/base/components.md +3 -3
  92. package/content/references/base/connectors.md +14 -14
  93. package/content/references/base/controllers.md +12 -12
  94. package/content/references/base/datasources-reference.md +32 -31
  95. package/content/references/base/datasources.md +6 -6
  96. package/content/references/base/dependency-injection.md +12 -11
  97. package/content/references/base/filter-system/application-usage.md +54 -30
  98. package/content/references/base/filter-system/array-operators.md +24 -46
  99. package/content/references/base/filter-system/comparison-operators.md +47 -67
  100. package/content/references/base/filter-system/default-filter.md +59 -53
  101. package/content/references/base/filter-system/fields-order-pagination.md +92 -146
  102. package/content/references/base/filter-system/index.md +25 -13
  103. package/content/references/base/filter-system/json-filtering.md +45 -184
  104. package/content/references/base/filter-system/list-operators.md +23 -53
  105. package/content/references/base/filter-system/logical-operators.md +63 -121
  106. package/content/references/base/filter-system/null-operators.md +34 -104
  107. package/content/references/base/filter-system/pattern-matching.md +40 -55
  108. package/content/references/base/filter-system/quick-reference.md +86 -198
  109. package/content/references/base/filter-system/range-operators.md +18 -46
  110. package/content/references/base/filter-system/tips.md +6 -6
  111. package/content/references/base/filter-system/use-cases.md +33 -15
  112. package/content/references/base/grpc-controllers.md +53 -17
  113. package/content/references/base/index.md +5 -3
  114. package/content/references/base/middlewares.md +11 -10
  115. package/content/references/base/models-reference.md +17 -17
  116. package/content/references/base/models.md +4 -3
  117. package/content/references/base/providers.md +8 -8
  118. package/content/references/base/repositories/advanced.md +224 -326
  119. package/content/references/base/repositories/index.md +19 -6
  120. package/content/references/base/repositories/mixins.md +5 -5
  121. package/content/references/base/repositories/relations.md +160 -293
  122. package/content/references/base/repositories/soft-deletable.md +16 -6
  123. package/content/references/base/secrets.md +17 -13
  124. package/content/references/base/services.md +6 -4
  125. package/content/references/configuration/environment-variables.md +31 -23
  126. package/content/references/configuration/index.md +6 -4
  127. package/content/references/index.md +1 -1
  128. package/content/references/utilities/duration.md +85 -0
  129. package/content/references/utilities/index.md +5 -1
  130. package/content/references/utilities/jsx-reference.md +11 -11
  131. package/content/references/utilities/jsx.md +2 -2
  132. package/content/references/utilities/module.md +78 -25
  133. package/content/references/utilities/request.md +2 -1
  134. package/content/references/utilities/retry.md +139 -0
  135. package/content/references/utilities/schema.md +2 -2
  136. package/content/references/utilities/statuses-reference.md +4 -4
  137. package/content/references/utilities/statuses.md +5 -5
  138. package/dist/mcp-server/index.js +0 -0
  139. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  140. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
  141. package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
  142. package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
  143. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
  144. package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
  145. 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. Where the built-in Winston provider gives colorized console, daily-rotating info/error files, and UDP shipping at ~1.2-1.6us per line, the pino provider emits newline-delimited JSON at ~0.5-0.6us per line (measured). Winston remains the DEFAULT; nothing changes for apps that never register pino.
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 (e.g. src/index.ts)
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, and exactly ONE provider is ever loaded: registering pino here means winston is never loaded (nor bundled).
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
- From that moment EVERY factory-issued logger runs on pino: `BaseHelper.logger` in every controller/service/repository/helper, `ApplicationLogger.get(...)`, and - thanks to swap-on-use delegation - even module-level `const logger = LoggerFactory.getLogger([...])` constants that were captured at import time, BEFORE this line ran. Import order does not matter; the factory re-points every wrapper it has ever issued when `use()` is called.
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` (colorized dev output) |
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
- - **Default (`APP_ENV_LOGGER_FORMAT=json`, or unset in production practice): NDJSON to stdout** - the k8s/docker collector pattern.
43
- - **`APP_ENV_LOGGER_FORMAT=text`**: pretty colorized lines via a `pino-pretty` worker-thread transport - dev only.
44
- - **`APP_ENV_LOGGER_FOLDER_PATH` set**: writes to a rotating file via `pino-roll`, honoring the SAME env vars winston uses:
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; `emerg` is the single custom pino level (above `error`), and the default `debug` floor admits every level - identical to the winston provider.
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: the `[Scope] ` message prefix, args formatting through `formatLogMessage` (deep inspection + secret REDACTION - a `token` field renders `[REDACTED]` on pino exactly as on winston), the level vocabulary and floor semantics, the `DEBUG` gate on `debug()`.
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 (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.
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** (with `winston-daily-rotate-file` for file rotation). All provider packages are OPTIONAL peers - an application loads exactly ONE provider (single-provider loading, below).
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
- ## Architecture & ILogger
28
+ ## Find what you need
29
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.
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 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.
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 - 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.
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": `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`.
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
- 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`).
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
- 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.
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
- | 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 |
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`, 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.
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, appending the method name to the scope with a `-` separator, backed by the same provider instance as the parent. Default-backed results are cached.
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
- - **`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.
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 drops message and stack (non-enumerable on Error)
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
- ### Object inspection depth for `%s`
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` - 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.
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 (e.g. `APP`) comes from `APP_ENV_APPLICATION_NAME` (defaults to `'APP'`). Set this env var to customize the label for your application.
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, 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`).
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
- 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.
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 (DailyRotateFile)
505
+ ### File rotation transport
367
506
 
368
- Configure file rotation through environment variables or programmatically via `IFileTransportOptions`.
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; 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.
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) transports.**
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 (HfLogger)
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 (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.
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; 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 |
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` | `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 |
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
- `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.
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`. 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`.
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 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):
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: 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.
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 and can evict other cached messages once the FIFO-bounded cache (4096 entries) fills.
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; 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 |
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 (e.g. `qa`), add it to `APP_ENV_EXTRA_LOG_ENVS=qa`.
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 (e.g. `error,warn,info`). An empty value results in no transport.
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