tpaga-logger 0.0.7 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +132 -16
- package/dist/fastify.cjs +198 -0
- package/dist/fastify.cjs.map +1 -0
- package/dist/fastify.d.cts +28 -0
- package/dist/fastify.d.ts +28 -0
- package/dist/fastify.js +164 -0
- package/dist/fastify.js.map +1 -0
- package/dist/index.cjs +11 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -31
- package/dist/index.d.ts +3 -31
- package/dist/index.js +11 -5
- package/dist/index.js.map +1 -1
- package/dist/types-BtTFSWeZ.d.cts +31 -0
- package/dist/types-BtTFSWeZ.d.ts +31 -0
- package/package.json +31 -10
package/README.md
CHANGED
|
@@ -39,50 +39,166 @@ export const handler = withLogger(logger, 'createCharge', async (event, log) =>
|
|
|
39
39
|
|
|
40
40
|
### Express
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
Register two middlewares from the library — one before routes, one after. Both are zero-config.
|
|
43
43
|
|
|
44
44
|
```typescript
|
|
45
45
|
import express from 'express';
|
|
46
|
-
import { createLogger, withTpagaExpressLogger,
|
|
46
|
+
import { createLogger, withTpagaExpressLogger, tpagaExpressErrorLogger } from 'tpaga-logger';
|
|
47
|
+
import { errorHandler } from './middleware/errorHandler';
|
|
47
48
|
|
|
48
|
-
const logger = createLogger({
|
|
49
|
-
service: 'url-signer-service',
|
|
50
|
-
environment: process.env.NODE_ENV ?? 'dev',
|
|
51
|
-
});
|
|
49
|
+
const logger = createLogger({ service: 'url-signer-service', environment: process.env.NODE_ENV ?? 'dev' });
|
|
52
50
|
|
|
53
51
|
const app = express();
|
|
54
52
|
app.use(express.json());
|
|
55
|
-
app.use(withTpagaExpressLogger(logger)); //
|
|
53
|
+
app.use(withTpagaExpressLogger(logger)); // before routes: timing, correlationId, req.log
|
|
56
54
|
|
|
57
|
-
app.
|
|
58
|
-
req.log.with({ userId: req.body.userId, resource: req.body.resource });
|
|
55
|
+
app.use('/api/v1/sign', signRouter);
|
|
59
56
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
// terminal log is emitted automatically on res.finish
|
|
63
|
-
});
|
|
57
|
+
app.use(tpagaExpressErrorLogger); // after routes: serialize + log any error
|
|
58
|
+
app.use(errorHandler); // after routes: map error → HTTP response (your code)
|
|
64
59
|
```
|
|
65
60
|
|
|
61
|
+
> Express error middleware must be registered **after** routes — this is an Express constraint.
|
|
62
|
+
|
|
66
63
|
**What `withTpagaExpressLogger` does automatically:**
|
|
67
|
-
- Extracts `correlationId` from headers (generates
|
|
68
|
-
- Attaches a typed `WideEventBuilder` to `req.log`
|
|
64
|
+
- Extracts `correlationId` from `x-correlation-id` / `x-request-id` headers (generates UUID if absent)
|
|
65
|
+
- Attaches a typed `WideEventBuilder` to `req.log` and `res.log`
|
|
66
|
+
- Emits `outcome: "success"` for `statusCode < 400`, `outcome: "error"` otherwise
|
|
67
|
+
- Calculates `durationMs` — always matches the actual response time
|
|
68
|
+
|
|
69
|
+
**What `tpagaExpressErrorLogger` does automatically:**
|
|
70
|
+
- Serializes the error via `serializeError` — no stack, structured `errorDetails` for Zod-like errors
|
|
71
|
+
- Adds `error` to the log context via `req.log.with({ error })`
|
|
72
|
+
- Calls `next(err)` to pass the error to your `errorHandler`
|
|
73
|
+
|
|
74
|
+
**`req.log` / `res.log` in controllers:**
|
|
75
|
+
|
|
76
|
+
`req.log` is available anywhere in the request lifecycle — no import needed:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
export const signUrl = async (req: Request, res: Response, next: NextFunction) => {
|
|
80
|
+
try {
|
|
81
|
+
const input = signUrlSchema.parse(req.body);
|
|
82
|
+
req.log.with({ url: input.url, ttlSeconds: input.ttlSeconds });
|
|
83
|
+
res.status(200).json(generateSignedUrl(input));
|
|
84
|
+
} catch (err) {
|
|
85
|
+
req.log.with({ attemptedUrl: req.body?.url, stage: 'signing' }); // extra context on error
|
|
86
|
+
next(err);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`res.log` works the same — useful in error handler middleware where `req` params are prefixed with `_`.
|
|
92
|
+
|
|
93
|
+
**`statusCode` in the log always matches the HTTP response** — it's read from `res.statusCode` after the response is sent.
|
|
94
|
+
|
|
95
|
+
**`serializeError` output:**
|
|
96
|
+
|
|
97
|
+
| Error type | `type` | `message` | `errorDetails` |
|
|
98
|
+
|-----------|--------|-----------|----------------|
|
|
99
|
+
| ZodError | `ZodError` | `Validation failed` | array of Zod issues |
|
|
100
|
+
| AppError / Error | `Error` | original message | — |
|
|
101
|
+
| Unknown | `UnknownError` | `String(err)` | — |
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
### NestJS + Fastify <sup>v0.0.9+</sup>
|
|
106
|
+
|
|
107
|
+
Import from `tpaga-logger/fastify`. Register the plugin and the global interceptor — both are zero-config.
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
// app.module.ts
|
|
111
|
+
import { Module } from '@nestjs/common';
|
|
112
|
+
import { TpagaLoggerModule } from 'tpaga-logger/fastify';
|
|
113
|
+
|
|
114
|
+
@Module({
|
|
115
|
+
imports: [
|
|
116
|
+
TpagaLoggerModule.forRoot({
|
|
117
|
+
service: process.env.SERVICE_NAME ?? 'my-service',
|
|
118
|
+
environment: process.env.NODE_ENV ?? 'dev',
|
|
119
|
+
}),
|
|
120
|
+
],
|
|
121
|
+
})
|
|
122
|
+
export class AppModule {}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
// main.ts
|
|
127
|
+
import { NestFactory } from '@nestjs/core';
|
|
128
|
+
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
|
|
129
|
+
import { withTpagaFastifyLogger, TpagaLoggerInterceptor, TPAGA_LOGGER } from 'tpaga-logger/fastify';
|
|
130
|
+
import type { Logger } from 'tpaga-logger';
|
|
131
|
+
import { AppModule } from './app.module';
|
|
132
|
+
|
|
133
|
+
async function bootstrap() {
|
|
134
|
+
const app = await NestFactory.create<NestFastifyApplication>(AppModule, new FastifyAdapter());
|
|
135
|
+
|
|
136
|
+
const logger = app.get<Logger>(TPAGA_LOGGER);
|
|
137
|
+
await app.register(withTpagaFastifyLogger(logger)); // before routes: timing, correlationId, request.tpagaLog
|
|
138
|
+
app.useGlobalInterceptors(new TpagaLoggerInterceptor()); // catch errors before exception filters
|
|
139
|
+
|
|
140
|
+
await app.listen(3000, '0.0.0.0');
|
|
141
|
+
}
|
|
142
|
+
bootstrap();
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**What `withTpagaFastifyLogger` does automatically:**
|
|
146
|
+
- Extracts `correlationId` from `x-correlation-id` / `x-request-id` headers (generates UUID if absent)
|
|
147
|
+
- Attaches a typed `WideEventBuilder` to `request.tpagaLog`
|
|
69
148
|
- Emits `outcome: "success"` for `statusCode < 400`, `outcome: "error"` otherwise
|
|
70
|
-
- Calculates `durationMs`
|
|
149
|
+
- Calculates `durationMs` — always matches the actual response time
|
|
150
|
+
|
|
151
|
+
**What `TpagaLoggerInterceptor` does automatically:**
|
|
152
|
+
- Wraps every route handler and catches thrown exceptions before NestJS exception filters run
|
|
153
|
+
- Serializes the error via `serializeError` and adds it to the log context via `request.tpagaLog.with({ error })`
|
|
154
|
+
- Re-throws so NestJS exception handling continues normally
|
|
155
|
+
|
|
156
|
+
> Use `request.tpagaLog` (not `request.log`) — Fastify already attaches its own pino logger to `request.log`.
|
|
157
|
+
|
|
158
|
+
**`request.tpagaLog` in controllers:**
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
import { Controller, Get, Param, Req } from '@nestjs/common';
|
|
162
|
+
import type { FastifyRequest } from 'fastify';
|
|
163
|
+
|
|
164
|
+
@Controller('charges')
|
|
165
|
+
export class ChargesController {
|
|
166
|
+
@Get(':id')
|
|
167
|
+
async getCharge(@Req() req: FastifyRequest, @Param('id') id: string) {
|
|
168
|
+
req.tpagaLog.with({ chargeId: id });
|
|
169
|
+
const charge = await this.chargesService.findById(id);
|
|
170
|
+
req.tpagaLog.with({ merchantId: charge.merchantId });
|
|
171
|
+
return charge;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
71
175
|
|
|
72
176
|
---
|
|
73
177
|
|
|
74
178
|
## API
|
|
75
179
|
|
|
180
|
+
**`tpaga-logger`** (Express + Lambda)
|
|
181
|
+
|
|
76
182
|
| Export | Description |
|
|
77
183
|
|--------|-------------|
|
|
78
184
|
| `createLogger(config)` | Creates a logger instance for a service |
|
|
79
185
|
| `withLogger(logger, name, handler)` | Lambda handler wrapper |
|
|
80
186
|
| `withTpagaExpressLogger(logger)` | Express middleware (register with `app.use`) |
|
|
187
|
+
| `tpagaExpressErrorLogger` | Express error middleware (register after routes) |
|
|
81
188
|
| `getLog(res)` | Gets the `WideEventBuilder` from an Express response |
|
|
82
189
|
| `serializeError(err)` | Serializes an unknown error to a structured object |
|
|
83
190
|
| `LOG_LEVELS` | `['info', 'warn', 'error', 'debug']` |
|
|
84
191
|
| `OUTCOMES` | `['success', 'error', 'validation_failed', ...]` |
|
|
85
192
|
|
|
193
|
+
**`tpaga-logger/fastify`** (NestJS + Fastify)
|
|
194
|
+
|
|
195
|
+
| Export | Description |
|
|
196
|
+
|--------|-------------|
|
|
197
|
+
| `withTpagaFastifyLogger(logger)` | Fastify plugin — register with `app.register()` |
|
|
198
|
+
| `TpagaLoggerInterceptor` | NestJS interceptor — register with `app.useGlobalInterceptors()` |
|
|
199
|
+
| `TpagaLoggerModule` | NestJS dynamic module — import in `AppModule` |
|
|
200
|
+
| `TPAGA_LOGGER` | Injection token to retrieve the `Logger` instance via DI |
|
|
201
|
+
|
|
86
202
|
### `LoggerConfig`
|
|
87
203
|
|
|
88
204
|
```typescript
|
package/dist/fastify.cjs
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
|
|
9
|
+
var __typeError = (msg) => {
|
|
10
|
+
throw TypeError(msg);
|
|
11
|
+
};
|
|
12
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
13
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
27
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
28
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
29
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
30
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
31
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
32
|
+
mod
|
|
33
|
+
));
|
|
34
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
35
|
+
var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
|
|
36
|
+
var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
|
37
|
+
var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
|
|
38
|
+
var __decoratorContext = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) });
|
|
39
|
+
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
|
|
40
|
+
var __runInitializers = (array, flags, self, value) => {
|
|
41
|
+
for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
|
|
42
|
+
return value;
|
|
43
|
+
};
|
|
44
|
+
var __decorateElement = (array, flags, name, decorators, target, extra) => {
|
|
45
|
+
var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
|
|
46
|
+
var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
|
|
47
|
+
var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
|
|
48
|
+
var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : { get [name]() {
|
|
49
|
+
return __privateGet(this, extra);
|
|
50
|
+
}, set [name](x) {
|
|
51
|
+
return __privateSet(this, extra, x);
|
|
52
|
+
} }, name));
|
|
53
|
+
k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
|
|
54
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
55
|
+
ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
|
|
56
|
+
if (k) {
|
|
57
|
+
ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x };
|
|
58
|
+
if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
|
|
59
|
+
if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
|
|
60
|
+
}
|
|
61
|
+
it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1;
|
|
62
|
+
if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
|
|
63
|
+
else if (typeof it !== "object" || it === null) __typeError("Object expected");
|
|
64
|
+
else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
|
|
65
|
+
}
|
|
66
|
+
return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
67
|
+
};
|
|
68
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
69
|
+
var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
|
|
70
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
71
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
72
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
73
|
+
|
|
74
|
+
// src/fastify.ts
|
|
75
|
+
var fastify_exports = {};
|
|
76
|
+
__export(fastify_exports, {
|
|
77
|
+
TPAGA_LOGGER: () => TPAGA_LOGGER,
|
|
78
|
+
TpagaLoggerInterceptor: () => TpagaLoggerInterceptor,
|
|
79
|
+
TpagaLoggerModule: () => TpagaLoggerModule,
|
|
80
|
+
withTpagaFastifyLogger: () => withTpagaFastifyLogger
|
|
81
|
+
});
|
|
82
|
+
module.exports = __toCommonJS(fastify_exports);
|
|
83
|
+
var import_rxjs = require("rxjs");
|
|
84
|
+
var import_fastify_plugin = __toESM(require("fastify-plugin"), 1);
|
|
85
|
+
var import_common = require("@nestjs/common");
|
|
86
|
+
|
|
87
|
+
// src/logger.ts
|
|
88
|
+
var import_pino = __toESM(require("pino"), 1);
|
|
89
|
+
|
|
90
|
+
// src/utils.ts
|
|
91
|
+
var import_crypto = require("crypto");
|
|
92
|
+
var resolveCorrelationId = (headers = {}) => {
|
|
93
|
+
const h = Object.fromEntries(
|
|
94
|
+
Object.entries(headers).map(([k, v]) => [k.toLowerCase(), Array.isArray(v) ? v[0] : v])
|
|
95
|
+
);
|
|
96
|
+
return h["x-correlation-id"] ?? h["x-request-id"] ?? (0, import_crypto.randomUUID)();
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// src/logger.ts
|
|
100
|
+
var serializeError = (err) => {
|
|
101
|
+
if (!(err instanceof Error)) return { type: "UnknownError", message: String(err) };
|
|
102
|
+
const errorDetails = err.issues;
|
|
103
|
+
return {
|
|
104
|
+
type: err.name,
|
|
105
|
+
message: errorDetails != null ? "Validation failed" : err.message,
|
|
106
|
+
code: err.code,
|
|
107
|
+
errorDetails
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
var createWideEventBuilder = (base, log) => {
|
|
111
|
+
const ctx = { ...base };
|
|
112
|
+
return {
|
|
113
|
+
with: (fields) => Object.assign(ctx, fields),
|
|
114
|
+
emit: (level, message, terminal) => log[level]({ ...ctx, ...terminal }, message)
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
var createLogger = (config) => {
|
|
118
|
+
const pretty = process.env.LOG_PRETTY === "true";
|
|
119
|
+
const log = (0, import_pino.default)({
|
|
120
|
+
level: process.env.LOG_LEVEL ?? "info",
|
|
121
|
+
base: { service: config.service, environment: config.environment },
|
|
122
|
+
formatters: {
|
|
123
|
+
level: (label) => ({ level: label })
|
|
124
|
+
},
|
|
125
|
+
timestamp: import_pino.default.stdTimeFunctions.isoTime,
|
|
126
|
+
redact: {
|
|
127
|
+
paths: config.redactKeys ?? [],
|
|
128
|
+
censor: "[REDACTED]"
|
|
129
|
+
},
|
|
130
|
+
...pretty ? { transport: { target: "pino-pretty", options: { colorize: true } } } : {}
|
|
131
|
+
});
|
|
132
|
+
return {
|
|
133
|
+
startOperation: (functionName, base = {}) => createWideEventBuilder({ function: functionName, ...base }, log)
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// src/fastify.ts
|
|
138
|
+
var START_KEY = /* @__PURE__ */ Symbol("tpagaStart");
|
|
139
|
+
var withTpagaFastifyLogger = (logger) => (0, import_fastify_plugin.default)(async (fastify) => {
|
|
140
|
+
fastify.addHook("onRequest", (request, _reply, done) => {
|
|
141
|
+
const correlationId = resolveCorrelationId(request.headers);
|
|
142
|
+
request.tpagaLog = logger.startOperation(request.url, {
|
|
143
|
+
correlationId,
|
|
144
|
+
method: request.method
|
|
145
|
+
});
|
|
146
|
+
request[START_KEY] = Date.now();
|
|
147
|
+
done();
|
|
148
|
+
});
|
|
149
|
+
fastify.addHook("onResponse", (request, reply, done) => {
|
|
150
|
+
const start = request[START_KEY];
|
|
151
|
+
request.tpagaLog.emit("info", "request completed", {
|
|
152
|
+
outcome: reply.statusCode < 400 ? "success" : "error",
|
|
153
|
+
durationMs: Date.now() - start,
|
|
154
|
+
statusCode: reply.statusCode
|
|
155
|
+
});
|
|
156
|
+
done();
|
|
157
|
+
});
|
|
158
|
+
fastify.addHook("onError", (request, _reply, error, done) => {
|
|
159
|
+
request.tpagaLog.with({ error: serializeError(error) });
|
|
160
|
+
done();
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
var _TpagaLoggerInterceptor_decorators, _init;
|
|
164
|
+
_TpagaLoggerInterceptor_decorators = [(0, import_common.Injectable)()];
|
|
165
|
+
var TpagaLoggerInterceptor = class {
|
|
166
|
+
intercept(context, next) {
|
|
167
|
+
return next.handle().pipe(
|
|
168
|
+
(0, import_rxjs.catchError)((error) => {
|
|
169
|
+
const request = context.switchToHttp().getRequest();
|
|
170
|
+
request.tpagaLog?.with({ error: serializeError(error) });
|
|
171
|
+
return (0, import_rxjs.throwError)(() => error);
|
|
172
|
+
})
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
_init = __decoratorStart(null);
|
|
177
|
+
TpagaLoggerInterceptor = __decorateElement(_init, 0, "TpagaLoggerInterceptor", _TpagaLoggerInterceptor_decorators, TpagaLoggerInterceptor);
|
|
178
|
+
__runInitializers(_init, 1, TpagaLoggerInterceptor);
|
|
179
|
+
var TPAGA_LOGGER = "TPAGA_LOGGER";
|
|
180
|
+
var TpagaLoggerModule = class _TpagaLoggerModule {
|
|
181
|
+
static forRoot(config) {
|
|
182
|
+
const logger = createLogger(config);
|
|
183
|
+
return {
|
|
184
|
+
module: _TpagaLoggerModule,
|
|
185
|
+
global: true,
|
|
186
|
+
providers: [{ provide: TPAGA_LOGGER, useValue: logger }],
|
|
187
|
+
exports: [TPAGA_LOGGER]
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
192
|
+
0 && (module.exports = {
|
|
193
|
+
TPAGA_LOGGER,
|
|
194
|
+
TpagaLoggerInterceptor,
|
|
195
|
+
TpagaLoggerModule,
|
|
196
|
+
withTpagaFastifyLogger
|
|
197
|
+
});
|
|
198
|
+
//# sourceMappingURL=fastify.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/fastify.ts","../src/logger.ts","../src/utils.ts"],"sourcesContent":["import { catchError, throwError } from 'rxjs';\nimport type { Observable } from 'rxjs';\nimport fp from 'fastify-plugin';\nimport type { FastifyInstance, FastifyRequest } from 'fastify';\nimport type { CallHandler, ExecutionContext, NestInterceptor } from '@nestjs/common';\nimport { Injectable } from '@nestjs/common';\nimport { createLogger, serializeError } from './logger.js';\nimport { resolveCorrelationId } from './utils.js';\nimport type { Logger, LoggerConfig, WideEventBuilder } from './types.js';\n\ndeclare module 'fastify' {\n interface FastifyRequest {\n tpagaLog: WideEventBuilder;\n }\n}\n\nconst START_KEY = Symbol('tpagaStart');\n\nexport const withTpagaFastifyLogger = (logger: Logger) =>\n fp(async (fastify: FastifyInstance): Promise<void> => {\n fastify.addHook('onRequest', (request, _reply, done) => {\n const correlationId = resolveCorrelationId(request.headers);\n request.tpagaLog = logger.startOperation(request.url, {\n correlationId,\n method: request.method,\n });\n (request as unknown as Record<symbol, number>)[START_KEY] = Date.now();\n done();\n });\n\n fastify.addHook('onResponse', (request, reply, done) => {\n const start = (request as unknown as Record<symbol, number>)[START_KEY];\n request.tpagaLog.emit('info', 'request completed', {\n outcome: reply.statusCode < 400 ? 'success' : 'error',\n durationMs: Date.now() - start,\n statusCode: reply.statusCode,\n });\n done();\n });\n\n fastify.addHook('onError', (request, _reply, error, done) => {\n request.tpagaLog.with({ error: serializeError(error) });\n done();\n });\n });\n\n@Injectable()\nexport class TpagaLoggerInterceptor implements NestInterceptor {\n intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {\n return next.handle().pipe(\n catchError((error: unknown) => {\n const request = context.switchToHttp().getRequest<FastifyRequest>();\n request.tpagaLog?.with({ error: serializeError(error) });\n return throwError(() => error);\n }),\n );\n }\n}\n\nexport const TPAGA_LOGGER = 'TPAGA_LOGGER' as const;\n\nexport class TpagaLoggerModule {\n static forRoot(config: LoggerConfig) {\n const logger = createLogger(config);\n return {\n module: TpagaLoggerModule,\n global: true,\n providers: [{ provide: TPAGA_LOGGER, useValue: logger }],\n exports: [TPAGA_LOGGER],\n };\n }\n}\n","import pino from 'pino';\nimport type { NextFunction, Request, Response } from 'express';\nimport type { Logger, LoggerConfig, SerializedError, TerminalFields, WideEventBuilder } from './types.js';\nimport { resolveCorrelationId } from './utils.js';\n\ndeclare global {\n namespace Express {\n interface Request {\n log: WideEventBuilder;\n }\n interface Response {\n log: WideEventBuilder;\n }\n }\n}\n\nexport const serializeError = (err: unknown): SerializedError => {\n if (!(err instanceof Error)) return { type: 'UnknownError', message: String(err) };\n const errorDetails = (err as { issues?: unknown }).issues;\n return {\n type: err.name,\n message: errorDetails != null ? 'Validation failed' : err.message,\n code: (err as { code?: string }).code,\n errorDetails,\n };\n};\n\nconst createWideEventBuilder = (\n base: Record<string, unknown>,\n log: pino.Logger,\n): WideEventBuilder => {\n const ctx = { ...base };\n return {\n with: (fields) => Object.assign(ctx, fields),\n emit: (level, message, terminal: TerminalFields) => log[level]({ ...ctx, ...terminal }, message),\n };\n};\n\nexport const createLogger = (config: LoggerConfig): Logger => {\n const pretty = process.env.LOG_PRETTY === 'true';\n const log = pino({\n level: process.env.LOG_LEVEL ?? 'info',\n base: { service: config.service, environment: config.environment },\n formatters: {\n level: (label) => ({ level: label }),\n },\n timestamp: pino.stdTimeFunctions.isoTime,\n redact: {\n paths: (config.redactKeys as string[]) ?? [],\n censor: '[REDACTED]',\n },\n ...(pretty ? { transport: { target: 'pino-pretty', options: { colorize: true } } } : {}),\n });\n\n return {\n startOperation: (functionName, base = {}) =>\n createWideEventBuilder({ function: functionName, ...base }, log),\n };\n};\n\ntype EventWithHeaders = { headers?: Record<string, string | undefined> };\ntype HandlerWithBuilder<TEvent> = (event: TEvent, builder: WideEventBuilder) => Promise<unknown>;\n\nconst EXPRESS_LOG_KEY = 'tpagaLog';\n\nexport const withTpagaExpressLogger = (logger: Logger) =>\n (req: Request, res: Response, next: NextFunction): void => {\n const start = Date.now();\n const correlationId = resolveCorrelationId(req.headers as Record<string, string | undefined>);\n const log = logger.startOperation(req.path, { correlationId, method: req.method });\n\n req.log = log;\n res.log = log;\n res.locals[EXPRESS_LOG_KEY] = log;\n\n res.on('finish', () => {\n log.emit('info', 'request completed', {\n outcome: res.statusCode < 400 ? 'success' : 'error',\n durationMs: Date.now() - start,\n statusCode: res.statusCode,\n });\n });\n\n next();\n };\n\nexport const tpagaExpressErrorLogger = (err: unknown, req: Request, _res: Response, next: NextFunction): void => {\n req.log.with({ error: serializeError(err) });\n next(err);\n};\n\nexport const getLog = (res: Response): WideEventBuilder =>\n res.locals[EXPRESS_LOG_KEY] as WideEventBuilder;\n\nexport const withLogger = <TEvent extends EventWithHeaders>(\n logger: Logger,\n operationName: string,\n handler: HandlerWithBuilder<TEvent>,\n) =>\n async (event: TEvent, ..._rest: unknown[]): Promise<unknown> => {\n const start = Date.now();\n const correlationId = resolveCorrelationId(event.headers);\n const builder = logger.startOperation(operationName, { correlationId });\n\n try {\n const result = await handler(event, builder);\n const statusCode =\n result != null &&\n typeof result === 'object' &&\n 'statusCode' in result &&\n typeof (result as { statusCode: unknown }).statusCode === 'number'\n ? (result as { statusCode: number }).statusCode\n : undefined;\n builder.emit('info', `${operationName} completed`, {\n outcome: 'success',\n durationMs: Date.now() - start,\n ...(statusCode !== undefined ? { statusCode } : {}),\n });\n return result;\n } catch (err) {\n builder.emit('error', `${operationName} failed`, {\n outcome: 'error',\n durationMs: Date.now() - start,\n error: serializeError(err),\n });\n throw err;\n }\n };\n","import { randomUUID } from 'crypto';\n\nexport const resolveCorrelationId = (\n headers: Record<string, string | string[] | undefined> = {},\n): string => {\n const h = Object.fromEntries(\n Object.entries(headers).map(([k, v]) => [k.toLowerCase(), Array.isArray(v) ? v[0] : v]),\n );\n return h['x-correlation-id'] ?? h['x-request-id'] ?? randomUUID();\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAAuC;AAEvC,4BAAe;AAGf,oBAA2B;;;ACL3B,kBAAiB;;;ACAjB,oBAA2B;AAEpB,IAAM,uBAAuB,CAClC,UAAyD,CAAC,MAC/C;AACX,QAAM,IAAI,OAAO;AAAA,IACf,OAAO,QAAQ,OAAO,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC;AAAA,EACxF;AACA,SAAO,EAAE,kBAAkB,KAAK,EAAE,cAAc,SAAK,0BAAW;AAClE;;;ADOO,IAAM,iBAAiB,CAAC,QAAkC;AAC/D,MAAI,EAAE,eAAe,OAAQ,QAAO,EAAE,MAAM,gBAAgB,SAAS,OAAO,GAAG,EAAE;AACjF,QAAM,eAAgB,IAA6B;AACnD,SAAO;AAAA,IACL,MAAM,IAAI;AAAA,IACV,SAAS,gBAAgB,OAAO,sBAAsB,IAAI;AAAA,IAC1D,MAAO,IAA0B;AAAA,IACjC;AAAA,EACF;AACF;AAEA,IAAM,yBAAyB,CAC7B,MACA,QACqB;AACrB,QAAM,MAAM,EAAE,GAAG,KAAK;AACtB,SAAO;AAAA,IACL,MAAM,CAAC,WAAW,OAAO,OAAO,KAAK,MAAM;AAAA,IAC3C,MAAM,CAAC,OAAO,SAAS,aAA6B,IAAI,KAAK,EAAE,EAAE,GAAG,KAAK,GAAG,SAAS,GAAG,OAAO;AAAA,EACjG;AACF;AAEO,IAAM,eAAe,CAAC,WAAiC;AAC5D,QAAM,SAAS,QAAQ,IAAI,eAAe;AAC1C,QAAM,UAAM,YAAAA,SAAK;AAAA,IACf,OAAO,QAAQ,IAAI,aAAa;AAAA,IAChC,MAAM,EAAE,SAAS,OAAO,SAAS,aAAa,OAAO,YAAY;AAAA,IACjE,YAAY;AAAA,MACV,OAAO,CAAC,WAAW,EAAE,OAAO,MAAM;AAAA,IACpC;AAAA,IACA,WAAW,YAAAA,QAAK,iBAAiB;AAAA,IACjC,QAAQ;AAAA,MACN,OAAQ,OAAO,cAA2B,CAAC;AAAA,MAC3C,QAAQ;AAAA,IACV;AAAA,IACA,GAAI,SAAS,EAAE,WAAW,EAAE,QAAQ,eAAe,SAAS,EAAE,UAAU,KAAK,EAAE,EAAE,IAAI,CAAC;AAAA,EACxF,CAAC;AAED,SAAO;AAAA,IACL,gBAAgB,CAAC,cAAc,OAAO,CAAC,MACrC,uBAAuB,EAAE,UAAU,cAAc,GAAG,KAAK,GAAG,GAAG;AAAA,EACnE;AACF;;;AD1CA,IAAM,YAAY,uBAAO,YAAY;AAE9B,IAAM,yBAAyB,CAAC,eACrC,sBAAAC,SAAG,OAAO,YAA4C;AACpD,UAAQ,QAAQ,aAAa,CAAC,SAAS,QAAQ,SAAS;AACtD,UAAM,gBAAgB,qBAAqB,QAAQ,OAAO;AAC1D,YAAQ,WAAW,OAAO,eAAe,QAAQ,KAAK;AAAA,MACpD;AAAA,MACA,QAAQ,QAAQ;AAAA,IAClB,CAAC;AACD,IAAC,QAA8C,SAAS,IAAI,KAAK,IAAI;AACrE,SAAK;AAAA,EACP,CAAC;AAED,UAAQ,QAAQ,cAAc,CAAC,SAAS,OAAO,SAAS;AACtD,UAAM,QAAS,QAA8C,SAAS;AACtE,YAAQ,SAAS,KAAK,QAAQ,qBAAqB;AAAA,MACjD,SAAS,MAAM,aAAa,MAAM,YAAY;AAAA,MAC9C,YAAY,KAAK,IAAI,IAAI;AAAA,MACzB,YAAY,MAAM;AAAA,IACpB,CAAC;AACD,SAAK;AAAA,EACP,CAAC;AAED,UAAQ,QAAQ,WAAW,CAAC,SAAS,QAAQ,OAAO,SAAS;AAC3D,YAAQ,SAAS,KAAK,EAAE,OAAO,eAAe,KAAK,EAAE,CAAC;AACtD,SAAK;AAAA,EACP,CAAC;AACH,CAAC;AA5CH;AA8CA,0CAAC,0BAAW;AACL,IAAM,yBAAN,MAAwD;AAAA,EAC7D,UAAU,SAA2B,MAAwC;AAC3E,WAAO,KAAK,OAAO,EAAE;AAAA,UACnB,wBAAW,CAAC,UAAmB;AAC7B,cAAM,UAAU,QAAQ,aAAa,EAAE,WAA2B;AAClE,gBAAQ,UAAU,KAAK,EAAE,OAAO,eAAe,KAAK,EAAE,CAAC;AACvD,mBAAO,wBAAW,MAAM,KAAK;AAAA,MAC/B,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAVO;AAAM,yBAAN,sDADP,oCACa;AAAN,4BAAM;AAYN,IAAM,eAAe;AAErB,IAAM,oBAAN,MAAM,mBAAkB;AAAA,EAC7B,OAAO,QAAQ,QAAsB;AACnC,UAAM,SAAS,aAAa,MAAM;AAClC,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,WAAW,CAAC,EAAE,SAAS,cAAc,UAAU,OAAO,CAAC;AAAA,MACvD,SAAS,CAAC,YAAY;AAAA,IACxB;AAAA,EACF;AACF;","names":["pino","fp"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { FastifyInstance } from 'fastify';
|
|
3
|
+
import { NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
|
|
4
|
+
import { W as WideEventBuilder, c as LoggerConfig, b as Logger } from './types-BtTFSWeZ.cjs';
|
|
5
|
+
|
|
6
|
+
declare module 'fastify' {
|
|
7
|
+
interface FastifyRequest {
|
|
8
|
+
tpagaLog: WideEventBuilder;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
declare const withTpagaFastifyLogger: (logger: Logger) => (fastify: FastifyInstance) => Promise<void>;
|
|
12
|
+
declare class TpagaLoggerInterceptor implements NestInterceptor {
|
|
13
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown>;
|
|
14
|
+
}
|
|
15
|
+
declare const TPAGA_LOGGER: "TPAGA_LOGGER";
|
|
16
|
+
declare class TpagaLoggerModule {
|
|
17
|
+
static forRoot(config: LoggerConfig): {
|
|
18
|
+
module: typeof TpagaLoggerModule;
|
|
19
|
+
global: boolean;
|
|
20
|
+
providers: {
|
|
21
|
+
provide: "TPAGA_LOGGER";
|
|
22
|
+
useValue: Logger;
|
|
23
|
+
}[];
|
|
24
|
+
exports: "TPAGA_LOGGER"[];
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { TPAGA_LOGGER, TpagaLoggerInterceptor, TpagaLoggerModule, withTpagaFastifyLogger };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { FastifyInstance } from 'fastify';
|
|
3
|
+
import { NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
|
|
4
|
+
import { W as WideEventBuilder, c as LoggerConfig, b as Logger } from './types-BtTFSWeZ.js';
|
|
5
|
+
|
|
6
|
+
declare module 'fastify' {
|
|
7
|
+
interface FastifyRequest {
|
|
8
|
+
tpagaLog: WideEventBuilder;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
declare const withTpagaFastifyLogger: (logger: Logger) => (fastify: FastifyInstance) => Promise<void>;
|
|
12
|
+
declare class TpagaLoggerInterceptor implements NestInterceptor {
|
|
13
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown>;
|
|
14
|
+
}
|
|
15
|
+
declare const TPAGA_LOGGER: "TPAGA_LOGGER";
|
|
16
|
+
declare class TpagaLoggerModule {
|
|
17
|
+
static forRoot(config: LoggerConfig): {
|
|
18
|
+
module: typeof TpagaLoggerModule;
|
|
19
|
+
global: boolean;
|
|
20
|
+
providers: {
|
|
21
|
+
provide: "TPAGA_LOGGER";
|
|
22
|
+
useValue: Logger;
|
|
23
|
+
}[];
|
|
24
|
+
exports: "TPAGA_LOGGER"[];
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { TPAGA_LOGGER, TpagaLoggerInterceptor, TpagaLoggerModule, withTpagaFastifyLogger };
|
package/dist/fastify.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
|
|
5
|
+
var __typeError = (msg) => {
|
|
6
|
+
throw TypeError(msg);
|
|
7
|
+
};
|
|
8
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
9
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
10
|
+
var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
|
|
11
|
+
var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
|
12
|
+
var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
|
|
13
|
+
var __decoratorContext = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) });
|
|
14
|
+
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
|
|
15
|
+
var __runInitializers = (array, flags, self, value) => {
|
|
16
|
+
for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
|
|
17
|
+
return value;
|
|
18
|
+
};
|
|
19
|
+
var __decorateElement = (array, flags, name, decorators, target, extra) => {
|
|
20
|
+
var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
|
|
21
|
+
var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
|
|
22
|
+
var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
|
|
23
|
+
var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : { get [name]() {
|
|
24
|
+
return __privateGet(this, extra);
|
|
25
|
+
}, set [name](x) {
|
|
26
|
+
return __privateSet(this, extra, x);
|
|
27
|
+
} }, name));
|
|
28
|
+
k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
|
|
29
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
30
|
+
ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
|
|
31
|
+
if (k) {
|
|
32
|
+
ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x };
|
|
33
|
+
if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
|
|
34
|
+
if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
|
|
35
|
+
}
|
|
36
|
+
it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1;
|
|
37
|
+
if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
|
|
38
|
+
else if (typeof it !== "object" || it === null) __typeError("Object expected");
|
|
39
|
+
else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
|
|
40
|
+
}
|
|
41
|
+
return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
42
|
+
};
|
|
43
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
44
|
+
var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
|
|
45
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
46
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
47
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
48
|
+
|
|
49
|
+
// src/fastify.ts
|
|
50
|
+
import { catchError, throwError } from "rxjs";
|
|
51
|
+
import fp from "fastify-plugin";
|
|
52
|
+
import { Injectable } from "@nestjs/common";
|
|
53
|
+
|
|
54
|
+
// src/logger.ts
|
|
55
|
+
import pino from "pino";
|
|
56
|
+
|
|
57
|
+
// src/utils.ts
|
|
58
|
+
import { randomUUID } from "crypto";
|
|
59
|
+
var resolveCorrelationId = (headers = {}) => {
|
|
60
|
+
const h = Object.fromEntries(
|
|
61
|
+
Object.entries(headers).map(([k, v]) => [k.toLowerCase(), Array.isArray(v) ? v[0] : v])
|
|
62
|
+
);
|
|
63
|
+
return h["x-correlation-id"] ?? h["x-request-id"] ?? randomUUID();
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// src/logger.ts
|
|
67
|
+
var serializeError = (err) => {
|
|
68
|
+
if (!(err instanceof Error)) return { type: "UnknownError", message: String(err) };
|
|
69
|
+
const errorDetails = err.issues;
|
|
70
|
+
return {
|
|
71
|
+
type: err.name,
|
|
72
|
+
message: errorDetails != null ? "Validation failed" : err.message,
|
|
73
|
+
code: err.code,
|
|
74
|
+
errorDetails
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
var createWideEventBuilder = (base, log) => {
|
|
78
|
+
const ctx = { ...base };
|
|
79
|
+
return {
|
|
80
|
+
with: (fields) => Object.assign(ctx, fields),
|
|
81
|
+
emit: (level, message, terminal) => log[level]({ ...ctx, ...terminal }, message)
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
var createLogger = (config) => {
|
|
85
|
+
const pretty = process.env.LOG_PRETTY === "true";
|
|
86
|
+
const log = pino({
|
|
87
|
+
level: process.env.LOG_LEVEL ?? "info",
|
|
88
|
+
base: { service: config.service, environment: config.environment },
|
|
89
|
+
formatters: {
|
|
90
|
+
level: (label) => ({ level: label })
|
|
91
|
+
},
|
|
92
|
+
timestamp: pino.stdTimeFunctions.isoTime,
|
|
93
|
+
redact: {
|
|
94
|
+
paths: config.redactKeys ?? [],
|
|
95
|
+
censor: "[REDACTED]"
|
|
96
|
+
},
|
|
97
|
+
...pretty ? { transport: { target: "pino-pretty", options: { colorize: true } } } : {}
|
|
98
|
+
});
|
|
99
|
+
return {
|
|
100
|
+
startOperation: (functionName, base = {}) => createWideEventBuilder({ function: functionName, ...base }, log)
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// src/fastify.ts
|
|
105
|
+
var START_KEY = /* @__PURE__ */ Symbol("tpagaStart");
|
|
106
|
+
var withTpagaFastifyLogger = (logger) => fp(async (fastify) => {
|
|
107
|
+
fastify.addHook("onRequest", (request, _reply, done) => {
|
|
108
|
+
const correlationId = resolveCorrelationId(request.headers);
|
|
109
|
+
request.tpagaLog = logger.startOperation(request.url, {
|
|
110
|
+
correlationId,
|
|
111
|
+
method: request.method
|
|
112
|
+
});
|
|
113
|
+
request[START_KEY] = Date.now();
|
|
114
|
+
done();
|
|
115
|
+
});
|
|
116
|
+
fastify.addHook("onResponse", (request, reply, done) => {
|
|
117
|
+
const start = request[START_KEY];
|
|
118
|
+
request.tpagaLog.emit("info", "request completed", {
|
|
119
|
+
outcome: reply.statusCode < 400 ? "success" : "error",
|
|
120
|
+
durationMs: Date.now() - start,
|
|
121
|
+
statusCode: reply.statusCode
|
|
122
|
+
});
|
|
123
|
+
done();
|
|
124
|
+
});
|
|
125
|
+
fastify.addHook("onError", (request, _reply, error, done) => {
|
|
126
|
+
request.tpagaLog.with({ error: serializeError(error) });
|
|
127
|
+
done();
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
var _TpagaLoggerInterceptor_decorators, _init;
|
|
131
|
+
_TpagaLoggerInterceptor_decorators = [Injectable()];
|
|
132
|
+
var TpagaLoggerInterceptor = class {
|
|
133
|
+
intercept(context, next) {
|
|
134
|
+
return next.handle().pipe(
|
|
135
|
+
catchError((error) => {
|
|
136
|
+
const request = context.switchToHttp().getRequest();
|
|
137
|
+
request.tpagaLog?.with({ error: serializeError(error) });
|
|
138
|
+
return throwError(() => error);
|
|
139
|
+
})
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
_init = __decoratorStart(null);
|
|
144
|
+
TpagaLoggerInterceptor = __decorateElement(_init, 0, "TpagaLoggerInterceptor", _TpagaLoggerInterceptor_decorators, TpagaLoggerInterceptor);
|
|
145
|
+
__runInitializers(_init, 1, TpagaLoggerInterceptor);
|
|
146
|
+
var TPAGA_LOGGER = "TPAGA_LOGGER";
|
|
147
|
+
var TpagaLoggerModule = class _TpagaLoggerModule {
|
|
148
|
+
static forRoot(config) {
|
|
149
|
+
const logger = createLogger(config);
|
|
150
|
+
return {
|
|
151
|
+
module: _TpagaLoggerModule,
|
|
152
|
+
global: true,
|
|
153
|
+
providers: [{ provide: TPAGA_LOGGER, useValue: logger }],
|
|
154
|
+
exports: [TPAGA_LOGGER]
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
export {
|
|
159
|
+
TPAGA_LOGGER,
|
|
160
|
+
TpagaLoggerInterceptor,
|
|
161
|
+
TpagaLoggerModule,
|
|
162
|
+
withTpagaFastifyLogger
|
|
163
|
+
};
|
|
164
|
+
//# sourceMappingURL=fastify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/fastify.ts","../src/logger.ts","../src/utils.ts"],"sourcesContent":["import { catchError, throwError } from 'rxjs';\nimport type { Observable } from 'rxjs';\nimport fp from 'fastify-plugin';\nimport type { FastifyInstance, FastifyRequest } from 'fastify';\nimport type { CallHandler, ExecutionContext, NestInterceptor } from '@nestjs/common';\nimport { Injectable } from '@nestjs/common';\nimport { createLogger, serializeError } from './logger.js';\nimport { resolveCorrelationId } from './utils.js';\nimport type { Logger, LoggerConfig, WideEventBuilder } from './types.js';\n\ndeclare module 'fastify' {\n interface FastifyRequest {\n tpagaLog: WideEventBuilder;\n }\n}\n\nconst START_KEY = Symbol('tpagaStart');\n\nexport const withTpagaFastifyLogger = (logger: Logger) =>\n fp(async (fastify: FastifyInstance): Promise<void> => {\n fastify.addHook('onRequest', (request, _reply, done) => {\n const correlationId = resolveCorrelationId(request.headers);\n request.tpagaLog = logger.startOperation(request.url, {\n correlationId,\n method: request.method,\n });\n (request as unknown as Record<symbol, number>)[START_KEY] = Date.now();\n done();\n });\n\n fastify.addHook('onResponse', (request, reply, done) => {\n const start = (request as unknown as Record<symbol, number>)[START_KEY];\n request.tpagaLog.emit('info', 'request completed', {\n outcome: reply.statusCode < 400 ? 'success' : 'error',\n durationMs: Date.now() - start,\n statusCode: reply.statusCode,\n });\n done();\n });\n\n fastify.addHook('onError', (request, _reply, error, done) => {\n request.tpagaLog.with({ error: serializeError(error) });\n done();\n });\n });\n\n@Injectable()\nexport class TpagaLoggerInterceptor implements NestInterceptor {\n intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {\n return next.handle().pipe(\n catchError((error: unknown) => {\n const request = context.switchToHttp().getRequest<FastifyRequest>();\n request.tpagaLog?.with({ error: serializeError(error) });\n return throwError(() => error);\n }),\n );\n }\n}\n\nexport const TPAGA_LOGGER = 'TPAGA_LOGGER' as const;\n\nexport class TpagaLoggerModule {\n static forRoot(config: LoggerConfig) {\n const logger = createLogger(config);\n return {\n module: TpagaLoggerModule,\n global: true,\n providers: [{ provide: TPAGA_LOGGER, useValue: logger }],\n exports: [TPAGA_LOGGER],\n };\n }\n}\n","import pino from 'pino';\nimport type { NextFunction, Request, Response } from 'express';\nimport type { Logger, LoggerConfig, SerializedError, TerminalFields, WideEventBuilder } from './types.js';\nimport { resolveCorrelationId } from './utils.js';\n\ndeclare global {\n namespace Express {\n interface Request {\n log: WideEventBuilder;\n }\n interface Response {\n log: WideEventBuilder;\n }\n }\n}\n\nexport const serializeError = (err: unknown): SerializedError => {\n if (!(err instanceof Error)) return { type: 'UnknownError', message: String(err) };\n const errorDetails = (err as { issues?: unknown }).issues;\n return {\n type: err.name,\n message: errorDetails != null ? 'Validation failed' : err.message,\n code: (err as { code?: string }).code,\n errorDetails,\n };\n};\n\nconst createWideEventBuilder = (\n base: Record<string, unknown>,\n log: pino.Logger,\n): WideEventBuilder => {\n const ctx = { ...base };\n return {\n with: (fields) => Object.assign(ctx, fields),\n emit: (level, message, terminal: TerminalFields) => log[level]({ ...ctx, ...terminal }, message),\n };\n};\n\nexport const createLogger = (config: LoggerConfig): Logger => {\n const pretty = process.env.LOG_PRETTY === 'true';\n const log = pino({\n level: process.env.LOG_LEVEL ?? 'info',\n base: { service: config.service, environment: config.environment },\n formatters: {\n level: (label) => ({ level: label }),\n },\n timestamp: pino.stdTimeFunctions.isoTime,\n redact: {\n paths: (config.redactKeys as string[]) ?? [],\n censor: '[REDACTED]',\n },\n ...(pretty ? { transport: { target: 'pino-pretty', options: { colorize: true } } } : {}),\n });\n\n return {\n startOperation: (functionName, base = {}) =>\n createWideEventBuilder({ function: functionName, ...base }, log),\n };\n};\n\ntype EventWithHeaders = { headers?: Record<string, string | undefined> };\ntype HandlerWithBuilder<TEvent> = (event: TEvent, builder: WideEventBuilder) => Promise<unknown>;\n\nconst EXPRESS_LOG_KEY = 'tpagaLog';\n\nexport const withTpagaExpressLogger = (logger: Logger) =>\n (req: Request, res: Response, next: NextFunction): void => {\n const start = Date.now();\n const correlationId = resolveCorrelationId(req.headers as Record<string, string | undefined>);\n const log = logger.startOperation(req.path, { correlationId, method: req.method });\n\n req.log = log;\n res.log = log;\n res.locals[EXPRESS_LOG_KEY] = log;\n\n res.on('finish', () => {\n log.emit('info', 'request completed', {\n outcome: res.statusCode < 400 ? 'success' : 'error',\n durationMs: Date.now() - start,\n statusCode: res.statusCode,\n });\n });\n\n next();\n };\n\nexport const tpagaExpressErrorLogger = (err: unknown, req: Request, _res: Response, next: NextFunction): void => {\n req.log.with({ error: serializeError(err) });\n next(err);\n};\n\nexport const getLog = (res: Response): WideEventBuilder =>\n res.locals[EXPRESS_LOG_KEY] as WideEventBuilder;\n\nexport const withLogger = <TEvent extends EventWithHeaders>(\n logger: Logger,\n operationName: string,\n handler: HandlerWithBuilder<TEvent>,\n) =>\n async (event: TEvent, ..._rest: unknown[]): Promise<unknown> => {\n const start = Date.now();\n const correlationId = resolveCorrelationId(event.headers);\n const builder = logger.startOperation(operationName, { correlationId });\n\n try {\n const result = await handler(event, builder);\n const statusCode =\n result != null &&\n typeof result === 'object' &&\n 'statusCode' in result &&\n typeof (result as { statusCode: unknown }).statusCode === 'number'\n ? (result as { statusCode: number }).statusCode\n : undefined;\n builder.emit('info', `${operationName} completed`, {\n outcome: 'success',\n durationMs: Date.now() - start,\n ...(statusCode !== undefined ? { statusCode } : {}),\n });\n return result;\n } catch (err) {\n builder.emit('error', `${operationName} failed`, {\n outcome: 'error',\n durationMs: Date.now() - start,\n error: serializeError(err),\n });\n throw err;\n }\n };\n","import { randomUUID } from 'crypto';\n\nexport const resolveCorrelationId = (\n headers: Record<string, string | string[] | undefined> = {},\n): string => {\n const h = Object.fromEntries(\n Object.entries(headers).map(([k, v]) => [k.toLowerCase(), Array.isArray(v) ? v[0] : v]),\n );\n return h['x-correlation-id'] ?? h['x-request-id'] ?? randomUUID();\n};\n"],"mappings":"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","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -42,8 +42,18 @@ __export(index_exports, {
|
|
|
42
42
|
module.exports = __toCommonJS(index_exports);
|
|
43
43
|
|
|
44
44
|
// src/logger.ts
|
|
45
|
-
var import_crypto = require("crypto");
|
|
46
45
|
var import_pino = __toESM(require("pino"), 1);
|
|
46
|
+
|
|
47
|
+
// src/utils.ts
|
|
48
|
+
var import_crypto = require("crypto");
|
|
49
|
+
var resolveCorrelationId = (headers = {}) => {
|
|
50
|
+
const h = Object.fromEntries(
|
|
51
|
+
Object.entries(headers).map(([k, v]) => [k.toLowerCase(), Array.isArray(v) ? v[0] : v])
|
|
52
|
+
);
|
|
53
|
+
return h["x-correlation-id"] ?? h["x-request-id"] ?? (0, import_crypto.randomUUID)();
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// src/logger.ts
|
|
47
57
|
var serializeError = (err) => {
|
|
48
58
|
if (!(err instanceof Error)) return { type: "UnknownError", message: String(err) };
|
|
49
59
|
const errorDetails = err.issues;
|
|
@@ -80,10 +90,6 @@ var createLogger = (config) => {
|
|
|
80
90
|
startOperation: (functionName, base = {}) => createWideEventBuilder({ function: functionName, ...base }, log)
|
|
81
91
|
};
|
|
82
92
|
};
|
|
83
|
-
var resolveCorrelationId = (headers = {}) => {
|
|
84
|
-
const h = Object.fromEntries(Object.entries(headers).map(([k, v]) => [k.toLowerCase(), v]));
|
|
85
|
-
return h["x-correlation-id"] ?? h["x-request-id"] ?? (0, import_crypto.randomUUID)();
|
|
86
|
-
};
|
|
87
93
|
var EXPRESS_LOG_KEY = "tpagaLog";
|
|
88
94
|
var withTpagaExpressLogger = (logger) => (req, res, next) => {
|
|
89
95
|
const start = Date.now();
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/logger.ts","../src/constants.ts"],"sourcesContent":["export { createLogger, serializeError, withLogger, withTpagaExpressLogger, tpagaExpressErrorLogger, getLog } from './logger.js';\nexport { LOG_LEVELS, OUTCOMES } from './constants.js';\nexport type {\n Logger,\n LoggerConfig,\n LogLevel,\n Outcome,\n SerializedError,\n TerminalFields,\n WideEventBuilder,\n} from './types.js';\n","import
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/logger.ts","../src/utils.ts","../src/constants.ts"],"sourcesContent":["export { createLogger, serializeError, withLogger, withTpagaExpressLogger, tpagaExpressErrorLogger, getLog } from './logger.js';\nexport { LOG_LEVELS, OUTCOMES } from './constants.js';\nexport type {\n Logger,\n LoggerConfig,\n LogLevel,\n Outcome,\n SerializedError,\n TerminalFields,\n WideEventBuilder,\n} from './types.js';\n","import pino from 'pino';\nimport type { NextFunction, Request, Response } from 'express';\nimport type { Logger, LoggerConfig, SerializedError, TerminalFields, WideEventBuilder } from './types.js';\nimport { resolveCorrelationId } from './utils.js';\n\ndeclare global {\n namespace Express {\n interface Request {\n log: WideEventBuilder;\n }\n interface Response {\n log: WideEventBuilder;\n }\n }\n}\n\nexport const serializeError = (err: unknown): SerializedError => {\n if (!(err instanceof Error)) return { type: 'UnknownError', message: String(err) };\n const errorDetails = (err as { issues?: unknown }).issues;\n return {\n type: err.name,\n message: errorDetails != null ? 'Validation failed' : err.message,\n code: (err as { code?: string }).code,\n errorDetails,\n };\n};\n\nconst createWideEventBuilder = (\n base: Record<string, unknown>,\n log: pino.Logger,\n): WideEventBuilder => {\n const ctx = { ...base };\n return {\n with: (fields) => Object.assign(ctx, fields),\n emit: (level, message, terminal: TerminalFields) => log[level]({ ...ctx, ...terminal }, message),\n };\n};\n\nexport const createLogger = (config: LoggerConfig): Logger => {\n const pretty = process.env.LOG_PRETTY === 'true';\n const log = pino({\n level: process.env.LOG_LEVEL ?? 'info',\n base: { service: config.service, environment: config.environment },\n formatters: {\n level: (label) => ({ level: label }),\n },\n timestamp: pino.stdTimeFunctions.isoTime,\n redact: {\n paths: (config.redactKeys as string[]) ?? [],\n censor: '[REDACTED]',\n },\n ...(pretty ? { transport: { target: 'pino-pretty', options: { colorize: true } } } : {}),\n });\n\n return {\n startOperation: (functionName, base = {}) =>\n createWideEventBuilder({ function: functionName, ...base }, log),\n };\n};\n\ntype EventWithHeaders = { headers?: Record<string, string | undefined> };\ntype HandlerWithBuilder<TEvent> = (event: TEvent, builder: WideEventBuilder) => Promise<unknown>;\n\nconst EXPRESS_LOG_KEY = 'tpagaLog';\n\nexport const withTpagaExpressLogger = (logger: Logger) =>\n (req: Request, res: Response, next: NextFunction): void => {\n const start = Date.now();\n const correlationId = resolveCorrelationId(req.headers as Record<string, string | undefined>);\n const log = logger.startOperation(req.path, { correlationId, method: req.method });\n\n req.log = log;\n res.log = log;\n res.locals[EXPRESS_LOG_KEY] = log;\n\n res.on('finish', () => {\n log.emit('info', 'request completed', {\n outcome: res.statusCode < 400 ? 'success' : 'error',\n durationMs: Date.now() - start,\n statusCode: res.statusCode,\n });\n });\n\n next();\n };\n\nexport const tpagaExpressErrorLogger = (err: unknown, req: Request, _res: Response, next: NextFunction): void => {\n req.log.with({ error: serializeError(err) });\n next(err);\n};\n\nexport const getLog = (res: Response): WideEventBuilder =>\n res.locals[EXPRESS_LOG_KEY] as WideEventBuilder;\n\nexport const withLogger = <TEvent extends EventWithHeaders>(\n logger: Logger,\n operationName: string,\n handler: HandlerWithBuilder<TEvent>,\n) =>\n async (event: TEvent, ..._rest: unknown[]): Promise<unknown> => {\n const start = Date.now();\n const correlationId = resolveCorrelationId(event.headers);\n const builder = logger.startOperation(operationName, { correlationId });\n\n try {\n const result = await handler(event, builder);\n const statusCode =\n result != null &&\n typeof result === 'object' &&\n 'statusCode' in result &&\n typeof (result as { statusCode: unknown }).statusCode === 'number'\n ? (result as { statusCode: number }).statusCode\n : undefined;\n builder.emit('info', `${operationName} completed`, {\n outcome: 'success',\n durationMs: Date.now() - start,\n ...(statusCode !== undefined ? { statusCode } : {}),\n });\n return result;\n } catch (err) {\n builder.emit('error', `${operationName} failed`, {\n outcome: 'error',\n durationMs: Date.now() - start,\n error: serializeError(err),\n });\n throw err;\n }\n };\n","import { randomUUID } from 'crypto';\n\nexport const resolveCorrelationId = (\n headers: Record<string, string | string[] | undefined> = {},\n): string => {\n const h = Object.fromEntries(\n Object.entries(headers).map(([k, v]) => [k.toLowerCase(), Array.isArray(v) ? v[0] : v]),\n );\n return h['x-correlation-id'] ?? h['x-request-id'] ?? randomUUID();\n};\n","export const LOG_LEVELS = ['info', 'warn', 'error', 'debug'] as const;\n\nexport const OUTCOMES = [\n 'success',\n 'error',\n 'validation_failed',\n 'not_found',\n 'conflict',\n 'timeout',\n 'skipped',\n] as const;\n"],"mappings":"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","names":["pino"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,34 +1,6 @@
|
|
|
1
1
|
import { Response, Request, NextFunction } from 'express';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
declare const OUTCOMES: readonly ["success", "error", "validation_failed", "not_found", "conflict", "timeout", "skipped"];
|
|
5
|
-
|
|
6
|
-
type LogLevel = (typeof LOG_LEVELS)[number];
|
|
7
|
-
type Outcome = (typeof OUTCOMES)[number];
|
|
8
|
-
type LoggerConfig = {
|
|
9
|
-
service: string;
|
|
10
|
-
environment?: string;
|
|
11
|
-
redactKeys?: readonly string[];
|
|
12
|
-
};
|
|
13
|
-
type SerializedError = {
|
|
14
|
-
type: string;
|
|
15
|
-
message: string;
|
|
16
|
-
code?: string;
|
|
17
|
-
errorDetails?: unknown;
|
|
18
|
-
};
|
|
19
|
-
type TerminalFields = {
|
|
20
|
-
outcome: Outcome | string;
|
|
21
|
-
durationMs: number;
|
|
22
|
-
statusCode?: number;
|
|
23
|
-
error?: SerializedError;
|
|
24
|
-
};
|
|
25
|
-
type WideEventBuilder = {
|
|
26
|
-
with: (fields: Record<string, unknown>) => void;
|
|
27
|
-
emit: (level: LogLevel, message: string, terminal: TerminalFields) => void;
|
|
28
|
-
};
|
|
29
|
-
type Logger = {
|
|
30
|
-
startOperation: (functionName: string, base?: Record<string, unknown>) => WideEventBuilder;
|
|
31
|
-
};
|
|
2
|
+
import { W as WideEventBuilder, c as LoggerConfig, b as Logger, S as SerializedError } from './types-BtTFSWeZ.cjs';
|
|
3
|
+
export { L as LOG_LEVELS, a as LogLevel, O as OUTCOMES, d as Outcome, T as TerminalFields } from './types-BtTFSWeZ.cjs';
|
|
32
4
|
|
|
33
5
|
declare global {
|
|
34
6
|
namespace Express {
|
|
@@ -51,4 +23,4 @@ declare const tpagaExpressErrorLogger: (err: unknown, req: Request, _res: Respon
|
|
|
51
23
|
declare const getLog: (res: Response) => WideEventBuilder;
|
|
52
24
|
declare const withLogger: <TEvent extends EventWithHeaders>(logger: Logger, operationName: string, handler: HandlerWithBuilder<TEvent>) => (event: TEvent, ..._rest: unknown[]) => Promise<unknown>;
|
|
53
25
|
|
|
54
|
-
export {
|
|
26
|
+
export { Logger, LoggerConfig, SerializedError, WideEventBuilder, createLogger, getLog, serializeError, tpagaExpressErrorLogger, withLogger, withTpagaExpressLogger };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,34 +1,6 @@
|
|
|
1
1
|
import { Response, Request, NextFunction } from 'express';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
declare const OUTCOMES: readonly ["success", "error", "validation_failed", "not_found", "conflict", "timeout", "skipped"];
|
|
5
|
-
|
|
6
|
-
type LogLevel = (typeof LOG_LEVELS)[number];
|
|
7
|
-
type Outcome = (typeof OUTCOMES)[number];
|
|
8
|
-
type LoggerConfig = {
|
|
9
|
-
service: string;
|
|
10
|
-
environment?: string;
|
|
11
|
-
redactKeys?: readonly string[];
|
|
12
|
-
};
|
|
13
|
-
type SerializedError = {
|
|
14
|
-
type: string;
|
|
15
|
-
message: string;
|
|
16
|
-
code?: string;
|
|
17
|
-
errorDetails?: unknown;
|
|
18
|
-
};
|
|
19
|
-
type TerminalFields = {
|
|
20
|
-
outcome: Outcome | string;
|
|
21
|
-
durationMs: number;
|
|
22
|
-
statusCode?: number;
|
|
23
|
-
error?: SerializedError;
|
|
24
|
-
};
|
|
25
|
-
type WideEventBuilder = {
|
|
26
|
-
with: (fields: Record<string, unknown>) => void;
|
|
27
|
-
emit: (level: LogLevel, message: string, terminal: TerminalFields) => void;
|
|
28
|
-
};
|
|
29
|
-
type Logger = {
|
|
30
|
-
startOperation: (functionName: string, base?: Record<string, unknown>) => WideEventBuilder;
|
|
31
|
-
};
|
|
2
|
+
import { W as WideEventBuilder, c as LoggerConfig, b as Logger, S as SerializedError } from './types-BtTFSWeZ.js';
|
|
3
|
+
export { L as LOG_LEVELS, a as LogLevel, O as OUTCOMES, d as Outcome, T as TerminalFields } from './types-BtTFSWeZ.js';
|
|
32
4
|
|
|
33
5
|
declare global {
|
|
34
6
|
namespace Express {
|
|
@@ -51,4 +23,4 @@ declare const tpagaExpressErrorLogger: (err: unknown, req: Request, _res: Respon
|
|
|
51
23
|
declare const getLog: (res: Response) => WideEventBuilder;
|
|
52
24
|
declare const withLogger: <TEvent extends EventWithHeaders>(logger: Logger, operationName: string, handler: HandlerWithBuilder<TEvent>) => (event: TEvent, ..._rest: unknown[]) => Promise<unknown>;
|
|
53
25
|
|
|
54
|
-
export {
|
|
26
|
+
export { Logger, LoggerConfig, SerializedError, WideEventBuilder, createLogger, getLog, serializeError, tpagaExpressErrorLogger, withLogger, withTpagaExpressLogger };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
// src/logger.ts
|
|
2
|
-
import { randomUUID } from "crypto";
|
|
3
2
|
import pino from "pino";
|
|
3
|
+
|
|
4
|
+
// src/utils.ts
|
|
5
|
+
import { randomUUID } from "crypto";
|
|
6
|
+
var resolveCorrelationId = (headers = {}) => {
|
|
7
|
+
const h = Object.fromEntries(
|
|
8
|
+
Object.entries(headers).map(([k, v]) => [k.toLowerCase(), Array.isArray(v) ? v[0] : v])
|
|
9
|
+
);
|
|
10
|
+
return h["x-correlation-id"] ?? h["x-request-id"] ?? randomUUID();
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// src/logger.ts
|
|
4
14
|
var serializeError = (err) => {
|
|
5
15
|
if (!(err instanceof Error)) return { type: "UnknownError", message: String(err) };
|
|
6
16
|
const errorDetails = err.issues;
|
|
@@ -37,10 +47,6 @@ var createLogger = (config) => {
|
|
|
37
47
|
startOperation: (functionName, base = {}) => createWideEventBuilder({ function: functionName, ...base }, log)
|
|
38
48
|
};
|
|
39
49
|
};
|
|
40
|
-
var resolveCorrelationId = (headers = {}) => {
|
|
41
|
-
const h = Object.fromEntries(Object.entries(headers).map(([k, v]) => [k.toLowerCase(), v]));
|
|
42
|
-
return h["x-correlation-id"] ?? h["x-request-id"] ?? randomUUID();
|
|
43
|
-
};
|
|
44
50
|
var EXPRESS_LOG_KEY = "tpagaLog";
|
|
45
51
|
var withTpagaExpressLogger = (logger) => (req, res, next) => {
|
|
46
52
|
const start = Date.now();
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/logger.ts","../src/constants.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"sources":["../src/logger.ts","../src/utils.ts","../src/constants.ts"],"sourcesContent":["import pino from 'pino';\nimport type { NextFunction, Request, Response } from 'express';\nimport type { Logger, LoggerConfig, SerializedError, TerminalFields, WideEventBuilder } from './types.js';\nimport { resolveCorrelationId } from './utils.js';\n\ndeclare global {\n namespace Express {\n interface Request {\n log: WideEventBuilder;\n }\n interface Response {\n log: WideEventBuilder;\n }\n }\n}\n\nexport const serializeError = (err: unknown): SerializedError => {\n if (!(err instanceof Error)) return { type: 'UnknownError', message: String(err) };\n const errorDetails = (err as { issues?: unknown }).issues;\n return {\n type: err.name,\n message: errorDetails != null ? 'Validation failed' : err.message,\n code: (err as { code?: string }).code,\n errorDetails,\n };\n};\n\nconst createWideEventBuilder = (\n base: Record<string, unknown>,\n log: pino.Logger,\n): WideEventBuilder => {\n const ctx = { ...base };\n return {\n with: (fields) => Object.assign(ctx, fields),\n emit: (level, message, terminal: TerminalFields) => log[level]({ ...ctx, ...terminal }, message),\n };\n};\n\nexport const createLogger = (config: LoggerConfig): Logger => {\n const pretty = process.env.LOG_PRETTY === 'true';\n const log = pino({\n level: process.env.LOG_LEVEL ?? 'info',\n base: { service: config.service, environment: config.environment },\n formatters: {\n level: (label) => ({ level: label }),\n },\n timestamp: pino.stdTimeFunctions.isoTime,\n redact: {\n paths: (config.redactKeys as string[]) ?? [],\n censor: '[REDACTED]',\n },\n ...(pretty ? { transport: { target: 'pino-pretty', options: { colorize: true } } } : {}),\n });\n\n return {\n startOperation: (functionName, base = {}) =>\n createWideEventBuilder({ function: functionName, ...base }, log),\n };\n};\n\ntype EventWithHeaders = { headers?: Record<string, string | undefined> };\ntype HandlerWithBuilder<TEvent> = (event: TEvent, builder: WideEventBuilder) => Promise<unknown>;\n\nconst EXPRESS_LOG_KEY = 'tpagaLog';\n\nexport const withTpagaExpressLogger = (logger: Logger) =>\n (req: Request, res: Response, next: NextFunction): void => {\n const start = Date.now();\n const correlationId = resolveCorrelationId(req.headers as Record<string, string | undefined>);\n const log = logger.startOperation(req.path, { correlationId, method: req.method });\n\n req.log = log;\n res.log = log;\n res.locals[EXPRESS_LOG_KEY] = log;\n\n res.on('finish', () => {\n log.emit('info', 'request completed', {\n outcome: res.statusCode < 400 ? 'success' : 'error',\n durationMs: Date.now() - start,\n statusCode: res.statusCode,\n });\n });\n\n next();\n };\n\nexport const tpagaExpressErrorLogger = (err: unknown, req: Request, _res: Response, next: NextFunction): void => {\n req.log.with({ error: serializeError(err) });\n next(err);\n};\n\nexport const getLog = (res: Response): WideEventBuilder =>\n res.locals[EXPRESS_LOG_KEY] as WideEventBuilder;\n\nexport const withLogger = <TEvent extends EventWithHeaders>(\n logger: Logger,\n operationName: string,\n handler: HandlerWithBuilder<TEvent>,\n) =>\n async (event: TEvent, ..._rest: unknown[]): Promise<unknown> => {\n const start = Date.now();\n const correlationId = resolveCorrelationId(event.headers);\n const builder = logger.startOperation(operationName, { correlationId });\n\n try {\n const result = await handler(event, builder);\n const statusCode =\n result != null &&\n typeof result === 'object' &&\n 'statusCode' in result &&\n typeof (result as { statusCode: unknown }).statusCode === 'number'\n ? (result as { statusCode: number }).statusCode\n : undefined;\n builder.emit('info', `${operationName} completed`, {\n outcome: 'success',\n durationMs: Date.now() - start,\n ...(statusCode !== undefined ? { statusCode } : {}),\n });\n return result;\n } catch (err) {\n builder.emit('error', `${operationName} failed`, {\n outcome: 'error',\n durationMs: Date.now() - start,\n error: serializeError(err),\n });\n throw err;\n }\n };\n","import { randomUUID } from 'crypto';\n\nexport const resolveCorrelationId = (\n headers: Record<string, string | string[] | undefined> = {},\n): string => {\n const h = Object.fromEntries(\n Object.entries(headers).map(([k, v]) => [k.toLowerCase(), Array.isArray(v) ? v[0] : v]),\n );\n return h['x-correlation-id'] ?? h['x-request-id'] ?? randomUUID();\n};\n","export const LOG_LEVELS = ['info', 'warn', 'error', 'debug'] as const;\n\nexport const OUTCOMES = [\n 'success',\n 'error',\n 'validation_failed',\n 'not_found',\n 'conflict',\n 'timeout',\n 'skipped',\n] as const;\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
declare const LOG_LEVELS: readonly ["info", "warn", "error", "debug"];
|
|
2
|
+
declare const OUTCOMES: readonly ["success", "error", "validation_failed", "not_found", "conflict", "timeout", "skipped"];
|
|
3
|
+
|
|
4
|
+
type LogLevel = (typeof LOG_LEVELS)[number];
|
|
5
|
+
type Outcome = (typeof OUTCOMES)[number];
|
|
6
|
+
type LoggerConfig = {
|
|
7
|
+
service: string;
|
|
8
|
+
environment?: string;
|
|
9
|
+
redactKeys?: readonly string[];
|
|
10
|
+
};
|
|
11
|
+
type SerializedError = {
|
|
12
|
+
type: string;
|
|
13
|
+
message: string;
|
|
14
|
+
code?: string;
|
|
15
|
+
errorDetails?: unknown;
|
|
16
|
+
};
|
|
17
|
+
type TerminalFields = {
|
|
18
|
+
outcome: Outcome | string;
|
|
19
|
+
durationMs: number;
|
|
20
|
+
statusCode?: number;
|
|
21
|
+
error?: SerializedError;
|
|
22
|
+
};
|
|
23
|
+
type WideEventBuilder = {
|
|
24
|
+
with: (fields: Record<string, unknown>) => void;
|
|
25
|
+
emit: (level: LogLevel, message: string, terminal: TerminalFields) => void;
|
|
26
|
+
};
|
|
27
|
+
type Logger = {
|
|
28
|
+
startOperation: (functionName: string, base?: Record<string, unknown>) => WideEventBuilder;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export { LOG_LEVELS as L, OUTCOMES as O, type SerializedError as S, type TerminalFields as T, type WideEventBuilder as W, type LogLevel as a, type Logger as b, type LoggerConfig as c, type Outcome as d };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
declare const LOG_LEVELS: readonly ["info", "warn", "error", "debug"];
|
|
2
|
+
declare const OUTCOMES: readonly ["success", "error", "validation_failed", "not_found", "conflict", "timeout", "skipped"];
|
|
3
|
+
|
|
4
|
+
type LogLevel = (typeof LOG_LEVELS)[number];
|
|
5
|
+
type Outcome = (typeof OUTCOMES)[number];
|
|
6
|
+
type LoggerConfig = {
|
|
7
|
+
service: string;
|
|
8
|
+
environment?: string;
|
|
9
|
+
redactKeys?: readonly string[];
|
|
10
|
+
};
|
|
11
|
+
type SerializedError = {
|
|
12
|
+
type: string;
|
|
13
|
+
message: string;
|
|
14
|
+
code?: string;
|
|
15
|
+
errorDetails?: unknown;
|
|
16
|
+
};
|
|
17
|
+
type TerminalFields = {
|
|
18
|
+
outcome: Outcome | string;
|
|
19
|
+
durationMs: number;
|
|
20
|
+
statusCode?: number;
|
|
21
|
+
error?: SerializedError;
|
|
22
|
+
};
|
|
23
|
+
type WideEventBuilder = {
|
|
24
|
+
with: (fields: Record<string, unknown>) => void;
|
|
25
|
+
emit: (level: LogLevel, message: string, terminal: TerminalFields) => void;
|
|
26
|
+
};
|
|
27
|
+
type Logger = {
|
|
28
|
+
startOperation: (functionName: string, base?: Record<string, unknown>) => WideEventBuilder;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export { LOG_LEVELS as L, OUTCOMES as O, type SerializedError as S, type TerminalFields as T, type WideEventBuilder as W, type LogLevel as a, type Logger as b, type LoggerConfig as c, type Outcome as d };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tpaga-logger",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"description": "Structured logging SDK for Tpaga microservices (wide events → CloudWatch)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -14,6 +14,11 @@
|
|
|
14
14
|
"types": "./dist/index.d.ts",
|
|
15
15
|
"import": "./dist/index.js",
|
|
16
16
|
"require": "./dist/index.cjs"
|
|
17
|
+
},
|
|
18
|
+
"./fastify": {
|
|
19
|
+
"types": "./dist/fastify.d.ts",
|
|
20
|
+
"import": "./dist/fastify.js",
|
|
21
|
+
"require": "./dist/fastify.cjs"
|
|
17
22
|
}
|
|
18
23
|
},
|
|
19
24
|
"files": [
|
|
@@ -22,33 +27,49 @@
|
|
|
22
27
|
"engmake sines": {
|
|
23
28
|
"node": ">=20"
|
|
24
29
|
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsup",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"test:watch": "vitest",
|
|
34
|
+
"lint": "eslint src tests",
|
|
35
|
+
"typecheck": "tsc --noEmit"
|
|
36
|
+
},
|
|
25
37
|
"dependencies": {
|
|
38
|
+
"fastify-plugin": "^6.0.0",
|
|
26
39
|
"pino": "^10.3.1",
|
|
27
40
|
"pino-pretty": "^13.1.3"
|
|
28
41
|
},
|
|
29
42
|
"peerDependencies": {
|
|
30
|
-
"
|
|
43
|
+
"@nestjs/common": ">=10",
|
|
44
|
+
"express": ">=4",
|
|
45
|
+
"fastify": ">=4",
|
|
46
|
+
"rxjs": ">=7"
|
|
31
47
|
},
|
|
32
48
|
"peerDependenciesMeta": {
|
|
33
49
|
"express": {
|
|
34
50
|
"optional": true
|
|
51
|
+
},
|
|
52
|
+
"fastify": {
|
|
53
|
+
"optional": true
|
|
54
|
+
},
|
|
55
|
+
"@nestjs/common": {
|
|
56
|
+
"optional": true
|
|
57
|
+
},
|
|
58
|
+
"rxjs": {
|
|
59
|
+
"optional": true
|
|
35
60
|
}
|
|
36
61
|
},
|
|
37
62
|
"devDependencies": {
|
|
38
63
|
"@eslint/js": "^9.28.0",
|
|
64
|
+
"@nestjs/common": "^11.1.27",
|
|
39
65
|
"@types/express": "^5.0.6",
|
|
40
66
|
"@types/node": "^22.15.0",
|
|
41
67
|
"eslint": "^9.28.0",
|
|
68
|
+
"fastify": "^5.9.0",
|
|
69
|
+
"rxjs": "^7.8.2",
|
|
42
70
|
"tsup": "^8.5.0",
|
|
43
71
|
"typescript": "^5.8.3",
|
|
44
72
|
"typescript-eslint": "^8.33.0",
|
|
45
73
|
"vitest": "^3.2.0"
|
|
46
|
-
},
|
|
47
|
-
"scripts": {
|
|
48
|
-
"build": "tsup",
|
|
49
|
-
"test": "vitest run",
|
|
50
|
-
"test:watch": "vitest",
|
|
51
|
-
"lint": "eslint src tests",
|
|
52
|
-
"typecheck": "tsc --noEmit"
|
|
53
74
|
}
|
|
54
|
-
}
|
|
75
|
+
}
|