logixia 1.3.1 → 1.4.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 +848 -7
- package/dist/.tsbuildinfo +1 -1
- package/dist/{index-iDTW2-eY.d.mts → index-Ium497V3.d.mts} +180 -2
- package/dist/index-Ium497V3.d.mts.map +1 -0
- package/dist/{index-CHIsdA9n.d.ts → index-t-ActikQ.d.ts} +180 -2
- package/dist/index-t-ActikQ.d.ts.map +1 -0
- package/dist/index.d.mts +458 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +458 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +436 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +416 -3
- package/dist/index.mjs.map +1 -1
- package/dist/{logitron-logger.module-Bq3_KckP.d.ts → logitron-logger.module-9vOhKWDG.d.ts} +120 -6
- package/dist/logitron-logger.module-9vOhKWDG.d.ts.map +1 -0
- package/dist/{logitron-logger.module-2AzkadqZ.mjs → logitron-logger.module-C939PIEV.mjs} +331 -10
- package/dist/logitron-logger.module-C939PIEV.mjs.map +1 -0
- package/dist/{logitron-logger.module-BqNKp0Fs.js → logitron-logger.module-CCnX7GwK.js} +383 -8
- package/dist/logitron-logger.module-CCnX7GwK.js.map +1 -0
- package/dist/{logitron-logger.module-DW_mmXbj.d.mts → logitron-logger.module-SArymP6b.d.mts} +120 -6
- package/dist/logitron-logger.module-SArymP6b.d.mts.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 +2 -2
- package/dist/nest.mjs +2 -2
- package/dist/{promise-DaiZ2BaH.js → promise-BI-3eI4n.js} +285 -128
- package/dist/promise-BI-3eI4n.js.map +1 -0
- package/dist/{promise-C4pQPcK4.mjs → promise-BrZcjavs.mjs} +285 -128
- package/dist/promise-BrZcjavs.mjs.map +1 -0
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +7 -1
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +7 -1
- package/dist/testing.mjs.map +1 -1
- package/dist/{transport.manager-5VVdqS3o.mjs → transport.manager-DR7TLXQT.mjs} +82 -4
- package/dist/transport.manager-DR7TLXQT.mjs.map +1 -0
- package/dist/{transport.manager-DCOm4uIQ.js → transport.manager-DVTM978M.js} +82 -4
- package/dist/transport.manager-DVTM978M.js.map +1 -0
- package/dist/transports.d.mts +61 -168
- package/dist/transports.d.mts.map +1 -1
- package/dist/transports.d.ts +61 -168
- package/dist/transports.d.ts.map +1 -1
- package/dist/transports.js +1 -1
- package/dist/transports.mjs +1 -1
- package/package.json +62 -1
- package/dist/index-CHIsdA9n.d.ts.map +0 -1
- package/dist/index-iDTW2-eY.d.mts.map +0 -1
- package/dist/logitron-logger.module-2AzkadqZ.mjs.map +0 -1
- package/dist/logitron-logger.module-Bq3_KckP.d.ts.map +0 -1
- package/dist/logitron-logger.module-BqNKp0Fs.js.map +0 -1
- package/dist/logitron-logger.module-DW_mmXbj.d.mts.map +0 -1
- package/dist/promise-C4pQPcK4.mjs.map +0 -1
- package/dist/promise-DaiZ2BaH.js.map +0 -1
- package/dist/transport.manager-5VVdqS3o.mjs.map +0 -1
- package/dist/transport.manager-DCOm4uIQ.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<strong>The async-first logging library that ships complete.</strong><br/>
|
|
5
|
-
TypeScript-first · Non-blocking by design · NestJS · Database · Tracing · OTel
|
|
5
|
+
TypeScript-first · Non-blocking by design · NestJS · Database · Cloud · Tracing · OTel · Browser
|
|
6
6
|
</p>
|
|
7
7
|
|
|
8
8
|
<p align="center">
|
|
@@ -41,7 +41,7 @@ npm install winston winston-daily-rotate-file
|
|
|
41
41
|
npm install logixia
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
logixia ships **console + file rotation + database + request tracing + NestJS module + field redaction + log search + OpenTelemetry** in one package — non-blocking on every transport, zero extra installs.
|
|
44
|
+
logixia ships **console + file rotation + database + request tracing + NestJS module + field redaction + log search + OpenTelemetry + plugin API + Prometheus metrics + visual TUI explorer** in one package — non-blocking on every transport, zero extra installs.
|
|
45
45
|
|
|
46
46
|
```typescript
|
|
47
47
|
import { createLogger } from 'logixia';
|
|
@@ -82,6 +82,10 @@ await logger.info('Server started', { port: 3000 });
|
|
|
82
82
|
- [Analytics](#analytics)
|
|
83
83
|
- [Multiple transports simultaneously](#multiple-transports-simultaneously)
|
|
84
84
|
- [Custom transport](#custom-transport)
|
|
85
|
+
- [Cloud adapters](#cloud-adapters)
|
|
86
|
+
- [AWS CloudWatch](#aws-cloudwatch)
|
|
87
|
+
- [Google Cloud Logging](#google-cloud-logging)
|
|
88
|
+
- [Azure Monitor](#azure-monitor)
|
|
85
89
|
- [Request tracing](#request-tracing)
|
|
86
90
|
- [Core trace utilities](#core-trace-utilities)
|
|
87
91
|
- [Express / Fastify middleware](#express--fastify-middleware)
|
|
@@ -89,6 +93,14 @@ await logger.info('Server started', { port: 3000 });
|
|
|
89
93
|
- [Kafka trace interceptor](#kafka-trace-interceptor)
|
|
90
94
|
- [WebSocket trace interceptor](#websocket-trace-interceptor)
|
|
91
95
|
- [NestJS integration](#nestjs-integration)
|
|
96
|
+
- [@LogMethod decorator](#logmethod-decorator)
|
|
97
|
+
- [LogixiaExceptionFilter](#logixiaexceptionfilter)
|
|
98
|
+
- [Correlation ID propagation](#correlation-id-propagation)
|
|
99
|
+
- [Express middleware](#correlation-express-middleware)
|
|
100
|
+
- [Fastify hook](#correlation-fastify-hook)
|
|
101
|
+
- [Outbound fetch / axios](#outbound-fetch--axios)
|
|
102
|
+
- [Kafka / SQS helpers](#kafka--sqs-helpers)
|
|
103
|
+
- [Browser support](#browser-support)
|
|
92
104
|
- [Log redaction](#log-redaction)
|
|
93
105
|
- [Timer API](#timer-api)
|
|
94
106
|
- [Field management](#field-management)
|
|
@@ -96,8 +108,20 @@ await logger.info('Server started', { port: 3000 });
|
|
|
96
108
|
- [Log search](#log-search)
|
|
97
109
|
- [OpenTelemetry](#opentelemetry)
|
|
98
110
|
- [Graceful shutdown](#graceful-shutdown)
|
|
111
|
+
- [Plugin / extension API](#plugin--extension-api)
|
|
112
|
+
- [Writing a plugin](#writing-a-plugin)
|
|
113
|
+
- [Registering plugins globally](#registering-plugins-globally)
|
|
114
|
+
- [Per-logger plugins](#per-logger-plugins)
|
|
115
|
+
- [Cancelling a log entry](#cancelling-a-log-entry)
|
|
116
|
+
- [Metrics → Prometheus](#metrics--prometheus)
|
|
117
|
+
- [Quick start (counters)](#quick-start-counters)
|
|
118
|
+
- [Histograms](#histograms)
|
|
119
|
+
- [Gauges](#gauges)
|
|
120
|
+
- [Exposing the /metrics endpoint](#exposing-the-metrics-endpoint)
|
|
121
|
+
- [Metric configuration reference](#metric-configuration-reference)
|
|
99
122
|
- [Logger instance API](#logger-instance-api)
|
|
100
123
|
- [CLI tool](#cli-tool)
|
|
124
|
+
- [explore — Visual TUI log explorer](#explore--visual-tui-log-explorer)
|
|
101
125
|
- [Configuration reference](#configuration-reference)
|
|
102
126
|
- [Contributing](#contributing)
|
|
103
127
|
- [License](#license)
|
|
@@ -112,16 +136,22 @@ logixia takes a different approach: **everything ships built-in, and nothing blo
|
|
|
112
136
|
|
|
113
137
|
- **Async by design** — every log call is non-blocking, even to file and database transports
|
|
114
138
|
- **Built-in database transports** — PostgreSQL, MySQL, MongoDB, SQLite with zero extra drivers
|
|
115
|
-
- **
|
|
139
|
+
- **Cloud adapters** — AWS CloudWatch (EMF), Google Cloud Logging, and Azure Monitor out of the box
|
|
140
|
+
- **NestJS module** — plug in with `LogixiaLoggerModule.forRoot()`, inject anywhere in the DI tree; `@LogMethod()` for auto-logging method entry/exit
|
|
116
141
|
- **File rotation** — `maxSize`, `maxFiles`, gzip archive, time-based rotation — no extra packages needed
|
|
117
142
|
- **Log search** — query your in-memory log store without shipping to an external service
|
|
118
143
|
- **Field redaction** — mask passwords, tokens, and PII before they touch any transport; supports dot-notation paths and regex patterns
|
|
119
144
|
- **Request tracing** — `AsyncLocalStorage`-based trace propagation with no manual thread-locals; includes Kafka and WebSocket interceptors
|
|
145
|
+
- **Correlation ID propagation** — auto-generate and forward `X-Correlation-ID` through `fetch`, axios, Kafka, and SQS across microservice boundaries
|
|
146
|
+
- **Browser support** — tree-shakeable `logixia/browser` entry point with console and remote batch transports; no Node.js built-ins
|
|
120
147
|
- **OpenTelemetry** — W3C `traceparent` and `tracestate` support, zero extra dependencies
|
|
121
148
|
- **Multi-transport** — write to console, file, and database concurrently with one log call
|
|
122
149
|
- **TypeScript-first** — typed log entries, typed metadata, custom-level IntelliSense throughout
|
|
123
150
|
- **Adaptive log level** — auto-configures based on `NODE_ENV` and CI environment
|
|
124
|
-
- **Custom transports** — ship to Slack,
|
|
151
|
+
- **Custom transports** — ship to Slack, PagerDuty, S3, or anywhere else via a simple interface
|
|
152
|
+
- **Plugin / extension API** — lifecycle hooks (`onInit`, `onLog`, `onError`, `onShutdown`); plugins can mutate or cancel log entries; register globally or per-logger
|
|
153
|
+
- **Prometheus metrics** — turn log events into counters, histograms, and gauges with zero code; expose `GET /metrics` in Prometheus text format; works with any HTTP framework
|
|
154
|
+
- **Visual TUI explorer** — `logixia explore` opens a full-screen terminal log browser with real-time search, level filtering, syntax-highlighted JSON detail panel, stack trace rendering, and one-key export to JSON / CSV / NDJSON
|
|
125
155
|
|
|
126
156
|
---
|
|
127
157
|
|
|
@@ -133,16 +163,22 @@ logixia takes a different approach: **everything ships built-in, and nothing blo
|
|
|
133
163
|
| Async / non-blocking writes | yes | no | no | no |
|
|
134
164
|
| NestJS module (built-in) | yes | no | no | no |
|
|
135
165
|
| Database transports (built-in) | yes | no | no | no |
|
|
166
|
+
| Cloud transports (CW, GCP, Azure) | yes | no | no | no |
|
|
136
167
|
| File rotation (built-in) | yes | pino-roll | winston-daily-rotate-file | no |
|
|
137
168
|
| Multi-transport concurrent | yes | no | yes | no |
|
|
138
169
|
| Log search | yes | no | no | no |
|
|
139
170
|
| Field redaction (built-in) | yes | pino-redact | no | no |
|
|
140
171
|
| Request tracing (AsyncLocalStorage) | yes | no | no | no |
|
|
141
172
|
| Kafka + WebSocket trace interceptors | yes | no | no | no |
|
|
173
|
+
| Correlation ID propagation | yes | no | no | no |
|
|
174
|
+
| Browser / Edge / Bun / Deno support | yes | partial | no | no |
|
|
142
175
|
| OpenTelemetry / W3C headers | yes | no | no | no |
|
|
143
176
|
| Graceful shutdown / flush | yes | no | no | no |
|
|
144
177
|
| Custom log levels | yes | yes | yes | yes |
|
|
145
178
|
| Adaptive log level (NODE_ENV) | yes | no | no | no |
|
|
179
|
+
| Plugin / extension API | yes | no | no | no |
|
|
180
|
+
| Prometheus metrics extraction | yes | no | no | no |
|
|
181
|
+
| Visual TUI log explorer | yes | no | no | no |
|
|
146
182
|
| Actively maintained | yes | yes | yes | no |
|
|
147
183
|
|
|
148
184
|
---
|
|
@@ -195,11 +231,20 @@ const logger = createLogger({
|
|
|
195
231
|
environment: 'production',
|
|
196
232
|
});
|
|
197
233
|
|
|
234
|
+
// ✅ Structured data — machine-readable, searchable, alertable
|
|
198
235
|
await logger.info('Server started', { port: 3000 });
|
|
199
|
-
await logger.warn('High memory usage', { used: '87%' });
|
|
200
|
-
await logger.error('Request failed',
|
|
236
|
+
await logger.warn('High memory usage', { used: '87%', threshold: '80%' });
|
|
237
|
+
await logger.error('Request failed', { orderId: 'ord_123', retryable: true });
|
|
238
|
+
|
|
239
|
+
// ✅ Pass an Error object directly — logixia serializes the full cause chain
|
|
240
|
+
await logger.error(new Error('Connection timeout'));
|
|
241
|
+
|
|
242
|
+
// ❌ Avoid string interpolation — you lose structured fields
|
|
243
|
+
// await logger.info(`Server started on port ${port}`);
|
|
201
244
|
```
|
|
202
245
|
|
|
246
|
+
No `try/catch` needed — logixia swallows transport errors internally so a flaky DB or disk-full condition never crashes your app.
|
|
247
|
+
|
|
203
248
|
Without a `transports` key, logs go to stdout/stderr. Add a `transports` key to write to file, database, or anywhere else — all transports run concurrently.
|
|
204
249
|
|
|
205
250
|
There is also a pre-configured default instance you can import directly:
|
|
@@ -658,7 +703,7 @@ const logger = createLogger({
|
|
|
658
703
|
});
|
|
659
704
|
```
|
|
660
705
|
|
|
661
|
-
The `TransportLogEntry` shape is:
|
|
706
|
+
The `write` method may return `void` or `Promise<void>` — both are supported. The `TransportLogEntry` shape is:
|
|
662
707
|
|
|
663
708
|
```typescript
|
|
664
709
|
interface TransportLogEntry {
|
|
@@ -675,6 +720,102 @@ interface TransportLogEntry {
|
|
|
675
720
|
|
|
676
721
|
---
|
|
677
722
|
|
|
723
|
+
## Cloud adapters
|
|
724
|
+
|
|
725
|
+
logixia ships three production-ready cloud transports. All are batched, non-blocking, and implement the same `flush()` / `close()` lifecycle as the built-in transports. Import and pass directly to the `custom` transport array.
|
|
726
|
+
|
|
727
|
+
### AWS CloudWatch
|
|
728
|
+
|
|
729
|
+
Batches log events and sends them to a CloudWatch Logs stream using `PutLogEvents`. Supports **EMF** (Embedded Metric Format) so numeric fields in your log entries are automatically promoted to CloudWatch Metrics — no separate SDK needed.
|
|
730
|
+
|
|
731
|
+
```typescript
|
|
732
|
+
import { CloudWatchTransport } from 'logixia';
|
|
733
|
+
|
|
734
|
+
const logger = createLogger({
|
|
735
|
+
appName: 'api',
|
|
736
|
+
environment: 'production',
|
|
737
|
+
transports: {
|
|
738
|
+
custom: [
|
|
739
|
+
new CloudWatchTransport({
|
|
740
|
+
region: 'us-east-1', // or set AWS_REGION env var
|
|
741
|
+
logGroupName: '/app/api',
|
|
742
|
+
logStreamName: 'api-server-1', // defaults to hostname + PID
|
|
743
|
+
// Credentials fall back to AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY env vars,
|
|
744
|
+
// or the EC2/ECS/Lambda metadata service — no hard-coding needed.
|
|
745
|
+
batchSize: 100, // default: 100
|
|
746
|
+
flushIntervalMs: 5000, // default: 5000
|
|
747
|
+
emf: true, // emit numeric fields as CloudWatch Metrics
|
|
748
|
+
level: 'warn', // forward only warn+ to CloudWatch
|
|
749
|
+
}),
|
|
750
|
+
],
|
|
751
|
+
},
|
|
752
|
+
});
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
With `emf: true`, any numeric field in your log data is published as a CloudWatch Metric under the `Logixia` namespace:
|
|
756
|
+
|
|
757
|
+
```typescript
|
|
758
|
+
await logger.info('Request completed', { duration: 142, statusCode: 200 });
|
|
759
|
+
// → CloudWatch Metric: Logixia/duration, Logixia/statusCode
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
### Google Cloud Logging
|
|
763
|
+
|
|
764
|
+
Maps logixia levels to GCP `severity` values (`DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`), auto-injects `logging.googleapis.com/trace` for Cloud Trace correlation, and supports Application Default Credentials (ADC) — no service account JSON required when running on GKE / Cloud Run / App Engine.
|
|
765
|
+
|
|
766
|
+
```typescript
|
|
767
|
+
import { GCPTransport } from 'logixia';
|
|
768
|
+
|
|
769
|
+
const logger = createLogger({
|
|
770
|
+
appName: 'api',
|
|
771
|
+
environment: 'production',
|
|
772
|
+
transports: {
|
|
773
|
+
custom: [
|
|
774
|
+
new GCPTransport({
|
|
775
|
+
projectId: 'my-gcp-project', // or set GOOGLE_CLOUD_PROJECT env var
|
|
776
|
+
logName: 'projects/my-gcp-project/logs/api',
|
|
777
|
+
resource: { type: 'k8s_container', labels: { cluster_name: 'prod' } },
|
|
778
|
+
// credentials: { client_email: '...', private_key: '...' }
|
|
779
|
+
// Omit to use ADC (recommended on GCP-hosted infrastructure)
|
|
780
|
+
batchSize: 200,
|
|
781
|
+
flushIntervalMs: 5000,
|
|
782
|
+
}),
|
|
783
|
+
],
|
|
784
|
+
},
|
|
785
|
+
});
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
### Azure Monitor
|
|
789
|
+
|
|
790
|
+
Sends logs to Azure Monitor via the **Logs Ingestion API** (Data Collection Rule). Uses OAuth2 client-credentials to obtain a bearer token automatically.
|
|
791
|
+
|
|
792
|
+
```typescript
|
|
793
|
+
import { AzureMonitorTransport } from 'logixia';
|
|
794
|
+
|
|
795
|
+
const logger = createLogger({
|
|
796
|
+
appName: 'api',
|
|
797
|
+
environment: 'production',
|
|
798
|
+
transports: {
|
|
799
|
+
custom: [
|
|
800
|
+
new AzureMonitorTransport({
|
|
801
|
+
endpoint: 'https://<dce-name>.ingest.monitor.azure.com',
|
|
802
|
+
ruleId: 'dcr-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
|
|
803
|
+
streamName: 'Custom-LogixiaLogs_CL',
|
|
804
|
+
tenantId: process.env.AZURE_TENANT_ID,
|
|
805
|
+
clientId: process.env.AZURE_CLIENT_ID,
|
|
806
|
+
clientSecret: process.env.AZURE_CLIENT_SECRET,
|
|
807
|
+
batchSize: 200,
|
|
808
|
+
flushIntervalMs: 5000,
|
|
809
|
+
}),
|
|
810
|
+
],
|
|
811
|
+
},
|
|
812
|
+
});
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
All three cloud transports expose `flush()` and `close()` so they participate in logixia's [graceful shutdown](#graceful-shutdown) flow automatically.
|
|
816
|
+
|
|
817
|
+
---
|
|
818
|
+
|
|
678
819
|
## Request tracing
|
|
679
820
|
|
|
680
821
|
logixia uses `AsyncLocalStorage` to propagate trace IDs through your entire async call graph automatically — no passing of context objects, no manual threading.
|
|
@@ -908,6 +1049,261 @@ export class OrdersService {
|
|
|
908
1049
|
|
|
909
1050
|
`LogixiaLoggerService` exposes the full `LogixiaLogger` API: `info`, `warn`, `error`, `debug`, `trace`, `verbose`, `logLevel`, `time`, `timeEnd`, `timeAsync`, `setLevel`, `getLevel`, `setContext`, `child`, `close`, `getCurrentTraceId`, and more.
|
|
910
1051
|
|
|
1052
|
+
### @LogMethod decorator
|
|
1053
|
+
|
|
1054
|
+
Automatically logs method entry, exit, duration, and errors — no manual `try/catch` or `logger.debug` calls needed. Works on both sync and async methods. Reads the `logger` property from the class instance (the NestJS convention).
|
|
1055
|
+
|
|
1056
|
+
```typescript
|
|
1057
|
+
import { Injectable } from '@nestjs/common';
|
|
1058
|
+
import { LogixiaLoggerService, LogMethod } from 'logixia';
|
|
1059
|
+
|
|
1060
|
+
@Injectable()
|
|
1061
|
+
export class PaymentService {
|
|
1062
|
+
constructor(private readonly logger: LogixiaLoggerService) {}
|
|
1063
|
+
|
|
1064
|
+
// Logs entry with args, exit with duration, and errors with full stack trace
|
|
1065
|
+
@LogMethod({ level: 'info', logArgs: true, logResult: false })
|
|
1066
|
+
async processPayment(orderId: string, amount: number): Promise<void> {
|
|
1067
|
+
// your business logic — no try/catch needed for logging
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
// Minimal — just tracks duration at debug level
|
|
1071
|
+
@LogMethod()
|
|
1072
|
+
async fetchExchangeRate(currency: string): Promise<number> {
|
|
1073
|
+
return 1.0;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
```
|
|
1077
|
+
|
|
1078
|
+
`@LogMethod` options:
|
|
1079
|
+
|
|
1080
|
+
| Option | Type | Default | Description |
|
|
1081
|
+
| ----------- | -------------------------------- | --------- | ------------------------------------------------------- |
|
|
1082
|
+
| `level` | `'debug' \| 'info' \| 'verbose'` | `'debug'` | Log level for entry / exit messages |
|
|
1083
|
+
| `logArgs` | `boolean` | `true` | Include method arguments in the entry log |
|
|
1084
|
+
| `logResult` | `boolean` | `false` | Include the return value in the exit log |
|
|
1085
|
+
| `logErrors` | `boolean` | `true` | Log errors with stack trace when the method throws |
|
|
1086
|
+
| `label` | `string` | auto | Override the auto-detected `ClassName.methodName` label |
|
|
1087
|
+
|
|
1088
|
+
### LogixiaExceptionFilter
|
|
1089
|
+
|
|
1090
|
+
A global NestJS exception filter that automatically logs unhandled exceptions — HTTP exceptions as `warn`, everything else as `error` — and returns a consistent JSON error shape. Reads the injected `LogixiaLoggerService`; works even without it.
|
|
1091
|
+
|
|
1092
|
+
```typescript
|
|
1093
|
+
// main.ts
|
|
1094
|
+
import { NestFactory } from '@nestjs/core';
|
|
1095
|
+
import { LogixiaExceptionFilter } from 'logixia';
|
|
1096
|
+
import { AppModule } from './app.module';
|
|
1097
|
+
|
|
1098
|
+
async function bootstrap() {
|
|
1099
|
+
const app = await NestFactory.create(AppModule);
|
|
1100
|
+
app.useGlobalFilters(new LogixiaExceptionFilter());
|
|
1101
|
+
await app.listen(3000);
|
|
1102
|
+
}
|
|
1103
|
+
bootstrap();
|
|
1104
|
+
```
|
|
1105
|
+
|
|
1106
|
+
With `LogixiaLoggerModule` set up, inject the service directly so it logs to your configured transports:
|
|
1107
|
+
|
|
1108
|
+
```typescript
|
|
1109
|
+
import { APP_FILTER } from '@nestjs/core';
|
|
1110
|
+
import { LogixiaExceptionFilter, LogixiaLoggerService } from 'logixia';
|
|
1111
|
+
|
|
1112
|
+
// In AppModule providers:
|
|
1113
|
+
{
|
|
1114
|
+
provide: APP_FILTER,
|
|
1115
|
+
useFactory: (logger: LogixiaLoggerService) => new LogixiaExceptionFilter(logger),
|
|
1116
|
+
inject: [LogixiaLoggerService],
|
|
1117
|
+
}
|
|
1118
|
+
```
|
|
1119
|
+
|
|
1120
|
+
The filter returns this shape on error:
|
|
1121
|
+
|
|
1122
|
+
```json
|
|
1123
|
+
{
|
|
1124
|
+
"statusCode": 500,
|
|
1125
|
+
"message": "Internal server error",
|
|
1126
|
+
"timestamp": "2025-03-14T10:22:01.412Z",
|
|
1127
|
+
"path": "/api/orders"
|
|
1128
|
+
}
|
|
1129
|
+
```
|
|
1130
|
+
|
|
1131
|
+
---
|
|
1132
|
+
|
|
1133
|
+
## Correlation ID propagation
|
|
1134
|
+
|
|
1135
|
+
`import { ... } from 'logixia/correlation'`
|
|
1136
|
+
|
|
1137
|
+
In a microservice architecture each incoming request should carry a `correlationId` that flows through every downstream service call, message queue event, and log line — so you can reconstruct the full request trace in any log aggregator.
|
|
1138
|
+
|
|
1139
|
+
logixia ships this as a dedicated `logixia/correlation` sub-package. It uses the same `AsyncLocalStorage` store as the main logger, so every `logger.*` call inside a correlated context automatically includes the ID.
|
|
1140
|
+
|
|
1141
|
+
### Correlation Express middleware
|
|
1142
|
+
|
|
1143
|
+
```typescript
|
|
1144
|
+
import { correlationMiddleware } from 'logixia/correlation';
|
|
1145
|
+
|
|
1146
|
+
// Zero-config — reads X-Correlation-ID / X-Request-ID from the incoming request.
|
|
1147
|
+
// Generates a UUID v4 if no header is present.
|
|
1148
|
+
// Sets X-Correlation-ID on the response.
|
|
1149
|
+
app.use(correlationMiddleware());
|
|
1150
|
+
|
|
1151
|
+
// Custom config:
|
|
1152
|
+
app.use(
|
|
1153
|
+
correlationMiddleware({
|
|
1154
|
+
header: 'X-Correlation-ID', // header to read / write. Default: 'X-Correlation-ID'
|
|
1155
|
+
generateId: () => crypto.randomUUID(),
|
|
1156
|
+
trustIncoming: true, // honour the header from the client. Default: true
|
|
1157
|
+
setResponseHeader: true, // echo the ID back in the response. Default: true
|
|
1158
|
+
})
|
|
1159
|
+
);
|
|
1160
|
+
```
|
|
1161
|
+
|
|
1162
|
+
### Correlation Fastify hook
|
|
1163
|
+
|
|
1164
|
+
```typescript
|
|
1165
|
+
import Fastify from 'fastify';
|
|
1166
|
+
import { correlationFastifyHook } from 'logixia/correlation';
|
|
1167
|
+
|
|
1168
|
+
const app = Fastify();
|
|
1169
|
+
app.addHook('onRequest', correlationFastifyHook());
|
|
1170
|
+
```
|
|
1171
|
+
|
|
1172
|
+
### Outbound fetch / axios
|
|
1173
|
+
|
|
1174
|
+
Every outbound HTTP call made inside a correlated context automatically carries the `X-Correlation-ID` header.
|
|
1175
|
+
|
|
1176
|
+
**fetch:**
|
|
1177
|
+
|
|
1178
|
+
```typescript
|
|
1179
|
+
import { correlationFetch } from 'logixia/correlation';
|
|
1180
|
+
|
|
1181
|
+
// Drop-in replacement for global fetch — forwards the active correlation ID automatically
|
|
1182
|
+
const res = await correlationFetch('https://inventory-service/api/items', {
|
|
1183
|
+
method: 'GET',
|
|
1184
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
1185
|
+
});
|
|
1186
|
+
```
|
|
1187
|
+
|
|
1188
|
+
**axios:**
|
|
1189
|
+
|
|
1190
|
+
```typescript
|
|
1191
|
+
import axios from 'axios';
|
|
1192
|
+
import { createCorrelationAxiosInterceptor } from 'logixia/correlation';
|
|
1193
|
+
|
|
1194
|
+
const client = axios.create({ baseURL: 'https://inventory-service' });
|
|
1195
|
+
createCorrelationAxiosInterceptor(client); // attaches X-Correlation-ID to every request
|
|
1196
|
+
```
|
|
1197
|
+
|
|
1198
|
+
### Kafka / SQS helpers
|
|
1199
|
+
|
|
1200
|
+
```typescript
|
|
1201
|
+
import {
|
|
1202
|
+
buildKafkaCorrelationHeaders, // → { 'X-Correlation-ID': '...', 'X-Request-ID': '...' }
|
|
1203
|
+
extractMessageCorrelationId, // read correlationId from a Kafka/SQS message body
|
|
1204
|
+
childFromRequest, // create a child logger pre-seeded with request context
|
|
1205
|
+
withCorrelationId, // run a callback inside an explicit correlation context
|
|
1206
|
+
getCurrentCorrelationId, // read the active correlation ID (or undefined)
|
|
1207
|
+
generateCorrelationId, // generate a new UUID v4 correlation ID
|
|
1208
|
+
} from 'logixia/correlation';
|
|
1209
|
+
|
|
1210
|
+
// Kafka producer — attach correlation headers to every message
|
|
1211
|
+
const producer = kafka.producer();
|
|
1212
|
+
await producer.send({
|
|
1213
|
+
topic: 'orders',
|
|
1214
|
+
messages: [{ value: JSON.stringify(order), headers: buildKafkaCorrelationHeaders() }],
|
|
1215
|
+
});
|
|
1216
|
+
|
|
1217
|
+
// Kafka consumer — restore context for the handler
|
|
1218
|
+
const correlationId = extractMessageCorrelationId(message.value);
|
|
1219
|
+
withCorrelationId(correlationId, async () => {
|
|
1220
|
+
await orderService.process(message.value);
|
|
1221
|
+
// all logger.* calls inside carry correlationId automatically
|
|
1222
|
+
});
|
|
1223
|
+
|
|
1224
|
+
// Create a child logger pre-loaded with request identifiers
|
|
1225
|
+
const reqLogger = childFromRequest(logger, req);
|
|
1226
|
+
await reqLogger.info('Order created', { orderId: 'ord_123' });
|
|
1227
|
+
// → log includes correlationId, requestId, originService
|
|
1228
|
+
```
|
|
1229
|
+
|
|
1230
|
+
**Standalone context (no HTTP framework):**
|
|
1231
|
+
|
|
1232
|
+
```typescript
|
|
1233
|
+
import { withCorrelationId, generateCorrelationId } from 'logixia/correlation';
|
|
1234
|
+
|
|
1235
|
+
const id = generateCorrelationId(); // UUID v4
|
|
1236
|
+
|
|
1237
|
+
withCorrelationId(id, async () => {
|
|
1238
|
+
await processJob(job);
|
|
1239
|
+
// all nested logger calls carry id
|
|
1240
|
+
});
|
|
1241
|
+
```
|
|
1242
|
+
|
|
1243
|
+
---
|
|
1244
|
+
|
|
1245
|
+
## Browser support
|
|
1246
|
+
|
|
1247
|
+
`import { ... } from 'logixia/browser'`
|
|
1248
|
+
|
|
1249
|
+
The `logixia/browser` entry point is a fully tree-shakeable, Node.js-free logger for browsers, Cloudflare Workers, Deno, Bun, and any other non-Node runtime. It has zero imports from `node:fs`, `node:async_hooks`, `node:worker_threads`, or any other Node.js built-in.
|
|
1250
|
+
|
|
1251
|
+
```typescript
|
|
1252
|
+
import { createBrowserLogger } from 'logixia/browser';
|
|
1253
|
+
|
|
1254
|
+
const logger = createBrowserLogger({
|
|
1255
|
+
appName: 'my-app',
|
|
1256
|
+
minLevel: 'info',
|
|
1257
|
+
pretty: true, // colorized dev-friendly output via console.group
|
|
1258
|
+
});
|
|
1259
|
+
|
|
1260
|
+
logger.info('App loaded', { route: '/home' });
|
|
1261
|
+
logger.warn('Feature flag missing', { flag: 'new-checkout' });
|
|
1262
|
+
logger.error('API call failed', { url: '/api/orders', status: 500 });
|
|
1263
|
+
```
|
|
1264
|
+
|
|
1265
|
+
**Browser console transport** — uses the native `console` API and maps levels to their correct methods (`console.error`, `console.warn`, `console.info`, `console.debug`):
|
|
1266
|
+
|
|
1267
|
+
```typescript
|
|
1268
|
+
import { BrowserLogger, BrowserConsoleTransport } from 'logixia/browser';
|
|
1269
|
+
|
|
1270
|
+
const logger = new BrowserLogger({
|
|
1271
|
+
appName: 'my-app',
|
|
1272
|
+
transports: [new BrowserConsoleTransport({ pretty: true })],
|
|
1273
|
+
});
|
|
1274
|
+
```
|
|
1275
|
+
|
|
1276
|
+
**Remote batch transport** — buffers log entries and ships them to a remote endpoint in batches. Zero `XMLHttpRequest` or Node.js `http` — uses the global `fetch` API:
|
|
1277
|
+
|
|
1278
|
+
```typescript
|
|
1279
|
+
import { BrowserLogger, BrowserRemoteTransport } from 'logixia/browser';
|
|
1280
|
+
|
|
1281
|
+
const logger = new BrowserLogger({
|
|
1282
|
+
appName: 'my-app',
|
|
1283
|
+
transports: [
|
|
1284
|
+
new BrowserRemoteTransport({
|
|
1285
|
+
endpoint: 'https://logs.my-company.com/ingest',
|
|
1286
|
+
batchSize: 20, // flush when 20 entries accumulate
|
|
1287
|
+
flushIntervalMs: 5000, // or every 5 seconds, whichever comes first
|
|
1288
|
+
headers: { Authorization: `Bearer ${TOKEN}` },
|
|
1289
|
+
minLevel: 'warn', // only ship warn+ to the remote endpoint
|
|
1290
|
+
}),
|
|
1291
|
+
],
|
|
1292
|
+
});
|
|
1293
|
+
```
|
|
1294
|
+
|
|
1295
|
+
The following utilities from the main package are also re-exported from `logixia/browser` (safe for non-Node runtimes):
|
|
1296
|
+
|
|
1297
|
+
```typescript
|
|
1298
|
+
import {
|
|
1299
|
+
createTypedLogger, // typed schema-enforced logger factory
|
|
1300
|
+
defineLogSchema, // define a compile-time log schema
|
|
1301
|
+
createOtelBridge, // OpenTelemetry bridge
|
|
1302
|
+
isOtelActive,
|
|
1303
|
+
withOtelSpan,
|
|
1304
|
+
} from 'logixia/browser';
|
|
1305
|
+
```
|
|
1306
|
+
|
|
911
1307
|
---
|
|
912
1308
|
|
|
913
1309
|
## Log redaction
|
|
@@ -1160,6 +1556,273 @@ const { healthy, details } = await logger.healthCheck();
|
|
|
1160
1556
|
|
|
1161
1557
|
---
|
|
1162
1558
|
|
|
1559
|
+
## Plugin / extension API
|
|
1560
|
+
|
|
1561
|
+
logixia's plugin system lets you hook into every stage of the log lifecycle without touching core logger code. Plugins are plain objects that implement one or more lifecycle methods.
|
|
1562
|
+
|
|
1563
|
+
```typescript
|
|
1564
|
+
import type { LogixiaPlugin, LogEntry } from 'logixia';
|
|
1565
|
+
```
|
|
1566
|
+
|
|
1567
|
+
### Writing a plugin
|
|
1568
|
+
|
|
1569
|
+
```typescript
|
|
1570
|
+
const myPlugin: LogixiaPlugin = {
|
|
1571
|
+
name: 'my-plugin',
|
|
1572
|
+
|
|
1573
|
+
// Called once when the logger is constructed (global plugins)
|
|
1574
|
+
// or when .use() is called (per-logger plugins).
|
|
1575
|
+
onInit() {
|
|
1576
|
+
console.log('Plugin initialised');
|
|
1577
|
+
},
|
|
1578
|
+
|
|
1579
|
+
// Called for every log entry before it is formatted and written.
|
|
1580
|
+
// Return the (optionally mutated) entry to let it through,
|
|
1581
|
+
// or return null to silently drop it.
|
|
1582
|
+
onLog(entry: LogEntry): LogEntry | null {
|
|
1583
|
+
// Example: enrich every entry with a deployment tag
|
|
1584
|
+
return { ...entry, data: { ...entry.data, deployId: process.env.DEPLOY_ID } };
|
|
1585
|
+
},
|
|
1586
|
+
|
|
1587
|
+
// Called whenever logger.error() receives an Error object.
|
|
1588
|
+
onError(error: Error, entry?: LogEntry) {
|
|
1589
|
+
// Example: forward to a Sentry-compatible sink
|
|
1590
|
+
externalErrorTracker.capture(error, { extra: entry?.data });
|
|
1591
|
+
},
|
|
1592
|
+
|
|
1593
|
+
// Called during logger.close() — await-able for graceful teardown.
|
|
1594
|
+
async onShutdown() {
|
|
1595
|
+
await flushBufferedEvents();
|
|
1596
|
+
},
|
|
1597
|
+
};
|
|
1598
|
+
```
|
|
1599
|
+
|
|
1600
|
+
### Registering plugins globally
|
|
1601
|
+
|
|
1602
|
+
Plugins registered on `globalPluginRegistry` are automatically seeded into every logger created after the call.
|
|
1603
|
+
|
|
1604
|
+
```typescript
|
|
1605
|
+
import { usePlugin } from 'logixia';
|
|
1606
|
+
|
|
1607
|
+
usePlugin(myPlugin);
|
|
1608
|
+
|
|
1609
|
+
// All loggers created from this point forward will run myPlugin.
|
|
1610
|
+
const logger = createLogger({
|
|
1611
|
+
/* ... */
|
|
1612
|
+
});
|
|
1613
|
+
```
|
|
1614
|
+
|
|
1615
|
+
### Per-logger plugins
|
|
1616
|
+
|
|
1617
|
+
Register or remove plugins on a specific logger instance at any time:
|
|
1618
|
+
|
|
1619
|
+
```typescript
|
|
1620
|
+
const logger = createLogger({ context: 'PaymentService' });
|
|
1621
|
+
|
|
1622
|
+
// Register
|
|
1623
|
+
logger.use(myPlugin);
|
|
1624
|
+
|
|
1625
|
+
// Deregister by name
|
|
1626
|
+
logger.unuse('my-plugin');
|
|
1627
|
+
```
|
|
1628
|
+
|
|
1629
|
+
`use()` is chainable:
|
|
1630
|
+
|
|
1631
|
+
```typescript
|
|
1632
|
+
createLogger({ context: 'api' }).use(metricsPlugin).use(auditPlugin).use(samplerPlugin);
|
|
1633
|
+
```
|
|
1634
|
+
|
|
1635
|
+
### Cancelling a log entry
|
|
1636
|
+
|
|
1637
|
+
Returning `null` from `onLog` drops the entry before it reaches any transport — useful for sampling, deduplication, or environment-based suppression:
|
|
1638
|
+
|
|
1639
|
+
```typescript
|
|
1640
|
+
const devOnlyPlugin: LogixiaPlugin = {
|
|
1641
|
+
name: 'dev-only',
|
|
1642
|
+
onLog(entry) {
|
|
1643
|
+
// Suppress debug/trace entries in production
|
|
1644
|
+
if (process.env.NODE_ENV === 'production' && entry.level <= 20) {
|
|
1645
|
+
return null; // drop it
|
|
1646
|
+
}
|
|
1647
|
+
return entry;
|
|
1648
|
+
},
|
|
1649
|
+
};
|
|
1650
|
+
```
|
|
1651
|
+
|
|
1652
|
+
Multiple `onLog` hooks run in registration order. If any hook returns `null` the pipeline stops and no further hooks are called.
|
|
1653
|
+
|
|
1654
|
+
---
|
|
1655
|
+
|
|
1656
|
+
## Metrics → Prometheus
|
|
1657
|
+
|
|
1658
|
+
`MetricsPlugin` converts log events into Prometheus-compatible counters, histograms, and gauges — no separate instrumentation library required. The `/metrics` endpoint serves the standard Prometheus text exposition format.
|
|
1659
|
+
|
|
1660
|
+
```typescript
|
|
1661
|
+
import { createMetricsPlugin } from 'logixia';
|
|
1662
|
+
```
|
|
1663
|
+
|
|
1664
|
+
### Quick start (counters)
|
|
1665
|
+
|
|
1666
|
+
```typescript
|
|
1667
|
+
import { createLogger, createMetricsPlugin } from 'logixia';
|
|
1668
|
+
import http from 'node:http';
|
|
1669
|
+
|
|
1670
|
+
const metrics = createMetricsPlugin({
|
|
1671
|
+
http_requests_total: {
|
|
1672
|
+
type: 'counter',
|
|
1673
|
+
help: 'Total HTTP requests processed',
|
|
1674
|
+
// Which log fields become Prometheus labels
|
|
1675
|
+
labels: ['method', 'status', 'route'],
|
|
1676
|
+
},
|
|
1677
|
+
auth_failures_total: {
|
|
1678
|
+
type: 'counter',
|
|
1679
|
+
help: 'Authentication failures',
|
|
1680
|
+
labels: ['reason'],
|
|
1681
|
+
},
|
|
1682
|
+
});
|
|
1683
|
+
|
|
1684
|
+
const logger = createLogger({ context: 'api' }).use(metrics);
|
|
1685
|
+
|
|
1686
|
+
// Every log entry automatically increments the matching counter
|
|
1687
|
+
await logger.info('request handled', { method: 'GET', status: 200, route: '/users' });
|
|
1688
|
+
await logger.warn('auth failed', { reason: 'bad-token' });
|
|
1689
|
+
|
|
1690
|
+
// Expose /metrics
|
|
1691
|
+
http.createServer(metrics.httpHandler()).listen(9100);
|
|
1692
|
+
```
|
|
1693
|
+
|
|
1694
|
+
The `/metrics` response looks like:
|
|
1695
|
+
|
|
1696
|
+
```
|
|
1697
|
+
# HELP logixia_http_requests_total Total HTTP requests processed
|
|
1698
|
+
# TYPE logixia_http_requests_total counter
|
|
1699
|
+
logixia_http_requests_total{method="GET",status="200",route="/users"} 1
|
|
1700
|
+
|
|
1701
|
+
# HELP logixia_auth_failures_total Authentication failures
|
|
1702
|
+
# TYPE logixia_auth_failures_total counter
|
|
1703
|
+
logixia_auth_failures_total{reason="bad-token"} 1
|
|
1704
|
+
```
|
|
1705
|
+
|
|
1706
|
+
### Histograms
|
|
1707
|
+
|
|
1708
|
+
Histograms record the distribution of a numeric field extracted from log entries. Typical use: request latency in milliseconds.
|
|
1709
|
+
|
|
1710
|
+
```typescript
|
|
1711
|
+
const metrics = createMetricsPlugin({
|
|
1712
|
+
http_request_duration_ms: {
|
|
1713
|
+
type: 'histogram',
|
|
1714
|
+
help: 'HTTP request latency in milliseconds',
|
|
1715
|
+
// The log field whose numeric value is recorded as the observation
|
|
1716
|
+
valueField: 'durationMs',
|
|
1717
|
+
labels: ['route', 'method'],
|
|
1718
|
+
// Custom bucket boundaries (defaults: [1, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000])
|
|
1719
|
+
buckets: [10, 50, 100, 200, 500, 1000, 5000],
|
|
1720
|
+
},
|
|
1721
|
+
});
|
|
1722
|
+
|
|
1723
|
+
await logger.info('request complete', { route: '/api/orders', method: 'POST', durationMs: 142 });
|
|
1724
|
+
```
|
|
1725
|
+
|
|
1726
|
+
Prometheus output includes `_bucket`, `_sum`, and `_count` lines, compatible with `histogram_quantile()`:
|
|
1727
|
+
|
|
1728
|
+
```
|
|
1729
|
+
# HELP logixia_http_request_duration_ms HTTP request latency in milliseconds
|
|
1730
|
+
# TYPE logixia_http_request_duration_ms histogram
|
|
1731
|
+
logixia_http_request_duration_ms_bucket{le="10",route="/api/orders",method="POST"} 0
|
|
1732
|
+
logixia_http_request_duration_ms_bucket{le="50",route="/api/orders",method="POST"} 0
|
|
1733
|
+
logixia_http_request_duration_ms_bucket{le="100",route="/api/orders",method="POST"} 0
|
|
1734
|
+
logixia_http_request_duration_ms_bucket{le="200",route="/api/orders",method="POST"} 1
|
|
1735
|
+
...
|
|
1736
|
+
logixia_http_request_duration_ms_bucket{le="+Inf",route="/api/orders",method="POST"} 1
|
|
1737
|
+
logixia_http_request_duration_ms_sum{route="/api/orders",method="POST"} 142
|
|
1738
|
+
logixia_http_request_duration_ms_count{route="/api/orders",method="POST"} 1
|
|
1739
|
+
```
|
|
1740
|
+
|
|
1741
|
+
### Gauges
|
|
1742
|
+
|
|
1743
|
+
Gauges track the current value of a numeric field — useful for queue depths, active connections, cache sizes:
|
|
1744
|
+
|
|
1745
|
+
```typescript
|
|
1746
|
+
const metrics = createMetricsPlugin({
|
|
1747
|
+
queue_depth: {
|
|
1748
|
+
type: 'gauge',
|
|
1749
|
+
help: 'Current number of items in the processing queue',
|
|
1750
|
+
valueField: 'depth',
|
|
1751
|
+
labels: ['queue'],
|
|
1752
|
+
},
|
|
1753
|
+
});
|
|
1754
|
+
|
|
1755
|
+
await logger.info('queue snapshot', { queue: 'email', depth: 47 });
|
|
1756
|
+
```
|
|
1757
|
+
|
|
1758
|
+
### Exposing the /metrics endpoint
|
|
1759
|
+
|
|
1760
|
+
**Plain Node.js `http` module:**
|
|
1761
|
+
|
|
1762
|
+
```typescript
|
|
1763
|
+
import http from 'node:http';
|
|
1764
|
+
|
|
1765
|
+
http.createServer(metrics.httpHandler()).listen(9100);
|
|
1766
|
+
// GET http://localhost:9100/ → Prometheus text format
|
|
1767
|
+
```
|
|
1768
|
+
|
|
1769
|
+
**Express:**
|
|
1770
|
+
|
|
1771
|
+
```typescript
|
|
1772
|
+
import express from 'express';
|
|
1773
|
+
|
|
1774
|
+
const app = express();
|
|
1775
|
+
app.get('/metrics', metrics.expressHandler());
|
|
1776
|
+
app.listen(3000);
|
|
1777
|
+
```
|
|
1778
|
+
|
|
1779
|
+
**Manual render (any framework):**
|
|
1780
|
+
|
|
1781
|
+
```typescript
|
|
1782
|
+
// Returns the full Prometheus text string
|
|
1783
|
+
const text = metrics.render();
|
|
1784
|
+
res.setHeader('Content-Type', 'text/plain; version=0.0.4; charset=utf-8');
|
|
1785
|
+
res.end(text);
|
|
1786
|
+
```
|
|
1787
|
+
|
|
1788
|
+
**Reset all counters** (e.g., between tests):
|
|
1789
|
+
|
|
1790
|
+
```typescript
|
|
1791
|
+
metrics.reset();
|
|
1792
|
+
```
|
|
1793
|
+
|
|
1794
|
+
### Metric configuration reference
|
|
1795
|
+
|
|
1796
|
+
```typescript
|
|
1797
|
+
interface CounterConfig {
|
|
1798
|
+
type: 'counter';
|
|
1799
|
+
help?: string; // # HELP line in Prometheus output
|
|
1800
|
+
labels?: string[]; // Log entry fields to use as label keys
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
interface HistogramConfig {
|
|
1804
|
+
type: 'histogram';
|
|
1805
|
+
help?: string;
|
|
1806
|
+
valueField: string; // The log field holding the numeric observation
|
|
1807
|
+
labels?: string[];
|
|
1808
|
+
buckets?: number[]; // Upper-inclusive bucket boundaries (ms or any unit)
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
interface GaugeConfig {
|
|
1812
|
+
type: 'gauge';
|
|
1813
|
+
help?: string;
|
|
1814
|
+
valueField: string; // The log field whose value sets the gauge
|
|
1815
|
+
labels?: string[];
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
// Map of Prometheus metric name → config
|
|
1819
|
+
type MetricsMap = Record<string, CounterConfig | HistogramConfig | GaugeConfig>;
|
|
1820
|
+
```
|
|
1821
|
+
|
|
1822
|
+
All metric names are automatically prefixed with `logixia_` in the output. If a histogram or gauge entry is missing the `valueField`, the entry is still counted but no numeric observation is recorded.
|
|
1823
|
+
|
|
1824
|
+
---
|
|
1825
|
+
|
|
1163
1826
|
## Logger instance API
|
|
1164
1827
|
|
|
1165
1828
|
Complete reference for every method available on a logger instance returned by `createLogger` or `LogixiaLoggerService`:
|
|
@@ -1201,6 +1864,10 @@ logger.setTransportLevels(transportId: string, levels: string[]): void
|
|
|
1201
1864
|
logger.getTransportLevels(transportId: string): string[] | undefined
|
|
1202
1865
|
logger.clearTransportLevelPreferences(): void
|
|
1203
1866
|
|
|
1867
|
+
// Plugin API
|
|
1868
|
+
logger.use(plugin: LogixiaPlugin): this // register a plugin; chainable
|
|
1869
|
+
logger.unuse(pluginName: string): this // remove a plugin by name; chainable
|
|
1870
|
+
|
|
1204
1871
|
// Lifecycle
|
|
1205
1872
|
await logger.flush(): Promise<void>
|
|
1206
1873
|
await logger.close(): Promise<void>
|
|
@@ -1224,7 +1891,40 @@ import {
|
|
|
1224
1891
|
registerForShutdown, // (logger) => void
|
|
1225
1892
|
deregisterFromShutdown, // (logger) => void
|
|
1226
1893
|
resetShutdownHandlers, // () => void — useful in tests
|
|
1894
|
+
// NestJS extras
|
|
1895
|
+
InjectLogger, // @InjectLogger() parameter decorator
|
|
1896
|
+
LogMethod, // @LogMethod() method decorator
|
|
1897
|
+
LogixiaExceptionFilter, // global exception filter
|
|
1898
|
+
} from 'logixia';
|
|
1899
|
+
|
|
1900
|
+
// Cloud transports (import directly):
|
|
1901
|
+
import { CloudWatchTransport } from 'logixia';
|
|
1902
|
+
import { GCPTransport } from 'logixia';
|
|
1903
|
+
import { AzureMonitorTransport } from 'logixia';
|
|
1904
|
+
|
|
1905
|
+
// Plugin API:
|
|
1906
|
+
import type { LogixiaPlugin } from 'logixia';
|
|
1907
|
+
import { globalPluginRegistry, PluginRegistry, usePlugin } from 'logixia';
|
|
1908
|
+
|
|
1909
|
+
// Metrics → Prometheus:
|
|
1910
|
+
import type {
|
|
1911
|
+
CounterConfig,
|
|
1912
|
+
GaugeConfig,
|
|
1913
|
+
HistogramConfig,
|
|
1914
|
+
MetricConfig,
|
|
1915
|
+
MetricsMap,
|
|
1227
1916
|
} from 'logixia';
|
|
1917
|
+
import { createMetricsPlugin, MetricsPlugin } from 'logixia';
|
|
1918
|
+
|
|
1919
|
+
// Correlation ID sub-package:
|
|
1920
|
+
import { correlationMiddleware, correlationFetch, withCorrelationId } from 'logixia/correlation';
|
|
1921
|
+
|
|
1922
|
+
// Browser / Edge sub-package:
|
|
1923
|
+
import {
|
|
1924
|
+
createBrowserLogger,
|
|
1925
|
+
BrowserConsoleTransport,
|
|
1926
|
+
BrowserRemoteTransport,
|
|
1927
|
+
} from 'logixia/browser';
|
|
1228
1928
|
```
|
|
1229
1929
|
|
|
1230
1930
|
---
|
|
@@ -1237,6 +1937,18 @@ logixia ships a CLI for working with log files directly. After installing, the `
|
|
|
1237
1937
|
npx logixia --help
|
|
1238
1938
|
```
|
|
1239
1939
|
|
|
1940
|
+
Seven subcommands are available:
|
|
1941
|
+
|
|
1942
|
+
| Command | One-line summary |
|
|
1943
|
+
| --------- | ------------------------------------------------------------ |
|
|
1944
|
+
| `tail` | Stream a log file in real-time with level highlighting |
|
|
1945
|
+
| `search` | Full-text or field-specific search with table / JSON output |
|
|
1946
|
+
| `stats` | Level counts and time distribution summary |
|
|
1947
|
+
| `analyze` | Pattern recognition and anomaly detection |
|
|
1948
|
+
| `export` | Convert between NDJSON, JSON, and CSV |
|
|
1949
|
+
| `query` | SQL-like queries with aggregations, sorting, and live follow |
|
|
1950
|
+
| `explore` | Full-screen interactive TUI browser with search and export |
|
|
1951
|
+
|
|
1240
1952
|
**`tail`** — stream a log file in real-time, with optional filtering and level highlighting:
|
|
1241
1953
|
|
|
1242
1954
|
```bash
|
|
@@ -1286,6 +1998,135 @@ npx logixia analyze ./logs/app.log
|
|
|
1286
1998
|
npx logixia export ./logs/app.log --format csv --output ./logs/app.csv
|
|
1287
1999
|
```
|
|
1288
2000
|
|
|
2001
|
+
**`query`** — run SQL-like queries over NDJSON / JSON log files directly in your terminal:
|
|
2002
|
+
|
|
2003
|
+
```bash
|
|
2004
|
+
# Basic filter
|
|
2005
|
+
npx logixia query ./logs/app.log --sql "SELECT * FROM logs WHERE level='error'"
|
|
2006
|
+
|
|
2007
|
+
# Multiple conditions
|
|
2008
|
+
npx logixia query ./logs/app.log --sql "WHERE level='error' AND duration > 500"
|
|
2009
|
+
|
|
2010
|
+
# Select specific fields
|
|
2011
|
+
npx logixia query ./logs/app.log --sql "SELECT level, message, duration FROM logs WHERE level='warn'"
|
|
2012
|
+
|
|
2013
|
+
# Time-range shortcuts
|
|
2014
|
+
npx logixia query ./logs/app.log --since "last 2 hours"
|
|
2015
|
+
npx logixia query ./logs/app.log --since "last 30 minutes" --until "last 5 minutes"
|
|
2016
|
+
npx logixia query ./logs/app.log --since today
|
|
2017
|
+
npx logixia query ./logs/app.log --since yesterday
|
|
2018
|
+
|
|
2019
|
+
# Aggregations
|
|
2020
|
+
npx logixia query ./logs/app.log --sql "COUNT BY level"
|
|
2021
|
+
npx logixia query ./logs/app.log --sql "GROUP BY statusCode"
|
|
2022
|
+
npx logixia query ./logs/app.log --sql "AVG(duration) BY endpoint"
|
|
2023
|
+
npx logixia query ./logs/app.log --sql "SUM(duration) BY service"
|
|
2024
|
+
|
|
2025
|
+
# Sorting and limiting
|
|
2026
|
+
npx logixia query ./logs/app.log --sql "WHERE level='error' ORDER BY timestamp DESC LIMIT 20"
|
|
2027
|
+
npx logixia query ./logs/app.log --order-by duration --limit 10
|
|
2028
|
+
|
|
2029
|
+
# Output formats
|
|
2030
|
+
npx logixia query ./logs/app.log --sql "COUNT BY level" --format table
|
|
2031
|
+
npx logixia query ./logs/app.log --sql "WHERE level='error'" --format json
|
|
2032
|
+
|
|
2033
|
+
# Live tail with a SQL filter — streams new entries as they are written
|
|
2034
|
+
npx logixia query ./logs/app.log --follow --sql "WHERE level='error'"
|
|
2035
|
+
npx logixia query ./logs/app.log --follow --since "last 1 hour" --sql "WHERE duration > 1000"
|
|
2036
|
+
```
|
|
2037
|
+
|
|
2038
|
+
Supported SQL features:
|
|
2039
|
+
|
|
2040
|
+
| Clause / keyword | Example |
|
|
2041
|
+
| ---------------------------- | ---------------------------------------- |
|
|
2042
|
+
| `SELECT fields` | `SELECT level, message, duration` |
|
|
2043
|
+
| `WHERE` conditions | `WHERE level='error' AND duration > 500` |
|
|
2044
|
+
| Comparison ops | `= != > >= < <=` |
|
|
2045
|
+
| `LIKE` / `NOT LIKE` | `WHERE message LIKE '%timeout%'` |
|
|
2046
|
+
| `IN` / `NOT IN` | `WHERE level IN ('error', 'warn')` |
|
|
2047
|
+
| `COUNT BY field` | `COUNT BY statusCode` |
|
|
2048
|
+
| `GROUP BY field` | `GROUP BY endpoint` |
|
|
2049
|
+
| `AVG(f) BY g` | `AVG(duration) BY endpoint` |
|
|
2050
|
+
| `SUM / MIN / MAX(f) BY g` | `MAX(duration) BY service` |
|
|
2051
|
+
| `ORDER BY field [ASC\|DESC]` | `ORDER BY timestamp DESC` |
|
|
2052
|
+
| `LIMIT n` | `LIMIT 50` |
|
|
2053
|
+
|
|
2054
|
+
`--since` / `--until` accept: `"last N minutes"`, `"last N hours"`, `"last N days"`, `"today"`, `"yesterday"`, or any ISO 8601 date string.
|
|
2055
|
+
|
|
2056
|
+
### explore — Visual TUI log explorer
|
|
2057
|
+
|
|
2058
|
+
`logixia explore` opens a full-screen interactive terminal UI built on raw ANSI + chalk — no extra runtime dependencies required.
|
|
2059
|
+
|
|
2060
|
+
```bash
|
|
2061
|
+
# Open a log file in the TUI
|
|
2062
|
+
npx logixia explore ./logs/app.log
|
|
2063
|
+
|
|
2064
|
+
# Start with only error and warn entries visible
|
|
2065
|
+
npx logixia explore ./logs/app.log --levels error,warn
|
|
2066
|
+
|
|
2067
|
+
# Pre-populate the search field
|
|
2068
|
+
npx logixia explore ./logs/app.log --search "payment"
|
|
2069
|
+
|
|
2070
|
+
# Follow mode: append new entries as the file grows
|
|
2071
|
+
npx logixia explore ./logs/app.log --follow
|
|
2072
|
+
```
|
|
2073
|
+
|
|
2074
|
+
**Options:**
|
|
2075
|
+
|
|
2076
|
+
| Flag | Default | Description |
|
|
2077
|
+
| ------------------ | ------- | --------------------------------------------------------------------- |
|
|
2078
|
+
| `--follow` | off | Tail the file; append new entries as they are written |
|
|
2079
|
+
| `--levels <list>` | all | Comma-separated levels to show: `error,warn,info,debug,trace,verbose` |
|
|
2080
|
+
| `--search <query>` | — | Pre-populate the search field on open |
|
|
2081
|
+
|
|
2082
|
+
**Layout:**
|
|
2083
|
+
|
|
2084
|
+
```
|
|
2085
|
+
┌────────────────────────────────────────────────────────────────────────────┐
|
|
2086
|
+
│ LOGIXIA EXPLORE app.log [42/127] /payment │
|
|
2087
|
+
│ E W I D T V /: search │
|
|
2088
|
+
│ TIME LVL MESSAGE │
|
|
2089
|
+
│ 08:00:01.042 ERR Request failed status=500 user=abc │
|
|
2090
|
+
│▶08:00:02.117 INF Request completed status=200 user=def ← selected │
|
|
2091
|
+
│ 08:00:03.890 WRN Slow query detected duration=1240 │
|
|
2092
|
+
│──────────────────────────────────────────────────────────────── ▼ DETAIL ─│
|
|
2093
|
+
│ { │
|
|
2094
|
+
│ "timestamp": "...", │
|
|
2095
|
+
│ "level": "info", │
|
|
2096
|
+
│ "message": "Request completed", │
|
|
2097
|
+
│ "status": 200, │
|
|
2098
|
+
│ "user": "def" │
|
|
2099
|
+
│ } │
|
|
2100
|
+
│ j/k move /search x export E/W/I/D/T/V filter J/K detail↕ q quit │
|
|
2101
|
+
└────────────────────────────────────────────────────────────────────────────┘
|
|
2102
|
+
```
|
|
2103
|
+
|
|
2104
|
+
**Keyboard shortcuts:**
|
|
2105
|
+
|
|
2106
|
+
| Key | Action |
|
|
2107
|
+
| --------------- | ------------------------------------------------------------------------------ |
|
|
2108
|
+
| `j` / `↓` | Move selection down |
|
|
2109
|
+
| `k` / `↑` | Move selection up |
|
|
2110
|
+
| `g` / `Home` | Jump to first entry |
|
|
2111
|
+
| `G` / `End` | Jump to last entry |
|
|
2112
|
+
| `PgUp` / `PgDn` | Page up / page down |
|
|
2113
|
+
| `J` / `K` | Scroll detail panel down / up |
|
|
2114
|
+
| `/` | Enter search mode (type query → `Enter` to confirm, `Esc` to clear) |
|
|
2115
|
+
| `E W I D T V` | Toggle error / warn / info / debug / trace / verbose filter |
|
|
2116
|
+
| `f` | Toggle real-time follow mode |
|
|
2117
|
+
| `x` | Export filtered entries (enter a path ending in `.json`, `.csv`, or `.ndjson`) |
|
|
2118
|
+
| `q` / `Ctrl+C` | Quit and return to terminal |
|
|
2119
|
+
|
|
2120
|
+
**Detail panel** shows syntax-highlighted JSON of the selected entry. For error entries with a `stack` field, the full stack trace is rendered below the JSON with frame locations highlighted.
|
|
2121
|
+
|
|
2122
|
+
**Export** supports three formats determined by the file extension you type:
|
|
2123
|
+
|
|
2124
|
+
- `.json` — pretty-printed JSON array
|
|
2125
|
+
- `.csv` — comma-separated with auto-detected column headers
|
|
2126
|
+
- `.ndjson` / `.jsonl` — newline-delimited JSON (one entry per line)
|
|
2127
|
+
|
|
2128
|
+
The explorer works in any TTY-capable terminal (macOS Terminal, iTerm2, Windows Terminal, VS Code integrated terminal) and degrades gracefully in non-TTY environments (useful when piping to other commands).
|
|
2129
|
+
|
|
1289
2130
|
---
|
|
1290
2131
|
|
|
1291
2132
|
## Configuration reference
|