logixia 1.10.3 → 1.11.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 +121 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/{index-Co47qPnq.d.mts → index-CSFeEGLb.d.ts} +32 -2
- package/dist/index-CSFeEGLb.d.ts.map +1 -0
- package/dist/{index-F-A7hg1u.d.ts → index-Cw-sN_0_.d.mts} +32 -2
- package/dist/index-Cw-sN_0_.d.mts.map +1 -0
- package/dist/index.d.mts +251 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +251 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +499 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +488 -7
- package/dist/index.mjs.map +1 -1
- package/dist/{logitron-logger.module-BLT1y5Iq.d.ts → logitron-logger.module-DGwNfjBX.d.mts} +21 -2
- package/dist/logitron-logger.module-DGwNfjBX.d.mts.map +1 -0
- package/dist/{logitron-logger.module-Dlf5GwJ9.js → logitron-logger.module-DHFampon.js} +69 -2
- package/dist/logitron-logger.module-DHFampon.js.map +1 -0
- package/dist/{logitron-logger.module-B8NklSC4.d.mts → logitron-logger.module-DfyBsT_K.d.ts} +21 -2
- package/dist/logitron-logger.module-DfyBsT_K.d.ts.map +1 -0
- package/dist/{logitron-logger.module-BBC9nO5q.mjs → logitron-logger.module-QYBy_Kkq.mjs} +69 -2
- package/dist/logitron-logger.module-QYBy_Kkq.mjs.map +1 -0
- package/dist/middleware.d.mts +1 -1
- package/dist/middleware.d.ts +1 -1
- package/dist/nest.d.mts +2 -2
- package/dist/nest.d.ts +2 -2
- package/dist/nest.js +1 -1
- package/dist/nest.mjs +1 -1
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/transports.d.mts +1 -1
- package/dist/transports.d.mts.map +1 -1
- package/dist/transports.d.ts +1 -1
- package/package.json +1 -1
- package/dist/index-Co47qPnq.d.mts.map +0 -1
- package/dist/index-F-A7hg1u.d.ts.map +0 -1
- package/dist/logitron-logger.module-B8NklSC4.d.mts.map +0 -1
- package/dist/logitron-logger.module-BBC9nO5q.mjs.map +0 -1
- package/dist/logitron-logger.module-BLT1y5Iq.d.ts.map +0 -1
- package/dist/logitron-logger.module-Dlf5GwJ9.js.map +0 -1
package/README.md
CHANGED
|
@@ -108,6 +108,11 @@ await logger.info('Server started', { port: 3000 });
|
|
|
108
108
|
- [Transport filter predicate](#transport-filter-predicate)
|
|
109
109
|
- [Log search](#log-search)
|
|
110
110
|
- [OpenTelemetry](#opentelemetry)
|
|
111
|
+
- [OTLP logs export](#otlp-logs-export-opentelemetry-native)
|
|
112
|
+
- [Wide events / canonical log lines](#wide-events--canonical-log-lines)
|
|
113
|
+
- [Dynamic runtime reconfiguration](#dynamic-runtime-reconfiguration)
|
|
114
|
+
- [Adaptive sampling](#adaptive-sampling)
|
|
115
|
+
- [Robust serialization](#robust-serialization)
|
|
111
116
|
- [Graceful shutdown](#graceful-shutdown)
|
|
112
117
|
- [Plugin / extension API](#plugin--extension-api)
|
|
113
118
|
- [Writing a plugin](#writing-a-plugin)
|
|
@@ -1640,10 +1645,126 @@ app.post('/checkout', async (req, res) => {
|
|
|
1640
1645
|
|
|
1641
1646
|
---
|
|
1642
1647
|
|
|
1648
|
+
## OTLP logs export (OpenTelemetry-native)
|
|
1649
|
+
|
|
1650
|
+
logixia doesn't just _read_ the active OTel span (above) — it can _emit_ logs in the **OTLP/HTTP** format so they land in any OpenTelemetry backend (Grafana Loki, OpenObserve, Better Stack, Axiom, Datadog, SigNoz…) already correlated with traces. No `@opentelemetry/*` packages required (that JS API is still alpha) — the OTLP JSON is built directly, with proper `SeverityNumber` mapping (DEBUG=5, INFO=9, WARN=13, ERROR=17) and resource attributes.
|
|
1651
|
+
|
|
1652
|
+
```typescript
|
|
1653
|
+
import { OtlpLogTransport } from 'logixia';
|
|
1654
|
+
|
|
1655
|
+
const logger = createLogger({
|
|
1656
|
+
appName: 'api',
|
|
1657
|
+
transports: {
|
|
1658
|
+
custom: [
|
|
1659
|
+
new OtlpLogTransport({
|
|
1660
|
+
url: 'http://localhost:4318/v1/logs',
|
|
1661
|
+
serviceName: 'api',
|
|
1662
|
+
serviceVersion: '1.4.0',
|
|
1663
|
+
environment: 'production',
|
|
1664
|
+
headers: { 'x-api-key': process.env.OTLP_KEY! },
|
|
1665
|
+
}),
|
|
1666
|
+
],
|
|
1667
|
+
},
|
|
1668
|
+
});
|
|
1669
|
+
// Every log is exported as an OTel LogRecord with traceId/spanId for native
|
|
1670
|
+
// trace↔log correlation. Buffers drain on close() — no loss on shutdown.
|
|
1671
|
+
```
|
|
1672
|
+
|
|
1673
|
+
---
|
|
1674
|
+
|
|
1675
|
+
## Wide events / canonical log lines
|
|
1676
|
+
|
|
1677
|
+
Instead of scattering a request's story across many narrow log lines that you have to JOIN during an incident, emit **one dense, structured event per request** — the "canonical log line" (Stripe) / "wide event" (Honeycomb, _Observability 2.0_) pattern. Fields accumulate as the request flows through middleware and business logic via `AsyncLocalStorage`, then the whole event is emitted **once** — in a teardown path so it fires even on errors.
|
|
1678
|
+
|
|
1679
|
+
```typescript
|
|
1680
|
+
import { wideEventMiddleware, addEventFields } from 'logixia';
|
|
1681
|
+
|
|
1682
|
+
// One canonical line per request, auto-emitted on response finish/close:
|
|
1683
|
+
app.use(wideEventMiddleware(logger)); // pre-fills method, url, ip, status, duration
|
|
1684
|
+
|
|
1685
|
+
app.get('/checkout', (req, res) => {
|
|
1686
|
+
addEventFields({ userId: req.user.id, planTier: 'pro' }); // from anywhere
|
|
1687
|
+
addEventFields({ dbQueries: 4, cacheHit: true });
|
|
1688
|
+
res.json({ ok: true });
|
|
1689
|
+
// → ONE log line: { method, url, statusCode, durationMs, userId, planTier,
|
|
1690
|
+
// dbQueries, cacheHit, traceId } — no JOINs at query time
|
|
1691
|
+
});
|
|
1692
|
+
```
|
|
1693
|
+
|
|
1694
|
+
Or wrap any unit of work manually — the event is emitted once, even if the callback throws:
|
|
1695
|
+
|
|
1696
|
+
```typescript
|
|
1697
|
+
import { withWideEvent, addEventFields } from 'logixia';
|
|
1698
|
+
|
|
1699
|
+
await withWideEvent(logger, { job: 'reindex' }, async () => {
|
|
1700
|
+
addEventFields({ shard: 3 });
|
|
1701
|
+
await doWork(); // throws? → event still emitted with { error: true, errorMessage }
|
|
1702
|
+
});
|
|
1703
|
+
```
|
|
1704
|
+
|
|
1705
|
+
---
|
|
1706
|
+
|
|
1707
|
+
## Dynamic runtime reconfiguration
|
|
1708
|
+
|
|
1709
|
+
Change log levels in a **running** process — no restart — to chase a bug without raising global volume. This is the single most-requested feature across the Winston ([#1107](https://github.com/winstonjs/winston/issues/1107)) and Pino ([#206](https://github.com/pinojs/pino/issues/206)) trackers; logixia ships it first-class.
|
|
1710
|
+
|
|
1711
|
+
```typescript
|
|
1712
|
+
import { registerLevelSignal, createLevelControlHandler } from 'logixia';
|
|
1713
|
+
|
|
1714
|
+
// 1. Per-namespace level, live:
|
|
1715
|
+
logger.setNamespaceLevels({ 'db.*': 'debug', '*': 'info' }); // db.* → debug now
|
|
1716
|
+
|
|
1717
|
+
// 2. Cycle the global level with one signal (kill -USR2 <pid>):
|
|
1718
|
+
registerLevelSignal(logger); // info → debug → trace → … → info
|
|
1719
|
+
|
|
1720
|
+
// 3. Ops endpoint (mount behind your auth):
|
|
1721
|
+
app.all('/admin/log-level', createLevelControlHandler(logger));
|
|
1722
|
+
// GET → { level, namespaceLevels }
|
|
1723
|
+
// POST { "level": "debug", "namespaceLevels": { "db.*": "trace" } }
|
|
1724
|
+
```
|
|
1725
|
+
|
|
1726
|
+
---
|
|
1727
|
+
|
|
1728
|
+
## Adaptive sampling
|
|
1729
|
+
|
|
1730
|
+
On top of static / per-level / trace-consistent sampling + a token-bucket rate cap, logixia can **boost the sample rate automatically during an incident** — so high-volume cost control never costs you the logs that matter most. When the windowed error rate crosses a threshold, sampling lifts toward 1.0; in steady state it relaxes back to the base rate.
|
|
1731
|
+
|
|
1732
|
+
```typescript
|
|
1733
|
+
const logger = createLogger({
|
|
1734
|
+
appName: 'api',
|
|
1735
|
+
sampling: {
|
|
1736
|
+
rate: 0.1, // keep 10% in steady state
|
|
1737
|
+
adaptive: {
|
|
1738
|
+
errorRateThreshold: 0.05, // ≥5% errors in the window…
|
|
1739
|
+
boostRate: 1.0, // …keep everything until it subsides
|
|
1740
|
+
windowMs: 10_000,
|
|
1741
|
+
},
|
|
1742
|
+
},
|
|
1743
|
+
});
|
|
1744
|
+
```
|
|
1745
|
+
|
|
1746
|
+
---
|
|
1747
|
+
|
|
1748
|
+
## Robust serialization
|
|
1749
|
+
|
|
1750
|
+
logixia never throws while serializing a log payload. Circular references become `[Circular]`, and — going beyond what Winston/Pino do — **BigInt** is handled (raw `JSON.stringify` throws on it) and you can opt into **true round-trippable decycling** for shared/circular graphs:
|
|
1751
|
+
|
|
1752
|
+
```typescript
|
|
1753
|
+
import { safeStringify, decycleValue, retrocycle } from 'logixia';
|
|
1754
|
+
|
|
1755
|
+
safeStringify({ id: 9007199254740993n, self: obj }); // BigInt + cycle safe
|
|
1756
|
+
const json = safeStringify(graph, { decycle: true }); // $ref pointers, not [Circular]
|
|
1757
|
+
const restored = retrocycle(JSON.parse(json)); // shared refs reconstructed
|
|
1758
|
+
```
|
|
1759
|
+
|
|
1760
|
+
---
|
|
1761
|
+
|
|
1643
1762
|
## Graceful shutdown
|
|
1644
1763
|
|
|
1645
1764
|
Ensures all buffered log entries are flushed to every transport before the process exits. Critical for database and analytics transports that batch writes.
|
|
1646
1765
|
|
|
1766
|
+
> **Reliability guarantee — no log loss on shutdown.** The most painful, still-open bug in the most popular Node logger is exactly this: Pino's [#1705](https://github.com/pinojs/pino/issues/1705) ("Logs are not flushed, missing log entries after `process.exit()`") has been open since 2023, with its maintainer noting a race condition in the transport flush path that "I won't be able to fix it anytime soon." It recurs across [#542](https://github.com/pinojs/pino/issues/542), [#1774](https://github.com/pinojs/pino/issues/1774), [#1889](https://github.com/pinojs/pino/issues/1889), and [#2054](https://github.com/pinojs/pino/issues/2054). logixia is built the other way around: **every** batching/async transport (database, analytics, CloudWatch/GCP/Azure, worker-thread, browser, OTLP) drains its buffer synchronously on `close()` with bounded retry, and the SIGTERM/SIGINT handler is guarded against concurrent signals so a second Ctrl+C can't truncate the flush. Each guarantee is covered by a regression test.
|
|
1767
|
+
|
|
1647
1768
|
The simplest approach is to set `gracefulShutdown: true` in config — logixia registers SIGTERM and SIGINT handlers automatically:
|
|
1648
1769
|
|
|
1649
1770
|
```typescript
|