autotel
Version:
Write Once, Observe Anywhere
170 lines (168 loc) • 6.45 kB
JavaScript
import { t as requireModule } from "./node-require-vROmTeJ8.js";
import { i as getLogger } from "./init-BS2JVkrL.js";
import { TailSamplingSpanProcessor } from "./tail-sampling-processor.js";
import { NodeSDK } from "@opentelemetry/sdk-node";
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { detectResources, hostDetector, processDetector, resourceFromAttributes } from "@opentelemetry/resources";
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
import { BatchLogRecordProcessor } from "@opentelemetry/sdk-logs";
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions/incubating";
//#region src/instrumentation.ts
/**
* Parse OTLP headers string into object format
* @param headersString - Headers as "key1=value1,key2=value2" or "Authorization=Basic ..."
* @returns Headers object for OTLP exporters
*/
function parseOtlpHeaders(headersString) {
if (!headersString) return {};
const headers = {};
const pairs = headersString.split(",");
for (const pair of pairs) {
const [key, ...valueParts] = pair.split("=");
if (key && valueParts.length > 0) headers[key.trim()] = valueParts.join("=").trim();
}
return headers;
}
/**
* Parse resource attributes string into object format
* @param attributesString - Attributes as "key1=value1,key2=value2"
* @returns Resource attributes object
*/
function parseResourceAttributes(attributesString) {
if (!attributesString) return {};
const attributes = {};
const pairs = attributesString.split(",");
for (const pair of pairs) {
const [key, ...valueParts] = pair.split("=");
if (key && valueParts.length > 0) attributes[key.trim()] = valueParts.join("=").trim();
}
return attributes;
}
/**
* Initialize OpenTelemetry instrumentation with OTLP exporters
*
* This sets up:
* - Traces (OTLP HTTP)
* - Metrics (OTLP HTTP)
* - Logs (OTLP HTTP)
* - Auto-instrumentation for common Node.js libraries
*
* @example
* // Call this at the very start of your application
* import { initInstrumentation } from '@your-org/otel-decorators'
*
* initInstrumentation({
* serviceName: 'my-service' }
* serviceVersion: '1.0.0',
* deploymentEnvironment: 'production',
* otlpEndpoint: 'http://localhost:4318'
* })
*
* // Or with async resource detection (top-level await required)
* await initInstrumentation({
* serviceName: 'my-service' }
* detectResources: true
* })
*/
let currentSDK = null;
let shutdownHandlerRegistered = false;
/**
* Shutdown the OpenTelemetry SDK gracefully
* Call this before process exit or during hot-reloads
*/
async function shutdownInstrumentation(sdk) {
const sdkToShutdown = sdk || currentSDK;
if (!sdkToShutdown) {
getLogger().warn({}, "No SDK to shutdown");
return;
}
try {
await sdkToShutdown.shutdown();
getLogger().info({}, "OpenTelemetry terminated successfully");
if (sdkToShutdown === currentSDK) currentSDK = null;
} catch (error) {
getLogger().error({ err: error instanceof Error ? error : void 0 }, "Error terminating OpenTelemetry");
throw error;
}
}
async function initInstrumentation(config) {
if (currentSDK) {
getLogger().info({}, "Shutting down existing OpenTelemetry SDK before reinitializing...");
await shutdownInstrumentation(currentSDK);
}
const otlpHeaders = parseOtlpHeaders(config.headers);
const customResourceAttributes = parseResourceAttributes(config.resourceAttributes);
let resource;
const detectors = [processDetector, hostDetector];
try {
const awsDetectors = await import("@opentelemetry/resource-detector-aws");
detectors.push(awsDetectors.awsEc2Detector, awsDetectors.awsEcsDetector, awsDetectors.awsEksDetector);
} catch {}
try {
const gcpDetectors = await import("@opentelemetry/resource-detector-gcp");
detectors.push(gcpDetectors.gcpDetector);
} catch {}
try {
const containerDetectors = await import("@opentelemetry/resource-detector-container");
detectors.push(containerDetectors.containerDetector);
} catch {}
if (config.detectResources) resource = (await detectResources({ detectors })).merge(resourceFromAttributes({
[ATTR_SERVICE_NAME]: config.serviceName,
[ATTR_SERVICE_VERSION]: config.serviceVersion || "1.0.0",
"deployment.environment": config.deploymentEnvironment || "development",
...customResourceAttributes
}));
else resource = resourceFromAttributes({
[ATTR_SERVICE_NAME]: config.serviceName,
[ATTR_SERVICE_VERSION]: config.serviceVersion || "1.0.0",
"deployment.environment": config.deploymentEnvironment || "development",
...customResourceAttributes
});
let instrumentations = config.instrumentations || [];
if (config.selectiveInstrumentation === false) instrumentations = [requireModule("@opentelemetry/auto-instrumentations-node").getNodeAutoInstrumentations()];
const spanProcessor = new TailSamplingSpanProcessor(new BatchSpanProcessor(new OTLPTraceExporter({
url: `${config.otlpEndpoint || "http://localhost:4318"}/v1/traces`,
headers: otlpHeaders
})));
const sdk = new NodeSDK({
resource,
spanProcessor,
metricReader: new PeriodicExportingMetricReader({ exporter: new OTLPMetricExporter({
url: `${config.otlpEndpoint || "http://localhost:4318"}/v1/metrics`,
headers: otlpHeaders
}) }),
logRecordProcessors: [new BatchLogRecordProcessor(new OTLPLogExporter({
url: `${config.otlpEndpoint || "http://localhost:4318"}/v1/logs`,
headers: otlpHeaders
}))],
instrumentations
});
try {
await sdk.start();
getLogger().info({}, "OpenTelemetry instrumentation started successfully");
} catch (error) {
getLogger().error({ err: error instanceof Error ? error : void 0 }, "Failed to start OpenTelemetry SDK");
throw error;
}
currentSDK = sdk;
if (!shutdownHandlerRegistered) {
shutdownHandlerRegistered = true;
const shutdownHandler = () => {
shutdownInstrumentation().then(() => {
process.exit(0);
}).catch((error) => {
getLogger().error({ err: error instanceof Error ? error : void 0 }, "Shutdown error");
process.exit(1);
});
};
process.on("SIGTERM", shutdownHandler);
process.on("SIGINT", shutdownHandler);
}
return sdk;
}
//#endregion
export { initInstrumentation, shutdownInstrumentation };
//# sourceMappingURL=instrumentation.js.map