tpaga-logger 0.0.8 → 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 +1 -1
- package/dist/fastify.cjs +3 -2
- package/dist/fastify.cjs.map +1 -1
- package/dist/fastify.js +3 -2
- package/dist/fastify.js.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -102,7 +102,7 @@ export const signUrl = async (req: Request, res: Response, next: NextFunction) =
|
|
|
102
102
|
|
|
103
103
|
---
|
|
104
104
|
|
|
105
|
-
### NestJS + Fastify <sup>v0.0.
|
|
105
|
+
### NestJS + Fastify <sup>v0.0.9+</sup>
|
|
106
106
|
|
|
107
107
|
Import from `tpaga-logger/fastify`. Register the plugin and the global interceptor — both are zero-config.
|
|
108
108
|
|
package/dist/fastify.cjs
CHANGED
|
@@ -81,6 +81,7 @@ __export(fastify_exports, {
|
|
|
81
81
|
});
|
|
82
82
|
module.exports = __toCommonJS(fastify_exports);
|
|
83
83
|
var import_rxjs = require("rxjs");
|
|
84
|
+
var import_fastify_plugin = __toESM(require("fastify-plugin"), 1);
|
|
84
85
|
var import_common = require("@nestjs/common");
|
|
85
86
|
|
|
86
87
|
// src/logger.ts
|
|
@@ -135,7 +136,7 @@ var createLogger = (config) => {
|
|
|
135
136
|
|
|
136
137
|
// src/fastify.ts
|
|
137
138
|
var START_KEY = /* @__PURE__ */ Symbol("tpagaStart");
|
|
138
|
-
var withTpagaFastifyLogger = (logger) => async (fastify) => {
|
|
139
|
+
var withTpagaFastifyLogger = (logger) => (0, import_fastify_plugin.default)(async (fastify) => {
|
|
139
140
|
fastify.addHook("onRequest", (request, _reply, done) => {
|
|
140
141
|
const correlationId = resolveCorrelationId(request.headers);
|
|
141
142
|
request.tpagaLog = logger.startOperation(request.url, {
|
|
@@ -158,7 +159,7 @@ var withTpagaFastifyLogger = (logger) => async (fastify) => {
|
|
|
158
159
|
request.tpagaLog.with({ error: serializeError(error) });
|
|
159
160
|
done();
|
|
160
161
|
});
|
|
161
|
-
};
|
|
162
|
+
});
|
|
162
163
|
var _TpagaLoggerInterceptor_decorators, _init;
|
|
163
164
|
_TpagaLoggerInterceptor_decorators = [(0, import_common.Injectable)()];
|
|
164
165
|
var TpagaLoggerInterceptor = class {
|
package/dist/fastify.cjs.map
CHANGED
|
@@ -1 +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 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 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;
|
|
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":["pino","fp"]}
|
package/dist/fastify.js
CHANGED
|
@@ -48,6 +48,7 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
48
48
|
|
|
49
49
|
// src/fastify.ts
|
|
50
50
|
import { catchError, throwError } from "rxjs";
|
|
51
|
+
import fp from "fastify-plugin";
|
|
51
52
|
import { Injectable } from "@nestjs/common";
|
|
52
53
|
|
|
53
54
|
// src/logger.ts
|
|
@@ -102,7 +103,7 @@ var createLogger = (config) => {
|
|
|
102
103
|
|
|
103
104
|
// src/fastify.ts
|
|
104
105
|
var START_KEY = /* @__PURE__ */ Symbol("tpagaStart");
|
|
105
|
-
var withTpagaFastifyLogger = (logger) => async (fastify) => {
|
|
106
|
+
var withTpagaFastifyLogger = (logger) => fp(async (fastify) => {
|
|
106
107
|
fastify.addHook("onRequest", (request, _reply, done) => {
|
|
107
108
|
const correlationId = resolveCorrelationId(request.headers);
|
|
108
109
|
request.tpagaLog = logger.startOperation(request.url, {
|
|
@@ -125,7 +126,7 @@ var withTpagaFastifyLogger = (logger) => async (fastify) => {
|
|
|
125
126
|
request.tpagaLog.with({ error: serializeError(error) });
|
|
126
127
|
done();
|
|
127
128
|
});
|
|
128
|
-
};
|
|
129
|
+
});
|
|
129
130
|
var _TpagaLoggerInterceptor_decorators, _init;
|
|
130
131
|
_TpagaLoggerInterceptor_decorators = [Injectable()];
|
|
131
132
|
var TpagaLoggerInterceptor = class {
|
package/dist/fastify.js.map
CHANGED
|
@@ -1 +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 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 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,SAAS,YAAY,kBAAkB;
|
|
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/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": {
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"typecheck": "tsc --noEmit"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
+
"fastify-plugin": "^6.0.0",
|
|
38
39
|
"pino": "^10.3.1",
|
|
39
40
|
"pino-pretty": "^13.1.3"
|
|
40
41
|
},
|