@svayam-opensource/svm-util-log 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,82 +1,396 @@
1
1
  # @svayam-opensource/svm-util-log
2
2
 
3
- Svayam's shared logging framework — the logger from `svm-util`, carrying only the
4
- dependencies a logger needs.
3
+ Structured logging for Node applications and command-line tools.
5
4
 
6
- **Mandated by POL-423.** All Svayam-owned Node code uses this framework and leaves messages
7
- at the various levels, so a run can be reviewed afterwards for issue and problem resolution.
5
+ Every line carries **where it came from**, so a log you receive from someone else can be read
6
+ back to the code that wrote it. Debug output is **switched on from the environment**, so the
7
+ person in front of the problem can turn it on without a rebuild. Secrets are **masked before
8
+ anything is written**.
8
9
 
9
- ## Why this package exists
10
-
11
- `@svayam/svm-util` is one package: the logger, the helpers, database access and the API
12
- server. Taking it whole installs **675 packages, 274 MB** — right for an API service, and the
13
- whole cost for a command-line tool that wants one log line. `@svayam-opensource/gov` is
14
- installed with `npm i -g` on adopters' laptops.
15
-
16
- This package is **25 packages, 8.4 MB**.
10
+ ```bash
11
+ npm install @svayam-opensource/svm-util-log
12
+ ```
17
13
 
18
- It is also on the **public** scope, because `@svayam/svm-util` publishes to the internal
19
- registry, which adopters of a public CLI cannot resolve. A logging mandate that makes the
20
- governance CLI uninstallable is a mandate nothing can obey.
14
+ Node 20+. TypeScript types included. Works from both `import` and `require`.
21
15
 
