logixia 1.3.0 → 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.
Files changed (59) hide show
  1. package/README.md +848 -7
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/{index-iDTW2-eY.d.mts → index-Ium497V3.d.mts} +180 -2
  4. package/dist/index-Ium497V3.d.mts.map +1 -0
  5. package/dist/{index-CHIsdA9n.d.ts → index-t-ActikQ.d.ts} +180 -2
  6. package/dist/index-t-ActikQ.d.ts.map +1 -0
  7. package/dist/index.d.mts +458 -3
  8. package/dist/index.d.mts.map +1 -1
  9. package/dist/index.d.ts +458 -3
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +436 -2
  12. package/dist/index.js.map +1 -1
  13. package/dist/index.mjs +416 -3
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/{logitron-logger.module-C0G8JGVf.d.ts → logitron-logger.module-9vOhKWDG.d.ts} +120 -6
  16. package/dist/logitron-logger.module-9vOhKWDG.d.ts.map +1 -0
  17. package/dist/{logitron-logger.module-2AzkadqZ.mjs → logitron-logger.module-C939PIEV.mjs} +331 -10
  18. package/dist/logitron-logger.module-C939PIEV.mjs.map +1 -0
  19. package/dist/{logitron-logger.module-BqNKp0Fs.js → logitron-logger.module-CCnX7GwK.js} +383 -8
  20. package/dist/logitron-logger.module-CCnX7GwK.js.map +1 -0
  21. package/dist/{logitron-logger.module-DQKaZTJL.d.mts → logitron-logger.module-SArymP6b.d.mts} +120 -6
  22. package/dist/logitron-logger.module-SArymP6b.d.mts.map +1 -0
  23. package/dist/middleware.d.mts +1 -1
  24. package/dist/middleware.d.ts +1 -1
  25. package/dist/nest.d.mts +2 -2
  26. package/dist/nest.d.ts +2 -2
  27. package/dist/nest.js +2 -2
  28. package/dist/nest.mjs +2 -2
  29. package/dist/{promise-DaiZ2BaH.js → promise-BI-3eI4n.js} +285 -128
  30. package/dist/promise-BI-3eI4n.js.map +1 -0
  31. package/dist/{promise-C4pQPcK4.mjs → promise-BrZcjavs.mjs} +285 -128
  32. package/dist/promise-BrZcjavs.mjs.map +1 -0
  33. package/dist/testing.d.mts +1 -1
  34. package/dist/testing.d.ts +1 -1
  35. package/dist/testing.js +7 -1
  36. package/dist/testing.js.map +1 -1
  37. package/dist/testing.mjs +7 -1
  38. package/dist/testing.mjs.map +1 -1
  39. package/dist/{transport.manager-5VVdqS3o.mjs → transport.manager-DR7TLXQT.mjs} +82 -4
  40. package/dist/transport.manager-DR7TLXQT.mjs.map +1 -0
  41. package/dist/{transport.manager-DCOm4uIQ.js → transport.manager-DVTM978M.js} +82 -4
  42. package/dist/transport.manager-DVTM978M.js.map +1 -0
  43. package/dist/transports.d.mts +61 -168
  44. package/dist/transports.d.mts.map +1 -1
  45. package/dist/transports.d.ts +61 -168
  46. package/dist/transports.d.ts.map +1 -1
  47. package/dist/transports.js +1 -1
  48. package/dist/transports.mjs +1 -1
  49. package/package.json +72 -9
  50. package/dist/index-CHIsdA9n.d.ts.map +0 -1
  51. package/dist/index-iDTW2-eY.d.mts.map +0 -1
  52. package/dist/logitron-logger.module-2AzkadqZ.mjs.map +0 -1
  53. package/dist/logitron-logger.module-BqNKp0Fs.js.map +0 -1
  54. package/dist/logitron-logger.module-C0G8JGVf.d.ts.map +0 -1
  55. package/dist/logitron-logger.module-DQKaZTJL.d.mts.map +0 -1
  56. package/dist/promise-C4pQPcK4.mjs.map +0 -1
  57. package/dist/promise-DaiZ2BaH.js.map +0 -1
  58. package/dist/transport.manager-5VVdqS3o.mjs.map +0 -1
  59. 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 &middot; Non-blocking by design &middot; NestJS &middot; Database &middot; Tracing &middot; OTel
5
+ TypeScript-first &middot; Non-blocking by design &middot; NestJS &middot; Database &middot; Cloud &middot; Tracing &middot; OTel &middot; 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
- - **NestJS module** — plug in with `LogixiaLoggerModule.forRoot()`, inject anywhere in the DI tree
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, Datadog, S3, or anywhere else via a simple interface
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', new Error('Connection timeout'));
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