{"_id":"@alrajhitakaful/art-trace-logger","_rev":"6-df55cb299a1985f6b9824e2223fe6a8a","name":"@alrajhitakaful/art-trace-logger","dist-tags":{"latest":"1.5.0"},"versions":{"1.0.0":{"name":"@alrajhitakaful/art-trace-logger","version":"1.0.0","_id":"@alrajhitakaful/art-trace-logger@1.0.0","maintainers":[{"name":"h_abdallah","email":"habdallah@alrajhitakaful.com"}],"dist":{"shasum":"7265ae87fa96abfbe5613cf592ee51e7a0b5c75b","tarball":"https://registry.npmjs.org/@alrajhitakaful/art-trace-logger/-/art-trace-logger-1.0.0.tgz","fileCount":74,"integrity":"sha512-dQaZ0MBv4S6sxtqQrMjVnPoTc03hwJ3wOW8uqnE3b3LAT8mpPJ/aN8Hz9+IhUva3llCKRsuqDo4ViUkLPLkPYg==","signatures":[{"sig":"MEQCIBaeldloJfdnrHV7fZsrR5ZImAgSA14o50sezEp58EScAiAMSLZVcQT8pZdNAX2Kre2sUjvxP/NIsJyjpTsxHCUgdA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":121353},"main":"dist/core/index.js","types":"dist/core/index.d.ts","exports":{".":{"types":"./dist/core/index.d.ts","default":"./dist/core/index.js"},"./lookup":{"types":"./dist/lookup/index.d.ts","default":"./dist/lookup/index.js"},"./nestjs":{"types":"./dist/nestjs/index.d.ts","default":"./dist/nestjs/index.js"},"./scripts":{"default":"./scripts/patch-index.js"}},"gitHead":"6088ccb92ffafbcb51556a357624807b79eae1cd","scripts":{"build":"tsc","prepare":"npm run build"},"_npmUser":{"name":"h_abdallah","email":"habdallah@alrajhitakaful.com"},"_npmVersion":"10.8.2","description":"A lightweight, distributed trace logger for Node.js services. Streams structured request/response/error trace events to **Kafka**, with built-in **trace context propagation** via `AsyncLocalStorage` and a first-class **NestJS** integration.","directories":{},"_nodeVersion":"20.19.5","dependencies":{"ioredis":"^5.11.1","kafkajs":"^2.2.4","typeorm":"^1.0.0","oracledb":"^6.10.0"},"_hasShrinkwrap":false,"devDependencies":{"rxjs":"^7.8.2","express":"^4.18.0","ioredis":"^5.11.1","typescript":"^6.0.3","@types/node":"^25.6.0","@nestjs/core":"^11.1.19","@nestjs/common":"^11.1.19","@types/express":"^4.17.13","@types/oracledb":"^7.0.1","reflect-metadata":"^0.2.2"},"peerDependencies":{"rxjs":"^7.8.2","express":"^4.18.0","@nestjs/core":"^11.1.19","@nestjs/common":"^11.1.19"},"peerDependenciesMeta":{"rxjs":{"optional":true},"express":{"optional":true},"@nestjs/core":{"optional":true},"@nestjs/common":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/art-trace-logger_1.0.0_1784490398064_0.5412253608172077","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@alrajhitakaful/art-trace-logger","version":"1.1.0","_id":"@alrajhitakaful/art-trace-logger@1.1.0","maintainers":[{"name":"h_abdallah","email":"habdallah@alrajhitakaful.com"}],"dist":{"shasum":"a2ca9f811a4b31bd3c051bd1b535b10d82c63748","tarball":"https://registry.npmjs.org/@alrajhitakaful/art-trace-logger/-/art-trace-logger-1.1.0.tgz","fileCount":77,"integrity":"sha512-7l6UbUJ7I50ttVXBXWmlOJd4qfDa7xBnf10psfvGML4CHs6u4He97oCPgolORXCCbr61xz9fcUN54YitKGG3wQ==","signatures":[{"sig":"MEYCIQCeY7VIhMkosq+KWDcuOVrogJZq1uYHBIMHxw1nJanXEAIhAP445+98XK/lsR5jKshaHCG0GiFIJXkdvZQVopLGIul0","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":121656},"main":"dist/core/index.js","types":"dist/core/index.d.ts","exports":{".":{"types":"./dist/core/index.d.ts","default":"./dist/core/index.js"},"./lookup":{"types":"./dist/lookup/index.d.ts","default":"./dist/lookup/index.js"},"./nestjs":{"types":"./dist/nestjs/index.d.ts","default":"./dist/nestjs/index.js"},"./scripts":{"default":"./scripts/patch-index.js"}},"gitHead":"09511ccd7a780d58648180c5ea2dfb90e145cdaa","scripts":{"build":"tsc","prepare":"npm run build"},"_npmUser":{"name":"h_abdallah","email":"habdallah@alrajhitakaful.com"},"_npmVersion":"10.8.2","description":"A lightweight, distributed trace logger for Node.js services. Streams structured request/response/error trace events to **Kafka**, with built-in **trace context propagation** via `AsyncLocalStorage` and a first-class **NestJS** integration.","directories":{},"_nodeVersion":"20.19.5","dependencies":{"ioredis":"^5.11.1","kafkajs":"^2.2.4","typeorm":"^1.0.0","oracledb":"^6.10.0"},"_hasShrinkwrap":false,"devDependencies":{"rxjs":"^7.8.2","express":"^4.18.0","ioredis":"^5.11.1","typescript":"^6.0.3","@types/node":"^25.6.0","@nestjs/core":"^11.1.19","@nestjs/common":"^11.1.19","@types/express":"^4.17.13","@types/oracledb":"^7.0.1","reflect-metadata":"^0.2.2"},"peerDependencies":{"rxjs":"^7.8.2","express":"^4.18.0","@nestjs/core":"^11.1.19","@nestjs/common":"^11.1.19"},"peerDependenciesMeta":{"rxjs":{"optional":true},"express":{"optional":true},"@nestjs/core":{"optional":true},"@nestjs/common":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/art-trace-logger_1.1.0_1784491174147_0.4481704091541212","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@alrajhitakaful/art-trace-logger","version":"1.2.0","_id":"@alrajhitakaful/art-trace-logger@1.2.0","maintainers":[{"name":"h_abdallah","email":"habdallah@alrajhitakaful.com"}],"dist":{"shasum":"6a6371507bdb7435cd7b7f65a39975315683604d","tarball":"https://registry.npmjs.org/@alrajhitakaful/art-trace-logger/-/art-trace-logger-1.2.0.tgz","fileCount":77,"integrity":"sha512-aPzhx4Dz04j1wW/+jDM2LimvpABhvVREDFBLn4uj/SiTCSBjqX8O4ux3Xz33uKa/MsfrwfGMC96OlijlN+DvFQ==","signatures":[{"sig":"MEUCIBiPPFtHPkSiX85CB2Aq4oJY8+FCYUyqpV5yjiL7dHUvAiEAvHpnkjP0zlQQmDRMrhwYf4N16HPrYkDbILKX/3KI70g=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":121646},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/core/index.d.ts","default":"./dist/core/index.js"},"./lookup":{"types":"./dist/lookup/index.d.ts","default":"./dist/lookup/index.js"},"./nestjs":{"types":"./dist/nestjs/index.d.ts","default":"./dist/nestjs/index.js"},"./scripts":{"default":"./scripts/patch-index.js"}},"gitHead":"25f4d062232f6c225b8e7aba41537d8ce24d0ee9","scripts":{"build":"tsc","prepare":"npm run build"},"_npmUser":{"name":"h_abdallah","email":"habdallah@alrajhitakaful.com"},"_npmVersion":"10.8.2","description":"A lightweight, distributed trace logger for Node.js services. Streams structured request/response/error trace events to **Kafka**, with built-in **trace context propagation** via `AsyncLocalStorage` and a first-class **NestJS** integration.","directories":{},"_nodeVersion":"20.19.5","dependencies":{"ioredis":"^5.11.1","kafkajs":"^2.2.4","typeorm":"^1.0.0","oracledb":"^6.10.0"},"_hasShrinkwrap":false,"devDependencies":{"rxjs":"^7.8.2","express":"^4.18.0","ioredis":"^5.11.1","typescript":"^6.0.3","@types/node":"^25.6.0","@nestjs/core":"^11.1.19","@nestjs/common":"^11.1.19","@types/express":"^4.17.13","@types/oracledb":"^7.0.1","reflect-metadata":"^0.2.2"},"peerDependencies":{"rxjs":"^7.8.2","express":"^4.18.0","@nestjs/core":"^11.1.19","@nestjs/common":"^11.1.19"},"peerDependenciesMeta":{"rxjs":{"optional":true},"express":{"optional":true},"@nestjs/core":{"optional":true},"@nestjs/common":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/art-trace-logger_1.2.0_1784491606541_0.6746282782232429","host":"s3://npm-registry-packages-npm-production"}},"1.3.0":{"name":"@alrajhitakaful/art-trace-logger","version":"1.3.0","_id":"@alrajhitakaful/art-trace-logger@1.3.0","maintainers":[{"name":"h_abdallah","email":"habdallah@alrajhitakaful.com"}],"dist":{"shasum":"fa9837d1d4e426290148cb606700a63deb8abc87","tarball":"https://registry.npmjs.org/@alrajhitakaful/art-trace-logger/-/art-trace-logger-1.3.0.tgz","fileCount":77,"integrity":"sha512-l5CLA7iLztmrTu5GPTv454fjHL2hLCQj0Lm+yai2czFp64Emu4V2o0z6s6RO78AsIDnCJp/qEaUl0CWVbt18og==","signatures":[{"sig":"MEUCIQD/bQkKzzGNl3sZHGsp3CJe4oo0U17FXiZ4vz73o8ygLwIgFL6mbcCVi7EMMijav/7vICQGCgfak5h8ewW/A2oj3aM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":121636},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./lookup":{"types":"./dist/lookup/index.d.ts","default":"./dist/lookup/index.js"},"./nestjs":{"types":"./dist/nestjs/index.d.ts","default":"./dist/nestjs/index.js"},"./scripts":{"default":"./scripts/patch-index.js"}},"gitHead":"bef74edd39c96d79e5970d1e6135c1c47c2d2ea9","scripts":{"build":"tsc","prepare":"npm run build"},"_npmUser":{"name":"h_abdallah","email":"habdallah@alrajhitakaful.com"},"_npmVersion":"10.8.2","description":"A lightweight, distributed trace logger for Node.js services. Streams structured request/response/error trace events to **Kafka**, with built-in **trace context propagation** via `AsyncLocalStorage` and a first-class **NestJS** integration.","directories":{},"_nodeVersion":"20.19.5","dependencies":{"ioredis":"^5.11.1","kafkajs":"^2.2.4","typeorm":"^1.0.0","oracledb":"^6.10.0"},"_hasShrinkwrap":false,"devDependencies":{"rxjs":"^7.8.2","express":"^4.18.0","ioredis":"^5.11.1","typescript":"^6.0.3","@types/node":"^25.6.0","@nestjs/core":"^11.1.19","@nestjs/common":"^11.1.19","@types/express":"^4.17.13","@types/oracledb":"^7.0.1","reflect-metadata":"^0.2.2"},"peerDependencies":{"rxjs":"^7.8.2","express":"^4.18.0","@nestjs/core":"^11.1.19","@nestjs/common":"^11.1.19"},"peerDependenciesMeta":{"rxjs":{"optional":true},"express":{"optional":true},"@nestjs/core":{"optional":true},"@nestjs/common":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/art-trace-logger_1.3.0_1784894936762_0.8707437739710364","host":"s3://npm-registry-packages-npm-production"}},"1.4.0":{"name":"@alrajhitakaful/art-trace-logger","version":"1.4.0","_id":"@alrajhitakaful/art-trace-logger@1.4.0","maintainers":[{"name":"h_abdallah","email":"habdallah@alrajhitakaful.com"}],"dist":{"shasum":"5fadf4c9958236776e0141947ddc0581e2a1df1d","tarball":"https://registry.npmjs.org/@alrajhitakaful/art-trace-logger/-/art-trace-logger-1.4.0.tgz","fileCount":77,"integrity":"sha512-o1flE3EHHTjoxma1NZHCGC4wl7S/A1cj1dAuXSweEmeM0aaxMd5+RYnecu+SFIMCwFtlwMgrwPRmQeeTedU+Lw==","signatures":[{"sig":"MEUCICfhzIGh8InZ1NTg5r1cnGPOGp6AwmX59yP61/MfDSddAiEAx285G04q5/DlpHCp1CD+LuMKvFVVb5ezYDzwLqLkSiI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":121719},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./lookup":{"types":"./dist/lookup/index.d.ts","default":"./dist/lookup/index.js"},"./nestjs":{"types":"./dist/nestjs/index.d.ts","default":"./dist/nestjs/index.js"},"./scripts":{"default":"./scripts/patch-index.js"}},"gitHead":"b316af83b1cabab06c6502e68d17491eb06e21d2","scripts":{"build":"tsc","prepare":"npm run build"},"_npmUser":{"name":"h_abdallah","email":"habdallah@alrajhitakaful.com"},"_npmVersion":"10.8.2","description":"A lightweight, distributed trace logger for Node.js services. Streams structured request/response/error trace events to **Kafka**, with built-in **trace context propagation** via `AsyncLocalStorage` and a first-class **NestJS** integration.","directories":{},"_nodeVersion":"20.19.5","dependencies":{"ioredis":"^5.11.1","kafkajs":"^2.2.4","oracledb":"^6.10.0"},"_hasShrinkwrap":false,"devDependencies":{"rxjs":"^7.8.2","express":"^4.18.0","ioredis":"^5.11.1","typeorm":"^0.3.30","typescript":"^6.0.3","@types/node":"^25.6.0","@nestjs/core":"^11.1.19","@nestjs/common":"^11.1.19","@types/express":"^4.17.13","@types/oracledb":"^7.0.1","reflect-metadata":"^0.2.2"},"peerDependencies":{"rxjs":"^7.8.2","express":"^4.18.0","typeorm":"^0.3.0 || ^1.0.0","@nestjs/core":"^11.1.19","@nestjs/common":"^11.1.19"},"peerDependenciesMeta":{"rxjs":{"optional":true},"express":{"optional":true},"typeorm":{"optional":true},"@nestjs/core":{"optional":true},"@nestjs/common":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/art-trace-logger_1.4.0_1785078827138_0.40523011674789666","host":"s3://npm-registry-packages-npm-production"}},"1.5.0":{"name":"@alrajhitakaful/art-trace-logger","version":"1.5.0","main":"dist/index.js","types":"dist/index.d.ts","typesVersions":{"*":{"core":["dist/core/index.d.ts"],"nestjs":["dist/nestjs/index.d.ts"],"neutrinos":["dist/neutrinos/index.d.ts"],"lookup":["dist/lookup/index.d.ts"]}},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./core":{"types":"./dist/core/index.d.ts","default":"./dist/core/index.js"},"./nestjs":{"types":"./dist/nestjs/index.d.ts","default":"./dist/nestjs/index.js"},"./neutrinos":{"types":"./dist/neutrinos/index.d.ts","default":"./dist/neutrinos/index.js"},"./lookup":{"types":"./dist/lookup/index.d.ts","default":"./dist/lookup/index.js"},"./scripts":{"default":"./scripts/patch-index.js"}},"scripts":{"build":"tsc","test:types":"tsc -p test/types/tsconfig.node.json","test":"npm run build && npm run test:types && node --test test/*.test.js","prepublishOnly":"npm test","prepare":"npm run build"},"dependencies":{"ioredis":"^5.11.1","kafkajs":"^2.2.4","oracledb":"^6.10.0"},"peerDependencies":{"@nestjs/common":"^11.1.19","@nestjs/core":"^11.1.19","express":"^4.18.0","rxjs":"^7.8.2","typeorm":"^0.3.0 || ^1.0.0"},"peerDependenciesMeta":{"@nestjs/common":{"optional":true},"@nestjs/core":{"optional":true},"express":{"optional":true},"rxjs":{"optional":true},"typeorm":{"optional":true}},"devDependencies":{"@nestjs/common":"^11.1.19","@nestjs/core":"^11.1.19","@types/express":"^4.17.13","@types/node":"^25.6.0","@types/oracledb":"^7.0.1","express":"^4.18.0","ioredis":"^5.11.1","reflect-metadata":"^0.2.2","rxjs":"^7.8.2","typeorm":"^0.3.30","typescript":"^6.0.3"},"_id":"@alrajhitakaful/art-trace-logger@1.5.0","gitHead":"7c936055c2186c89aa5242aa36d32f380c5a21aa","description":"A lightweight, distributed trace logger for Node.js services. Streams structured request/response/error trace events to **Kafka**, with built-in **trace context propagation** via `AsyncLocalStorage` and a first-class **NestJS** integration.","_nodeVersion":"22.22.3","_npmVersion":"10.9.8","dist":{"integrity":"sha512-el8uIGOeUSQrnIrj+9PEmOxN8NrvbFAm4OthisI8+y7yVoCLRU3ZgR0ZsjUQHUa0jwbcD57yParEwLn69bLPtw==","shasum":"0345f2711231f9cd813914815eb7e28d53f2e4c9","tarball":"https://registry.npmjs.org/@alrajhitakaful/art-trace-logger/-/art-trace-logger-1.5.0.tgz","fileCount":98,"unpackedSize":166669,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCW5d0F6TBMMLEKX4xiCeUgDuvPKgZLG8e33OTQboljxQIhAMak3GtgW945nYIFLw8naDryrd+vUOB5bubd5jyMKz4g"}]},"_npmUser":{"name":"h_abdallah","email":"habdallah@alrajhitakaful.com"},"directories":{},"maintainers":[{"name":"h_abdallah","email":"habdallah@alrajhitakaful.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/art-trace-logger_1.5.0_1787482578721_0.9973447185217763"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-19T19:46:37.872Z","modified":"2026-08-23T10:56:19.016Z","1.0.0":"2026-07-19T19:46:38.218Z","1.1.0":"2026-07-19T19:59:34.290Z","1.2.0":"2026-07-19T20:06:46.693Z","1.3.0":"2026-07-24T12:08:56.900Z","1.4.0":"2026-07-26T15:13:47.285Z","1.5.0":"2026-08-23T10:56:18.869Z"},"description":"A lightweight, distributed trace logger for Node.js services. Streams structured request/response/error trace events to **Kafka**, with built-in **trace context propagation** via `AsyncLocalStorage` and a first-class **NestJS** integration.","maintainers":[{"name":"h_abdallah","email":"habdallah@alrajhitakaful.com"}],"readme":"# @utility/art-trace-logger\n\nA lightweight, distributed trace logger for Node.js services. Streams structured request/response/error trace events to **Kafka**, with built-in **trace context propagation** via `AsyncLocalStorage` and a first-class **NestJS** integration.\n\nDesigned to plug into a Kafka → Logstash → Elasticsearch → Kibana pipeline.\n\n---\n\n## Features\n\n- Structured log entries (`LogEntry`) with predefined log points (`CLIENT_REQUEST`, `SERVICE_REQUEST`, `SERVICE_ERROR`, …)\n- Optional HTTP body capture (inbound Express + outbound `http`/`https` patch) with redaction and size limits\n- Automatic outbound tracing for `got` / `axios` via `installOutboundHttpInstrumentation`\n- Automatic `traceId` + monotonically-increasing `sequence` propagation via `AsyncLocalStorage`\n- Fluent `LogEntryBuilder` API\n- Kafka producer (built on [`kafkajs`](https://kafka.js.org/))\n- Optional NestJS `DynamicModule` with lifecycle-managed connect/disconnect\n- Framework-agnostic core — works in plain Node.js, Express, etc.\n\n---\n\n## Installation\n\n```bash\nnpm install @utility/art-trace-logger\n```\n\nPin an exact version in production:\n\n```json\n{\n  \"dependencies\": {\n    \"@utility/art-trace-logger\": \"1.0.0\"\n  }\n}\n```\n\n> The package ships pre-built (`dist/`) so no build step is required after install.\n\n### Peer dependencies\n\nNestJS and RxJS are **optional peers** — only required if you use the `/nestjs` entrypoint:\n\n```bash\nnpm install @nestjs/common @nestjs/core rxjs reflect-metadata\n```\n\n---\n\n## Configuration\n\nAll consumers (NestJS or plain) take the same config object:\n\n```ts\ninterface TraceLoggerConfig {\n  serviceName: string;     // logical service identity, stamped on every log\n  environment: string;     // 'dev' | 'staging' | 'prod' | …\n  kafkaBrokers: string[];  // e.g. ['10.0.0.10:9094']\n  kafkaTopic: string;      // e.g. 'Middleware-APILogs'\n  enableConsole?: boolean; // mirror entries to stdout (useful in dev)\n  consoleLogPayload?: boolean;       // include payload on console lines (truncated)\n  consoleMaxPayloadBytes?: number;   // console payload limit (default 2048)\n  kafkaEnabled?: boolean;  // set false for console-only mode\n  ssl?: TraceLoggerSslConfig; // TLS for the Kafka connection — set it to null for local test\n  lookup?: LoggerLookupModuleOptions; // service-code lookup cache (see \"Service-code lookup\" below)\n}\n```\n\n### Environment variables (Express bootstrap)\n\nTypical flags used by Express services (`src/trace/bootstrap.ts`):\n\n| Variable | Description |\n| --- | --- |\n| `TRACE_LOGGER_ENABLED` | Master switch (`true` / `false`) |\n| `TRACE_SERVICE_NAME` | `serviceName` on every entry (e.g. `orders-api`) |\n| `TRACE_LOGGER_CONSOLE` | Mirror trace lines to stdout |\n| `TRACE_LOGGER_KAFKA` | Send to Kafka (`false` = console-only) |\n| `KAFKA_BROKERS` | Comma-separated broker list |\n| `KAFKA_TRACE_TOPIC` | Kafka topic (e.g. `Middleware-APILogs`) |\n| `TRACE_LOGGER_LOG_BODIES` | Capture request + response bodies on trace events |\n| `TRACE_LOGGER_LOG_REQUEST_BODY` | Request bodies only |\n| `TRACE_LOGGER_LOG_RESPONSE_BODY` | Response bodies only |\n| `TRACE_LOGGER_MAX_PAYLOAD_BYTES` | Max body size stored on Kafka entries (default `32768`) |\n| `TRACE_LOGGER_CONSOLE_LOG_BODIES` | Print bodies on console (same line as trace metadata) |\n| `TRACE_LOGGER_CONSOLE_MAX_PAYLOAD_BYTES` | Console body truncation (default `2048`) |\n\nHelpers: `resolveBodyLoggingOptions(process.env)`, `resolveConsolePayloadOptions(process.env)`.\n\n> **Kafka shape:** each `tracer.log()` call produces **one** Kafka message — a single JSON `LogEntry`. The `payload` field is part of that object, not a separate message. A full HTTP flow emits multiple messages (`CLIENT_REQUEST` → `SERVICE_REQUEST` → …) sharing the same `traceId`.\n\n---\n\n## Kafka SSL / TLS\n\nThe package never ships cert material of its own — supply it from a central runtime source (a mounted Kubernetes secret, a secret manager, or files on disk) via config or env vars.\n\n### Config shape\n\n```ts\ninterface TraceLoggerSslConfig {\n  // Preferred: filesystem paths (point at a mounted secret / shared volume).\n  // If omitted, the matching KAFKA_SSL_*_PATH env var is used as a fallback.\n  caPath?: string;\n  certPath?: string;\n  keyPath?: string;\n\n  // Escape hatch: inline PEM contents (e.g. fetched from a secret manager).\n  // Takes precedence over the *Path equivalents.\n  ca?: string;\n  cert?: string;\n  key?: string;\n\n  rejectUnauthorized?: boolean; // default true; set false only for self-signed in non-prod\n}\n```\n\n**Per cert, precedence is:** inline PEM → config `*Path` → `KAFKA_SSL_*_PATH` env var.\n\n### Via env vars\n\nPoint at certs from a mounted secret by setting the env vars (e.g. via a local `.env` loaded with `node --env-file=.env`):\n\n```bash\nKAFKA_SSL_CA_PATH=/etc/kafka-certs/ca.pem\nKAFKA_SSL_CERT_PATH=/etc/kafka-certs/client.pem\nKAFKA_SSL_KEY_PATH=/etc/kafka-certs/client-key.pem\n```\n\n### Via config\n\nCallers that want to control it can pass `ssl` directly — config values win over env vars:\n\n```ts\nTraceLogger.create({\n  /* …core config… */\n  ssl: {\n    caPath: '/etc/kafka-certs/ca.pem',\n    certPath: '/etc/kafka-certs/client.pem',\n    keyPath: '/etc/kafka-certs/client-key.pem',\n  },\n});\n```\n\n> **Behavior:** a path you configure explicitly (config or env) that can't be read throws at startup rather than being silently skipped — so a real misconfiguration fails loud. When no cert material is configured at all and `ssl` is omitted, the connection is plaintext.\n\n---\n\n## Service-code lookup\n\nThe optional lookup module resolves `externalService` codes into full service metadata via a Redis-cached lookup table (optionally seeded from an Oracle DB). **No endpoints or credentials ship with the package** — configure them per deployment, or leave them unset to disable the lookup cache entirely (service codes then pass through unresolved).\n\nConfigure via `TraceModule.forRoot({ ..., lookup })`:\n\n```ts\nTraceModule.forRoot({\n  /* …core config… */\n  lookup: {\n    redis: {\n      // Sentinel mode:\n      sentinels: [{ host: 'redis-sentinel.example.internal', port: 26379 }],\n      name: 'mymaster',\n      // …or standalone mode:\n      // host: 'redis.example.internal', port: 6379,\n      password: process.env.LOOKUP_REDIS_PASSWORD,\n      cacheKey: 'art:trace:logger:lookup',   // default\n      cacheExpireSeconds: 31536000,          // default (1 year)\n    },\n    db: {\n      host: 'oracle.example.internal',\n      port: 1521,\n      username: process.env.LOOKUP_DB_USERNAME,\n      password: process.env.LOOKUP_DB_PASSWORD,\n      database: 'MYSERVICE',\n    },\n  },\n});\n```\n\n…or via env vars (used when the corresponding config value is omitted):\n\n| Variable | Description |\n| --- | --- |\n| `LOOKUP_REDIS_SENTINELS` | Comma-separated `host:port` sentinel list |\n| `LOOKUP_REDIS_NAME` | Sentinel master group name (default `mymaster`) |\n| `LOOKUP_REDIS_HOST` / `LOOKUP_REDIS_PORT` | Standalone Redis (when no sentinels) |\n| `LOOKUP_REDIS_PASSWORD` / `LOOKUP_REDIS_DB` | Redis auth / db index |\n| `LOOKUP_REDIS_CACHE_KEY` | Cache key (default `art:trace:logger:lookup`) |\n| `LOOKUP_REDIS_CACHE_EXPIRE` | Cache TTL in seconds (default `31536000`) |\n| `LOOKUP_DB_HOST` / `LOOKUP_DB_PORT` | Oracle lookup DB endpoint |\n| `LOOKUP_DB_USERNAME` / `LOOKUP_DB_PASSWORD` | Oracle credentials |\n| `LOOKUP_DB_NAME` | Service name (contains `.`) or SID |\n\n### Neutrinos / Express service-name lookup\n\nNeutrinos services read the same DB-backed lookup rows from Redis once at\nstartup and keep an in-memory index. Outbound calls are matched by HTTP method\nand normalized pathname (host, query string, fragment, and trailing slash are\nignored). Lookup URI parameters such as `{datasetId}` match exactly one path\nsegment. Fixed segments remain strict: a version mismatch such as `/v2/...`\nversus `/v3/...` does not match; update `LOGGER_LOOKUP` with the URI actually\ncalled by the service.\n\n```ts\nimport {\n  initializeNeutrinosLookup,\n  installNeutrinosOutboundInstrumentation,\n} from '@alrajhitakaful/art-trace-logger/neutrinos';\n\nconst serviceLookup = await initializeNeutrinosLookup({\n  onWarning: (message) => console.warn(message),\n});\n\ninstallNeutrinosOutboundInstrumentation({\n  tracer,\n  serviceLookup,\n  // ...body logging options\n});\n```\n\nThe shared Redis cache remains the runtime source. If its key is empty and the\n`LOOKUP_DB_*` variables are configured, the initializer seeds Redis once from\n`LOGGER_LOOKUP`; Neutrinos services never query Oracle per request. Set\n`LOOKUP_REFRESH_INTERVAL_MS=0` to disable the default five-minute in-memory\nrefresh. If Redis/Oracle is missing or unavailable, tracing continues and\n`externalService` falls back to the outbound hostname.\n\n---\n\n## Quick Start — NestJS\n\n### 1. Register the module\n\n```ts\n// src/app.module.ts\nimport { Module } from '@nestjs/common';\nimport { TraceModule } from '@utility/art-trace-logger/nestjs';\n\n@Module({\n  imports: [\n    TraceModule.forRoot({\n      serviceName: 'orders-api',\n      environment: process.env.NODE_ENV ?? 'dev',\n      kafkaBrokers: (process.env.KAFKA_BROKERS ?? 'localhost:9094').split(','),\n      kafkaTopic: 'Middleware-APILogs',\n      enableConsole: true,\n      consoleLogPayload: true,\n      consoleMaxPayloadBytes: 2048,\n    }),\n  ],\n})\nexport class AppModule {}\n```\n\n`TraceModule` is `@Global()`, so `TraceLoggerService` is injectable everywhere without re-importing. Kafka connect/disconnect is wired to Nest's lifecycle (`OnModuleInit` / `OnModuleDestroy`).\n\n> **Do not** combine `createExpressTraceMiddleware` and `TraceInterceptor` on the same app — you will get duplicate `CLIENT_*` events. Pick one inbound approach.\n\n### 2. Wrap each request in a trace context\n\nUse a **NestJS interceptor** to open a trace scope (picking up an upstream `X-Trace-Id` if present) and log the request *and* the actual response body / errors via RxJS `tap` and `catchError`. This is preferred over a middleware + `res.on('finish')` because the interceptor's Observable gives you the real response payload and the thrown error object.\n\n```ts\n// src/trace.interceptor.ts\nimport {\n  CallHandler,\n  ExecutionContext,\n  Injectable,\n  NestInterceptor,\n} from '@nestjs/common';\nimport { Observable, throwError } from 'rxjs';\nimport { catchError, tap } from 'rxjs/operators';\nimport {\n  LogEntryBuilder,\n  TraceLoggerService,\n} from '@utility/art-trace-logger/nestjs';\nimport type { Request, Response } from 'express';\n\n@Injectable()\nexport class TraceInterceptor implements NestInterceptor {\n  constructor(private readonly tracer: TraceLoggerService) {}\n\n  intercept(ctx: ExecutionContext, next: CallHandler): Observable<unknown> {\n    const http = ctx.switchToHttp();\n    const req = http.getRequest<Request>();\n    const res = http.getResponse<Response>();\n    const upstream = req.header('x-trace-id');\n    const start = Date.now();\n\n    // runInTrace returns whatever the callback returns. The Observable is\n    // created *inside* the AsyncLocalStorage scope, so every downstream\n    // operator (and any awaited work in the handler) inherits the same traceId.\n    return this.tracer.runInTrace(() => {\n      this.tracer.log(\n        LogEntryBuilder.clientRequest()\n          .method(req.method)\n          .uri(req.originalUrl)\n          .clientIp(req.ip ?? '')\n          .headers(req.headers as Record<string, string>)\n          .payload(req.body),\n      );\n\n      return next.handle().pipe(\n        tap((body) =>\n          this.tracer.log(\n            LogEntryBuilder.clientResponse()\n              .method(req.method)\n              .uri(req.originalUrl)\n              .statusCode(res.statusCode)\n              .executionTimeMs(Date.now() - start)\n              .payload(body), // ← real response body, available here\n          ),\n        ),\n        catchError((err) => {\n          this.tracer.log(\n            LogEntryBuilder.serviceError()\n              .method(req.method)\n              .uri(req.originalUrl)\n              .statusCode(err?.status ?? 500)\n              .executionTimeMs(Date.now() - start)\n              .errorCode(err?.code ?? 'UNHANDLED')\n              .errorMessage(err?.message ?? String(err))\n              .stackTrace(err?.stack ?? ''),\n          );\n          return throwError(() => err);\n        }),\n      );\n    }, upstream);\n  }\n}\n```\n\nRegister it globally so every route is traced:\n\n```ts\n// src/app.module.ts\nimport { Module } from '@nestjs/common';\nimport { APP_INTERCEPTOR } from '@nestjs/core';\nimport { TraceModule } from '@utility/art-trace-logger/nestjs';\nimport { TraceInterceptor } from './trace.interceptor';\n\n@Module({\n  imports: [\n    TraceModule.forRoot({\n      serviceName: 'orders-api',\n      environment: process.env.NODE_ENV ?? 'dev',\n      kafkaBrokers: (process.env.KAFKA_BROKERS ?? 'localhost:9094').split(','),\n      kafkaTopic: 'Middleware-APILogs',\n      enableConsole: true,\n    }),\n  ],\n  providers: [\n    { provide: APP_INTERCEPTOR, useClass: TraceInterceptor },\n  ],\n})\nexport class AppModule {}\n```\n\n> Why an interceptor for Nest? `tap()` receives the controller return value directly. The Express middleware can log `req.body` and bodies sent via `res.json` / `res.send`, but not arbitrary handler return objects — for Nest APIs the interceptor remains the better fit for response payloads.\n\n### 3. Log inside your services\n\nBecause the middleware ran the request inside `runInTrace`, every log call automatically inherits the same `traceId` and an incrementing `sequence`:\n\n```ts\n// src/orders.service.ts\nimport { Injectable } from '@nestjs/common';\nimport { TraceLoggerService, LogEntryBuilder } from '@utility/art-trace-logger/nestjs';\n\n@Injectable()\nexport class OrdersService {\n  constructor(private readonly tracer: TraceLoggerService) {}\n\n  async create(dto: unknown) {\n    const started = Date.now();\n    try {\n      this.tracer.log(\n        LogEntryBuilder.serviceRequest()\n          .externalService('payments-api')\n          .externalSubService('POST /charge')\n          .payload(dto),\n      );\n\n      const result = await fetch('https://payments/charge', { /* … */ });\n\n      this.tracer.log(\n        LogEntryBuilder.serviceResponse()\n          .externalService('payments-api')\n          .statusCode(result.status)\n          .executionTimeMs(Date.now() - started),\n      );\n\n      return result;\n    } catch (err: any) {\n      this.tracer.log(\n        LogEntryBuilder.serviceError()\n          .errorCode('PAYMENTS_FAILED')\n          .errorMessage(err.message)\n          .stackTrace(err.stack),\n      );\n      throw err;\n    }\n  }\n}\n```\n\n---\n\n## Distributed tracing (Express + outbound HTTP)\n\nUse the same `traceId` on every service by forwarding `X-Trace-Id`.\n\n**Recommended bootstrap**:\n\n```ts\n// src/trace/bootstrap.ts\nimport {\n  TraceLogger,\n  createExpressTraceMiddleware,\n  installOutboundHttpInstrumentation,\n  resolveBodyLoggingOptions,\n  resolveConsolePayloadOptions,\n} from '@utility/art-trace-logger';\n\nconst bodyLoggingOptions = resolveBodyLoggingOptions(process.env);\nconst consolePayloadOptions = resolveConsolePayloadOptions(process.env);\n\nexport const tracer = TraceLogger.create({\n  serviceName: 'my-service',\n  environment: process.env.NEUTRINOS_APP_ENV ?? 'dev',\n  kafkaBrokers: (process.env.KAFKA_BROKERS ?? 'localhost:9094').split(','),\n  kafkaTopic: process.env.KAFKA_TRACE_TOPIC ?? 'Middleware-APILogs',\n  enableConsole: process.env.TRACE_LOGGER_CONSOLE === 'true',\n  consoleLogPayload: consolePayloadOptions.consoleLogPayload,\n  consoleMaxPayloadBytes: consolePayloadOptions.consoleMaxPayloadBytes,\n  kafkaEnabled: process.env.TRACE_LOGGER_KAFKA !== 'false',\n});\n\nexport async function initTrace() {\n  installOutboundHttpInstrumentation(tracer, bodyLoggingOptions);\n  await tracer.connect();\n}\n\nexport function applyTraceMiddleware(app: Express.Application) {\n  app.use(createExpressTraceMiddleware(tracer, bodyLoggingOptions));\n}\n```\n\nCall `initTrace()` before routes and `applyTraceMiddleware(app)` **after** `express.json()`.\n\n**What the middleware / patch capture automatically**\n\n| Direction | Metadata | Bodies (when `TRACE_LOGGER_LOG_BODIES=true`) |\n| --- | --- | --- |\n| Inbound | method, uri, status, timing | `req.body`; response via `res.json` / `res.send` |\n| Outbound (`got`, `axios`, …) | method, url, status, timing | `http.request` options body; response stream (size-limited) |\n\n**Manual outbound** (if you do not use the patch):\n\n```ts\nimport { injectTraceHeaders, logServiceRequest, logServiceResponse } from '@utility/art-trace-logger';\n\ninjectTraceHeaders(headers);\nlogServiceRequest(tracer, 'POST', url);\n// … call …\nlogServiceResponse(tracer, 'POST', url, statusCode, ms);\n```\n\nDownstream services that use `createExpressTraceMiddleware` read `X-Trace-Id` and log `CLIENT_REQUEST` / `CLIENT_RESPONSE` with the same id.\n\n**Body logging limits:** passwords/tokens are redacted; large payloads are truncated. Multipart streams and handlers that only use `res.end()` may omit response bodies on the Express path — use the Nest interceptor if you need controller return values.\n\n---\n\n## Neutrinos Studio adapter\n\nNeutrinos-generated Express services use the isolated `/neutrinos` adapter.\nThe core logger and the existing NestJS adapter remain unchanged.\n\n```ts\nimport { TraceLogger } from '@alrajhitakaful/art-trace-logger/core';\nimport {\n  createNeutrinosTraceMiddleware,\n  installNeutrinosOutboundInstrumentation,\n} from '@alrajhitakaful/art-trace-logger/neutrinos';\n\nconst tracer = TraceLogger.create({\n  serviceName: 'art-gi-b2c-bff',\n  environment: process.env.NEUTRINOS_APP_ENV ?? 'dev',\n  kafkaBrokers: (process.env.KAFKA_BROKERS ?? 'localhost:9094').split(','),\n  kafkaTopic: process.env.KAFKA_TRACE_TOPIC ?? 'Middleware-APILogs',\n});\n\nconst adapterOptions = {\n  tracer,\n  logRequestBody: true,\n  logResponseBody: true,\n};\n\ninstallNeutrinosOutboundInstrumentation(adapterOptions);\nawait tracer.connect();\n\n// Register after express.json() and before application routes.\napp.use(createNeutrinosTraceMiddleware(adapterOptions));\n```\n\nThe adapter automatically propagates:\n\n- `x-trace-id`: one identifier for the complete distributed journey.\n- `x-business-trace-id`: the configured business identifier.\n- `x-client-trace-id`: the upstream client identifier when supplied.\n- `x-span-id`: a unique identifier for each inbound or outbound operation.\n- `x-parent-span-id`: the calling operation's span identifier.\n\n`CLIENT_REQUEST` and `CLIENT_RESPONSE` share the inbound span. Every outbound\nrequest gets a new child span; its `SERVICE_REQUEST` and response/error share\nthat child span, and the receiving service uses it for its client events. This\nmakes parallel calls unambiguous without a distributed sequence counter.\n\nUse `traceId + serviceName + spanId + sequence` as the event identity in ELK.\nMap `spanId` and `parentSpanId` as Elasticsearch `keyword` fields.\n\n---\n\n## Quick Start — Plain Node.js (no NestJS)\n\nThe core is framework-agnostic. Import from the package root.\n\n### 1. Bootstrap a single logger instance\n\n```ts\n// src/logger.ts\nimport { TraceLogger } from '@utility/art-trace-logger';\n\nexport const tracer = TraceLogger.create({\n  serviceName: 'orders-api',\n  environment: process.env.NODE_ENV ?? 'dev',\n  kafkaBrokers: (process.env.KAFKA_BROKERS ?? 'localhost:9094').split(','),\n  kafkaTopic: 'Middleware-APILogs',\n  enableConsole: true,\n});\n\n// Connect once at startup\nawait tracer.connect();\n\n// Disconnect on shutdown\nprocess.on('SIGTERM', async () => {\n  await tracer.disconnect();\n  process.exit(0);\n});\n```\n\n### 2. Wrap units of work in a trace context\n\n```ts\nimport { tracer } from './logger';\nimport { LogEntryBuilder } from '@utility/art-trace-logger';\n\ntracer.runInTrace(() => {\n  tracer.log(\n    LogEntryBuilder.clientRequest()\n      .method('POST')\n      .uri('/orders')\n      .clientIdentity('partner-xyz'),\n  );\n\n  // …business logic…\n\n  tracer.log(\n    LogEntryBuilder.clientResponse()\n      .method('POST')\n      .uri('/orders')\n      .statusCode(201)\n      .executionTimeMs(42),\n  );\n});\n```\n\n### 3. Express example\n\nPrefer the built-in middleware (see [Distributed tracing](#distributed-tracing-express--outbound-http)):\n\n```ts\nimport express from 'express';\nimport { initArtTraceLogger, applyArtTraceMiddleware } from './trace/bootstrap';\n\nconst app = express();\napp.use(express.json());\n\nawait initArtTraceLogger();\napplyArtTraceMiddleware(app);\n\napp.listen(3000);\n```\n\nFor hand-rolled middleware, call `createExpressTraceMiddleware(tracer, resolveBodyLoggingOptions(process.env))` after `express.json()`.\n\n---\n\n## API Reference\n\n### `TraceLogger` (core)\n\n| Method | Description |\n| --- | --- |\n| `TraceLogger.create(config)` | Factory — returns a logger instance. |\n| `connect()` | Connects the underlying Kafka producer. Call once at startup. |\n| `disconnect()` | Disconnects gracefully. Call on shutdown. |\n| `log(builder \\| partial)` | Emits a log entry. Accepts a `LogEntryBuilder` or a `Partial<LogEntry>`. |\n| `runInTrace(fn, traceId?)` | Runs `fn` inside an `AsyncLocalStorage` scope where every nested `log()` shares the same `traceId`. If `traceId` is omitted a UUID is generated. |\n\n### `installOutboundHttpInstrumentation(tracer, bodyOptions?)`\n\nPatches Node `http` / `https` once per process. While inside `runInTrace`, outbound calls emit `SERVICE_REQUEST` / `SERVICE_RESPONSE`, forward `X-Trace-Id`, and optionally capture bodies. Call at startup before handling traffic.\n\n### `resolveBodyLoggingOptions(env?)` / `resolveConsolePayloadOptions(env?)`\n\nMap `TRACE_LOGGER_*` environment variables to `BodyLoggingOptions` and console payload settings.\n\n### `TraceLoggerService` (NestJS)\n\nSame surface as `TraceLogger`, minus `connect`/`disconnect` (handled by Nest lifecycle hooks). Import from `@utility/art-trace-logger/nestjs` (re-exports the core).\n\n### `LogEntryBuilder`\n\nStatic factories — one per `LogPoint`:\n\n- `clientRequest()` / `clientResponse()`\n- `serviceRequest()` / `serviceResponse()`\n- `serviceError()` (alias: `internalError()` — deprecated)\n- `custom()`\n\nFluent setters: `clientTraceId`, `clientIp`, `clientIdentity`, `method`, `uri`, `headers`, `statusCode`, `executionTimeMs`, `externalService`, `externalSubService`, `payload`, `errorCode`, `errorMessage`, `errorContext`, `stackTrace`.\n\n### `TraceContext`\n\nLow-level helper around `AsyncLocalStorage`:\n\n- `TraceContext.run(fn, traceId?)` — start/inherit a trace scope.\n- `TraceContext.getTraceId()` — current trace id (`'no-trace'` outside a scope).\n- `TraceContext.nextSequence()` — increment & return the per-trace counter.\n\nThe `TraceLogger` uses these automatically — you rarely call them directly.\n\n### `LogEntry` shape\n\nEach emitted Kafka message is a JSON-serialized `LogEntry`:\n\n```ts\n{\n  logPoint: 'CLIENT_REQUEST',\n  traceId: 'b1a2…',\n  sequence: 1,\n  serviceName: 'orders-api',\n  environment: 'prod',\n  machineName: 'pod-7c9f',\n  timestamp: '2026-05-04T10:22:15.043Z',\n  method: 'POST',\n  uri: '/orders',\n  statusCode: 201,\n  executionTimeMs: 42,\n  // …plus any fields you set on the builder\n}\n```\n\n---\n\n## Local Infrastructure\n\nFor local development, run a Kafka + Logstash + Elasticsearch + Kibana stack (e.g. via Docker Compose) with a Logstash pipeline that consumes the configured trace topic (e.g. `Middleware-APILogs`) and indexes it into Elasticsearch.\n\n---\n\n## Operational Notes\n\n- `log()` is **fire-and-forget** — Kafka send errors are swallowed (and logged to stderr) so they never break a request path.\n- A single `TraceLogger` instance is sufficient per process; share it.\n- Outside a `runInTrace` scope, entries are stamped with `traceId: 'no-trace'` and `sequence: 0` — useful for boot-time logs but propagation is lost.\n- To propagate trace ids across services, forward the `traceId` as an `X-Trace-Id` header on outbound HTTP calls and pass it to `runInTrace(fn, traceId)` on the receiving side.\n\n---\n\n## License\n\nInternal — Digital Transformation.\n","readmeFilename":"README.md"}