UNPKG

@accounter/server

Version:
123 lines (111 loc) 3.76 kB
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'; import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; import { NodeSDK, resources, tracing } from '@opentelemetry/sdk-node'; import { ATTR_SERVICE_NAME, ATTR_SERVICE_NAMESPACE } from '@opentelemetry/semantic-conventions'; import { env } from '../environment.js'; const ATTR_DEPLOYMENT_ENVIRONMENT_NAME = 'deployment.environment.name'; const HEALTH_CHECK_PATHS = new Set(['/health', '/ready', '/readiness']); function shouldIgnoreIncomingRequest(url: string | undefined): boolean { if (!url) { return false; } try { const pathname = new URL(url, 'http://localhost').pathname; return HEALTH_CHECK_PATHS.has(pathname); } catch { const pathname = url.split('?')[0] ?? ''; return HEALTH_CHECK_PATHS.has(pathname); } } /** * Parses an OTLP header string of the form "key1=value1,key2=value2" into a * Record<string, string>. Returns undefined when the input is absent or empty. */ function parseHeaders(headersStr: string | undefined): Record<string, string> | undefined { if (!headersStr) return undefined; const result: Record<string, string> = {}; for (const part of headersStr.split(',')) { const eqIdx = part.indexOf('='); if (eqIdx === -1) continue; const key = part.slice(0, eqIdx).trim(); const value = part.slice(eqIdx + 1).trim(); if (key) result[key] = value; } return Object.keys(result).length > 0 ? result : undefined; } function buildSampler( samplerType: | 'always_on' | 'always_off' | 'parentbased_traceidratio' | 'traceidratio' | 'parentbased_always_on' | 'parentbased_always_off', arg: number | undefined, ) { switch (samplerType) { case 'always_off': return new tracing.AlwaysOffSampler(); case 'parentbased_traceidratio': { const ratio = arg ?? 1.0; return new tracing.ParentBasedSampler({ root: new tracing.TraceIdRatioBasedSampler(ratio), }); } case 'traceidratio': { const ratio = arg ?? 1.0; return new tracing.TraceIdRatioBasedSampler(ratio); } case 'parentbased_always_on': return new tracing.ParentBasedSampler({ root: new tracing.AlwaysOnSampler(), }); case 'parentbased_always_off': return new tracing.ParentBasedSampler({ root: new tracing.AlwaysOffSampler(), }); default: return new tracing.AlwaysOnSampler(); } } /** * Builds and returns a configured (but not yet started) NodeSDK instance using * the OTEL settings from `env`. Returns null when OTEL is disabled. */ export function buildOtelSdk(): NodeSDK | null { const otel = env.otel; if (!otel.enabled) { return null; } const resource = resources.resourceFromAttributes({ [ATTR_SERVICE_NAME]: otel.serviceName, [ATTR_SERVICE_NAMESPACE]: otel.serviceNamespace, [ATTR_DEPLOYMENT_ENVIRONMENT_NAME]: otel.deploymentEnv, }); const traceExporter = new OTLPTraceExporter({ url: otel.exporterEndpoint, headers: parseHeaders(otel.exporterHeaders), }); const sampler = buildSampler(otel.tracesSampler, otel.tracesSamplerArg); return new NodeSDK({ resource, traceExporter, sampler, instrumentations: [ getNodeAutoInstrumentations({ '@opentelemetry/instrumentation-fs': { enabled: false, }, '@opentelemetry/instrumentation-http': { ignoreIncomingRequestHook: request => shouldIgnoreIncomingRequest(request.url), }, '@opentelemetry/instrumentation-graphql': { enabled: true, }, '@opentelemetry/instrumentation-pg': { enhancedDatabaseReporting: false, }, }), ], }); }