From e967214a664438277c8df94b085d143b965838da Mon Sep 17 00:00:00 2001 From: dillonstreator Date: Fri, 24 Jul 2026 10:40:09 -0500 Subject: [PATCH 1/3] Align request/response log fields with OpenTelemetry semantic conventions. Co-authored-by: Cursor --- src/app.ts | 2 ++ src/logging.ts | 80 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/src/app.ts b/src/app.ts index 36cdc0c..079ee8e 100644 --- a/src/app.ts +++ b/src/app.ts @@ -16,6 +16,7 @@ import fastifySwagger from '@fastify/swagger'; import fastifySwaggerUI from '@fastify/swagger-ui'; import { Config } from './config'; +import { AppLogController } from './logging'; const { name: pkgName, version: pkgVersion } = JSON.parse( readFileSync(join(__dirname, '..', 'package.json'), 'utf8') @@ -33,6 +34,7 @@ export const initApp = async (config: Config, logger: pino.Logger) => { trustProxy: true, bodyLimit: 1024, genReqId: () => randomUUID(), + logController: new AppLogController(), }); app.setValidatorCompiler(validatorCompiler); app.setSerializerCompiler(serializerCompiler); diff --git a/src/logging.ts b/src/logging.ts index fd85d57..541d8d4 100644 --- a/src/logging.ts +++ b/src/logging.ts @@ -1,8 +1,82 @@ -import pino from "pino"; -import { Config } from "./config"; +import pino from 'pino'; +import { + FastifyReply, + FastifyRequest, + LogController, +} from 'fastify'; +import { + ATTR_CLIENT_ADDRESS, + ATTR_CLIENT_PORT, + ATTR_HTTP_REQUEST_METHOD, + ATTR_HTTP_RESPONSE_STATUS_CODE, + ATTR_HTTP_ROUTE, + ATTR_SERVER_ADDRESS, + ATTR_URL_PATH, + ATTR_USER_AGENT_ORIGINAL, +} from '@opentelemetry/semantic-conventions'; + +import { Config } from './config'; + +/** Aligns with OpenTelemetry / ECS-style HTTP attribute naming. */ +export const REQUEST_ID_LOG_LABEL = 'http.request.id'; +export const DURATION_MS_LOG_LABEL = 'duration_ms'; + +export const serializeRequest = (req: FastifyRequest) => ({ + [ATTR_HTTP_REQUEST_METHOD]: req.method, + [ATTR_URL_PATH]: req.url, + [ATTR_HTTP_ROUTE]: req.routeOptions?.url, + [ATTR_SERVER_ADDRESS]: req.host, + [ATTR_CLIENT_ADDRESS]: req.ip, + [ATTR_CLIENT_PORT]: req.socket?.remotePort, + [ATTR_USER_AGENT_ORIGINAL]: req.headers['user-agent'], +}); + +export const serializeResponse = (reply: FastifyReply) => ({ + [ATTR_HTTP_RESPONSE_STATUS_CODE]: reply.statusCode, +}); + +export class AppLogController extends LogController { + constructor(options?: ConstructorParameters[0]) { + super({ + ...options, + requestIdLogLabel: REQUEST_ID_LOG_LABEL, + }); + } + + requestCompleted( + error: Error | null, + request: FastifyRequest, + reply: FastifyReply + ) { + if (this.isLogDisabled(request)) return; + + if (error) { + reply.log.error( + { + res: reply, + err: error, + [DURATION_MS_LOG_LABEL]: reply.elapsedTime, + }, + 'request errored' + ); + } else { + reply.log.info( + { + res: reply, + [DURATION_MS_LOG_LABEL]: reply.elapsedTime, + }, + 'request completed' + ); + } + } +} export const initLogging = async (config: Config): Promise => { return pino({ level: config.logLevel, + serializers: { + req: serializeRequest, + res: serializeResponse, + }, }); -} +}; From e128695c8744ff1c1a07b5d8775c2a17d66285af Mon Sep 17 00:00:00 2001 From: dillonstreator Date: Fri, 24 Jul 2026 12:18:56 -0500 Subject: [PATCH 2/3] Use short nested req/res log field names instead of OTel paths. Co-authored-by: Cursor --- src/logging.ts | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/src/logging.ts b/src/logging.ts index 541d8d4..1ff651a 100644 --- a/src/logging.ts +++ b/src/logging.ts @@ -4,35 +4,24 @@ import { FastifyRequest, LogController, } from 'fastify'; -import { - ATTR_CLIENT_ADDRESS, - ATTR_CLIENT_PORT, - ATTR_HTTP_REQUEST_METHOD, - ATTR_HTTP_RESPONSE_STATUS_CODE, - ATTR_HTTP_ROUTE, - ATTR_SERVER_ADDRESS, - ATTR_URL_PATH, - ATTR_USER_AGENT_ORIGINAL, -} from '@opentelemetry/semantic-conventions'; import { Config } from './config'; -/** Aligns with OpenTelemetry / ECS-style HTTP attribute naming. */ -export const REQUEST_ID_LOG_LABEL = 'http.request.id'; +export const REQUEST_ID_LOG_LABEL = 'request_id'; export const DURATION_MS_LOG_LABEL = 'duration_ms'; export const serializeRequest = (req: FastifyRequest) => ({ - [ATTR_HTTP_REQUEST_METHOD]: req.method, - [ATTR_URL_PATH]: req.url, - [ATTR_HTTP_ROUTE]: req.routeOptions?.url, - [ATTR_SERVER_ADDRESS]: req.host, - [ATTR_CLIENT_ADDRESS]: req.ip, - [ATTR_CLIENT_PORT]: req.socket?.remotePort, - [ATTR_USER_AGENT_ORIGINAL]: req.headers['user-agent'], + method: req.method, + path: req.url, + route: req.routeOptions?.url, + host: req.host, + remote_address: req.ip, + remote_port: req.socket?.remotePort, + user_agent: req.headers['user-agent'], }); export const serializeResponse = (reply: FastifyReply) => ({ - [ATTR_HTTP_RESPONSE_STATUS_CODE]: reply.statusCode, + status_code: reply.statusCode, }); export class AppLogController extends LogController { From 0b8e06f182a4c72432060e3a5cbc5a47e0363ca8 Mon Sep 17 00:00:00 2001 From: dillonstreator Date: Fri, 24 Jul 2026 12:27:39 -0500 Subject: [PATCH 3/3] Override vulnerable find-my-way, @fastify/static, and tar versions. Co-authored-by: Cursor --- package.json | 6 ++++-- pnpm-lock.yaml | 39 +++++++++++++++++++++------------------ 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 0c01bb1..a48cc46 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "minimatch@<3.1.3": "3.1.3", "minimatch@>=5.0.0 <5.1.8": "5.1.8", "minimatch@>=9.0.0 <9.0.7": "9.0.7", - "tar": "^7.5.16", + "tar": "^7.5.21", "brace-expansion@>=2.0.0 <2.0.3": "2.0.3", "picomatch@^2": "2.3.2", "yaml": "^2.8.3", @@ -70,7 +70,9 @@ "sigstore": "^4.1.1", "@sigstore/core": "^3.2.1", "@opentelemetry/core": "^2.8.0", - "ws": "^8.21.0" + "ws": "^8.21.0", + "find-my-way": "^9.7.0", + "@fastify/static": "^10.1.2" } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 79c1d7c..dbc553c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,7 +12,7 @@ overrides: minimatch@<3.1.3: 3.1.3 minimatch@>=5.0.0 <5.1.8: 5.1.8 minimatch@>=9.0.0 <9.0.7: 9.0.7 - tar: ^7.5.16 + tar: ^7.5.21 brace-expansion@>=2.0.0 <2.0.3: 2.0.3 picomatch@^2: 2.3.2 yaml: ^2.8.3 @@ -26,6 +26,8 @@ overrides: '@sigstore/core': ^3.2.1 '@opentelemetry/core': ^2.8.0 ws: ^8.21.0 + find-my-way: ^9.7.0 + '@fastify/static': ^10.1.2 importers: @@ -738,8 +740,8 @@ packages: '@fastify/send@4.1.0': resolution: {integrity: sha512-TMYeQLCBSy2TOFmV95hQWkiTYgC/SEx7vMdV+wnZVX4tt8VBLKzmH8vV9OzJehV0+XBfg+WxPMt5wp+JBUKsVw==} - '@fastify/static@9.3.0': - resolution: {integrity: sha512-9YMYRpCOtMBrqKYWcqiw7ykOrn4D0jogHpJrFS0KGeSuOwzKMM5/mjj7B0CFLVoQ6htqKYw//Zs7APn9DBq05w==} + '@fastify/static@10.1.2': + resolution: {integrity: sha512-G/g18cG9tLutT/OVyN1AIsHIl9L1UwmJ+S3dkyhVpplIx0nEMicd7RGQ+uJLyhKKF4a3tTcQydccn3Mop1fX+Q==} '@fastify/swagger-ui@5.2.6': resolution: {integrity: sha512-OMnms0O5s9wb6wis/K5nlrAMLsgUbr1GA8uphM41IasWe3AFdgxz6r/3bA9HTxlDNUYc2FGGKeqMp3ntxmSiNA==} @@ -1883,8 +1885,8 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - content-disposition@1.1.0: - resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} + content-disposition@2.0.1: + resolution: {integrity: sha512-e+H0ZXHSWYrENhQzw1LPuP4oF5MzVKmDU6d3hxlvaPEYLLg62MxtQNPRx4SYSuYJSBUgnQIG4HIN2tEtNv7Dog==} engines: {node: '>=18'} convert-source-map@2.0.0: @@ -2089,8 +2091,8 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - find-my-way@9.6.0: - resolution: {integrity: sha512-Zf4Xve4RymLl7NgaavNebZ01joJ8MfVerOG43wy7SHLO+r+K0C6d/SE0BiR7AV5V1VOCFlOP7ecdo+I4qmiHrQ==} + find-my-way@9.7.0: + resolution: {integrity: sha512-f2JHn75x2JlwUwLenZypgczR7YWMb/uO9BvUXtus+JMgkbIkLADd38cI4EiV+OQqrGo1Zlq6V8wnqMJ8e62wUQ==} engines: {node: '>=20'} find-up@4.1.0: @@ -3133,8 +3135,8 @@ packages: engines: {node: 20 || >=22} hasBin: true - tar@7.5.20: - resolution: {integrity: sha512-9FcyK4PA6+WbzlTM9WhQm6vB5W7cP7dUiPsv1g7YDwEQnQ1CGpK3MGlKk/ITVWMk05kHZuBhmVhiv8LZoy/PFQ==} + tar@7.5.22: + resolution: {integrity: sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==} engines: {node: '>=18'} tcompare@9.3.2: @@ -4272,18 +4274,19 @@ snapshots: http-errors: 2.0.1 mime: 3.0.0 - '@fastify/static@9.3.0': + '@fastify/static@10.1.2': dependencies: '@fastify/accept-negotiator': 2.0.1 + '@fastify/error': 4.2.0 '@fastify/send': 4.1.0 - content-disposition: 1.1.0 + content-disposition: 2.0.1 fastify-plugin: 6.0.0 fastq: 1.20.1 glob: 13.0.6 '@fastify/swagger-ui@5.2.6': dependencies: - '@fastify/static': 9.3.0 + '@fastify/static': 10.1.2 fastify-plugin: 5.1.0 openapi-types: 12.1.3 rfdc: 1.4.1 @@ -5779,7 +5782,7 @@ snapshots: color-name@1.1.4: {} - content-disposition@1.1.0: {} + content-disposition@2.0.1: {} convert-source-map@2.0.0: {} @@ -5935,7 +5938,7 @@ snapshots: abstract-logging: 2.0.1 avvio: 9.3.0 fast-json-stringify: 7.0.1 - find-my-way: 9.6.0 + find-my-way: 9.7.0 light-my-request: 6.6.0 pino: 9.14.0 process-warning: 5.0.0 @@ -5961,7 +5964,7 @@ snapshots: dependencies: to-regex-range: 5.0.1 - find-my-way@9.6.0: + find-my-way@9.7.0: dependencies: fast-deep-equal: 3.1.3 fast-querystring: 1.1.2 @@ -6429,7 +6432,7 @@ snapshots: nopt: 9.0.0 proc-log: 6.1.0 semver: 7.8.5 - tar: 7.5.20 + tar: 7.5.22 tinyglobby: 0.2.17 undici: 6.27.0 which: 6.0.1 @@ -6565,7 +6568,7 @@ snapshots: proc-log: 6.1.0 sigstore: 4.1.1 ssri: 13.0.1 - tar: 7.5.20 + tar: 7.5.22 transitivePeerDependencies: - supports-color @@ -7060,7 +7063,7 @@ snapshots: - typescript - utf-8-validate - tar@7.5.20: + tar@7.5.22: dependencies: '@isaacs/fs-minipass': 4.0.1 chownr: 3.0.0