{"version":3,"file":"instrumentation.cjs","names":["processDetector","hostDetector","ATTR_SERVICE_NAME","ATTR_SERVICE_VERSION","requireModule","TailSamplingSpanProcessor","BatchSpanProcessor","OTLPTraceExporter","NodeSDK","PeriodicExportingMetricReader","OTLPMetricExporter","BatchLogRecordProcessor","OTLPLogExporter"],"sources":["../src/instrumentation.ts"],"sourcesContent":["import { NodeSDK } from '@opentelemetry/sdk-node';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';\nimport { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';\nimport { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';\nimport { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';\nimport { TailSamplingSpanProcessor } from './tail-sampling-processor';\nimport { getLogger } from './init';\nimport {\n  resourceFromAttributes,\n  detectResources,\n  processDetector,\n  hostDetector,\n  type Resource,\n  type ResourceDetector,\n} from '@opentelemetry/resources';\nimport {\n  ATTR_SERVICE_NAME,\n  ATTR_SERVICE_VERSION,\n} from '@opentelemetry/semantic-conventions/incubating';\nimport { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';\nimport { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';\nimport { requireModule } from './node-require';\n\n/**\n * Parse OTLP headers string into object format\n * @param headersString - Headers as \"key1=value1,key2=value2\" or \"Authorization=Basic ...\"\n * @returns Headers object for OTLP exporters\n */\nfunction parseOtlpHeaders(headersString?: string): Record<string, string> {\n  if (!headersString) return {};\n\n  const headers: Record<string, string> = {};\n  const pairs = headersString.split(',');\n\n  for (const pair of pairs) {\n    const [key, ...valueParts] = pair.split('=');\n    if (key && valueParts.length > 0) {\n      headers[key.trim()] = valueParts.join('=').trim();\n    }\n  }\n\n  return headers;\n}\n\n/**\n * Parse resource attributes string into object format\n * @param attributesString - Attributes as \"key1=value1,key2=value2\"\n * @returns Resource attributes object\n */\nfunction parseResourceAttributes(\n  attributesString?: string,\n): Record<string, string> {\n  if (!attributesString) return {};\n\n  const attributes: Record<string, string> = {};\n  const pairs = attributesString.split(',');\n\n  for (const pair of pairs) {\n    const [key, ...valueParts] = pair.split('=');\n    if (key && valueParts.length > 0) {\n      attributes[key.trim()] = valueParts.join('=').trim();\n    }\n  }\n\n  return attributes;\n}\n\nexport interface InstrumentationConfig {\n  serviceName: string;\n  serviceVersion?: string;\n  deploymentEnvironment?: string;\n  otlpEndpoint?: string;\n  /** Headers for authentication (e.g., Grafana Cloud, Honeycomb) */\n  headers?: string;\n  /** Resource attributes as comma-separated key=value pairs */\n  resourceAttributes?: string;\n  /** Enable async resource detection for process/host info (default: false) */\n  detectResources?: boolean;\n  /**\n   * Use selective instrumentation instead of full auto-instrumentation\n   * **Default: true** (performance-first)\n   *\n   * When true, auto-instrumentation is disabled. You can manually add\n   * specific instrumentations via the `instrumentations` field.\n   * This reduces overhead from ~81% to near-zero based on Platformatic benchmarks.\n   *\n   * Set to false to enable full auto-instrumentation (not recommended for production).\n   *\n   * @see https://blogger.platformatic.dev/the-hidden-cost-of-context\n   */\n  selectiveInstrumentation?: boolean;\n\n  /**\n   * Custom instrumentations to use (only when selectiveInstrumentation is true)\n   * @example\n   * ```typescript\n   * import { HttpInstrumentation } from '@opentelemetry/instrumentation-http'\n   *\n   * initInstrumentation({\n   *   serviceName: 'api',\n   *   selectiveInstrumentation: true,\n   *   instrumentations: [new HttpInstrumentation()]\n   * })\n   * ```\n   */\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  instrumentations?: any[];\n}\n\n/**\n * Initialize OpenTelemetry instrumentation with OTLP exporters\n *\n * This sets up:\n * - Traces (OTLP HTTP)\n * - Metrics (OTLP HTTP)\n * - Logs (OTLP HTTP)\n * - Auto-instrumentation for common Node.js libraries\n *\n * @example\n * // Call this at the very start of your application\n * import { initInstrumentation } from '@your-org/otel-decorators'\n *\n * initInstrumentation({\n *   serviceName: 'my-service' }\n *   serviceVersion: '1.0.0',\n *   deploymentEnvironment: 'production',\n *   otlpEndpoint: 'http://localhost:4318'\n * })\n *\n * // Or with async resource detection (top-level await required)\n * await initInstrumentation({\n *   serviceName: 'my-service' }\n *   detectResources: true\n * })\n */\n// Enables graceful shutdown and prevents SDK leaks on hot-reload\nlet currentSDK: NodeSDK | null = null;\nlet shutdownHandlerRegistered = false;\n\n/**\n * Shutdown the OpenTelemetry SDK gracefully\n * Call this before process exit or during hot-reloads\n */\nexport async function shutdownInstrumentation(sdk?: NodeSDK): Promise<void> {\n  const sdkToShutdown = sdk || currentSDK;\n  if (!sdkToShutdown) {\n    getLogger().warn({}, 'No SDK to shutdown');\n    return;\n  }\n\n  try {\n    await sdkToShutdown.shutdown();\n    getLogger().info({}, 'OpenTelemetry terminated successfully');\n    if (sdkToShutdown === currentSDK) {\n      currentSDK = null;\n    }\n  } catch (error) {\n    getLogger().error(\n      {\n        err: error instanceof Error ? error : undefined,\n      },\n      'Error terminating OpenTelemetry',\n    );\n    throw error;\n  }\n}\n\nexport async function initInstrumentation(\n  config: InstrumentationConfig,\n): Promise<NodeSDK> {\n  // Prevents resource leaks on hot-reload or multiple init calls\n  if (currentSDK) {\n    getLogger().info(\n      {},\n      'Shutting down existing OpenTelemetry SDK before reinitializing...',\n    );\n    await shutdownInstrumentation(currentSDK);\n  }\n\n  // Parse headers and resource attributes\n  const otlpHeaders = parseOtlpHeaders(config.headers);\n  const customResourceAttributes = parseResourceAttributes(\n    config.resourceAttributes,\n  );\n\n  let resource: Resource;\n\n  // Dynamically load optional resource detectors\n  const detectors: ResourceDetector[] = [processDetector, hostDetector];\n  try {\n    const awsDetectors = await import('@opentelemetry/resource-detector-aws');\n    detectors.push(\n      awsDetectors.awsEc2Detector,\n      awsDetectors.awsEcsDetector,\n      awsDetectors.awsEksDetector,\n    );\n  } catch {\n    // ignore\n  }\n  try {\n    const gcpDetectors = await import('@opentelemetry/resource-detector-gcp');\n    detectors.push(gcpDetectors.gcpDetector);\n  } catch {\n    // ignore\n  }\n  try {\n    const containerDetectors =\n      await import('@opentelemetry/resource-detector-container');\n    detectors.push(containerDetectors.containerDetector);\n  } catch {\n    // ignore\n  }\n\n  if (config.detectResources) {\n    const detectedResource = await detectResources({\n      detectors,\n    });\n\n    resource = detectedResource.merge(\n      resourceFromAttributes({\n        [ATTR_SERVICE_NAME]: config.serviceName,\n        [ATTR_SERVICE_VERSION]: config.serviceVersion || '1.0.0',\n        'deployment.environment': config.deploymentEnvironment || 'development',\n        ...customResourceAttributes, // Merge custom resource attributes\n      }),\n    );\n  } else {\n    resource = resourceFromAttributes({\n      [ATTR_SERVICE_NAME]: config.serviceName,\n      [ATTR_SERVICE_VERSION]: config.serviceVersion || '1.0.0',\n      'deployment.environment': config.deploymentEnvironment || 'development',\n      ...customResourceAttributes, // Merge custom resource attributes\n    });\n  }\n\n  // Default to selective (near-zero overhead) vs full auto (~81% overhead)\n  // Lazy-load to avoid importing ~40+ packages at module evaluation time\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  let instrumentations: any[] = config.instrumentations || [];\n  if (config.selectiveInstrumentation === false) {\n    const mod = requireModule<{\n      getNodeAutoInstrumentations: () => unknown[];\n    }>('@opentelemetry/auto-instrumentations-node');\n    instrumentations = [mod.getNodeAutoInstrumentations()];\n  }\n\n  const traceExporter = new OTLPTraceExporter({\n    url: `${config.otlpEndpoint || 'http://localhost:4318'}/v1/traces`,\n    headers: otlpHeaders,\n  });\n\n  // Enables tail sampling via autotel.sampling.tail.keep attribute\n  const spanProcessor = new TailSamplingSpanProcessor(\n    new BatchSpanProcessor(traceExporter),\n  );\n\n  const sdk = new NodeSDK({\n    resource,\n    spanProcessor, // Use our wrapped processor instead of traceExporter directly\n    metricReader: new PeriodicExportingMetricReader({\n      exporter: new OTLPMetricExporter({\n        url: `${config.otlpEndpoint || 'http://localhost:4318'}/v1/metrics`,\n        headers: otlpHeaders,\n      }),\n    }),\n    logRecordProcessors: [\n      new BatchLogRecordProcessor(\n        new OTLPLogExporter({\n          url: `${config.otlpEndpoint || 'http://localhost:4318'}/v1/logs`,\n          headers: otlpHeaders,\n        }),\n      ),\n    ],\n    instrumentations,\n  });\n\n  try {\n    await sdk.start();\n    getLogger().info({}, 'OpenTelemetry instrumentation started successfully');\n  } catch (error) {\n    getLogger().error(\n      {\n        err: error instanceof Error ? error : undefined,\n      },\n      'Failed to start OpenTelemetry SDK',\n    );\n    throw error;\n  }\n\n  // Track current SDK for shutdown handler\n  currentSDK = sdk;\n\n  if (!shutdownHandlerRegistered) {\n    shutdownHandlerRegistered = true;\n\n    const shutdownHandler = () => {\n      shutdownInstrumentation()\n        .then(() => {\n          // eslint-disable-next-line unicorn/no-process-exit\n          process.exit(0);\n        })\n        .catch((error) => {\n          getLogger().error(\n            {\n              err: error instanceof Error ? error : undefined,\n            },\n            'Shutdown error',\n          );\n          // eslint-disable-next-line unicorn/no-process-exit\n          process.exit(1);\n        });\n    };\n\n    process.on('SIGTERM', shutdownHandler);\n    process.on('SIGINT', shutdownHandler);\n  }\n\n  return sdk;\n}\n"],"mappings":"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"}