@venizia/ignis-docs 0.2.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 (174) hide show
  1. package/README.md +14 -14
  2. package/content/best-practices/api-usage-examples.md +39 -19
  3. package/content/best-practices/architectural-patterns.md +24 -13
  4. package/content/best-practices/architecture-decisions.md +29 -16
  5. package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
  6. package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
  7. package/content/best-practices/code-style-standards/control-flow.md +33 -1
  8. package/content/best-practices/code-style-standards/documentation.md +28 -8
  9. package/content/best-practices/code-style-standards/function-patterns.md +15 -4
  10. package/content/best-practices/code-style-standards/index.md +7 -2
  11. package/content/best-practices/code-style-standards/naming-conventions.md +27 -3
  12. package/content/best-practices/code-style-standards/route-definitions.md +9 -5
  13. package/content/best-practices/code-style-standards/tooling.md +14 -1
  14. package/content/best-practices/code-style-standards/type-safety.md +39 -6
  15. package/content/best-practices/common-pitfalls.md +59 -35
  16. package/content/best-practices/contribution-workflow.md +8 -4
  17. package/content/best-practices/data-modeling.md +78 -62
  18. package/content/best-practices/deployment-strategies.md +56 -19
  19. package/content/best-practices/error-handling.md +247 -153
  20. package/content/best-practices/index.md +6 -0
  21. package/content/best-practices/performance-optimization.md +26 -13
  22. package/content/best-practices/security-guidelines.md +35 -9
  23. package/content/best-practices/testing-strategies.md +7 -2
  24. package/content/best-practices/troubleshooting-tips.md +27 -17
  25. package/content/extensions/components/api-reference.md +109 -323
  26. package/content/extensions/components/authentication/api.md +489 -602
  27. package/content/extensions/components/authentication/errors.md +135 -498
  28. package/content/extensions/components/authentication/index.md +89 -801
  29. package/content/extensions/components/authentication/usage.md +231 -955
  30. package/content/extensions/components/authorization/api.md +991 -644
  31. package/content/extensions/components/authorization/errors.md +204 -208
  32. package/content/extensions/components/authorization/getting-started.md +227 -0
  33. package/content/extensions/components/authorization/index.md +88 -795
  34. package/content/extensions/components/authorization/usage.md +219 -528
  35. package/content/extensions/components/health-check.md +77 -243
  36. package/content/extensions/components/index.md +24 -90
  37. package/content/extensions/components/mail/api.md +553 -285
  38. package/content/extensions/components/mail/errors.md +76 -62
  39. package/content/extensions/components/mail/index.md +111 -463
  40. package/content/extensions/components/mail/usage.md +139 -173
  41. package/content/extensions/components/request-tracker.md +70 -173
  42. package/content/extensions/components/socket-io/api.md +462 -785
  43. package/content/extensions/components/socket-io/errors.md +49 -51
  44. package/content/extensions/components/socket-io/index.md +69 -372
  45. package/content/extensions/components/socket-io/usage.md +212 -105
  46. package/content/extensions/components/static-asset/api.md +461 -141
  47. package/content/extensions/components/static-asset/errors.md +121 -53
  48. package/content/extensions/components/static-asset/index.md +84 -606
  49. package/content/extensions/components/static-asset/usage.md +184 -299
  50. package/content/extensions/components/template/index.md +3 -3
  51. package/content/extensions/components/websocket/api.md +311 -404
  52. package/content/extensions/components/websocket/errors.md +47 -56
  53. package/content/extensions/components/websocket/index.md +75 -407
  54. package/content/extensions/components/websocket/usage.md +120 -338
  55. package/content/extensions/helpers/cron/index.md +52 -160
  56. package/content/extensions/helpers/crypto/index.md +67 -480
  57. package/content/extensions/helpers/crypto/reference.md +528 -0
  58. package/content/extensions/helpers/env/index.md +64 -178
  59. package/content/extensions/helpers/error/index.md +286 -196
  60. package/content/extensions/helpers/index.md +61 -47
  61. package/content/extensions/helpers/inversion/index.md +76 -550
  62. package/content/extensions/helpers/inversion/reference.md +530 -0
  63. package/content/extensions/helpers/kafka/admin.md +23 -3
  64. package/content/extensions/helpers/kafka/compile-binary.md +83 -52
  65. package/content/extensions/helpers/kafka/consumer.md +65 -28
  66. package/content/extensions/helpers/kafka/examples.md +46 -253
  67. package/content/extensions/helpers/kafka/index.md +55 -617
  68. package/content/extensions/helpers/kafka/producer.md +156 -22
  69. package/content/extensions/helpers/kafka/schema-registry.md +59 -79
  70. package/content/extensions/helpers/logger/hf-logger.md +220 -0
  71. package/content/extensions/helpers/logger/index.md +78 -552
  72. package/content/extensions/helpers/logger/pino.md +105 -0
  73. package/content/extensions/helpers/logger/reference.md +937 -0
  74. package/content/extensions/helpers/network/api.md +276 -195
  75. package/content/extensions/helpers/network/index.md +87 -524
  76. package/content/extensions/helpers/queue/index.md +80 -897
  77. package/content/extensions/helpers/queue/reference.md +494 -0
  78. package/content/extensions/helpers/redis/index.md +86 -640
  79. package/content/extensions/helpers/redis/reference.md +784 -0
  80. package/content/extensions/helpers/secrets/index.md +136 -0
  81. package/content/extensions/helpers/socket-io/api.md +325 -204
  82. package/content/extensions/helpers/socket-io/index.md +79 -429
  83. package/content/extensions/helpers/storage/api.md +584 -464
  84. package/content/extensions/helpers/storage/index.md +80 -573
  85. package/content/extensions/helpers/types/index.md +75 -495
  86. package/content/extensions/helpers/types/reference.md +689 -0
  87. package/content/extensions/helpers/uid/index.md +176 -189
  88. package/content/extensions/helpers/websocket/api.md +366 -214
  89. package/content/extensions/helpers/websocket/index.md +68 -503
  90. package/content/extensions/helpers/worker-thread/index.md +62 -396
  91. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  92. package/content/extensions/index.md +38 -39
  93. package/content/extensions/src-details/mcp-server.md +96 -548
  94. package/content/guides/core-concepts/persistent/datasources.md +2 -2
  95. package/content/guides/core-concepts/persistent/index.md +5 -1
  96. package/content/guides/core-concepts/persistent/models.md +1 -1
  97. package/content/guides/core-concepts/persistent/pglite.md +218 -0
  98. package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
  99. package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
  100. package/content/guides/core-concepts/persistent/search-typesense.md +28 -16
  101. package/content/guides/core-concepts/persistent/sqlite.md +296 -0
  102. package/content/guides/core-concepts/persistent/transactions.md +12 -11
  103. package/content/guides/core-concepts/secrets-vault.md +177 -0
  104. package/content/guides/core-concepts/services.md +1 -1
  105. package/content/guides/get-started/5-minute-quickstart.md +59 -206
  106. package/content/guides/get-started/philosophy.md +135 -670
  107. package/content/guides/get-started/setup.md +53 -74
  108. package/content/guides/migrations/redis-helpers-migration.md +5 -4
  109. package/content/guides/migrations/scoped-rbac-migration.md +5 -5
  110. package/content/guides/migrations/unified-connectors-migration.md +8 -7
  111. package/content/guides/tutorials/ecommerce-api.md +3 -8
  112. package/content/guides/tutorials/realtime-chat.md +1 -1
  113. package/content/references/base/application.md +64 -22
  114. package/content/references/base/components.md +3 -3
  115. package/content/references/base/connectors.md +79 -136
  116. package/content/references/base/controllers.md +12 -12
  117. package/content/references/base/datasources-reference.md +600 -0
  118. package/content/references/base/datasources.md +84 -444
  119. package/content/references/base/dependency-injection.md +25 -46
  120. package/content/references/base/filter-system/application-usage.md +95 -123
  121. package/content/references/base/filter-system/array-operators.md +33 -43
  122. package/content/references/base/filter-system/comparison-operators.md +55 -63
  123. package/content/references/base/filter-system/default-filter.md +144 -350
  124. package/content/references/base/filter-system/fields-order-pagination.md +116 -148
  125. package/content/references/base/filter-system/index.md +114 -253
  126. package/content/references/base/filter-system/json-filtering.md +52 -181
  127. package/content/references/base/filter-system/list-operators.md +28 -44
  128. package/content/references/base/filter-system/logical-operators.md +69 -114
  129. package/content/references/base/filter-system/null-operators.md +41 -98
  130. package/content/references/base/filter-system/pattern-matching.md +48 -51
  131. package/content/references/base/filter-system/quick-reference.md +93 -196
  132. package/content/references/base/filter-system/range-operators.md +22 -38
  133. package/content/references/base/filter-system/tips.md +70 -133
  134. package/content/references/base/filter-system/use-cases.md +173 -232
  135. package/content/references/base/grpc-controllers.md +53 -17
  136. package/content/references/base/index.md +5 -3
  137. package/content/references/base/middlewares.md +43 -28
  138. package/content/references/base/models-reference.md +886 -0
  139. package/content/references/base/models.md +81 -1452
  140. package/content/references/base/providers.md +8 -8
  141. package/content/references/base/repositories/advanced.md +281 -419
  142. package/content/references/base/repositories/index.md +84 -644
  143. package/content/references/base/repositories/mixins.md +25 -21
  144. package/content/references/base/repositories/relations.md +194 -375
  145. package/content/references/base/repositories/soft-deletable.md +67 -55
  146. package/content/references/base/secrets.md +267 -0
  147. package/content/references/base/services.md +8 -6
  148. package/content/references/configuration/environment-variables.md +73 -21
  149. package/content/references/configuration/index.md +51 -31
  150. package/content/references/index.md +1 -1
  151. package/content/references/quick-reference.md +3 -16
  152. package/content/references/utilities/crypto.md +35 -76
  153. package/content/references/utilities/date.md +33 -73
  154. package/content/references/utilities/duration.md +85 -0
  155. package/content/references/utilities/index.md +6 -2
  156. package/content/references/utilities/jsx-reference.md +298 -0
  157. package/content/references/utilities/jsx.md +82 -525
  158. package/content/references/utilities/module.md +81 -61
  159. package/content/references/utilities/parse.md +34 -64
  160. package/content/references/utilities/performance.md +33 -58
  161. package/content/references/utilities/promise.md +28 -62
  162. package/content/references/utilities/request.md +58 -218
  163. package/content/references/utilities/retry.md +139 -0
  164. package/content/references/utilities/schema.md +43 -137
  165. package/content/references/utilities/statuses-reference.md +361 -0
  166. package/content/references/utilities/statuses.md +63 -667
  167. package/dist/mcp-server/index.js +0 -0
  168. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  169. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
  170. package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
  171. package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
  172. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
  173. package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
  174. package/package.json +24 -23
