From 4471798adec80a7a0eb27b40673a364790fb9c32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Pedro=20Alves?= Date: Thu, 26 Feb 2026 22:25:58 -0300 Subject: [PATCH] refactor(telemetry): simplify HTTP request metrics and integrate automatic instrumentation --- .../infra/telemetry/http-request-metrics.ts | 44 ++++++------------- apps/backend/src/infra/telemetry/otel.ts | 4 ++ 2 files changed, 18 insertions(+), 30 deletions(-) diff --git a/apps/backend/src/infra/telemetry/http-request-metrics.ts b/apps/backend/src/infra/telemetry/http-request-metrics.ts index bb7e9cf9..8640d78a 100644 --- a/apps/backend/src/infra/telemetry/http-request-metrics.ts +++ b/apps/backend/src/infra/telemetry/http-request-metrics.ts @@ -1,45 +1,29 @@ import { metrics } from '@opentelemetry/api' import type { FastifyInstance } from 'fastify' -function getStatusClass(statusCode: number): 'ok' | 'error' { - return statusCode >= 200 && statusCode < 400 ? 'ok' : 'error' -} - +/** + * Registers a lightweight request counter on top of the automatic telemetry + * provided by @opentelemetry/instrumentation-http and @fastify/otel. + * + * - http.server.request.duration (histogram) is emitted automatically by + * instrumentation-http following OTel semantic conventions. + * - Span status ERROR + exception recording on 5xx is handled automatically + * by instrumentation-http (sets status) and @fastify/otel (recordExceptions). + * - This counter adds a simple `http.server.requests` metric useful for + * quick rate-of-requests queries without histogram overhead. + */ export function registerHttpRequestMetrics(app: FastifyInstance) { const meter = metrics.getMeter('plotwist-api', '0.1.0') const requestCounter = meter.createCounter('http.server.requests', { - description: 'HTTP server request count by status', + description: 'Total HTTP server requests', unit: '1', }) - const requestDuration = meter.createHistogram( - 'http.server.request.duration', - { - description: 'HTTP server request duration', - unit: 's', - } - ) - - app.addHook('onRequest', (request, _reply, done) => { - ;(request as { _startTime?: number })._startTime = Date.now() - done() - }) app.addHook('onResponse', (request, reply, done) => { const statusCode = reply.statusCode - const statusClass = getStatusClass(statusCode) - const startTime = (request as { _startTime?: number })._startTime - const durationSec = - typeof startTime === 'number' ? (Date.now() - startTime) / 1000 : 0 - requestCounter.add(1, { - 'http.status_code': statusCode, - 'http.response.status': statusClass, - 'http.method': request.method, - 'http.route': request.routeOptions?.url ?? request.url, - }) - requestDuration.record(durationSec, { - 'http.response.status': statusClass, - 'http.method': request.method, + 'http.response.status_code': statusCode, + 'http.request.method': request.method, 'http.route': request.routeOptions?.url ?? request.url, }) done() diff --git a/apps/backend/src/infra/telemetry/otel.ts b/apps/backend/src/infra/telemetry/otel.ts index a3264765..ae3bcb4e 100644 --- a/apps/backend/src/infra/telemetry/otel.ts +++ b/apps/backend/src/infra/telemetry/otel.ts @@ -3,6 +3,7 @@ import { metrics, trace } from '@opentelemetry/api' import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-proto' import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto' import { HostMetrics } from '@opentelemetry/host-metrics' +import { HttpInstrumentation } from '@opentelemetry/instrumentation-http' import { resourceFromAttributes } from '@opentelemetry/resources' import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics' import { NodeSDK } from '@opentelemetry/sdk-node' @@ -59,6 +60,8 @@ function parseOtlpHeaders(raw: string): Record { const { metricsUrl, tracesUrl, headers } = getOtlpConfig() +const httpInstrumentation = new HttpInstrumentation() + const sdk = new NodeSDK({ resource: resourceFromAttributes({ [ATTR_SERVICE_NAME]: 'plotwist-api', @@ -68,6 +71,7 @@ const sdk = new NodeSDK({ metricReader: new PeriodicExportingMetricReader({ exporter: new OTLPMetricExporter({ url: metricsUrl, headers }), }), + instrumentations: [httpInstrumentation], }) logger.info('Starting OTLP exporter')