{"_id":"@commencement.technology/ct-seri-logs","_rev":"2-60827d00c27148888512d964cf12cd2c","name":"@commencement.technology/ct-seri-logs","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@commencement.technology/ct-seri-logs","version":"0.1.0","keywords":["seri","serilog","logging","observability","tracing","request-logging","application-errors","mysql","postgres","nodejs","typescript","commencement-technology"],"author":{"name":"Commencement Technology","email":"commencement.technology@gmail.com"},"license":"MIT","_id":"@commencement.technology/ct-seri-logs@0.1.0","maintainers":[{"name":"parth200292","email":"parth200292@gmail.com"},{"name":"commencementtechnology","email":"commencement.technology@gmail.com"}],"homepage":"https://github.com/commencementtech/CT-Seri-Log","bugs":{"url":"https://github.com/commencementtech/CT-Seri-Log/issues"},"dist":{"shasum":"61c0be38b173b4d50aa0d2966531a4e578158c68","tarball":"https://registry.npmjs.org/@commencement.technology/ct-seri-logs/-/ct-seri-logs-0.1.0.tgz","fileCount":17,"integrity":"sha512-+m87cEQks3OavKAuukpIJlPpfszhcdZWJOw+rfcFyQHp/Al0GQ4fID2XL/mogg2fEt+g7rcVGplkJPm3UCQMfQ==","signatures":[{"sig":"MEYCIQCfBXhIGp0/EYYmitn9VAqA9XN5sq7E1UKA8Hqs43iYAQIhAK806VAMZ8+7DtxJLYzdlNbWk2526nd2Fc9wFJMjGjlr","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":260531},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"8758ca9f369bdc22e5f4ada9ba919e5ce18c128e","scripts":{"lint":"eslint .","test":"vitest run","build":"tsup src/index.ts --format esm,cjs --dts --sourcemap --clean","format":"prettier --write .","benchmark":"tsx benchmarks/queue-throughput.ts","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"pnpm run typecheck && pnpm run lint && pnpm test && pnpm run build"},"_npmUser":{"name":"commencementtechnology","email":"commencement.technology@gmail.com"},"repository":{"url":"git+https://github.com/commencementtech/CT-Seri-Logs.git","type":"git"},"_npmVersion":"11.6.2","description":"Commencement Technology SERI logging and observability SDK for Node.js/TypeScript applications.","directories":{},"sideEffects":false,"_nodeVersion":"24.14.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"pnpm@11.16.0","devDependencies":{"tsx":"^4.15.0","tsup":"^8.1.0","eslint":"^8.57.0","vitest":"^1.6.0","prettier":"^3.3.0","typescript":"^5.5.0","@types/node":"^20.14.0","@typescript-eslint/parser":"^7.13.0","@typescript-eslint/eslint-plugin":"^7.13.0"},"peerDependencies":{"pg":">=8","axios":">=1","mysql2":">=3","express":">=4","fastify":">=4"},"peerDependenciesMeta":{"pg":{"optional":true},"axios":{"optional":true},"mysql2":{"optional":true},"express":{"optional":true},"fastify":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ct-seri-logs_0.1.0_1786525294966_0.23720749105797756","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@commencement.technology/ct-seri-logs","version":"0.2.0","description":"SERI-structured logging and observability for Node.js and NestJS: Serilog-style message templates, page/API request logs on daily-rotated MySQL tables, retention, and the reporting and chart queries to go with them.","author":{"name":"Commencement Technology","email":"commencement.technology@gmail.com"},"license":"MIT","keywords":["seri","serilog","logging","logger","observability","tracing","request-logging","structured-logging","message-templates","application-errors","nestjs","express","fastify","mysql","postgres","nodejs","typescript","commencement-technology"],"type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./nest":{"types":"./dist/nest/index.d.ts","import":"./dist/nest/index.js","require":"./dist/nest/index.cjs"},"./migrations/*":"./migrations/*","./package.json":"./package.json"},"sideEffects":false,"packageManager":"pnpm@11.16.0","publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/commencementtech/CT-Seri-Logs.git"},"homepage":"https://github.com/commencementtech/CT-Seri-Log","bugs":{"url":"https://github.com/commencementtech/CT-Seri-Log/issues"},"scripts":{"build":"tsup","test":"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage","typecheck":"tsc --noEmit","lint":"eslint .","format":"prettier --write .","benchmark":"tsx benchmarks/queue-throughput.ts","generate:migrations":"node scripts/generate-migrations.mjs","prepublishOnly":"pnpm run typecheck && pnpm test && pnpm run build"},"engines":{"node":">=20"},"peerDependencies":{"@nestjs/common":">=9","@nestjs/core":">=9","axios":">=1","express":">=4","fastify":">=4","mysql2":">=3","pg":">=8"},"peerDependenciesMeta":{"@nestjs/common":{"optional":true},"@nestjs/core":{"optional":true},"axios":{"optional":true},"express":{"optional":true},"fastify":{"optional":true},"mysql2":{"optional":true},"pg":{"optional":true}},"devDependencies":{"@types/express":"^5.0.6","@types/node":"^20.14.0","@typescript-eslint/eslint-plugin":"^7.13.0","@typescript-eslint/parser":"^7.13.0","eslint":"^8.57.0","prettier":"^3.3.0","tsup":"^8.1.0","tsx":"^4.15.0","typescript":"^5.5.0","vitest":"^1.6.0"},"gitHead":"52495d4bb329823febee60b88251af0fb17b1496","_id":"@commencement.technology/ct-seri-logs@0.2.0","_nodeVersion":"24.14.1","_npmVersion":"11.6.2","dist":{"integrity":"sha512-xpMXBTVIukdoqkPnVk4h9vwiDbM2rxn2CGideaicIDoFdPrgHQzlD06zhB4rWZgjyv/6ixhe9KLBcV7IKqniZw==","shasum":"1e62b966080fb0432a651ec524d19846dfeb7643","tarball":"https://registry.npmjs.org/@commencement.technology/ct-seri-logs/-/ct-seri-logs-0.2.0.tgz","fileCount":29,"unpackedSize":3002619,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIHO1IT0M2z2xhIRkrRrLFE7v3qcusOGZ+B0ZwLWiaDOQAiAC43LIm7ia98RQrUlGUkQ4i+ubGVcwy6GfpEPW76u3Ew=="}]},"_npmUser":{"name":"commencementtechnology","email":"commencement.technology@gmail.com"},"directories":{},"maintainers":[{"name":"parth200292","email":"parth200292@gmail.com"},{"name":"commencementtechnology","email":"commencement.technology@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ct-seri-logs_0.2.0_1786697615328_0.3047585972161466"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-12T09:01:34.757Z","modified":"2026-08-14T08:53:35.660Z","0.1.0":"2026-08-12T09:01:35.099Z","0.2.0":"2026-08-14T08:53:35.477Z"},"bugs":{"url":"https://github.com/commencementtech/CT-Seri-Log/issues"},"author":{"name":"Commencement Technology","email":"commencement.technology@gmail.com"},"license":"MIT","homepage":"https://github.com/commencementtech/CT-Seri-Log","keywords":["seri","serilog","logging","logger","observability","tracing","request-logging","structured-logging","message-templates","application-errors","nestjs","express","fastify","mysql","postgres","nodejs","typescript","commencement-technology"],"repository":{"type":"git","url":"git+https://github.com/commencementtech/CT-Seri-Logs.git"},"description":"SERI-structured logging and observability for Node.js and NestJS: Serilog-style message templates, page/API request logs on daily-rotated MySQL tables, retention, and the reporting and chart queries to go with them.","maintainers":[{"name":"parth200292","email":"parth200292@gmail.com"},{"name":"commencementtechnology","email":"commencement.technology@gmail.com"}],"readme":"# @commencement.technology/ct-seri-logs\n\nSERI-structured logging and observability for Node.js, Express, Fastify and NestJS.\n\nThis is a port of the .NET/Serilog SERI logging structure, not a generic logger. It keeps the\nfour log streams the reference system defines, in the same tables, with the same rotation and\nretention behaviour — and adds the reporting and chart queries that go with them.\n\n| Stream | Table | What it holds |\n| --- | --- | --- |\n| Application logs | `serilog` | Templates, messages, exceptions, properties |\n| Page requests | `webrequestlog` + `_1`…`_7` | Browser traffic, keyed by `VisiterID` |\n| API requests | `webservicelog` + `_1`…`_7` | Device/API traffic, keyed by `DeviceID` |\n| Device locations | `networklocationlog` | Network location fixes |\n\n## Install\n\n```bash\npnpm add @commencement.technology/ct-seri-logs mysql2\n```\n\n`mysql2`, `pg`, `express`, `fastify`, `axios`, `@nestjs/common` and `@nestjs/core` are optional\npeers — install only the ones you use.\n\n## Quick start\n\n```ts\nimport { createObservability } from '@commencement.technology/ct-seri-logs';\n\nconst observability = createObservability({\n  serviceName: 'orders-api',\n  environment: process.env.NODE_ENV,\n  transports: [\n    { type: 'console' },\n    {\n      type: 'mysql',\n      connectionString: process.env.OBSERVABILITY_MYSQL_URL,\n      isolation: {\n        applicationDatabaseName: 'orders_app',\n        observabilityDatabaseName: 'orders_observability'\n      }\n    }\n  ],\n  rotation: { schedule: true },\n  retention: { schedule: true }\n});\n\nawait observability.start();\n\nconst log = observability.logger('OrdersService');\nlog.information('Order {OrderId} shipped to {Country}', 4711, 'IN');\n```\n\n## Message templates\n\nThe template is the identity of a log statement; the values are what change. All three parts are\nstored separately, so `Template` groups occurrences and `Properties` is queryable by field.\n\n```ts\nlog.information('Order {OrderId} shipped to {Country}', 4711, 'IN');\n```\n\n| Column | Value |\n| --- | --- |\n| `Template` | `Order {OrderId} shipped to {Country}` |\n| `Message` | `Order 4711 shipped to IN` |\n| `Properties` | `{\"OrderId\":4711,\"Country\":\"IN\", …}` |\n\n`{@Value}` captures structure, `{$Value}` forces the string form, `{Value:f2}` and `{Value,10}`\nformat and align. See [docs/message-templates.md](docs/message-templates.md).\n\nThe older style still works — a template with no holes plus one object is treated as properties:\n\n```ts\nlog.info('Application started', { port: 3000 });\n```\n\n## Errors\n\nPass the exception first, Serilog-style, so the stack is preserved into the `Exception` column:\n\n```ts\ntry {\n  await charge(order);\n} catch (error) {\n  log.error(error, 'Could not charge order {OrderId}', order.id);\n}\n```\n\nErrors get a stable `fingerprint` — type, normalised message and top frame — so `Order 41 not\nfound` and `Order 42 not found` group together. `observability.query.errorGroups()` reports by\nfingerprint.\n\n## HTTP request logging\n\n```ts\n// Express\napp.use(observability.express());\n// ... routes ...\napp.use(observability.expressErrorHandler());\n\n// Fastify\nawait app.register(observability.fastifyPlugin());\n```\n\nRequests are classified into `webrequestlog` or `webservicelog` — by default `/api` and anything\ncarrying a device-id header goes to the service stream. Configure it with `streams`.\n\nLogs written inside a handler automatically carry the request, trace and user ids; nothing needs\nto be threaded through your call signatures.\n\n```ts\napp.get('/orders/:id', (req, res) => {\n  log.information('Loading order {OrderId}', req.params.id); // already correlated\n  res.json({ id: req.params.id });\n});\n```\n\n## NestJS\n\n```ts\nimport { ObservabilityModule } from '@commencement.technology/ct-seri-logs/nest';\n\n@Module({\n  imports: [\n    ObservabilityModule.forRoot({\n      serviceName: 'orders-api',\n      transports: [{ type: 'console' }, { type: 'mysql', connectionString: process.env.DB_URL }]\n    })\n  ]\n})\nexport class AppModule {}\n```\n\nThat registers the request middleware, a global exception filter, an interceptor that tags events\nwith the controller and handler, and a shutdown hook that flushes the final batch.\n\nSee [docs/nestjs.md](docs/nestjs.md).\n\n## Reporting and charts\n\nEvery stored procedure in the reference system has an equivalent:\n\n```ts\nconst page = await observability.query.webRequestLogs({\n  from: '2026-08-14T00:00:00Z',\n  to: '2026-08-14T23:59:59Z',\n  statusCode: 500,\n  pageSize: 50\n});\n\nconst detail = await observability.query.webRequestLogById(page.rows[0].id, page.rows[0].dateUpdated);\n\nconst chart = await observability.query.chart({\n  name: 'WebRequestLog-byhr',\n  type: 'data',\n  date: '2026-08-14'\n});\n```\n\nCharts return Highcharts-shaped output, so an existing dashboard can call this with the same\n`{name, type, date, filter}` payload it sent to `SP_Chart`.\n\nSee [docs/queries-and-charts.md](docs/queries-and-charts.md).\n\n## Daily rotation and retention\n\nThe request tables age out by being renamed, not deleted: nightly, `webrequestlog` becomes `_1`,\n`_1` becomes `_2`, `_7` is dropped, and a fresh live table takes over. Dropping a day's table is\ninstant; deleting tens of millions of rows is not.\n\n```ts\nrotation: { schedule: true, runAtUtcHour: 0, retainDays: 7 },\nretention: { schedule: true, runAtUtcHour: 1, seriLogDays: 7 }\n```\n\nBoth jobs claim the day in a bookkeeping table before running, so they are safe with any number\nof replicas. Run them manually if you prefer an external scheduler:\n\n```ts\nawait observability.maintenance?.runRotation();\nawait observability.maintenance?.runRetention();\n```\n\nSee [docs/operations.md](docs/operations.md).\n\n## Safe by default\n\n- Request and response bodies are **not** captured unless you enable them\n- Passwords, tokens, keys, card numbers and auth headers are redacted, by name and by value shape\n- The queue is bounded; overflow drops rather than growing memory\n- Transport failures are retried with backoff, then the sink is tripped out of the rotation\n- A logging failure can never throw into your application\n- Uncaught exceptions, rejections and `SIGTERM` all flush before the process exits\n\n## Database setup\n\nRun the migration in a **dedicated** observability database:\n\n```bash\nmysql orders_observability < node_modules/@commencement.technology/ct-seri-logs/migrations/mysql/001_initial.sql\n```\n\nStartup fails fast if the application and observability database names match, because rotation\nrenames and drops tables and retention issues bulk deletes. See\n[docs/database-isolation.md](docs/database-isolation.md).\n\n## Health\n\n```ts\nobservability.health();\n// { status, queue: { queued, capacity, dropped, highWaterMark }, transports: [...], written, failed }\n```\n\n## Diagnosing the logger itself\n\nA logger that swallows its own failures looks identical to one that is working. Turn on SelfLog\nand internal failures are reported:\n\n```ts\ncreateObservability({ serviceName: 'orders-api', selfLog: true });\n```\n\n## Documentation\n\n- [Message templates](docs/message-templates.md)\n- [Schema and streams](docs/schema.md)\n- [Queries and charts](docs/queries-and-charts.md)\n- [NestJS](docs/nestjs.md)\n- [Operations: rotation, retention, tuning](docs/operations.md)\n- [Differences from the .NET reference](docs/reference-differences.md)\n- [Database isolation](docs/database-isolation.md)\n- [Database support](docs/database-support.md)\n- [Migrating from existing logging](docs/migration-from-existing-logging.md)\n\n## License\n\nMIT\n","readmeFilename":"README.md"}