syntropylog 0.12.7 → 0.12.9

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 (3) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +258 -174
  3. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.12.9
4
+
5
+ ### Patch Changes
6
+
7
+ - 384b630: **Docs:** README fully restructured for narrative coherence — sections now progress from infrastructure to data control, security, output, tracing, compliance, and production ops. Added item 15 (Hot reconfiguration per POD) to the feature table. Console transports section moved to Quick Start area as a setup clarification. "Key Concepts" and "Main Benefits" merged into a single "Core design" section to eliminate redundancy. Matrix in runtime moved to follow Logging Matrix (items 2→3). Sanitization and Serialization grouped after Masking. Universal Adapter and Fluent API repositioned after context features.
8
+
9
+ **Package:** `description` field updated to accurately reflect the framework's purpose.
10
+
11
+ ## 0.12.8
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated documentation: clarified that the `audit` log level is immune to log level filtering and is always written.
16
+
3
17
  ## 0.12.7
4
18
 
5
19
  ### Patch Changes
package/README.md CHANGED
@@ -17,7 +17,7 @@
17
17
  <a href="https://github.com/Syntropysoft/SyntropyLog/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/syntropylog.svg" alt="License"></a>
18
18
  <a href="https://github.com/Syntropysoft/SyntropyLog/actions/workflows/ci.yaml"><img src="https://github.com/Syntropysoft/SyntropyLog/actions/workflows/ci.yaml/badge.svg" alt="CI Status"></a>
19
19
  <a href="#"><img src="https://img.shields.io/badge/coverage-95.13%25-brightgreen" alt="Test Coverage"></a>
20
- <a href="#"><img src="https://img.shields.io/badge/status-v0.11.4-brightgreen.svg" alt="Version 0.11.4"></a>
20
+ <a href="#"><img src="https://img.shields.io/badge/status-v0.12.9-brightgreen.svg" alt="Version 0.12.9"></a>
21
21
  <a href="https://socket.dev/npm/package/syntropylog"><img src="https://socket.dev/api/badge/npm/package/syntropylog" alt="Socket Badge"></a>
22
22
  </p>
23
23
 
@@ -42,23 +42,19 @@ SyntropyLog solves this by allowing you to:
42
42
 
43
43
  ---
44
44
 
45
- ### Key Concepts
45
+ ### Core design
46
46
 
47
47
  | Concept | What it does |
48
48
  | :--- | :--- |
49
- | **Native Addon (Rust)** | Optional Rust module that processes logs at maximum speed (serialize + mask + sanitize). |
50
- | **Logging Matrix** | Declarative config defining context field visibility per log level. <br/>**Important:** Only fields/headers declared in the initial context configuration are processed. |
51
- | **MaskingEngine** | Real-time redaction of sensitive fields with rules/regex. <br/>**Config:** Enable default rules or define custom replacement patterns. |
52
- | **Universal Adapter** | Send logs to any backend with a single `executor`, avoiding vendor lock-in. |
49
+ | **Native Addon (Rust)** | Single-pass serialize + mask + sanitize at maximum speed. No CPU overhead on Node.js. |
50
+ | **Logging Matrix** | Declarative contract: which context fields appear per log level. Only declared fields are processed. |
51
+ | **MaskingEngine** | Real-time redaction of sensitive fields before any transport; built-in + custom rules. |
52
+ | **Universal Adapter** | One `executor` function any backend (DB, Elasticsearch, S3, OTel). No vendor lock-in. |
53
53
 
54
- ---
55
-
56
- ### Main Benefits
57
-
58
- * **Extreme Performance:** Rust addon makes logging light on Node.js CPU.
59
- * **Direct Compliance:** Facilitates audits (SOX, GDPR, PCI-DSS) with `audit` level and retention policies.
60
- * **Active Security:** Sanitizes strings to prevent Log Injection attacks.
61
- * **Traceability:** Manages `Correlation ID` and `Transaction ID` automatically.
54
+ * **Performance:** Rust addon keeps logging lightweight on Node.js CPU.
55
+ * **Compliance:** `audit` level + retention policies facilitate SOX, GDPR, PCI-DSS audits.
56
+ * **Security:** Masking + sanitization prevent data leakage and log injection.
57
+ * **Traceability:** Correlation ID and Transaction ID propagate automatically across all logs.
62
58
 
63
59
  ---
64
60
 
@@ -70,20 +66,21 @@ Everything below is part of the same stack (benchmarks use this full stack). Eac
70
66
  |---|--------|---------------|
71
67
  | 1 | **Native addon (Rust)** | Single-pass serialize + mask + sanitize; ANSI strip. Falls back to JS if unavailable. |
72
68
  | 2 | **Logging Matrix** | Declarative control of which context fields appear per level (lean on `info`, full on `error`). |
