|
| 1 | +/** |
| 2 | + * OpenTelemetry instrumentation bootstrap. |
| 3 | + * |
| 4 | + * This file MUST be imported before any other module (i.e. at the very top of |
| 5 | + * main.ts) so that auto-instrumentation patches are applied before the |
| 6 | + * libraries they instrument are loaded. |
| 7 | + * |
| 8 | + * Configuration via environment variables: |
| 9 | + * OTEL_EXPORTER_OTLP_ENDPOINT — OTLP gRPC/HTTP endpoint (e.g. http://localhost:4318) |
| 10 | + * Defaults to no-op (no export) when unset. |
| 11 | + * OTEL_SERVICE_NAME — Service name reported in traces (default: niffyinsure-backend) |
| 12 | + * OTEL_SAMPLING_RATIO — Head-sampling ratio 0.0–1.0 (default: 1.0 in dev, 0.1 in prod) |
| 13 | + * |
| 14 | + * Sensitive data policy: |
| 15 | + * - XDR payloads and private keys MUST NOT appear as span attributes. |
| 16 | + * - Request bodies are never captured by auto-instrumentation (HTTP body capture is disabled). |
| 17 | + */ |
| 18 | + |
| 19 | +import { NodeSDK } from '@opentelemetry/sdk-node' |
| 20 | +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' |
| 21 | +import { Resource } from '@opentelemetry/resources' |
| 22 | +import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions' |
| 23 | +import { TraceIdRatioBasedSampler } from '@opentelemetry/sdk-trace-base' |
| 24 | +import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node' |
| 25 | + |
| 26 | +const SERVICE_NAME = process.env.OTEL_SERVICE_NAME ?? 'niffyinsure-backend' |
| 27 | +const OTLP_ENDPOINT = process.env.OTEL_EXPORTER_OTLP_ENDPOINT |
| 28 | + |
| 29 | +// Sampling: configurable without redeployment via env var. |
| 30 | +// Default: 1.0 (sample everything) unless NODE_ENV=production, then 0.1. |
| 31 | +const defaultRatio = process.env.NODE_ENV === 'production' ? 0.1 : 1.0 |
| 32 | +const samplingRatio = parseFloat(process.env.OTEL_SAMPLING_RATIO ?? String(defaultRatio)) |
| 33 | + |
| 34 | +// Only configure an exporter when an endpoint is explicitly set. |
| 35 | +// In development (no endpoint), the SDK runs with a no-op exporter. |
| 36 | +const traceExporter = OTLP_ENDPOINT |
| 37 | + ? new OTLPTraceExporter({ url: `${OTLP_ENDPOINT}/v1/traces` }) |
| 38 | + : undefined |
| 39 | + |
| 40 | +const sdk = new NodeSDK({ |
| 41 | + resource: new Resource({ |
| 42 | + [ATTR_SERVICE_NAME]: SERVICE_NAME, |
| 43 | + [ATTR_SERVICE_VERSION]: process.env.npm_package_version ?? '0.0.0', |
| 44 | + }), |
| 45 | + sampler: new TraceIdRatioBasedSampler(samplingRatio), |
| 46 | + ...(traceExporter ? { traceExporter } : {}), |
| 47 | + instrumentations: [ |
| 48 | + getNodeAutoInstrumentations({ |
| 49 | + // HTTP instrumentation — captures incoming/outgoing HTTP spans. |
| 50 | + // Body capture is disabled to prevent XDR/key leakage. |
| 51 | + '@opentelemetry/instrumentation-http': { |
| 52 | + enabled: true, |
| 53 | + // Do not capture request/response bodies |
| 54 | + requestHook: () => undefined, |
| 55 | + responseHook: () => undefined, |
| 56 | + }, |
| 57 | + // Prisma / pg instrumentation for DB spans |
| 58 | + '@opentelemetry/instrumentation-pg': { enabled: true }, |
| 59 | + // Redis instrumentation for cache spans |
| 60 | + '@opentelemetry/instrumentation-ioredis': { enabled: true }, |
| 61 | + // Disable noisy fs instrumentation |
| 62 | + '@opentelemetry/instrumentation-fs': { enabled: false }, |
| 63 | + }), |
| 64 | + ], |
| 65 | +}) |
| 66 | + |
| 67 | +sdk.start() |
| 68 | + |
| 69 | +// Graceful shutdown |
| 70 | +process.on('SIGTERM', () => { |
| 71 | + sdk.shutdown().catch((err) => console.error('OTel shutdown error', err)) |
| 72 | +}) |
| 73 | + |
| 74 | +export { sdk } |
0 commit comments