@@ -0,0 +1,937 @@
1
+ ---
2
+ title: Logger - Full Reference
3
+ description: Complete reference for Logger, LoggerFactory, HfLogger, transports, formatters, and every APP_ENV_LOGGER_* environment variable
4
+ difficulty: intermediate
5
+ ---
6
+
7
+ # Logger - Full Reference
8
+
9
+ Exhaustive reference for `Logger`, `LoggerFactory`, `HfLogger`/`HfLogFlusher`, the Winston formatter and transport internals, and every environment variable. For a readable introduction and the common tasks, start with the [Logger overview](/extensions/helpers/logger/).
10
+
11
+ The default provider is **Winston**, 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
+
13
+ **Files:**
14
+
15
+ - [`packages/helpers/src/modules/logger/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/common/types.ts) - `ILogger`, `TLogLevel`, `TLoggerFormat`
16
+ - [`packages/helpers/src/modules/logger/common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/common/constants.ts) - `LogLevels`, `LoggerFormats`
17
+ - [`packages/helpers/src/modules/logger/base/abstract.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/base/abstract.ts) - `AbstractLogger`
18
+ - [`packages/helpers/src/modules/logger/base/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/base/base.ts) - `BaseLogger`
19
+ - [`packages/helpers/src/modules/logger/formatting/deep-splat.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/formatting/deep-splat.ts) - `formatLogMessage`, `%s` inspection widening
20
+ - [`packages/helpers/src/modules/logger/winston/logger.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/winston/logger.ts) - `WinstonLogger`, `Logger` alias
21
+ - [`packages/helpers/src/modules/logger/winston/define.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/winston/define.ts) - `defineCustomLogger`, formatters
22
+ - [`packages/helpers/src/modules/logger/winston/formatters/deep-splat.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/winston/formatters/deep-splat.ts) - `deepSplat`
23
+ - [`packages/helpers/src/modules/logger/winston/transports/dgram.transport.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/winston/transports/dgram.transport.ts) - `DgramTransport`
24
+ - [`packages/helpers/src/modules/logger/hf/logger.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/hf/logger.ts) - `HfLogger`
25
+ - [`packages/helpers/src/modules/logger/hf/flusher.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/hf/flusher.ts) - `HfLogFlusher`
26
+ - [`packages/helpers/src/modules/logger/factory.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/factory.ts) - `LoggerFactory`, `ApplicationLogger`
27
+
28
+ ## Find what you need
29
+
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
53
+
54
+ ```
55
+ ILogger (interface) common/types.ts
56
+ └─ AbstractLogger (abstract) base/abstract.ts - the contract as a class, for `instanceof`
57
+ └─ BaseLogger (abstract) base/base.ts - scope, prefix, DEBUG gate, .for(), one write() sink
58
+ ├─ WinstonLogger winston/logger.ts - the built-in provider (default)
59
+ └─ PinoLogger pino/logger.ts - sub-path @venizia/ignis-helpers/pino
60
+ ```
61
+
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.
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.
69
+
70
+ **Which names follow `use()`:**
71
+
72
+ | Name | Is | Follows `use()`? |
73
+ |---|---|---|
74
+ | `LoggerFactory.getLogger()` / `BaseHelper.logger` | delegating wrapper | YES |
75
+ | `ApplicationLogger.get()` | facade over the factory; type = `ILogger` | YES |
76
+ | `Logger` / `Logger.get(scope, customWinston?)` | concrete `WinstonLogger` (instanceof, custom winston instances) | NO - names winston deliberately |
77
+ | `WinstonLogger` / `PinoLogger` | concrete providers | are the targets |
78
+
79
+ ## Quick Reference
80
+
81
+ | Class | Extends | Use Case |
82
+ |-------|---------|----------|
83
+ | `Logger` | `BaseLogger` -> `AbstractLogger` (`ILogger`) | General-purpose scoped logger with caching (permanent alias of `WinstonLogger`) |
84
+ | `LoggerFactory` | - | Provider registration (`use`) + `ILogger` acquisition from scope arrays |
85
+ | `PinoLogger` | `BaseLogger` (`ILogger`) | Throughput provider - NDJSON, sub-path only ([guide](/extensions/helpers/logger/pino)) |
86
+ | `HfLogger` | `AbstractLogger` (`ILogger`) | Ring-buffer logger for hot paths - bytes path ~59ns, string no-args path ~66ns |
87
+ | `HfLogFlusher` | - | Background flusher for `HfLogger` entries |
88
+ | `DgramTransport` | `winston-transport.Transport` | Custom Winston transport that sends logs over UDP |
89
+
90
+ ### Import paths
91
+
92
+ ```typescript
93
+ // Core classes - provider-neutral, root barrel
94
+ import { LoggerFactory, ApplicationLogger } from '@venizia/ignis-helpers';
95
+ import type { ILogger } from '@venizia/ignis-helpers';
96
+
97
+ // Abstract tiers - implementing ILogger yourself, or instanceof checks
98
+ import { AbstractLogger, BaseLogger } from '@venizia/ignis-helpers';
99
+
100
+ // High-frequency logger
101
+ import { HfLogger, HfLogFlusher } from '@venizia/ignis-helpers';
102
+
103
+ // Constants & types
104
+ import { LogLevels, LoggerFormats } from '@venizia/ignis-helpers';
105
+ import type { TLogLevel, TLoggerFormat } from '@venizia/ignis-helpers';
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
+
111
+ // Level resolution - provider-neutral
112
+ import { resolveLoggerLevel } from '@venizia/ignis-helpers';
113
+
114
+ // Winston provider + its utilities - SUB-PATH only (winston is an optional peer)
115
+ import {
116
+ Logger,
117
+ WinstonLogger,
118
+ defineCustomLogger,
119
+ defineLogFormatter,
120
+ defineJsonLoggerFormatter,
121
+ definePrettyLoggerFormatter,
122
+ applicationLogFormatter,
123
+ resolveDefaultTransportOptions,
124
+ } from '@venizia/ignis-helpers/winston';
125
+ import type { IFileTransportOptions, ICustomLoggerOptions } from '@venizia/ignis-helpers/winston';
126
+
127
+ // Pino provider - SUB-PATH only (optional peers: pino, pino-pretty, pino-roll)
128
+ import { PinoLogger, setPinoBackingLogger } from '@venizia/ignis-helpers/pino';
129
+ import type { ILoggerProvider } from '@venizia/ignis-helpers';
130
+
131
+ // UDP transport (winston)
132
+ import { DgramTransport } from '@venizia/ignis-helpers/winston';
133
+ import type { IDgramTransportOptions } from '@venizia/ignis-helpers/winston';
134
+ ```
135
+
136
+ The root barrel is provider-free: importing `@venizia/ignis-helpers` loads NO provider. Winston names resolve only through `@venizia/ignis-helpers/winston`, pino names only through `@venizia/ignis-helpers/pino`.
137
+
138
+ ## Creating an Instance
139
+
140
+ `Source ->` [`winston/logger.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/winston/logger.ts), [`factory.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/factory.ts)
141
+
142
+ ### Using LoggerFactory (recommended)
143
+
144
+ `LoggerFactory.getLogger` accepts an array of scope strings, joins them with `-`, and returns a cached provider-following wrapper (see the name/role table above).
145
+
146
+ ```typescript
147
+ import { LoggerFactory } from '@venizia/ignis-helpers';
148
+
149
+ const logger = LoggerFactory.getLogger(['MyService']);
150
+ logger.info('Service initialized');
151
+ // Output: [MyService] Service initialized
152
+
153
+ const scopedLogger = LoggerFactory.getLogger(['Payment', 'Stripe']);
154
+ scopedLogger.info('Charge created');
155
+ // Output: [Payment-Stripe] Charge created
156
+ ```
157
+
158
+ ### Using Logger.get() directly
159
+
160
+ ```typescript
161
+ import { Logger } from '@venizia/ignis-helpers/winston';
162
+
163
+ const logger = Logger.get('MyService');
164
+ logger.info('Direct logger access');
165
+ // Output: [MyService] Direct logger access
166
+ ```
167
+
168
+ Pass a custom Winston logger instance as the second parameter to use your own transport configuration:
169
+
170
+ ```typescript
171
+ import { Logger, defineCustomLogger, applicationLogFormatter } from '@venizia/ignis-helpers/winston';
172
+
173
+ const customWinstonLogger = defineCustomLogger({
174
+ formatter: applicationLogFormatter,
175
+ transports: {
176
+ info: { file: { prefix: 'custom', folder: './logs' } },
177
+ error: { file: { prefix: 'custom-error', folder: './logs' } },
178
+ },
179
+ });
180
+
181
+ const logger = Logger.get('MyService', customWinstonLogger);
182
+ ```
183
+
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.
185
+
186
+ ### Logger caching
187
+
188
+ Without a custom logger, both `Logger.get` and `LoggerFactory.getLogger` cache internally - the same scope always returns the same `Logger` instance:
189
+
190
+ ```typescript
191
+ const logger1 = Logger.get('MyService');
192
+ const logger2 = Logger.get('MyService');
193
+ // logger1 === logger2 (same instance)
194
+
195
+ const custom1 = Logger.get('MyService', customWinstonLogger);
196
+ const custom2 = Logger.get('MyService', customWinstonLogger);
197
+ // custom1 !== custom2 (fresh wrapper each call, same backing Winston instance)
198
+ ```
199
+
200
+ ### ApplicationLogger - the provider-following facade
201
+
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`.
205
+
206
+ ```typescript
207
+ import { ApplicationLogger } from '@venizia/ignis-helpers';
208
+
209
+ const logger = ApplicationLogger.get('MyService'); // ILogger, follows LoggerFactory.use()
210
+ ```
211
+
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`).
214
+
215
+ ## Log Levels
216
+
217
+ `Source ->` [`common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/common/constants.ts), [`base/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/base/base.ts), [`winston/define.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/winston/define.ts)
218
+
219
+ Five levels, each with a direct method on `ILogger`: `debug`, `info`, `warn`, `error`, `emerg`. The generic `log(level, ...)` remains for dynamic level selection.
220
+
221
+ ```typescript
222
+ logger.info('User created');
223
+ logger.warn('Rate limit approaching');
224
+ logger.error('Failed to process payment');
225
+ logger.emerg('System out of memory');
226
+ logger.debug('Query took 12ms'); // Requires DEBUG=true
227
+ logger.log('warn', 'Threshold exceeded'); // Generic method for any level
228
+ ```
229
+
230
+ ### What each level means
231
+
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` |
249
+
250
+ Winston gives `emerg` and `error` the same number, so a winston transport cannot admit `emerg` while rejecting `error`.
251
+
252
+ > [!NOTE]
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.
256
+
257
+ `LogLevels` defines all available levels and provides validation:
258
+
259
+ ```typescript
260
+ import { LogLevels } from '@venizia/ignis-helpers';
261
+ import type { TLogLevel } from '@venizia/ignis-helpers';
262
+
263
+ LogLevels.ERROR; // 'error'
264
+ LogLevels.EMERG; // 'emerg'
265
+ LogLevels.WARN; // 'warn'
266
+ LogLevels.INFO; // 'info'
267
+ LogLevels.DEBUG; // 'debug'
268
+
269
+ LogLevels.isValid('info'); // true
270
+ LogLevels.isValid('unknown'); // false
271
+
272
+ const level: TLogLevel = 'info';
273
+ ```
274
+
275
+ ### Winston level priority
276
+
277
+ `defineCustomLogger` configures Winston with these numeric priorities by default:
278
+
279
+ | Level | Priority | Color |
280
+ |-------|----------|-------|
281
+ | `error` | 0 | red |
282
+ | `emerg` | 0 | red |
283
+ | `warn` | 1 | yellow |
284
+ | `info` | 2 | green |
285
+ | `debug` | 3 | blue |
286
+
287
+ Lower numeric values have higher priority. `error` and `emerg` share priority `0`.
288
+
289
+ ## Method-Scoped Logging
290
+
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.
292
+
293
+ ```typescript
294
+ class UserService {
295
+ private logger = LoggerFactory.getLogger(['UserService']);
296
+
297
+ async createUser(data: CreateUserDto) {
298
+ this.logger.for('createUser').info('Creating user: %j', data);
299
+ // Output: [UserService-createUser] Creating user: {...}
300
+
301
+ try {
302
+ const user = await this.userRepo.create({ data });
303
+ this.logger.for('createUser').info('User created: %s', user.id);
304
+ return user;
305
+ } catch (error) {
306
+ this.logger.for('createUser').error('Failed to create user: %s', error);
307
+ throw error;
308
+ }
309
+ }
310
+ }
311
+ ```
312
+
313
+ ## Message Formatting
314
+
315
+ `Source ->` [`winston/formatters/deep-splat.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/winston/formatters/deep-splat.ts), [`formatting/deep-splat.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/formatting/deep-splat.ts)
316
+
317
+ ### Logging errors: `%s`, never `%j`
318
+
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.
321
+ - **Always pair an `Error` argument with `%s`**; reserve `%j`/`%o` for plain data objects.
322
+
323
+ ```typescript
324
+ // Good - %s prints message + stack
325
+ logger.error('Failed to create user: %s', error);
326
+
327
+ // Bad - %j dumps every own property the error happens to carry
328
+ logger.error('Failed to create user: %j', error);
329
+ ```
330
+
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`
334
+
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]"`.
338
+
339
+ ```typescript
340
+ logger.error('Failed: %s', error); // nested `error.cause` is now visible, not `[Object]`
341
+ ```
342
+
343
+ The inspection depth defaults to `5` and is configurable via `APP_ENV_LOGGER_INSPECT_DEPTH`:
344
+
345
+ ```bash
346
+ APP_ENV_LOGGER_INSPECT_DEPTH=8
347
+ ```
348
+
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.
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
+
437
+ ### Log formats
438
+
439
+ Output format is controlled by `APP_ENV_LOGGER_FORMAT` (default: `text`). `LoggerFormats` provides constants and validation:
440
+
441
+ ```typescript
442
+ import { LoggerFormats } from '@venizia/ignis-helpers';
443
+ import type { TLoggerFormat } from '@venizia/ignis-helpers';
444
+
445
+ LoggerFormats.JSON; // 'json'
446
+ LoggerFormats.TEXT; // 'text'
447
+ LoggerFormats.isValid('json'); // true
448
+
449
+ const fmt: TLoggerFormat = 'text';
450
+ ```
451
+
452
+ **JSON format** (`APP_ENV_LOGGER_FORMAT=json`):
453
+
454
+ ```json
455
+ {"level":"info","message":"[UserService] User created","timestamp":"2024-01-11T10:30:00.000Z","label":"APP"}
456
+ ```
457
+
458
+ **Pretty text format** (`APP_ENV_LOGGER_FORMAT=text`, default):
459
+
460
+ ```
461
+ 2024-01-11T10:30:00.000Z [APP] info: [UserService] User created
462
+ ```
463
+
464
+ > [!NOTE]
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.
466
+
467
+ ### Custom formatters
468
+
469
+ Build formatters directly using the exported helper functions:
470
+
471
+ ```typescript
472
+ import {
473
+ defineLogFormatter,
474
+ defineJsonLoggerFormatter,
475
+ definePrettyLoggerFormatter,
476
+ } from '@venizia/ignis-helpers';
477
+
478
+ // Auto-detect from APP_ENV_LOGGER_FORMAT (or override with format option)
479
+ const formatter = defineLogFormatter({ label: 'my-app' });
480
+ const jsonFmt = defineLogFormatter({ label: 'my-app', format: 'json' });
481
+
482
+ // Or use specific formatters directly
483
+ const jsonFormatter = defineJsonLoggerFormatter({ label: 'my-app' });
484
+ const prettyFormatter = definePrettyLoggerFormatter({ label: 'my-app' });
485
+ const plainFormatter = definePrettyLoggerFormatter({ label: 'my-app', colorize: false });
486
+ ```
487
+
488
+ Without an explicit `colorize`, `definePrettyLoggerFormatter` follows the [Color](#color) rules.
489
+
490
+ `defineLogFormatter` throws an `ApplicationError` if `format` (or `APP_ENV_LOGGER_FORMAT`) is not `'json'` or `'text'`.
491
+
492
+ ## Transports
493
+
494
+ `Source ->` [`winston/define.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/winston/define.ts)
495
+
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
502
+
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.
504
+
505
+ ### File rotation transport
506
+
507
+ Winston implements this transport with `DailyRotateFile`. Configure it through environment variables, or programmatically via `IFileTransportOptions`.
508
+
509
+ **Environment variables:**
510
+
511
+ | Variable | Default | Description |
512
+ |----------|---------|-------------|
513
+ | `APP_ENV_LOGGER_FOLDER_PATH` | _(unset)_ | Log files directory; file logging is OFF when unset |
514
+ | `APP_ENV_LOGGER_FILE_FREQUENCY` | `1h` | Rotation frequency |
515
+ | `APP_ENV_LOGGER_FILE_MAX_SIZE` | `100m` | Max file size before rotation |
516
+ | `APP_ENV_LOGGER_FILE_MAX_FILES` | `5d` | Retention period |
517
+ | `APP_ENV_LOGGER_FILE_DATE_PATTERN` | `YYYYMMDD_HH` | Date pattern in filename |
518
+
519
+ **Programmatic configuration:**
520
+
521
+ ```typescript
522
+ import { defineCustomLogger, applicationLogFormatter } from '@venizia/ignis-helpers/winston';
523
+
524
+ const customLogger = defineCustomLogger({
525
+ formatter: applicationLogFormatter,
526
+ transports: {
527
+ info: {
528
+ file: {
529
+ prefix: 'my-app',
530
+ folder: './logs',
531
+ frequency: '24h',
532
+ maxSize: '500m',
533
+ maxFiles: '30d',
534
+ datePattern: 'YYYYMMDD',
535
+ },
536
+ },
537
+ error: {
538
+ file: {
539
+ prefix: 'my-app-error',
540
+ folder: './logs',
541
+ maxFiles: '90d',
542
+ },
543
+ },
544
+ },
545
+ });
546
+ ```
547
+
548
+ Generated filename pattern: `{folder}/{prefix}-info-{DATE}.log` or `{folder}/{prefix}-error-{DATE}.log`. An `error`-level file transport is also registered as a Winston exception handler.
549
+
550
+ #### IFileTransportOptions
551
+
552
+ ```typescript
553
+ interface IFileTransportOptions {
554
+ prefix: string; // Filename prefix (required)
555
+ folder: string; // Output directory (required)
556
+ frequency?: string; // Rotation frequency (default: '1h')
557
+ maxSize?: string; // Max file size (default: '100m')
558
+ maxFiles?: string; // Retention period (default: '5d')
559
+ datePattern?: string; // Date pattern in filename (default: 'YYYYMMDD_HH')
560
+ }
561
+ ```
562
+
563
+ ### UDP transport (DgramTransport)
564
+
565
+ `Source ->` [`winston/transports/dgram.transport.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/winston/transports/dgram.transport.ts)
566
+
567
+ `DgramTransport` is a custom Winston transport that sends log entries over UDP. It filters by level - only messages whose level is in the configured `levels` set are forwarded.
568
+
569
+ ```typescript
570
+ import { DgramTransport } from '@venizia/ignis-helpers/winston';
571
+
572
+ const transport = new DgramTransport({
573
+ label: 'my-app',
574
+ host: '127.0.0.1',
575
+ port: 5000,
576
+ levels: ['error', 'warn', 'info'],
577
+ socketOptions: { type: 'udp4' },
578
+ });
579
+ ```
580
+
581
+ **Static factory with validation** - `fromPartial` returns `null` if any required field is missing, rather than throwing:
582
+
583
+ ```typescript
584
+ const transport = DgramTransport.fromPartial({
585
+ label: 'my-app',
586
+ host: '127.0.0.1',
587
+ port: 5000,
588
+ levels: ['error', 'warn'],
589
+ socketOptions: { type: 'udp4' },
590
+ });
591
+ // Returns null if label, host, port, levels (non-empty), or socketOptions is missing
592
+ ```
593
+
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.
597
+
598
+ **Environment variables for the default application logger:**
599
+
600
+ | Variable | Description |
601
+ |----------|-------------|
602
+ | `APP_ENV_LOGGER_DGRAM_HOST` | UDP log aggregator host |
603
+ | `APP_ENV_LOGGER_DGRAM_PORT` | UDP log aggregator port |
604
+ | `APP_ENV_LOGGER_DGRAM_LABEL` | Label to identify log source |
605
+ | `APP_ENV_LOGGER_DGRAM_LEVELS` | Comma-separated levels to send via UDP |
606
+
607
+ #### IDgramTransportOptions
608
+
609
+ ```typescript
610
+ interface IDgramTransportOptions extends Transport.TransportStreamOptions {
611
+ label: string; // Label to identify log source
612
+ host: string; // UDP host
613
+ port: number; // UDP port
614
+ levels: Array<string>; // Levels to forward over UDP
615
+ socketOptions: dgram.SocketOptions; // Node.js dgram socket options
616
+ }
617
+ ```
618
+
619
+ ### ICustomLoggerOptions
620
+
621
+ ```typescript
622
+ interface ICustomLoggerOptions {
623
+ levels?: { [name: string | symbol]: number };
624
+ colors?: { [name: string | symbol]: string };
625
+ // Full override: applied once for every transport, exactly as it produces the line
626
+ formatter?: ReturnType<typeof winston.format.combine>;
627
+ format?: TLoggerFormat; // 'json' | 'text'; defaults to APP_ENV_LOGGER_FORMAT
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
630
+ transports: {
631
+ info: {
632
+ file?: IFileTransportOptions;
633
+ dgram?: Partial<IDgramTransportOptions>;
634
+ };
635
+ error: {
636
+ file?: IFileTransportOptions;
637
+ dgram?: Partial<IDgramTransportOptions>;
638
+ };
639
+ };
640
+ }
641
+ ```
642
+
643
+ - **Both `info` and `error` transport groups support two optional transports:** `file` (DailyRotateFile) and `dgram` (UDP).
644
+ - **A console transport is always included**, regardless of what is configured.
645
+ - **Error file transports double as Winston exception handlers.**
646
+
647
+ ## Debug Logging Behavior
648
+
649
+ `Source ->` [`base/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/logger/base/base.ts)
650
+
651
+ Debug logs require **both** conditions to be met:
652
+
653
+ 1. `DEBUG=true` environment variable is set (parsed via `toBoolean`)
654
+ 2. `NODE_ENV` is either unset **or** is present in the `Environment.COMMON_ENVS` set
655
+
656
+ `COMMON_ENVS` includes: `local`, `debug`, `development`, `dev`, `sit`, `uat`, `alpha`, `beta`, `staging`, `production`. Extend this set with `APP_ENV_EXTRA_LOG_ENVS`:
657
+
658
+ ```bash
659
+ DEBUG=true
660
+ NODE_ENV=development
661
+ APP_ENV_EXTRA_LOG_ENVS=qa,preview # Comma-separated additional environments
662
+ ```
663
+
664
+ > [!IMPORTANT]
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.
666
+
667
+ ## High-Frequency Logger
668
+
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)
670
+
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.
676
+
677
+ ```typescript
678
+ import { HfLogger, HfLogFlusher } from '@venizia/ignis-helpers';
679
+
680
+ // At initialization time (once):
681
+ const logger = HfLogger.get('OrderEngine');
682
+ const MSG_ORDER_SENT = HfLogger.encodeMessage('Order sent');
683
+ const MSG_ORDER_FILLED = HfLogger.encodeMessage('Order filled');
684
+
685
+ // Start background flusher
686
+ const flusher = new HfLogFlusher();
687
+ flusher.start(100); // Flush every 100ms
688
+
689
+ // In hot path (bytes path, ~59ns, no allocation):
690
+ logger.log('info', MSG_ORDER_SENT);
691
+ logger.log('info', MSG_ORDER_FILLED);
692
+
693
+ // ILogger surface also works (string no-args path, ~66ns on a cache hit):
694
+ logger.info('Order sent');
695
+
696
+ // Shutdown:
697
+ await flusher.flush();
698
+ flusher.stop();
699
+ ```
700
+
701
+ ### HfLogger API
702
+
703
+ `HfLogger` implements the full `ILogger` contract plus its own static/bytes surface:
704
+
705
+ | Method | Signature | Description |
706
+ |--------|-----------|--------------|
707
+ | `HfLogger.get` | `(scope: string) => HfLogger` | Get or create a cached logger instance (allocates the ring lazily on first call) |
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 |
711
+ | `logger.for` | `(methodName: string) => ILogger` | Returns `HfLogger.get(`${scope}-${methodName}`)`, same dash composition as `BaseLogger` |
712
+
713
+ Supported levels (`TLogLevel`, full set): `debug` (0), `info` (1), `warn` (2), `error` (3), `emerg` (4).
714
+
715
+ ### HfLogFlusher API
716
+
717
+ | Method | Signature | Description |
718
+ |--------|-----------|--------------|
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 |
722
+ | `flusher.stop` | `() => void` | Clear the interval started by `start()` |
723
+
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.
735
+
736
+ ### Line format
737
+
738
+ The default sink renders each entry as:
739
+
740
+ ```
741
+ <ISO timestamp> [<level name>] <scope> <message>
742
+ ```
743
+
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
+ ```
751
+
752
+ ### Ring buffer entry format
753
+
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:
755
+
756
+ | Offset | Size | Field |
757
+ |--------|------|-------|
758
+ | 0-7 | 8 bytes | Timestamp (`float64` epoch milliseconds, sub-millisecond precision) |
759
+ | 8 | 1 byte | Level (`0`=debug, `1`=info, `2`=warn, `3`=error, `4`=emerg) |
760
+ | 9 | 1 byte | Scope length (0-32) |
761
+ | 10-41 | 32 bytes | Scope bytes |
762
+ | 42 | 1 byte | Message length (0-213) |
763
+ | 43-255 | 213 bytes | Message bytes |
764
+
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.
768
+
769
+ > [!WARNING]
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.
771
+
772
+ ## Environment Variables
773
+
774
+ ### Core configuration
775
+
776
+ | Variable | Default | Description |
777
+ |----------|---------|-------------|
778
+ | `APP_ENV_APPLICATION_NAME` | `APP` | Label prefix shown in all log output |
779
+ | `DEBUG` | `false` | Enable debug-level logging |
780
+ | `NODE_ENV` | _(unset)_ | Must be in `COMMON_ENVS` or unset for debug to activate |
781
+ | `APP_ENV_EXTRA_LOG_ENVS` | _(empty)_ | Comma-separated additional environments to allow debug |
782
+ | `APP_ENV_LOGGER_FORMAT` | `text` | Output format (`json` or `text`) |
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.
812
+
813
+ ### File rotation
814
+
815
+ | Variable | Default | Description |
816
+ |----------|---------|-------------|
817
+ | `APP_ENV_LOGGER_FILE_FREQUENCY` | `1h` | Rotation frequency |
818
+ | `APP_ENV_LOGGER_FILE_MAX_SIZE` | `100m` | Max file size before rotation |
819
+ | `APP_ENV_LOGGER_FILE_MAX_FILES` | `5d` | Retention period |
820
+ | `APP_ENV_LOGGER_FILE_DATE_PATTERN` | `YYYYMMDD_HH` | Date pattern in filename |
821
+
822
+ ### UDP transport
823
+
824
+ | Variable | Description |
825
+ |----------|-------------|
826
+ | `APP_ENV_LOGGER_DGRAM_HOST` | UDP log aggregator host |
827
+ | `APP_ENV_LOGGER_DGRAM_PORT` | UDP log aggregator port |
828
+ | `APP_ENV_LOGGER_DGRAM_LABEL` | Label to identify log source |
829
+ | `APP_ENV_LOGGER_DGRAM_LEVELS` | Comma-separated levels to send via UDP |
830
+
831
+ ### Example `.env`
832
+
833
+ ```bash
834
+ # Application
835
+ APP_ENV_APPLICATION_NAME=my-service
836
+
837
+ # Core
838
+ DEBUG=true
839
+ APP_ENV_LOGGER_FORMAT=json
840
+ APP_ENV_LOGGER_FOLDER_PATH=./app_data/logs
841
+ APP_ENV_LOGGER_INSPECT_DEPTH=5
842
+
843
+ # File rotation
844
+ APP_ENV_LOGGER_FILE_FREQUENCY=24h
845
+ APP_ENV_LOGGER_FILE_MAX_SIZE=500m
846
+ APP_ENV_LOGGER_FILE_MAX_FILES=30d
847
+
848
+ # UDP transport
849
+ APP_ENV_LOGGER_DGRAM_HOST=127.0.0.1
850
+ APP_ENV_LOGGER_DGRAM_PORT=5000
851
+ APP_ENV_LOGGER_DGRAM_LABEL=my-app
852
+ APP_ENV_LOGGER_DGRAM_LEVELS=error,warn,info
853
+ ```
854
+
855
+ ## API Summary
856
+
857
+ | Export | Kind | Description |
858
+ |--------|------|-------------|
859
+ | `ILogger` | interface | The logging contract every consumer types against - one direct method per level (`debug`, `info`, `warn`, `error`, `emerg`), plus `log` and `for` |
860
+ | `AbstractLogger` | abstract class | `ILogger` as a class - the `instanceof` check that works for EVERY provider |
861
+ | `BaseLogger` | abstract class | Provider-independent plumbing shared by every implementation: scope, prefix, the `DEBUG` gate, `.for()`, one abstract `write()` sink |
862
+ | `WinstonLogger` | class | The Winston-backed provider (the default); `Logger` is its permanent concrete alias |
863
+ | `Logger` | class | Concrete winston alias - `Logger.get(scope, customWinstonLogger?)`, `instanceof Logger`; deliberately does NOT follow `use()` |
864
+ | `PinoLogger` | class (sub-path `/pino`) | The throughput provider - register with `LoggerFactory.use` ([guide](/extensions/helpers/logger/pino)) |
865
+ | `ApplicationLogger` | const facade + type (`ILogger`) | `ApplicationLogger.get(scope)` always returns the REGISTERED provider's logger |
866
+ | `LoggerFactory` | class | Provider registration (`use({ provider })`) + `ILogger` acquisition from scope arrays |
867
+ | `ILoggerProvider` | interface | Static-side contract a provider class satisfies (`get(scope): ILogger`) |
868
+ | `HfLogger` | class | `ILogger`-conformant ring-buffer logger for hot paths |
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` |
872
+ | `LogLevels` | class (constants) | Log level constants (`ERROR`, `EMERG`, `WARN`, `INFO`, `DEBUG`) with `isValid()` |
873
+ | `LoggerFormats` | class (constants) | Format constants (`JSON`, `TEXT`) with `isValid()` |
874
+ | `defineCustomLogger` | `(opts: ICustomLoggerOptions) => winston.Logger` | Create a fully configured Winston logger |
875
+ | `defineLogFormatter` | `(opts: { label: string; format?: TLoggerFormat }) => winston.Logform.Format` | Create a formatter (auto-detects format from env) |
876
+ | `defineJsonLoggerFormatter` | `(opts: { label: string }) => winston.Logform.Format` | Create a JSON formatter |
877
+ | `definePrettyLoggerFormatter` | `(opts: { label: string; colorize?: boolean }) => winston.Logform.Format` | Create a pretty text formatter; `colorize: false` for files/aggregators |
878
+ | `applicationLogFormatter` | `winston.Logform.Format` | Pre-built formatter using `APP_ENV_APPLICATION_NAME` label |
879
+ | `resolveLoggerLevel` | `(opts: { configured?: string }) => TLogLevel` | Validate a level string; invalid or absent falls back to `debug` |
880
+ | `resolveDefaultTransportOptions` | `() => ICustomLoggerOptions['transports']` | Default transports from `APP_ENV_LOGGER_*`, resolved at call time |
881
+ | `DgramTransport` | class | Custom Winston transport for UDP logging |
882
+ | `TLogLevel` | type | Union of all log level string literals |
883
+ | `TLoggerFormat` | type | Union of `'json' \| 'text'` |
884
+ | `IFileTransportOptions` | interface | Options for daily-rotating file transport |
885
+ | `ICustomLoggerOptions` | interface | Options for `defineCustomLogger` |
886
+ | `IDgramTransportOptions` | interface | Options for `DgramTransport` |
887
+
888
+ ## Troubleshooting
889
+
890
+ ### Debug logs not appearing
891
+
892
+ **Cause:** Debug logging requires both `DEBUG=true` AND a `NODE_ENV` that is either unset or present in the `COMMON_ENVS` set. These values are pre-computed at module load time.
893
+
894
+ **Fix:**
895
+ 1. Verify `DEBUG=true` is set in your environment.
896
+ 2. Verify `NODE_ENV` is set to one of: `local`, `debug`, `development`, `dev`, `sit`, `uat`, `alpha`, `beta`, `staging`, `production` - or is unset entirely.
897
+ 3. If you use a custom environment name (for example, `qa`), add it to `APP_ENV_EXTRA_LOG_ENVS=qa`.
898
+
899
+ ```bash
900
+ DEBUG=true NODE_ENV=development bun run server:dev
901
+ ```
902
+
903
+ ### "[defineLogger] Invalid logger format | format: {format} | valids: json,text"
904
+
905
+ **Cause:** The `format` option passed to `defineLogFormatter` (or the `APP_ENV_LOGGER_FORMAT` environment variable) is not `json` or `text`.
906
+
907
+ **Fix:** Set `APP_ENV_LOGGER_FORMAT` to either `json` or `text`:
908
+
909
+ ```bash
910
+ APP_ENV_LOGGER_FORMAT=text
911
+ ```
912
+
913
+ ### UDP transport not sending logs
914
+
915
+ **Cause:** `DgramTransport.fromPartial()` returns `null` if any required option is missing (`label`, `host`, `port`, `levels` with at least one entry, or `socketOptions`). The transport is silently not registered.
916
+
917
+ **Fix:**
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`.
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.
920
+ 3. Verify the UDP aggregator is reachable from your host (firewall, port binding).
921
+
922
+ ### Log label shows "APP" instead of application name
923
+
924
+ **Cause:** The default label comes from `Defaults.APPLICATION_NAME`, which reads `APP_ENV_APPLICATION_NAME`. If the env var is not set, it falls back to `'APP'`.
925
+
926
+ **Fix:** Set `APP_ENV_APPLICATION_NAME` in your environment:
927
+
928
+ ```bash
929
+ APP_ENV_APPLICATION_NAME=my-service
930
+ ```
931
+
932
+ ## See also
933
+
934
+ - [Logger overview](/extensions/helpers/logger/) - introduction and the most common tasks
935
+ - [Request Tracker Component](/extensions/components/request-tracker) - request logging
936
+ - [Winston documentation](https://github.com/winstonjs/winston) - underlying logging library
937
+ - [winston-daily-rotate-file](https://github.com/winstonjs/winston-daily-rotate-file) - file rotation transport