73
- | 3 | **Universal Adapter** | Send logs to any backend (PostgreSQL, MongoDB, Elasticsearch, S3) via one `executor` function. |
69
+ | 3 | **Matrix in runtime** | `reconfigureLoggingMatrix()` without restart; only field visibility, not security. |
74
70
  | 4 | **MaskingEngine** | Redact sensitive fields before any transport; built-in + custom rules. |
75
- | 5 | **Serialization pipeline** | Circular refs, depth limit, timeout; logging never blocks the event loop. |
76
- | 6 | **SanitizationEngine** | Strip control characters; log injection resistant. |
71
+ | 5 | **SanitizationEngine** | Strip control characters; log injection resistant. |
72
+ | 6 | **Serialization pipeline** | Circular refs, depth limit, timeout; logging never blocks the event loop. |
77
73
  | 7 | **Context / headers** | Correlation ID and transaction ID from config; single source of truth. |
78
- | 8 | **Fluent API** | `withRetention`, `withSource`, `withTransactionId` bind once, carry on every log. |
74
+ | 8 | **Universal Adapter** | Send logs to any backend (PostgreSQL, MongoDB, Elasticsearch, S3) via one `executor` function. |
79
75
  | 9 | **Per-call transport control** | `.override()`, `.add()`, `.remove()` for one log call without new logger instances. |
80
- | 10 | **Audit & retention** | `audit` level (always logged); `withRetention(anyJson)` for compliance routing. |
81
- | 11 | **Lifecycle** | `init()` / `shutdown()`; graceful flush on SIGTERM/SIGINT. |
82
- | 12 | **Observability hooks** | `onLogFailure`, `onTransportError`, `onSerializationFallback`, etc.; `isNativeAddonInUse()`. |
83
- | 13 | **Matrix in runtime** | `reconfigureLoggingMatrix()` without restart; only field visibility, not security. |
84
- | 14 | **Tree-shaking** | `sideEffects: false` + ESM; bundle only what you import. |
76
+ | 10 | **Fluent API** | `withRetention`, `withSource`, `withTransactionId` bind once, carry on every log. |
77
+ | 11 | **Audit & retention** | `audit` level (always logged); `withRetention(anyJson)` for compliance routing. |
78
+ | 12 | **Lifecycle** | `init()` / `shutdown()`; graceful flush on SIGTERM/SIGINT. |
79
+ | 13 | **Observability hooks** | `onLogFailure`, `onTransportError`, `onSerializationFallback`, etc.; `isNativeAddonInUse()`. |
80
+ | 14 | **OpenTelemetry integration** | `UniversalAdapter` + `UniversalLogFormatter` → send logs to any OTel collector; no library changes needed. |
81
+ | 15 | **Hot reconfiguration (per POD)** | Change log level, add masking rules, or add a debug transport per POD at runtime via your own HTTP endpoint; no restart needed. |
85
82
 
86
- **More detail and examples:** this README (English). [Also in Spanish (ES): features and examples](doc-es/caracteristicas-y-ejemplos.md).
83
+ **More detail and examples:** this README (English). See also [docs/features-and-examples.md](docs/features-and-examples.md). [También en español (ES)](doc-es/caracteristicas-y-ejemplos.md).
87
84
 
88
85
  ---
89
86
 
