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 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.8+</sup>
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 {
@@ -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":"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","names":["pino"]}
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 {
@@ -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":"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","names":[]}
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.8",
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
  },