22
- First package of the split — see [911#224](https://github.com/Svayamtech/911-SVM-LIB-SVC/issues/224).
16
+ ---
23
17
 
24
- ## Use
18
+ ## Quick start
25
19
 
26
20
  ```ts
27
21
  import { createApplicationLogger, LogLevel } from "@svayam-opensource/svm-util-log";
28
22
 
29
23
  const logger = createApplicationLogger({
30
24
  appId: "gov-work",
31
- logDir: "~/.gov/svm/logs",
25
+ logDir: "/Users/me/.gov/logs",
32
26
  methods: [
33
- { METHOD: "console", LOGLEVEL: LogLevel.warn },
34
- { METHOD: "file", LOGLEVEL: LogLevel.debug },
27
+ { METHOD: "console", LOGLEVEL: LogLevel.warn }, // on screen: warnings and errors
28
+ { METHOD: "file", LOGLEVEL: LogLevel.debug }, // on disk: everything
35
29
  ],
36
30
  });
37
31
 
38
- logger.log(LogLevel.debug, "chose the api-key route", "gov-work:cli:sign-in-choice", "captureAgentKey");
32
+ logger.log(LogLevel.info, "starting up", "myapp:cli:main", "start");
33
+ logger.log(LogLevel.error, "could not reach the registry", "myapp:net:client", "fetch", 0, { status: 503 });
34
+
35
+ logger.close(); // flush before the process exits
36
+ ```
37
+
38
+ That is the whole setup. Everything below is detail you can come back for.
39
+
40
+ ---
41
+
42
+ ## What a line looks like
43
+
44
+ ```
45
+ 2026-09-09T03:41:02.240Z warn gov-work:cli:agent-install signIn 7 no browser on this machine — offering the key route
46
+ 2026-09-09T03:41:02.367Z debug gov-work:cli:sign-in-choice captureAgentKey 7 chose the api-key route meta={ user: 'rk', apiKey: '***' }
47
+ 2026-09-09T03:41:02.494Z error gov-work:cli:agent-install run 7 npm could not install the agent meta={ code: 127 }
48
+ ```
49
+
50
+ Columns are fixed-width and the origin comes early, because the usual thing to do with a log
51
+ is **scan one column for one module**, not read it as prose.
52
+
53
+ | Column | From | What it is for |
54
+ |---|---|---|
55
+ | `2026-09-09T03:41:02.240Z` | automatic | ISO timestamp, UTC |
56
+ | `warn` | `lvl` | severity — see [Levels](#levels) |
57
+ | `gov-work:cli:agent-install` | `pgm_cd` | **the code path** — which module wrote this |
58
+ | `signIn` | `fn_cd` | which function inside it |
59
+ | `7` | `act_id` | ties one unit of work together across modules |
60
+ | `no browser on this machine…` | `msg` | your message |
61
+ | `meta={ … }` | `meta` | structured detail, masked |
62
+
63
+ ### Naming `pgm_cd` — the one convention worth following
64
+
65
+ Use the **code path**: `<package>:<directory>:<module>`.
66
+
67
+ ```
68
+ gov-work:cli:sign-in-choice → src/cli/sign-in-choice.ts
69
+ myapp:net:client → src/net/client.ts
39
70
  ```
40
71
 
41
- ### `pgm_cd` is the code path
72
+ A reader who receives your log can then open the file without guessing, and — as the next
73
+ section shows — can switch that exact module on by name.
42
74
 
43
- `<package>:<dir>:<module>` — `gov-work:cli:sign-in-choice` is `src/cli/sign-in-choice.ts`.
44
- The point is that a reader can get from a line back to the code without guessing (POL-426).
75
+ ---
45
76
 
46
- ### Turning it on in the field
77
+ ## Turning on debug output in the field
47
78
 
48
- Errors and warnings always come through. Everything below them is opt-in:
79
+ **Errors and warnings are always written.** Everything below them is off until someone asks
80
+ for it, through the standard Node `NODE_DEBUG` variable:
49
81
 
50
82
  ```bash
51
- NODE_DEBUG=gov-work:cli:sign-in-choice gov work # one module
52
- NODE_DEBUG=gov-work:* gov work # the package
83
+ NODE_DEBUG=gov-work:cli:sign-in-choice gov work # one module
84
+ NODE_DEBUG=gov-work:cli:* gov work # one directory
85
+ NODE_DEBUG=gov-work:* gov work # the whole application
53
86
  ```
54
87
 
55
- That is POL-425: **the person who needs the log is usually not the person who can reproduce
56
- the fault.** They turn it on, reproduce, and send the file.
88
+ This matters because **the person who needs the log is usually not the person who can
89
+ reproduce the fault**. You send them one environment variable; they reproduce it and send back
90
+ the file. No debug build, no release, no code change.
91
+
92
+ ```mermaid
93
+ flowchart TD
94
+ A["logger.log(level, msg, pgm_cd, …)"] --> B{"level is<br/>error or warn?"}
95
+ B -- yes --> D["write it"]
96
+ B -- no --> C{"NODE_DEBUG matches<br/>pgm_cd, fn_cd or act_id?"}
97
+ C -- yes --> D
98
+ C -- no --> E["dropped, costs nothing"]
99
+ D --> F{"for each configured<br/>transport"}
100
+ F --> G["level within<br/>this transport's LOGLEVEL?"]
101
+ G -- yes --> H["mask meta → format → write"]
102
+ G -- no --> E
103
+ ```
104
+
105
+ Two gates, and they are different questions. `NODE_DEBUG` decides **whether this line exists
106
+ at all**; a transport's `LOGLEVEL` decides **where an existing line goes**. That is how the
107
+ quick-start config keeps the screen quiet at `warn` while the file keeps everything.
108
+
109
+ ---
57
110
 
58
- ### Secrets
111
+ ## Levels
59
112
 
60
- POL-427 is **C01**: no credential, key, token or PII reaches a log, at any level, in any
61
- transport. `meta` is masked before rendering — by field name, case-insensitively, matching a
62
- name that *contains* a masked word (`userPassword`, `db_pwd`, `authToken`). Extend
63
- `DEFAULT_MASK`, never replace it.
113
+ Ordered least to most verbose. A transport set to a level accepts that level and everything
114
+ above it.
64
115
 
65
- ## Transports
116
+ | Level | Use it for |
117
+ |---|---|
118
+ | `error` | the operation failed — **always written** |
119
+ | `warn` | something is wrong but the run continues — **always written** |
120
+ | `info` | milestones a user would recognise |
121
+ | `http` | requests and responses |
122
+ | `verbose` | more than `info`, less than a trace |
123
+ | `debug` | what you would want during an incident |
124
+ | `silly` | everything |
66
125
 
67
- `console` and `file` (daily-rotate) ship here. `database` and `api` belong to the packages
68
- carrying `mysql` and `axios`, and register themselves:
126
+ ```ts
127
+ import { withinLevel, LogLevel } from "@svayam-opensource/svm-util-log";
128
+
129
+ withinLevel(LogLevel.error, LogLevel.info); // true — error passes an info threshold
130
+ withinLevel(LogLevel.debug, LogLevel.info); // false — debug does not
131
+ ```
132
+
133
+ ---
134
+
135
+ ## Keeping secrets out of logs
136
+
137
+ A log is a file. It gets copied and sent to other people — precisely when something has gone
138
+ wrong. So masking happens **in the library, before rendering**, not in your memory at each
139
+ call site.
69
140
 
70
141
  ```ts
71
- registerTransport("database", (conf, config) => new MyDbTransport(conf, config));
142
+ logger.log(LogLevel.debug, "signing in", "myapp:auth:login", "run", 0, {
143
+ user: "rk",
144
+ apiKey: "sk-live-9f3a",
145
+ db: { host: "localhost", password: "hunter2" },
146
+ });
72
147
  ```
73
148
 
74
- A method declared in config with nothing registered for it is **ignored, not an error** — a
75
- line that does not go somewhere, never a crash at startup.
149
+ ```
150
+ … signing in meta={ user: 'rk', apiKey: '***', db: { host: 'localhost', password: '***' } }
151
+ ```
152
+
153
+ Matching is **by field name, case-insensitive, at any depth**, and matches names that
154
+ *contain* a masked word — so `userPassword`, `db_pwd` and `authToken` are all covered.
155
+
156
+ Covered out of the box:
157
+
158
+ ```
159
+ password passwd pwd secret token apiKey api_key authorization
160
+ auth credential credentials privateKey private_key sessionId
161
+ session_id cookie jwt bearer
162
+ ```
163
+
164
+ **Extend the default, never replace it:**
165
+
166
+ ```ts
167
+ import { DEFAULT_MASK } from "@svayam-opensource/svm-util-log";
168
+
169
+ createApplicationLogger({
170
+ appId: "myapp",
171
+ logDir: "/var/log/myapp",
172
+ methods: [{ METHOD: "file", LOGLEVEL: LogLevel.info }],
173
+ mask: [...DEFAULT_MASK, "panNumber", "aadhaar"],
174
+ });
175
+ ```
176
+
177
+ Passing `mask: ["panNumber"]` on its own would silently switch off every default — the one
178
+ mistake worth watching for here.
179
+
180
+ > Masking protects values inside `meta`. It cannot see a secret you interpolated into the
181
+ > message string yourself, so put values in `meta`, not in `msg`.
182
+
183
+ ---
184
+
185
+ ## Where lines go
186
+
187
+ Two transports ship in this package.
188
+
189
+ ### `console`
190
+
191
+ Writes to **stderr**, coloured by level. stderr because stdout is your program's answer — a
192
+ caller running `myapp | jq` should not receive log lines as data.
193
+
194
+ ```ts
195
+ { METHOD: "console", LOGLEVEL: LogLevel.warn }
196
+ ```
197
+
198
+ ### `file`
199
+
200
+ A daily-rotating file, `<date>-<appId>.log`, in `logDir`.
201
+
202
+ ```ts
203
+ {
204
+ METHOD: "file",
205
+ LOGLEVEL: LogLevel.debug,
206
+ DATA_STORE: {
207
+ dirname: "/var/log/myapp", // defaults to logDir
208
+ datePattern: "YYYY-MM-DD",
209
+ zippedArchive: true,
210
+ maxSize: "20m",
211
+ maxFiles: "14d", // older files are deleted
212
+ },
213
+ }
214
+ ```
215
+
216
+ Defaults are 20 MB per file, gzipped, kept a fortnight — long enough to diagnose something
217
+ reported today, short enough that a forgotten log directory is not a growing disclosure risk.
218
+
219
+ ### Adding your own
220
+
221
+ `database` and `api` transports are **not** in this package — they would drag a database
222
+ driver and an HTTP client into every install. Register one instead:
223
+
224
+ ```ts
225
+ import { registerTransport } from "@svayam-opensource/svm-util-log";
226
+ import Transport from "winston-transport";
227
+
228
+ class DbTransport extends Transport { /* … */ }
229
+
230
+ registerTransport("database", (conf, config) => new DbTransport(conf, config));
231
+ ```
232
+
233
+ Register **before** you call `createApplicationLogger`. A method named in your config with
234
+ nothing registered for it is **ignored, not an error** — the same config file can move between
235
+ a service that has a database and a CLI that does not, and the CLI simply writes fewer places.
236
+
237
+ ```mermaid
238
+ flowchart LR
239
+ L["logger.log(…)"] --> M["mask meta"] --> F["format one line"]
240
+ F --> C["console → stderr"]
241
+ F --> D["file → daily rotate"]
242
+ F --> X["your transport<br/><i>registerTransport</i>"]
243
+ style X stroke-dasharray: 4 3
244
+ ```
245
+
246
+ ---
247
+
248
+ ## Configuration reference
249
+
250
+ ```ts
251
+ createApplicationLogger({
252
+ appId, // string — names the app in every line and in the filename
253
+ logDir, // string — where file transports write by default
254
+ methods, // array — one entry per destination
255
+ mask, // string[] — optional; extend DEFAULT_MASK
256
+ inspect, // optional — { depth?: number; maxStringLength?: number } for meta rendering
257
+ });
258
+ ```
259
+
260
+ | Field | Required | Notes |
261
+ |---|---|---|
262
+ | `appId` | yes | appears as `app_cd` and in the log filename |
263
+ | `logDir` | yes | must exist or be creatable; used when a `file` method sets no `dirname` |
264
+ | `methods` | yes | must name at least one destination — see the note below |
265
+ | `mask` | no | defaults to `DEFAULT_MASK`; **extend it, don't replace it** |
266
+ | `inspect` | no | defaults to depth 4, 512-character strings |
267
+
268
+ > **Give `methods` at least one entry.** An empty array does not throw, but it is not silence
269
+ > either: winston then prints its own `Attempt to write logs with no transports` warning for
270
+ > every line, with the entry as raw JSON. That output bypasses this library's formatting — the
271
+ > masking still applies, because `meta` is masked on the way in, but the result is noisy and
272
+ > not what you configured. `logger.toLogObject().ENABLED_TRANSPORTS` shows what is really
273
+ > attached.
274
+
275
+ Configuration is **passed in, never read from a global**. Nothing has to be initialised before
276
+ you can log, which matters most on a program's first run — the moment you most want a log and
277
+ are least likely to have configuration.
278
+
279
+ ---
280
+
281
+ ## API
282
+
283
+ | Export | What it does |
284
+ |---|---|
285
+ | `createApplicationLogger(config)` | returns an `ApplicationLogger` |
286
+ | `registerTransport(method, factory)` | adds a destination this package does not ship |
287
+ | `LogLevel`, `LogLevelIndex`, `logLevelIndex` | the level names, their order, their index |
288
+ | `LogMethod` | `console` · `file` · `database` · `api` |
289
+ | `withinLevel(level, threshold)` | is `level` within `threshold`? |
290
+ | `DEFAULT_MASK` | the field names masked by default |
291
+ | `formatLogMessage(entry, config?)` | render one line — useful in your own transport |
292
+ | `toLogEntry(appId, lvl, msg, …)` | build the entry a transport receives |
293
+ | `maskValues(value, mask?)` | mask an object without logging it |
294
+ | `stringify(value, config?)` | the `meta=` renderer; never throws, handles cycles |
295
+ | `isLogEnabled(pgm_cd, fn_cd?, act_id?)` | is `NODE_DEBUG` on for this origin? |
296
+
297
+ ### `ApplicationLogger`
298
+
299
+ ```ts
300
+ logger.log(level, message, pgm_cd?, fn_cd?, act_id?, meta?); // write a line
301
+ logger.toLogObject(); // what is actually enabled — for a `doctor`/`status` command
302
+ logger.toString(); // the same, as JSON
303
+ logger.close(); // flush and close every transport
304
+ ```
305
+
306
+ **Always `close()` before a short-lived process exits.** File writes are asynchronous, so
307
+ without it the last lines of a run — the ones explaining why it ended — are the ones most
308
+ likely to be lost.
309
+
310
+ ---
311
+
312
+ ## Recipes
313
+
314
+ ### A command-line tool
315
+
316
+ Quiet by default, everything on disk, and the user can raise the screen level themselves.
317
+
318
+ ```ts
319
+ const logger = createApplicationLogger({
320
+ appId: "mycli",
321
+ logDir: path.join(os.homedir(), ".mycli", "logs"),
322
+ methods: [
323
+ { METHOD: "console", LOGLEVEL: LogLevel.warn },
324
+ { METHOD: "file", LOGLEVEL: LogLevel.debug },
325
+ ],
326
+ });
327
+
328
+ process.on("exit", () => logger.close());
329
+ ```
330
+
331
+ ### A long-running service
332
+
333
+ ```ts
334
+ const logger = createApplicationLogger({
335
+ appId: "orders-api",
336
+ logDir: "/var/log/orders-api",
337
+ methods: [
338
+ { METHOD: "console", LOGLEVEL: LogLevel.info },
339
+ { METHOD: "file", LOGLEVEL: LogLevel.http },
340
+ { METHOD: "database", LOGLEVEL: LogLevel.error }, // ignored unless registered
341
+ ],
342
+ });
343
+ ```
344
+
345
+ ### Following one request across modules
346
+
347
+ Give every line of one unit of work the same `act_id`:
348
+
349
+ ```ts
350
+ const actId = nextRequestId();
351
+ logger.log(LogLevel.http, "POST /orders", "orders:api:routes", "create", actId);
352
+ logger.log(LogLevel.debug, "reserving stock", "orders:domain:stock", "reserve", actId);
353
+ logger.log(LogLevel.error, "payment declined", "orders:domain:pay", "charge", actId, { code: "card_declined" });
354
+ ```
355
+
356
+ Then `grep ' 4471 '` returns that request and nothing else. `NODE_DEBUG` accepts an `act_id`
357
+ too, so a single transaction can be traced without switching on a whole module.
358
+
359
+ ---
360
+
361
+ ## Troubleshooting
362
+
363
+ | What you see | Why |
364
+ |---|---|
365
+ | Nothing below `warn` appears | Expected. Set `NODE_DEBUG` — see [above](#turning-on-debug-output-in-the-field). |
366
+ | Log lines mixed into piped output | `console` writes to **stderr** by design. Redirect with `2>/dev/null`, or read it with `2>&1`. |
367
+ | No file appears | Check `logDir` exists and is writable, and that a `file` method is in `methods`. |
368
+ | The last lines of a run are missing | Call `logger.close()` before exit. |
369
+ | A field you expected to be masked is not | Masking reads **field names** in `meta`. A secret placed in the message string is not visible to it. |
370
+ | Your custom transport never writes | `registerTransport` must run **before** `createApplicationLogger`. |
371
+ | `Attempt to write logs with no transports` | `methods` is empty, or every entry names a transport nothing registered. Check `logger.toLogObject()`. |
372
+ | `mask` seems to have switched off the defaults | It did. Use `[...DEFAULT_MASK, "yours"]`. |
373
+
374
+ ---
375
+
376
+ ## Notes
377
+
378
+ Console output uses [winston](https://github.com/winstonjs/winston); file rotation uses
379
+ [winston-daily-rotate-file](https://github.com/winstonjs/winston-daily-rotate-file).
380
+
381
+ ### Design rationale
382
+
383
+ Why this package is separate from `@svayam/svm-util`, why configuration is injected rather
384
+ than global, why console output goes to stderr, and what belongs in this package versus a
385
+ sibling — all of that is in `docs/architecture.md`.
386
+
387
+ That file lives in the **source repository, which is private to Svayam**; the link below will
388
+ not open for you unless you have access. Everything in this README stands on its own, and
389
+ nothing in the architecture document changes how the library behaves.
390
+
391
+ Svayam engineers: [`docs/architecture.md`](./docs/architecture.md).
76
392
 
77
- ## Two deliberate departures from `svm-util`
393
+ ### Licence and support
78
394
 
79
- 1. **Configuration is injected, never global.** `svm-util` reaches a module-level
80
- `getSvayamConfiguration()`; a CLI has a first run where no configuration exists at all.
81
- 2. **Console output goes to STDERR.** `stdout` is a program's answer, and a caller piping it
82
- should not receive the log as data.
395
+ Copyright © 2026 Svayam Infoware Pvt. Ltd. Published publicly so that Svayam's own public
396
+ tools — and anyone installing them — can resolve it without registry configuration.
package/lib/cjs/logger.js CHANGED
@@ -82,11 +82,20 @@ function createApplicationLogger(config) {
82
82
  });
83
83
  const effective = { mask: config_js_1.DEFAULT_MASK, ...config };
84
84
  const log = (lvl, msg, pgm_cd, fn_cd, act_id, meta) => {
85
- // ERRORS AND WARNINGS ALWAYS PASS. Everything below them is opt-in through NODE_DEBUG, so
86
- // an ordinary run is quiet — but a run that went wrong still said so without anybody
87
- // having had the foresight to enable anything.
85
+ // ERRORS AND WARNINGS ALWAYS PASS. Below them, the GATE decides — `debuglog` (the default, opt-in through
86
+ // NODE_DEBUG) or `level`, where each transport's own LOGLEVEL is the only threshold.
87
+ //
88
+ // WHY `level` EXISTS (2026-09-23). A CLI cannot switch `debuglog` on for itself: Node compiles NODE_DEBUG
89
+ // into a matcher at bootstrap, so setting the variable from inside the process — even on the first line,
90
+ // before any import — has no effect. An application that has DECIDED to record every run (gov: "info and
91
+ // above on every run, silent on screen") therefore could not, through this library: its `info` lines were
92
+ // dropped, and its log folders held nothing but the transport's bookkeeping file. Found when gov's six log
93
+ // calls turned out never to have written a line.
94
+ //
95
+ // The default is unchanged, so nothing that relies on quiet-by-default changes.
88
96
  const always = levels_js_1.LogLevelIndex[lvl] <= levels_js_1.LogLevelIndex[levels_js_1.LogLevel.warn];
89
- if (!always && !(0, format_js_1.isLogEnabled)(pgm_cd, fn_cd, act_id))
97
+ const gated = (effective.gate ?? "debuglog") === "debuglog";
98
+ if (!always && gated && !(0, format_js_1.isLogEnabled)(pgm_cd, fn_cd, act_id))
90
99
  return;
91
100
  winston.log((0, format_js_1.toLogEntry)(effective.appId, lvl, msg, pgm_cd, fn_cd, act_id, meta));
92
101
  };
@@ -64,6 +64,16 @@ export interface LoggerConfig {
64
64
  * is copied, and it is sent to other people precisely when something has gone wrong.
65
65
  */
66
66
  readonly mask?: readonly string[];
67
+ /**
68
+ * What decides whether a line BELOW `warn` is written.
69
+ *
70
+ * `debuglog` (default) — opt-in per origin through `NODE_DEBUG` (POL-425): quiet until someone in front of
71
+ * the problem switches it on, with no rebuild and no release.
72
+ * `level` — each transport's own LOGLEVEL is the threshold, and nothing else. For an
73
+ * application that records every run by decision rather than by environment: a
74
+ * process cannot enable `debuglog` for itself (Node reads NODE_DEBUG at bootstrap).
75
+ */
76
+ readonly gate?: "debuglog" | "level";
67
77
  /** Depth and length limits for `meta`, so one object cannot become the whole file. */
68
78
  readonly inspect?: {
69
79
  depth?: number;
package/lib/esm/logger.js CHANGED
@@ -75,11 +75,20 @@ export function createApplicationLogger(config) {
75
75
  });
76
76
  const effective = { mask: DEFAULT_MASK, ...config };
77
77
  const log = (lvl, msg, pgm_cd, fn_cd, act_id, meta) => {
78
- // ERRORS AND WARNINGS ALWAYS PASS. Everything below them is opt-in through NODE_DEBUG, so
79
- // an ordinary run is quiet — but a run that went wrong still said so without anybody
80
- // having had the foresight to enable anything.
78
+ // ERRORS AND WARNINGS ALWAYS PASS. Below them, the GATE decides — `debuglog` (the default, opt-in through
79
+ // NODE_DEBUG) or `level`, where each transport's own LOGLEVEL is the only threshold.
80
+ //
81
+ // WHY `level` EXISTS (2026-09-23). A CLI cannot switch `debuglog` on for itself: Node compiles NODE_DEBUG
82
+ // into a matcher at bootstrap, so setting the variable from inside the process — even on the first line,
83
+ // before any import — has no effect. An application that has DECIDED to record every run (gov: "info and
84
+ // above on every run, silent on screen") therefore could not, through this library: its `info` lines were
85
+ // dropped, and its log folders held nothing but the transport's bookkeeping file. Found when gov's six log
86
+ // calls turned out never to have written a line.
87
+ //
88
+ // The default is unchanged, so nothing that relies on quiet-by-default changes.
81
89
  const always = LogLevelIndex[lvl] <= LogLevelIndex[LogLevel.warn];
82
- if (!always && !isLogEnabled(pgm_cd, fn_cd, act_id))
90
+ const gated = (effective.gate ?? "debuglog") === "debuglog";
91
+ if (!always && gated && !isLogEnabled(pgm_cd, fn_cd, act_id))
83
92
  return;
84
93
  winston.log(toLogEntry(effective.appId, lvl, msg, pgm_cd, fn_cd, act_id, meta));
85
94
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svayam-opensource/svm-util-log",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Svayam's shared logging framework — the logger from svm-util, with only the dependencies a logger needs.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -50,8 +50,8 @@
50
50
  "typescript": "^5.7.2"
51
51
  },
52
52
  "gov": {
53
- "contentSha": "0683398e412c95dc454e7a9f90c2c673a1b6bb6a639215c9ae8274b321c2ea73",
53
+ "contentSha": "dcd002b72a0afcd5960ce3b6b53bd40a3742b775546893d0847f4fcd34335b71",
54
54
  "shaFrom": "built-ref",
55
- "commit": "b22df8f24ed4478f57ffcfaebf03a9953de3c375"
55
+ "commit": "0e172f8bae1d6951b7b86016f8c6cbf76da782ea"
56
56
  }
57
57
  }