@@ -145,6 +142,33 @@ process.on('SIGINT', async () => {
145
142
 
146
143
  ---
147
144
 
145
+ ## Console transports (default and pretty)
146
+
147
+ By default the library outputs **plain JSON** to the console. For colored, human-readable output in development, use a pretty transport:
148
+
149
+ | Transport | Style |
150
+ |-----------|--------|
151
+ | *(default)* | Plain JSON |
152
+ | `ClassicConsoleTransport` | Single-line, colored |
153
+ | `PrettyConsoleTransport` | Pretty-printed, colored |
154
+ | `CompactConsoleTransport` | Compact one-liner, colored |
155
+ | `ColorfulConsoleTransport` | Full-line colored |
156
+
157
+ Colors use built-in ANSI; no chalk. Disabled when stdout is not a TTY or when `NO_COLOR` is set.
158
+
159
+ ```typescript
160
+ import { ClassicConsoleTransport } from 'syntropylog';
161
+ syntropyLog.init({
162
+ logger: {
163
+ level: 'info',
164
+ serviceName: 'my-app',
165
+ transports: [new ClassicConsoleTransport()],
166
+ },
167
+ });
168
+ ```
169
+
170
+ ---
171
+
148
172
  ## 1. Native addon (Rust)
149
173
 
150
174
  **What:** Optional Rust addon does serialize + mask + sanitize in one pass. Used automatically when available; no config. Disable with `SYNTROPYLOG_NATIVE_DISABLE=1` (e.g. debugging).
@@ -157,13 +181,13 @@ if (syntropyLog.isNativeAddonInUse()) {
157
181
  }
158
182
  ```
159
183
 
160
- To build the native addon from source, see [doc-es/building-native-addon.es.md](doc-es/building-native-addon.es.md) (Spanish).
184
+ To build the native addon from source, see [docs/building-native-addon.md](docs/building-native-addon.md).
161
185
 
162
186
  ---
163
187
 
164
188
  ## 2. Logging Matrix
165
189
 
166
- **What:** A JSON contract that defines exactly which context fields appear at each log level. If a field isnt in the matrix for that level, it never appears in the output.
190
+ **What:** A JSON contract that defines exactly which context fields appear at each log level. If a field isn't in the matrix for that level, it never appears in the output.
167
191
 
168
192
  **How:** Set `loggingMatrix` in `init()`:
169
193
 
@@ -189,89 +213,21 @@ await syntropyLog.init({
189
213
 
190
214
  ---
191
215
 
192
- ## 3. Universal Adapter — log to any backend
193
-
194
- **What:** Send each log to PostgreSQL, MongoDB, Elasticsearch, S3, etc. by implementing a single `executor`. No vendor lock-in. You define **once** how the log entry maps to your schema; the executor only receives that mapped object and persists it (ORM, raw client, HTTP). If `executor` throws, SyntropyLog logs the error and continues (Silent Observer).
195
-
196
- **How:** Use `AdapterTransport` + `UniversalAdapter` + `UniversalLogFormatter`. Three steps:
197
-
198
- ---
199
-
200
- ### 1. Define the mapping (outside the executor)
201
-
202
- Define how each log entry field maps to your persistence shape. One place, no repetition in code. Use `UniversalLogFormatter` with a `mapping` object: keys = your schema (e.g. DB columns), values = path in the log entry.
203
-
204
- | Log entry path | Meaning |
205
- |----------------|--------|
206
- | `level` | Log level |
207
- | `message` | Message string |
208
- | `serviceName` | From init config |
209
- | `correlationId` | From context |
210
- | `timestamp` | ISO string |
211
- | `meta` | Merged metadata (use as payload/JSON column) |
212
-
213
- Example: map to a table with columns `level`, `message`, `serviceName`, `correlationId`, `payload`, `timestamp`.
214
-
215
- ```typescript
216
- import { UniversalLogFormatter } from 'syntropylog';
217
-
218
- const logToDbMapping = {
219
- level: 'level',
220
- message: 'message',
221
- serviceName: 'serviceName',
222
- correlationId: 'correlationId',
223
- payload: 'meta',
224
- timestamp: 'timestamp',
225
- };
226
-
227
- const formatter = new UniversalLogFormatter({ mapping: logToDbMapping });
228
- ```
229
-
230
- The formatter turns every log entry into an object with exactly those keys. Change the mapping here when your schema changes; the executor stays the same.
231
-
232
- ---
233
-
234
- ### 2. The object the executor receives
235
-
236
- When you attach this formatter to the transport, the `executor` receives **that mapped object** (not the raw log entry). So the executor only sees something like `{ level, message, serviceName, correlationId, payload, timestamp }`. Your only job in the executor is to **persist** that object.
237
-
238
- ---
239
-
240
- ### 3. Sending that object to your backend
216
+ ## 3. Matrix in runtime
241
217
 
242
- One mapping produces one object shape. You can send **that same object** to as many backends as you want in a single executor: same `data`, different destinations (e.g. Prisma, TypeORM, Mongoose, Elasticsearch, S3). No field list — the shape comes from the mapping.
218
+ **What:** Change which context fields are visible per level without restart. Security boundary: only field visibility changes; masking and transports stay as set at `init()`.
243
219
 
244
- **Example: one mapped object → three destinations**
220
+ **How:**
245
221
 
246
222
  ```typescript
247
- import { AdapterTransport, UniversalAdapter } from 'syntropylog';
248
- import { prisma } from './prisma';
249
- import { getRepository } from 'typeorm';
250
- import { SystemLog as TypeOrmSystemLog } from './entities/SystemLog';
251
- import { SystemLogModel } from './models/SystemLog';
252
-
253
- const dbTransport = new AdapterTransport({
254
- name: 'db',
255
- formatter,
256
- adapter: new UniversalAdapter({
257
- executor: async (data) => {
258
- const row = {
259
- ...data,
260
- timestamp: new Date(data.timestamp as string),
261
- };
262
- // Same object, three destinations (e.g. Postgres + TypeORM DB + MongoDB)
263
- await Promise.all([
264
- prisma.systemLog.create({ data: row }),
265
- getRepository(TypeOrmSystemLog).save(row),
266
- SystemLogModel.create(row),
267
- ]);
268
- },
269
- }),
223
+ syntropyLog.reconfigureLoggingMatrix({
224
+ default: ['correlationId'],
225
+ info: ['correlationId', 'userId', 'operation'],
226
+ error: ['*'],
270
227
  });
228
+ // Restore later with original matrix
271
229
  ```
272
230
 
273
- Use one transport and one executor; add or remove destinations in that same block. Use this transport in your `init()` (e.g. in `logger.transports` or `logger.transportList` + `logger.env`).
274
-
275
231
  ---
276
232
 
277
233
  ## 4. MaskingEngine
@@ -344,7 +300,7 @@ masking: {
344
300
  If you have a file in your project where you define **your own** sensitive words or aliases (e.g. `mySensitiveKeys.ts`), Sonar may report secrets (e.g. S2068) there. You can add an exception so that file does not block the deploy:
345
301
 
346
302
  - **Exclude the file from analysis:** in your `sonar-project.properties`, add it to `sonar.exclusions` (e.g. `sonar.exclusions=**/mySensitiveKeys.ts`).
347
- - **Or ignore only rule S2068 on that file:** use `sonar.issue.ignore.multicriteria` with `ruleKey=typescript:S2068` and `resourceKey` set to that files path.
303
+ - **Or ignore only rule S2068 on that file:** use `sonar.issue.ignore.multicriteria` with `ruleKey=typescript:S2068` and `resourceKey` set to that file's path.
348
304
 
349
305
  Full steps and examples: [docs/SONAR_FILE_EXCEPTION.md](docs/SONAR_FILE_EXCEPTION.md).
350
306
 
@@ -359,7 +315,15 @@ If masking fails, the pipeline does not throw (Silent Observer). Custom rules: u
359
315
 
360
316
  ---
361
317
 
362
- ## 5. Serialization pipeline (resilience)
318
+ ## 5. SanitizationEngine
319
+
320
+ **What:** Strips control characters and ANSI from string values before any transport writes. Reduces log injection risk.
321
+
322
+ **How:** No configuration; it runs inside the pipeline. Together with the Logging Matrix (whitelist), it forms the safety boundary for log content.
323
+
324
+ ---
325
+
326
+ ## 6. Serialization pipeline (resilience)
363
327
 
364
328
  **What:** Prevents "death by log": circular refs are neutralized, depth is limited (default 10 → `[MAX_DEPTH_REACHED]`), and a configurable timeout via `serializerTimeoutMs` aborts long serialization so the event loop keeps running. For most apps 50–100ms is enough; the library default is higher.
365
329
 
@@ -376,14 +340,6 @@ Logging never throws; failures are reported inside the log payload.
376
340
 
377
341
  ---
378
342
 
379
- ## 6. SanitizationEngine
380
-
381
- **What:** Strips control characters and ANSI from string values before any transport writes. Reduces log injection risk.
382
-
383
- **How:** No configuration; it runs inside the pipeline. Together with the Logging Matrix (whitelist), it forms the safety boundary for log content.
384
-
385
- ---
386
-
387
343
  ## 7. Context — correlation ID and transaction ID
388
344
 
389
345
  **What:** One config defines header names; correlation and transaction IDs propagate to all logs and operations inside the same context (e.g. one request).
@@ -413,29 +369,88 @@ After that, every `logger.info(...)` inside the request carries the same `correl
413
369
 
414
370
  ---
415
371
 
416
- ## 8. Fluent APIwithRetention, withSource, withTransactionId
372
+ ## 8. Universal Adapterlog to any backend
417
373
 
418
- **What:** Builders that return new loggers with bound metadata: `withSource('ModuleName')`, `withTransactionId('txn-123')`, `withRetention({ policy: 'SOX', years: 5 })`. Every log from that logger carries that data.
374
+ **What:** Send each log to PostgreSQL, MongoDB, Elasticsearch, S3, etc. by implementing a single `executor`. No vendor lock-in. You define **once** how the log entry maps to your schema; the executor only receives that mapped object and persists it (ORM, raw client, HTTP). If `executor` throws, SyntropyLog logs the error and continues (Silent Observer).
419
375
 
420
- **How:**
376
+ **How:** Use `AdapterTransport` + `UniversalAdapter` + `UniversalLogFormatter`. Three steps:
377
+
378
+ ---
379
+
380
+ ### 1. Define the mapping (outside the executor)
381
+
382
+ Define how each log entry field maps to your persistence shape. One place, no repetition in code. Use `UniversalLogFormatter` with a `mapping` object: keys = your schema (e.g. DB columns), values = path in the log entry.
383
+
384
+ | Log entry path | Meaning |
385
+ |----------------|--------|
386
+ | `level` | Log level |
387
+ | `message` | Message string |
388
+ | `serviceName` | From init config |
389
+ | `correlationId` | From context |
390
+ | `timestamp` | ISO string |
391
+ | `meta` | Merged metadata (use as payload/JSON column) |
392
+
393
+ Example: map to a table with columns `level`, `message`, `serviceName`, `correlationId`, `payload`, `timestamp`.
421
394
 
422
395
  ```typescript
423
- const log = syntropyLog.getLogger();
396
+ import { UniversalLogFormatter } from 'syntropylog';
424
397
 
425
- const auditLogger = log
426
- .withSource('PaymentService')
427
- .withRetention({ policy: 'SOX_AUDIT_TRAIL', years: 5 });
398
+ const logToDbMapping = {
399
+ level: 'level',
400
+ message: 'message',
401
+ serviceName: 'serviceName',
402
+ correlationId: 'correlationId',
403
+ payload: 'meta',
404
+ timestamp: 'timestamp',
405
+ };
428
406
 
429
- auditLogger.info({ userId: 123, action: 'payment' }, 'Payment processed');
430
- // Entry includes source and retention; your executor can route by retention.policy
407
+ const formatter = new UniversalLogFormatter({ mapping: logToDbMapping });
431
408
  ```
432
409
 
433
- | Builder | Binds |
434
- |---------|--------|
435
- | `withSource('X')` | `source: 'X'` |
436
- | `withTransactionId('id')` | `transactionId: 'id'` |
437
- | `withRetention({ ... })` | `retention: { ... }` (any JSON) |
438
- | `child({ k: v })` | arbitrary key-value |
410
+ The formatter turns every log entry into an object with exactly those keys. Change the mapping here when your schema changes; the executor stays the same.
411
+
412
+ ---
413
+
414
+ ### 2. The object the executor receives
415
+
416
+ When you attach this formatter to the transport, the `executor` receives **that mapped object** (not the raw log entry). So the executor only sees something like `{ level, message, serviceName, correlationId, payload, timestamp }`. Your only job in the executor is to **persist** that object.
417
+
418
+ ---
419
+
420
+ ### 3. Sending that object to your backend
421
+
422
+ One mapping produces one object shape. You can send **that same object** to as many backends as you want in a single executor: same `data`, different destinations (e.g. Prisma, TypeORM, Mongoose, Elasticsearch, S3). No field list — the shape comes from the mapping.
423
+
424
+ **Example: one mapped object → three destinations**
425
+
426
+ ```typescript
427
+ import { AdapterTransport, UniversalAdapter } from 'syntropylog';
428
+ import { prisma } from './prisma';
429
+ import { getRepository } from 'typeorm';
430
+ import { SystemLog as TypeOrmSystemLog } from './entities/SystemLog';
431
+ import { SystemLogModel } from './models/SystemLog';
432
+
433
+ const dbTransport = new AdapterTransport({
434
+ name: 'db',
435
+ formatter,
436
+ adapter: new UniversalAdapter({
437
+ executor: async (data) => {
438
+ const row = {
439
+ ...data,
440
+ timestamp: new Date(data.timestamp as string),
441
+ };
442
+ // Same object, three destinations (e.g. Postgres + TypeORM DB + MongoDB)
443
+ await Promise.all([
444
+ prisma.systemLog.create({ data: row }),
445
+ getRepository(TypeOrmSystemLog).save(row),
446
+ SystemLogModel.create(row),
447
+ ]);
448
+ },
449
+ }),
450
+ });
451
+ ```
452
+
453
+ Use one transport and one executor; add or remove destinations in that same block. Use this transport in your `init()` (e.g. in `logger.transports` or `logger.transportList` + `logger.env`).
439
454
 
440
455
  ---
441
456
 
@@ -477,7 +492,33 @@ See [examples/TRANSPORT_POOL_AND_ENV.md](examples/TRANSPORT_POOL_AND_ENV.md) and
477
492
 
478
493
  ---
479
494
 
480
- ## 10. Audit and retention
495
+ ## 10. Fluent API — withRetention, withSource, withTransactionId
496
+
497
+ **What:** Builders that return new loggers with bound metadata: `withSource('ModuleName')`, `withTransactionId('txn-123')`, `withRetention({ policy: 'SOX', years: 5 })`. Every log from that logger carries that data.
498
+
499
+ **How:**
500
+
501
+ ```typescript
502
+ const log = syntropyLog.getLogger();
503
+
504
+ const auditLogger = log
505
+ .withSource('PaymentService')
506
+ .withRetention({ policy: 'SOX_AUDIT_TRAIL', years: 5 });
507
+
508
+ auditLogger.audit({ userId: 123, action: 'payment' }, 'Payment processed');
509
+ // Entry includes source and retention; your executor can route by retention.policy
510
+ ```
511
+
512
+ | Builder | Binds |
513
+ |---------|--------|
514
+ | `withSource('X')` | `source: 'X'` |
515
+ | `withTransactionId('id')` | `transactionId: 'id'` |
516
+ | `withRetention({ ... })` | `retention: { ... }` (any JSON) |
517
+ | `child({ k: v })` | arbitrary key-value |
518
+
519
+ ---
520
+
521
+ ## 11. Audit and retention
481
522
 
482
523
  **What:** The `audit` level is always logged regardless of the configured level. `withRetention(anyJson)` attaches policy metadata (e.g. GDPR, SOX, PCI-DSS) so your `executor` can route to different tables or buckets.
483
524
 
@@ -493,9 +534,9 @@ Your `executor` can read `logEntry.retention?.policy` and persist to the right s
493
534
 
494
535
  ---
495
536
 
496
- ## 11. Lifecycle — init / shutdown
537
+ ## 12. Lifecycle — init / shutdown
497
538
 
498
- **What:** `init()` starts the pipeline and emits `ready` when safe to use. `shutdown()` flushes in-flight logs and closes resources; hook it to SIGTERM/SIGINT so you dont lose logs on exit.
539
+ **What:** `init()` starts the pipeline and emits `ready` when safe to use. `shutdown()` flushes in-flight logs and closes resources; hook it to SIGTERM/SIGINT so you don't lose logs on exit.
499
540
 
500
541
  **How:** Init is shown in Quick Start. Shutdown:
501
542
 
@@ -508,7 +549,7 @@ process.on('SIGTERM', async () => {
508
549
 
509
550
  ---
510
551
 
511
- ## 12. Observability hooks
552
+ ## 13. Observability hooks
512
553
 
513
554
  **What:** Optional callbacks to observe failures without logging throwing: `onLogFailure`, `onTransportError`, `onSerializationFallback`, `onStepError`, `masking.onMaskingError`. Plus `isNativeAddonInUse()` at runtime.
514
555
 
@@ -534,59 +575,93 @@ await syntropyLog.init({
534
575
 
535
576
  ---
536
577
 
537
- ## 13. Matrix in runtime
578
+ ## 14. OpenTelemetry integration
538
579
 
539
- **What:** Change which context fields are visible per level without restart. Security boundary: only field visibility changes; masking and transports stay as set at `init()`.
580
+ **What:** SyntropyLog requires no changes to integrate with OpenTelemetry. Define a formatter, write an executor that calls `otelLogger.emit()`, register it as a transport, and you're done.
540
581
 
541
582
  **How:**
542
583
 
543
584
  ```typescript
544
- syntropyLog.reconfigureLoggingMatrix({
545
- default: ['correlationId'],
546
- info: ['correlationId', 'userId', 'operation'],
547
- error: ['*'],
548
- });
549
- // Restore later with original matrix
550
- ```
551
-
552
- ---
585
+ import { syntropyLog, AdapterTransport, UniversalAdapter, UniversalLogFormatter } from 'syntropylog';
586
+ import { logs, SeverityNumber } from '@opentelemetry/api-logs';
553
587
 
554
- ## 14. Tree-shaking
588
+ // 1. Formatter — maps SyntropyLog fields to OTel shape
589
+ const otelFormatter = new UniversalLogFormatter({
590
+ mapping: { body: 'message', severityText: 'level', timestamp: 'timestamp' },
591
+ includeAllIn: 'attributes',
592
+ });
555
593
 
556
- **What:** Package is published with `sideEffects: false` and ESM so bundlers include only what you import.
594
+ // 2. Severity table
595
+ const SEVERITY_NUMBER: Record<string, number> = {
596
+ trace: 1, debug: 5, info: 9, audit: 9, warn: 13, error: 17, fatal: 21, silent: 0,
597
+ };
557
598
 
558
- **How:** Import only what you use; unused transports and adapters are dropped from the bundle.
599
+ // 3. Executor the bridge to OTel
600
+ function buildOtelExecutor(scopeName: string) {
601
+ return function executor(data: unknown): void {
602
+ const entry = data as { body: string; severityText: string; timestamp: string; attributes?: Record<string, unknown> };
603
+ const otelLogger = logs.getLogger(scopeName);
604
+ const ms = new Date(entry.timestamp).getTime();
605
+ const attrs = entry.attributes ?? {};
606
+ otelLogger.emit({
607
+ timestamp: [Math.floor(ms / 1000), (ms % 1000) * 1_000_000],
608
+ severityNumber: SEVERITY_NUMBER[entry.severityText] ?? SeverityNumber.UNSPECIFIED,
609
+ severityText: entry.severityText.toUpperCase(),
610
+ body: entry.body,
611
+ attributes: attrs,
612
+ traceId: typeof attrs.traceId === 'string' ? attrs.traceId : undefined,
613
+ spanId: typeof attrs.spanId === 'string' ? attrs.spanId : undefined,
614
+ traceFlags: typeof attrs.traceFlags === 'number' ? attrs.traceFlags : 1,
615
+ });
616
+ };
617
+ }
559
618
 
560
- ---
619
+ // 4. Transport
620
+ const otelTransport = new AdapterTransport({
621
+ name: 'otel',
622
+ adapter: new UniversalAdapter({ executor: buildOtelExecutor('my-service') }),
623
+ formatter: otelFormatter,
624
+ });
561
625
 
562
- ## Console transports (default and pretty)
626
+ // 5. Init wait for ready before logging
627
+ async function initializeSyntropyLog() {
628
+ return new Promise<void>((resolve, reject) => {
629
+ syntropyLog.on('ready', () => resolve());
630
+ syntropyLog.on('error', (err) => reject(err));
631
+ syntropyLog.init({
632
+ logger: {
633
+ serviceName: 'my-service',
634
+ level: 'info',
635
+ transportList: { otel: otelTransport },
636
+ },
637
+ loggingMatrix: {
638
+ info: ['correlationId', 'traceId', 'spanId'],
639
+ error: ['*'],
640
+ audit: ['*'],
641
+ },
642
+ });
643
+ });
644
+ }
563
645
 
564
- By default the library outputs **plain JSON** to the console. For colored, human-readable output in development, use a pretty transport:
646
+ // 6. Graceful shutdown
647
+ process.on('SIGTERM', async () => { await syntropyLog.shutdown(); process.exit(0); });
648
+ process.on('SIGINT', async () => { await syntropyLog.shutdown(); process.exit(0); });
565
649
 
566
- | Transport | Style |
567
- |-----------|--------|
568
- | *(default)* | Plain JSON |
569
- | `ClassicConsoleTransport` | Single-line, colored |
570
- | `PrettyConsoleTransport` | Pretty-printed, colored |
571
- | `CompactConsoleTransport` | Compact one-liner, colored |
572
- | `ColorfulConsoleTransport` | Full-line colored |
650
+ async function main() {
651
+ await initializeSyntropyLog();
652
+ const log = syntropyLog.getLogger();
653
+ log.info({ traceId: 'abc123', spanId: 'def456' }, 'Payment processed');
654
+ }
655
+ main();
656
+ ```
573
657
 
574
- Colors use built-in ANSI; no chalk. Disabled when stdout is not a TTY or when `NO_COLOR` is set.
658
+ Per-call routing works the same as any other transport: `.override('otel')`, `.remove('otel')`, `.add('otel')`.
575
659
 
576
- ```typescript
577
- import { ClassicConsoleTransport } from 'syntropylog';
578
- syntropyLog.init({
579
- logger: {
580
- level: 'info',
581
- serviceName: 'my-app',
582
- transports: [new ClassicConsoleTransport()],
583
- },
584
- });
585
- ```
660
+ For the full guide (formatter options, severity table, middleware injection, per-call routing): [docs/opentelemetry-integration.md](docs/opentelemetry-integration.md). [También en español](doc-es/integracion-opentelemetry.md).
586
661
 
587
662
  ---
588
663
 
589
- ## Reconfiguration in runtime (hot)
664
+ ## 15. Reconfiguration in runtime (hot)
590
665
 
591
666
  **The only things you can reconfigure without restart are:**
592
667
 
@@ -661,7 +736,7 @@ Secure this route (e.g. auth, internal only). When debugging in a POD is finishe
661
736
 
662
737
  **Filesystem access:** The package only reads the files described below; it does not scan or read arbitrary paths.
663
738
 
664
- - **Native addon loader** (`syntropylog-native`): Reads only (1) the presence of native `.node` binaries inside the packages own directory (`__dirname`) to choose the correct build for the current OS/arch, and (2) on Linux only, the system `ldd` binary (e.g. `/usr/bin/ldd`) to detect musl vs glibc. No user or application files are read.
739
+ - **Native addon loader** (`syntropylog-native`): Reads only (1) the presence of native `.node` binaries inside the package's own directory (`__dirname`) to choose the correct build for the current OS/arch, and (2) on Linux only, the system `ldd` binary (e.g. `/usr/bin/ldd`) to detect musl vs glibc. No user or application files are read.
665
740
  Configuration is passed to `init()` only; the package does not load config from files.
666
741
 
667
742
  | Dynamically configurable | Fixed at init |
@@ -674,21 +749,30 @@ Configuration is passed to `init()` only; the package does not load config from
674
749
 
675
750
  **English (primary)**
676
751
 
677
- - **This README** — Full picture, Quick Start, and a How section per feature (above).
752
+ - **This README** — Full picture, Quick Start, and a "How" section per feature (above).
678
753
  - **[Examples repository](https://github.com/Syntropysoft/syntropylog-examples)** — Runnable examples 01–17: setup, context, transports, HTTP correlation, testing, benchmark.
679
754
  - **[Transport pool and per-environment routing](examples/TRANSPORT_POOL_AND_ENV.md)** — `transportList`, `env`, override/add/remove; runnable [TransportPoolExample.ts](examples/TransportPoolExample.ts).
755
+ - **[Features and examples](docs/features-and-examples.md)** — Canonical stack list with explanations and code examples; aligned with the benchmark report.
756
+ - **[Benchmark report (throughput + memory)](docs/benchmark-report.md)** — Run `pnpm run bench` or `pnpm run bench:memory` from repo root.
757
+ - **[Benchmark memory run](docs/benchmark-memory-run.md)** — Detailed memory figures; compare native vs JS: `pnpm run bench` vs `SYNTROPYLOG_NATIVE_DISABLE=1 pnpm run bench`.
680
758
  - **[Sensitive key aliases](docs/SENSITIVE_KEY_ALIASES.md)** — Recommended use of `maskEnum` (single object, declarative); full list of `MASK_KEY_*` and grouped arrays.
681
759
  - **[Sonar: exception for a specific file](docs/SONAR_FILE_EXCEPTION.md)** — How to add a Sonar exception when you have your own file with sensitive words or aliases (so it does not block deploy).
760
+ - **[OpenTelemetry integration](docs/opentelemetry-integration.md)** — How to send logs to OTel using `UniversalAdapter` + `UniversalLogFormatter`; no library changes needed.
761
+ - **[Rust addon — build from source](docs/building-native-addon.md)** — Build instructions for macOS, Windows, Linux.
762
+ - **[Improvement plan & roadmap](docs/code-improvement-analysis-and-plan.md)** — Backlog and phased plan.
763
+ - **[Rust implementation plan](docs/rust-implementation-plan.md)** — Native addon checklist; links to [rust-pipeline-optimization.md](docs/rust-pipeline-optimization.md).
764
+ - **[Testing mocks](docs/testing-mocks.md)** — Public testing API: `SyntropyLogMock`, `createTestHelper`, etc.
682
765
  - **[CONTRIBUTING.md](./CONTRIBUTING.md)** — How to contribute.
683
766
  - **[SECURITY.md](./SECURITY.md)** — Security policy and environment variables.
684
767
 
685
768
  **Spanish (ES)**
686
769
 
687
- - **[Features and examples](doc-es/caracteristicas-y-ejemplos.md)** — Canonical stack list with explanations and code examples; aligned with the benchmark report.
688
- - **[Benchmark report (throughput + memory)](doc-es/benchmark-memory-run.md)** — Run `pnpm run bench` or `pnpm run bench:memory` from repo root. Compare native vs JS: `pnpm run bench` vs `SYNTROPYLOG_NATIVE_DISABLE=1 pnpm run bench`.
689
- - **[Rust addon build from source](doc-es/building-native-addon.es.md)**.
690
- - **[Improvement plan & roadmap](doc-es/code-improvement-analysis-and-plan.md)** — Backlog and phased plan.
691
- - **[Rust implementation plan](doc-es/rust-implementation-plan.md)** — Native addon checklist; links to [rust-pipeline-optimization.md](doc-es/rust-pipeline-optimization.md).
770
+ - **[Características y ejemplos](doc-es/caracteristicas-y-ejemplos.md)** — Lista canónica del stack con explicaciones y ejemplos de código.
771
+ - **[Informe de benchmarks (throughput + memoria)](doc-es/benchmark-memory-run.md)** — `pnpm run bench` o `pnpm run bench:memory` desde la raíz del repo.
772
+ - **[Addon Rust — compilar desde fuente](doc-es/building-native-addon.es.md)**.
773
+ - **[Plan de mejoras y roadmap](doc-es/code-improvement-analysis-and-plan.md)** — Backlog y plan por fases.
774
+ - **[Plan de implementación Rust](doc-es/rust-implementation-plan.md)** — Checklist del addon nativo.
775
+ - **[Integración OpenTelemetry](doc-es/integracion-opentelemetry.md)** — Cómo enviar logs a OTel con `UniversalAdapter` + `UniversalLogFormatter`.
692
776
 
693
777
  ---
694
778
 
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "syntropylog",
3
- "version": "0.12.7",
3
+ "version": "0.12.9",
4
4
  "engines": {
5
5
  "node": ">=20.0.0"
6
6
  },
7
- "description": "An instance manager with observability for Node.js applications",
7
+ "description": "Structured observability framework for Node.js — declarative logging, masking, compliance, and tracing for high-demand environments.",
8
8
  "keywords": [
9
9
  "instance-manager",
10
10
  "instance-management",