autotel 2.24.1 → 2.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auto.cjs +2 -2
- package/dist/auto.js +1 -1
- package/dist/{chunk-CSG2D3BZ.cjs → chunk-73SCHI7V.cjs} +7 -7
- package/dist/{chunk-CSG2D3BZ.cjs.map → chunk-73SCHI7V.cjs.map} +1 -1
- package/dist/{chunk-47KW5CV7.js → chunk-7RV6L24E.js} +4 -3
- package/dist/chunk-7RV6L24E.js.map +1 -0
- package/dist/{chunk-AXNPD6XW.cjs → chunk-7ZYUFMWU.cjs} +26 -26
- package/dist/{chunk-AXNPD6XW.cjs.map → chunk-7ZYUFMWU.cjs.map} +1 -1
- package/dist/{chunk-GSZJOYFF.js → chunk-BKIAH2YS.js} +3 -3
- package/dist/{chunk-GSZJOYFF.js.map → chunk-BKIAH2YS.js.map} +1 -1
- package/dist/{chunk-CLR5RQW4.cjs → chunk-DBUNRUDB.cjs} +5 -5
- package/dist/{chunk-CLR5RQW4.cjs.map → chunk-DBUNRUDB.cjs.map} +1 -1
- package/dist/{chunk-PDRIJURL.cjs → chunk-GIND746I.cjs} +4 -3
- package/dist/chunk-GIND746I.cjs.map +1 -0
- package/dist/{chunk-CZR7PJUZ.cjs → chunk-HCFBEOBM.cjs} +13 -13
- package/dist/{chunk-CZR7PJUZ.cjs.map → chunk-HCFBEOBM.cjs.map} +1 -1
- package/dist/{chunk-2D74YR4Z.js → chunk-JDV3LYOI.js} +3 -3
- package/dist/{chunk-2D74YR4Z.js.map → chunk-JDV3LYOI.js.map} +1 -1
- package/dist/{chunk-ZJTBAUXG.js → chunk-JZKMXKJX.js} +3 -3
- package/dist/{chunk-ZJTBAUXG.js.map → chunk-JZKMXKJX.js.map} +1 -1
- package/dist/{chunk-W6U2BUHU.cjs → chunk-LRFG6HRL.cjs} +5 -5
- package/dist/{chunk-W6U2BUHU.cjs.map → chunk-LRFG6HRL.cjs.map} +1 -1
- package/dist/{chunk-Z3BQYTZE.js → chunk-NYTCPK4J.js} +3 -3
- package/dist/{chunk-Z3BQYTZE.js.map → chunk-NYTCPK4J.js.map} +1 -1
- package/dist/{chunk-IJMW6CTV.js → chunk-P5YCN2Z3.js} +3 -3
- package/dist/{chunk-IJMW6CTV.js.map → chunk-P5YCN2Z3.js.map} +1 -1
- package/dist/decorators.cjs +2 -2
- package/dist/decorators.js +2 -2
- package/dist/event.cjs +5 -5
- package/dist/event.js +2 -2
- package/dist/functional.cjs +9 -9
- package/dist/functional.js +2 -2
- package/dist/index.cjs +41 -41
- package/dist/index.js +9 -9
- package/dist/instrumentation.cjs +8 -8
- package/dist/instrumentation.js +1 -1
- package/dist/messaging.cjs +6 -6
- package/dist/messaging.js +3 -3
- package/dist/semantic-helpers.cjs +7 -7
- package/dist/semantic-helpers.js +3 -3
- package/dist/test-span-collector.cjs +1 -0
- package/dist/test-span-collector.cjs.map +1 -1
- package/dist/test-span-collector.d.cts +2 -1
- package/dist/test-span-collector.d.ts +2 -1
- package/dist/test-span-collector.js +1 -1
- package/dist/test-span-collector.js.map +1 -1
- package/dist/webhook.cjs +3 -3
- package/dist/webhook.js +2 -2
- package/dist/workflow-distributed.cjs +4 -4
- package/dist/workflow-distributed.js +2 -2
- package/dist/workflow.cjs +7 -7
- package/dist/workflow.js +3 -3
- package/package.json +5 -5
- package/src/init.openllmetry.test.ts +98 -193
- package/src/init.ts +18 -1
- package/src/test-span-collector.ts +1 -1
- package/dist/chunk-47KW5CV7.js.map +0 -1
- package/dist/chunk-PDRIJURL.cjs.map +0 -1
package/dist/auto.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkGIND746I_cjs = require('./chunk-GIND746I.cjs');
|
|
4
4
|
var chunk6YIDHH2S_cjs = require('./chunk-6YIDHH2S.cjs');
|
|
5
5
|
require('./chunk-GVLK7YUU.cjs');
|
|
6
6
|
require('./chunk-ZNMBW67B.cjs');
|
|
@@ -23,7 +23,7 @@ module$1.register("import-in-the-middle/hook.mjs", (typeof document === 'undefin
|
|
|
23
23
|
var yamlConfig = chunk6YIDHH2S_cjs.loadYamlConfig();
|
|
24
24
|
var autoInstrumentationsEnv = process.env.AUTOTEL_INTEGRATIONS;
|
|
25
25
|
var autoInstrumentations = autoInstrumentationsEnv === "true" ? true : autoInstrumentationsEnv ? autoInstrumentationsEnv.split(",").map((s) => s.trim()) : yamlConfig?.autoInstrumentations ?? ["http", "express"];
|
|
26
|
-
|
|
26
|
+
chunkGIND746I_cjs.init({
|
|
27
27
|
service: yamlConfig?.service ?? process.env.OTEL_SERVICE_NAME ?? "unknown-service",
|
|
28
28
|
debug: yamlConfig?.debug ?? process.env.AUTOTEL_DEBUG === "true",
|
|
29
29
|
autoInstrumentations
|
package/dist/auto.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunk7ZYUFMWU_cjs = require('./chunk-7ZYUFMWU.cjs');
|
|
4
4
|
|
|
5
5
|
// src/semantic-helpers.ts
|
|
6
6
|
function traceLLM(config) {
|
|
7
7
|
return (fnFactory) => {
|
|
8
|
-
return
|
|
8
|
+
return chunk7ZYUFMWU_cjs.trace((ctx) => {
|
|
9
9
|
ctx.setAttribute("gen.ai.request.model", config.model);
|
|
10
10
|
ctx.setAttribute("gen.ai.operation.name", config.operation || "chat");
|
|
11
11
|
if (config.provider) {
|
|
@@ -25,7 +25,7 @@ function traceLLM(config) {
|
|
|
25
25
|
}
|
|
26
26
|
function traceDB(config) {
|
|
27
27
|
return (fnFactory) => {
|
|
28
|
-
return
|
|
28
|
+
return chunk7ZYUFMWU_cjs.trace((ctx) => {
|
|
29
29
|
ctx.setAttribute("db.system", config.system);
|
|
30
30
|
if (config.operation) {
|
|
31
31
|
ctx.setAttribute("db.operation", config.operation);
|
|
@@ -50,7 +50,7 @@ function traceDB(config) {
|
|
|
50
50
|
}
|
|
51
51
|
function traceHTTP(config) {
|
|
52
52
|
return (fnFactory) => {
|
|
53
|
-
return
|
|
53
|
+
return chunk7ZYUFMWU_cjs.trace((ctx) => {
|
|
54
54
|
if (config.method) {
|
|
55
55
|
ctx.setAttribute("http.request.method", config.method);
|
|
56
56
|
}
|
|
@@ -71,7 +71,7 @@ function traceHTTP(config) {
|
|
|
71
71
|
}
|
|
72
72
|
function traceMessaging(config) {
|
|
73
73
|
return (fnFactory) => {
|
|
74
|
-
return
|
|
74
|
+
return chunk7ZYUFMWU_cjs.trace((ctx) => {
|
|
75
75
|
ctx.setAttribute("messaging.system", config.system);
|
|
76
76
|
if (config.operation) {
|
|
77
77
|
ctx.setAttribute("messaging.operation", config.operation);
|
|
@@ -96,5 +96,5 @@ exports.traceDB = traceDB;
|
|
|
96
96
|
exports.traceHTTP = traceHTTP;
|
|
97
97
|
exports.traceLLM = traceLLM;
|
|
98
98
|
exports.traceMessaging = traceMessaging;
|
|
99
|
-
//# sourceMappingURL=chunk-
|
|
100
|
-
//# sourceMappingURL=chunk-
|
|
99
|
+
//# sourceMappingURL=chunk-73SCHI7V.cjs.map
|
|
100
|
+
//# sourceMappingURL=chunk-73SCHI7V.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/semantic-helpers.ts"],"names":["trace"],"mappings":"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","file":"chunk-CSG2D3BZ.cjs","sourcesContent":["/**\n * Semantic convention helpers for OpenTelemetry\n *\n * Pre-configured trace helpers that follow OpenTelemetry semantic conventions\n * for common operation types. Reduces boilerplate and ensures consistency.\n *\n * Based on: https://opentelemetry.io/docs/specs/semconv/\n */\n\nimport { trace } from './functional';\nimport type { TraceContext } from './trace-context';\nimport type { Attributes } from '@opentelemetry/api';\n\n/**\n * Configuration for LLM (Large Language Model) operations\n *\n * Follows Gen AI semantic conventions:\n * https://opentelemetry.io/docs/specs/semconv/gen-ai/\n */\nexport interface LLMConfig {\n /** Model name (e.g., 'gpt-4', 'claude-3-opus') */\n model: string;\n /** Operation type */\n operation?: 'chat' | 'completion' | 'embedding';\n /** Model provider (e.g., 'openai', 'anthropic', 'cohere') - maps to gen.ai.system */\n provider?: string;\n /** Additional attributes to add to the span */\n attributes?: Attributes;\n}\n\n/**\n * Configuration for database operations\n *\n * Follows DB semantic conventions:\n * https://opentelemetry.io/docs/specs/semconv/database/\n */\nexport interface DBConfig {\n /** Database system (e.g., 'postgresql', 'mongodb', 'redis') */\n system: string;\n /** Operation type (e.g., 'SELECT', 'INSERT', 'find', 'get') */\n operation?: string;\n /** Database name */\n database?: string;\n /** Collection/table name */\n collection?: string;\n /** Additional attributes to add to the span */\n attributes?: Attributes;\n}\n\n/**\n * Configuration for HTTP client operations\n *\n * Follows HTTP semantic conventions:\n * https://opentelemetry.io/docs/specs/semconv/http/\n */\nexport interface HTTPConfig {\n /** HTTP method (e.g., 'GET', 'POST') */\n method?: string;\n /** Target URL or URL template */\n url?: string;\n /** Additional attributes to add to the span */\n attributes?: Attributes;\n}\n\n/**\n * Configuration for messaging operations\n *\n * Follows Messaging semantic conventions:\n * https://opentelemetry.io/docs/specs/semconv/messaging/\n */\nexport interface MessagingConfig {\n /** Messaging system (e.g., 'kafka', 'rabbitmq', 'sqs') */\n system: string;\n /** Operation type */\n operation?: 'publish' | 'receive' | 'process';\n /** Destination name (queue/topic) */\n destination?: string;\n /** Additional attributes to add to the span */\n attributes?: Attributes;\n}\n\n/**\n * Trace LLM operations with Gen AI semantic conventions\n *\n * Automatically adds standard attributes for LLM operations:\n * - gen.ai.request.model\n * - gen.ai.operation.name\n * - gen.ai.system\n *\n * **Use Cases:**\n * - Chat completions\n * - Text generation\n * - Embeddings\n * - Multi-step LLM workflows\n *\n * @param config - LLM operation configuration\n * @returns Traced function factory with Gen AI attributes\n *\n * @example Chat completion with OpenAI\n * ```typescript\n * import { traceLLM } from 'autotel/semantic-helpers'\n * import OpenAI from 'openai'\n *\n * const openai = new OpenAI()\n *\n * export const generateResponse = traceLLM({\n * model: 'gpt-4-turbo',\n * operation: 'chat',\n * provider: 'openai'\n * })(ctx => async (prompt: string) => {\n * const response = await openai.chat.completions.create({\n * model: 'gpt-4-turbo',\n * messages: [{ role: 'user', content: prompt }]\n * })\n *\n * // Add usage metrics to span\n * ctx.setAttribute('gen.ai.usage.completion_tokens', response.usage?.completion_tokens)\n * ctx.setAttribute('gen.ai.usage.prompt_tokens', response.usage?.prompt_tokens)\n *\n * return response.choices[0].message.content\n * })\n * ```\n *\n * @example Anthropic Claude with streaming\n * ```typescript\n * import { traceLLM } from 'autotel/semantic-helpers'\n * import Anthropic from '@anthropic-ai/sdk'\n *\n * const anthropic = new Anthropic()\n *\n * export const streamResponse = traceLLM({\n * model: 'claude-3-opus-20240229',\n * operation: 'chat',\n * provider: 'anthropic'\n * })(ctx => async function* (prompt: string) {\n * const stream = await anthropic.messages.create({\n * model: 'claude-3-opus-20240229',\n * messages: [{ role: 'user', content: prompt }],\n * stream: true,\n * max_tokens: 1024\n * })\n *\n * let totalTokens = 0\n * for await (const event of stream) {\n * if (event.type === 'content_block_delta') {\n * yield event.delta.text\n * }\n * if (event.type === 'message_stop') {\n * ctx.setAttribute('gen.ai.usage.completion_tokens', event.message.usage.output_tokens)\n * totalTokens = event.message.usage.output_tokens\n * }\n * }\n *\n * return totalTokens\n * })\n * ```\n *\n * @example Embeddings\n * ```typescript\n * import { traceLLM } from 'autotel/semantic-helpers'\n * import { OpenAIEmbeddings } from '@langchain/openai'\n *\n * const embeddings = new OpenAIEmbeddings()\n *\n * export const embed = traceLLM({\n * model: 'text-embedding-3-small',\n * operation: 'embedding',\n * provider: 'openai'\n * })(ctx => async (text: string) => {\n * const result = await embeddings.embedQuery(text)\n * ctx.setAttribute('gen.ai.response.embedding_length', result.length)\n * return result\n * })\n * ```\n *\n * @public\n */\nexport function traceLLM<TArgs extends unknown[], TReturn>(config: LLMConfig) {\n return (\n fnFactory: (ctx: TraceContext) => (...args: TArgs) => Promise<TReturn>,\n ): ((...args: TArgs) => Promise<TReturn>) => {\n return trace<TArgs, TReturn>((ctx) => {\n // Set semantic convention attributes\n ctx.setAttribute('gen.ai.request.model', config.model);\n ctx.setAttribute('gen.ai.operation.name', config.operation || 'chat');\n if (config.provider) {\n ctx.setAttribute('gen.ai.system', config.provider);\n }\n if (config.attributes) {\n for (const [key, value] of Object.entries(config.attributes)) {\n if (value !== undefined && value !== null) {\n // setAttribute only accepts primitives (string | number | boolean)\n // Arrays and objects should be serialized\n const attrValue =\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ? value\n : JSON.stringify(value);\n ctx.setAttribute(key, attrValue);\n }\n }\n }\n\n // Call the user's factory to get their function and return it\n return fnFactory(ctx);\n });\n };\n}\n\n/**\n * Trace database operations with DB semantic conventions\n *\n * Automatically adds standard attributes for database operations:\n * - db.system\n * - db.operation\n * - db.name\n * - db.collection.name (for NoSQL)\n *\n * **Use Cases:**\n * - SQL queries (PostgreSQL, MySQL, SQLite)\n * - NoSQL operations (MongoDB, DynamoDB, Redis)\n * - ORM queries (Prisma, TypeORM, Drizzle)\n *\n * @param config - Database operation configuration\n * @returns Traced function factory with DB attributes\n *\n * @example PostgreSQL query\n * ```typescript\n * import { traceDB } from 'autotel/semantic-helpers'\n * import { pool } from './db'\n *\n * export const getUser = traceDB({\n * system: 'postgresql',\n * operation: 'SELECT',\n * database: 'app_db',\n * collection: 'users'\n * })(ctx => async (userId: string) => {\n * const query = 'SELECT * FROM users WHERE id = $1'\n * ctx.setAttribute('db.statement', query)\n *\n * const result = await pool.query(query, [userId])\n * ctx.setAttribute('db.rows_affected', result.rowCount)\n *\n * return result.rows[0]\n * })\n * ```\n *\n * @example MongoDB with Mongoose\n * ```typescript\n * import { traceDB } from 'autotel/semantic-helpers'\n * import { User } from './models/User'\n *\n * export const findUsers = traceDB({\n * system: 'mongodb',\n * operation: 'find',\n * database: 'app_db',\n * collection: 'users'\n * })(ctx => async (filter: object) => {\n * ctx.setAttribute('db.mongodb.filter', JSON.stringify(filter))\n *\n * const users = await User.find(filter).limit(100)\n * ctx.setAttribute('db.response.count', users.length)\n *\n * return users\n * })\n * ```\n *\n * @example Redis operations\n * ```typescript\n * import { traceDB } from 'autotel/semantic-helpers'\n * import { redis } from './redis'\n *\n * export const cacheGet = traceDB({\n * system: 'redis',\n * operation: 'GET'\n * })(ctx => async (key: string) => {\n * ctx.setAttribute('db.redis.key', key)\n *\n * const value = await redis.get(key)\n * ctx.setAttribute('db.response.cache_hit', value !== null)\n *\n * return value\n * })\n * ```\n *\n * @example Prisma with detailed query info\n * ```typescript\n * import { traceDB } from 'autotel/semantic-helpers'\n * import { prisma } from './prisma'\n *\n * export const createPost = traceDB({\n * system: 'postgresql',\n * operation: 'INSERT',\n * database: 'app_db',\n * collection: 'posts'\n * })(ctx => async (data: { title: string; content: string; authorId: string }) => {\n * ctx.setAttribute('db.prisma.model', 'Post')\n * ctx.setAttribute('db.prisma.action', 'create')\n *\n * const post = await prisma.post.create({ data })\n *\n * ctx.setAttribute('db.response.id', post.id)\n * return post\n * })\n * ```\n *\n * @public\n */\nexport function traceDB<TArgs extends unknown[], TReturn>(config: DBConfig) {\n return (\n fnFactory: (ctx: TraceContext) => (...args: TArgs) => Promise<TReturn>,\n ): ((...args: TArgs) => Promise<TReturn>) => {\n return trace<TArgs, TReturn>((ctx) => {\n // Set semantic convention attributes\n ctx.setAttribute('db.system', config.system);\n if (config.operation) {\n ctx.setAttribute('db.operation', config.operation);\n }\n if (config.database) {\n ctx.setAttribute('db.name', config.database);\n }\n if (config.collection) {\n ctx.setAttribute('db.collection.name', config.collection);\n }\n if (config.attributes) {\n for (const [key, value] of Object.entries(config.attributes)) {\n if (value !== undefined && value !== null) {\n // setAttribute only accepts primitives (string | number | boolean)\n // Arrays and objects should be serialized\n const attrValue =\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ? value\n : JSON.stringify(value);\n ctx.setAttribute(key, attrValue);\n }\n }\n }\n\n // Call the user's factory to get their function and return it\n return fnFactory(ctx);\n });\n };\n}\n\n/**\n * Trace HTTP client operations with HTTP semantic conventions\n *\n * Automatically adds standard attributes for HTTP requests:\n * - http.request.method\n * - url.full\n *\n * **Use Cases:**\n * - External API calls\n * - Microservice communication\n * - Third-party integrations\n *\n * @param config - HTTP operation configuration\n * @returns Traced function factory with HTTP attributes\n *\n * @example Fetch API\n * ```typescript\n * import { traceHTTP } from 'autotel/semantic-helpers'\n *\n * export const fetchUser = traceHTTP({\n * method: 'GET',\n * url: 'https://api.example.com/users/:id'\n * })(ctx => async (userId: string) => {\n * const url = `https://api.example.com/users/${userId}`\n * ctx.setAttribute('url.full', url)\n *\n * const response = await fetch(url)\n * ctx.setAttribute('http.response.status_code', response.status)\n *\n * if (!response.ok) {\n * ctx.setAttribute('error', true)\n * throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n * }\n *\n * return response.json()\n * })\n * ```\n *\n * @example Axios with retry logic\n * ```typescript\n * import { traceHTTP } from 'autotel/semantic-helpers'\n * import axios from 'axios'\n *\n * export const sendWebhook = traceHTTP({\n * method: 'POST',\n * url: 'https://webhook.example.com/events'\n * })(ctx => async (payload: object) => {\n * let attempts = 0\n * const maxAttempts = 3\n *\n * while (attempts < maxAttempts) {\n * try {\n * attempts++\n * ctx.setAttribute('http.request.resend_count', attempts - 1)\n *\n * const response = await axios.post('https://webhook.example.com/events', payload)\n * ctx.setAttribute('http.response.status_code', response.status)\n * return response.data\n * } catch (error) {\n * if (attempts >= maxAttempts) throw error\n * await new Promise(resolve => setTimeout(resolve, 1000 * attempts))\n * }\n * }\n * })\n * ```\n *\n * @public\n */\nexport function traceHTTP<TArgs extends unknown[], TReturn>(\n config: HTTPConfig,\n) {\n return (\n fnFactory: (ctx: TraceContext) => (...args: TArgs) => Promise<TReturn>,\n ): ((...args: TArgs) => Promise<TReturn>) => {\n return trace<TArgs, TReturn>((ctx) => {\n // Set semantic convention attributes\n if (config.method) {\n ctx.setAttribute('http.request.method', config.method);\n }\n if (config.url) {\n ctx.setAttribute('url.full', config.url);\n }\n if (config.attributes) {\n for (const [key, value] of Object.entries(config.attributes)) {\n if (value !== undefined && value !== null) {\n // setAttribute only accepts primitives (string | number | boolean)\n // Arrays and objects should be serialized\n const attrValue =\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ? value\n : JSON.stringify(value);\n ctx.setAttribute(key, attrValue);\n }\n }\n }\n\n // Call the user's factory to get their function and return it\n return fnFactory(ctx);\n });\n };\n}\n\n/**\n * Trace messaging operations with Messaging semantic conventions\n *\n * Automatically adds standard attributes for messaging:\n * - messaging.system\n * - messaging.operation\n * - messaging.destination.name\n *\n * **Use Cases:**\n * - Publishing messages to queues/topics\n * - Consuming messages from queues/topics\n * - Event-driven architectures\n *\n * @param config - Messaging operation configuration\n * @returns Traced function factory with Messaging attributes\n *\n * @example Publishing to Kafka\n * ```typescript\n * import { traceMessaging } from 'autotel/semantic-helpers'\n * import { kafka } from './kafka'\n *\n * const producer = kafka.producer()\n *\n * export const publishEvent = traceMessaging({\n * system: 'kafka',\n * operation: 'publish',\n * destination: 'user-events'\n * })(ctx => async (event: { type: string; userId: string; data: object }) => {\n * ctx.setAttribute('messaging.message.type', event.type)\n * ctx.setAttribute('messaging.kafka.partition', 0)\n *\n * await producer.send({\n * topic: 'user-events',\n * messages: [\n * {\n * key: event.userId,\n * value: JSON.stringify(event.data)\n * }\n * ]\n * })\n *\n * ctx.setAttribute('messaging.message.id', event.userId)\n * })\n * ```\n *\n * @example Consuming from RabbitMQ\n * ```typescript\n * import { traceMessaging } from 'autotel/semantic-helpers'\n * import { channel } from './rabbitmq'\n *\n * export const processOrder = traceMessaging({\n * system: 'rabbitmq',\n * operation: 'process',\n * destination: 'orders'\n * })(ctx => async (message: { orderId: string; items: object[] }) => {\n * ctx.setAttribute('messaging.message.id', message.orderId)\n * ctx.setAttribute('messaging.message.body.size', JSON.stringify(message).length)\n *\n * // Process order logic\n * const result = await processOrderInternal(message)\n *\n * ctx.setAttribute('messaging.operation.result', 'success')\n * return result\n * })\n * ```\n *\n * @example AWS SQS with batch processing\n * ```typescript\n * import { traceMessaging } from 'autotel/semantic-helpers'\n * import { SQS } from '@aws-sdk/client-sqs'\n *\n * const sqs = new SQS()\n *\n * export const sendBatch = traceMessaging({\n * system: 'aws_sqs',\n * operation: 'publish',\n * destination: 'notifications-queue'\n * })(ctx => async (messages: Array<{ id: string; body: object }>) => {\n * ctx.setAttribute('messaging.batch.message_count', messages.length)\n *\n * const result = await sqs.sendMessageBatch({\n * QueueUrl: process.env.QUEUE_URL,\n * Entries: messages.map(msg => ({\n * Id: msg.id,\n * MessageBody: JSON.stringify(msg.body)\n * }))\n * })\n *\n * ctx.setAttribute('messaging.operation.success_count', result.Successful?.length || 0)\n * ctx.setAttribute('messaging.operation.failed_count', result.Failed?.length || 0)\n *\n * return result\n * })\n * ```\n *\n * @public\n */\nexport function traceMessaging<TArgs extends unknown[], TReturn>(\n config: MessagingConfig,\n) {\n return (\n fnFactory: (ctx: TraceContext) => (...args: TArgs) => Promise<TReturn>,\n ): ((...args: TArgs) => Promise<TReturn>) => {\n return trace<TArgs, TReturn>((ctx) => {\n // Set semantic convention attributes\n ctx.setAttribute('messaging.system', config.system);\n if (config.operation) {\n ctx.setAttribute('messaging.operation', config.operation);\n }\n if (config.destination) {\n ctx.setAttribute('messaging.destination.name', config.destination);\n }\n if (config.attributes) {\n for (const [key, value] of Object.entries(config.attributes)) {\n if (value !== undefined && value !== null) {\n // setAttribute only accepts primitives (string | number | boolean)\n // Arrays and objects should be serialized\n const attrValue =\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ? value\n : JSON.stringify(value);\n ctx.setAttribute(key, attrValue);\n }\n }\n }\n\n // Call the user's factory to get their function and return it\n return fnFactory(ctx);\n });\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/semantic-helpers.ts"],"names":["trace"],"mappings":";;;;;AAiLO,SAAS,SAA2C,MAAA,EAAmB;AAC5E,EAAA,OAAO,CACL,SAAA,KAC2C;AAC3C,IAAA,OAAOA,uBAAA,CAAsB,CAAC,GAAA,KAAQ;AAEpC,MAAA,GAAA,CAAI,YAAA,CAAa,sBAAA,EAAwB,MAAA,CAAO,KAAK,CAAA;AACrD,MAAA,GAAA,CAAI,YAAA,CAAa,uBAAA,EAAyB,MAAA,CAAO,SAAA,IAAa,MAAM,CAAA;AACpE,MAAA,IAAI,OAAO,QAAA,EAAU;AACnB,QAAA,GAAA,CAAI,YAAA,CAAa,eAAA,EAAiB,MAAA,CAAO,QAAQ,CAAA;AAAA,MACnD;AACA,MAAA,IAAI,OAAO,UAAA,EAAY;AACrB,QAAA,KAAA,MAAW,CAAC,KAAK,KAAK,CAAA,IAAK,OAAO,OAAA,CAAQ,MAAA,CAAO,UAAU,CAAA,EAAG;AAC5D,UAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,KAAU,IAAA,EAAM;AAGzC,YAAA,MAAM,SAAA,GACJ,OAAO,KAAA,KAAU,QAAA,IACjB,OAAO,KAAA,KAAU,QAAA,IACjB,OAAO,KAAA,KAAU,SAAA,GACb,KAAA,GACA,IAAA,CAAK,UAAU,KAAK,CAAA;AAC1B,YAAA,GAAA,CAAI,YAAA,CAAa,KAAK,SAAS,CAAA;AAAA,UACjC;AAAA,QACF;AAAA,MACF;AAGA,MAAA,OAAO,UAAU,GAAG,CAAA;AAAA,IACtB,CAAC,CAAA;AAAA,EACH,CAAA;AACF;AAqGO,SAAS,QAA0C,MAAA,EAAkB;AAC1E,EAAA,OAAO,CACL,SAAA,KAC2C;AAC3C,IAAA,OAAOA,uBAAA,CAAsB,CAAC,GAAA,KAAQ;AAEpC,MAAA,GAAA,CAAI,YAAA,CAAa,WAAA,EAAa,MAAA,CAAO,MAAM,CAAA;AAC3C,MAAA,IAAI,OAAO,SAAA,EAAW;AACpB,QAAA,GAAA,CAAI,YAAA,CAAa,cAAA,EAAgB,MAAA,CAAO,SAAS,CAAA;AAAA,MACnD;AACA,MAAA,IAAI,OAAO,QAAA,EAAU;AACnB,QAAA,GAAA,CAAI,YAAA,CAAa,SAAA,EAAW,MAAA,CAAO,QAAQ,CAAA;AAAA,MAC7C;AACA,MAAA,IAAI,OAAO,UAAA,EAAY;AACrB,QAAA,GAAA,CAAI,YAAA,CAAa,oBAAA,EAAsB,MAAA,CAAO,UAAU,CAAA;AAAA,MAC1D;AACA,MAAA,IAAI,OAAO,UAAA,EAAY;AACrB,QAAA,KAAA,MAAW,CAAC,KAAK,KAAK,CAAA,IAAK,OAAO,OAAA,CAAQ,MAAA,CAAO,UAAU,CAAA,EAAG;AAC5D,UAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,KAAU,IAAA,EAAM;AAGzC,YAAA,MAAM,SAAA,GACJ,OAAO,KAAA,KAAU,QAAA,IACjB,OAAO,KAAA,KAAU,QAAA,IACjB,OAAO,KAAA,KAAU,SAAA,GACb,KAAA,GACA,IAAA,CAAK,UAAU,KAAK,CAAA;AAC1B,YAAA,GAAA,CAAI,YAAA,CAAa,KAAK,SAAS,CAAA;AAAA,UACjC;AAAA,QACF;AAAA,MACF;AAGA,MAAA,OAAO,UAAU,GAAG,CAAA;AAAA,IACtB,CAAC,CAAA;AAAA,EACH,CAAA;AACF;AAsEO,SAAS,UACd,MAAA,EACA;AACA,EAAA,OAAO,CACL,SAAA,KAC2C;AAC3C,IAAA,OAAOA,uBAAA,CAAsB,CAAC,GAAA,KAAQ;AAEpC,MAAA,IAAI,OAAO,MAAA,EAAQ;AACjB,QAAA,GAAA,CAAI,YAAA,CAAa,qBAAA,EAAuB,MAAA,CAAO,MAAM,CAAA;AAAA,MACvD;AACA,MAAA,IAAI,OAAO,GAAA,EAAK;AACd,QAAA,GAAA,CAAI,YAAA,CAAa,UAAA,EAAY,MAAA,CAAO,GAAG,CAAA;AAAA,MACzC;AACA,MAAA,IAAI,OAAO,UAAA,EAAY;AACrB,QAAA,KAAA,MAAW,CAAC,KAAK,KAAK,CAAA,IAAK,OAAO,OAAA,CAAQ,MAAA,CAAO,UAAU,CAAA,EAAG;AAC5D,UAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,KAAU,IAAA,EAAM;AAGzC,YAAA,MAAM,SAAA,GACJ,OAAO,KAAA,KAAU,QAAA,IACjB,OAAO,KAAA,KAAU,QAAA,IACjB,OAAO,KAAA,KAAU,SAAA,GACb,KAAA,GACA,IAAA,CAAK,UAAU,KAAK,CAAA;AAC1B,YAAA,GAAA,CAAI,YAAA,CAAa,KAAK,SAAS,CAAA;AAAA,UACjC;AAAA,QACF;AAAA,MACF;AAGA,MAAA,OAAO,UAAU,GAAG,CAAA;AAAA,IACtB,CAAC,CAAA;AAAA,EACH,CAAA;AACF;AAmGO,SAAS,eACd,MAAA,EACA;AACA,EAAA,OAAO,CACL,SAAA,KAC2C;AAC3C,IAAA,OAAOA,uBAAA,CAAsB,CAAC,GAAA,KAAQ;AAEpC,MAAA,GAAA,CAAI,YAAA,CAAa,kBAAA,EAAoB,MAAA,CAAO,MAAM,CAAA;AAClD,MAAA,IAAI,OAAO,SAAA,EAAW;AACpB,QAAA,GAAA,CAAI,YAAA,CAAa,qBAAA,EAAuB,MAAA,CAAO,SAAS,CAAA;AAAA,MAC1D;AACA,MAAA,IAAI,OAAO,WAAA,EAAa;AACtB,QAAA,GAAA,CAAI,YAAA,CAAa,4BAAA,EAA8B,MAAA,CAAO,WAAW,CAAA;AAAA,MACnE;AACA,MAAA,IAAI,OAAO,UAAA,EAAY;AACrB,QAAA,KAAA,MAAW,CAAC,KAAK,KAAK,CAAA,IAAK,OAAO,OAAA,CAAQ,MAAA,CAAO,UAAU,CAAA,EAAG;AAC5D,UAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,KAAU,IAAA,EAAM;AAGzC,YAAA,MAAM,SAAA,GACJ,OAAO,KAAA,KAAU,QAAA,IACjB,OAAO,KAAA,KAAU,QAAA,IACjB,OAAO,KAAA,KAAU,SAAA,GACb,KAAA,GACA,IAAA,CAAK,UAAU,KAAK,CAAA;AAC1B,YAAA,GAAA,CAAI,YAAA,CAAa,KAAK,SAAS,CAAA;AAAA,UACjC;AAAA,QACF;AAAA,MACF;AAGA,MAAA,OAAO,UAAU,GAAG,CAAA;AAAA,IACtB,CAAC,CAAA;AAAA,EACH,CAAA;AACF","file":"chunk-73SCHI7V.cjs","sourcesContent":["/**\n * Semantic convention helpers for OpenTelemetry\n *\n * Pre-configured trace helpers that follow OpenTelemetry semantic conventions\n * for common operation types. Reduces boilerplate and ensures consistency.\n *\n * Based on: https://opentelemetry.io/docs/specs/semconv/\n */\n\nimport { trace } from './functional';\nimport type { TraceContext } from './trace-context';\nimport type { Attributes } from '@opentelemetry/api';\n\n/**\n * Configuration for LLM (Large Language Model) operations\n *\n * Follows Gen AI semantic conventions:\n * https://opentelemetry.io/docs/specs/semconv/gen-ai/\n */\nexport interface LLMConfig {\n /** Model name (e.g., 'gpt-4', 'claude-3-opus') */\n model: string;\n /** Operation type */\n operation?: 'chat' | 'completion' | 'embedding';\n /** Model provider (e.g., 'openai', 'anthropic', 'cohere') - maps to gen.ai.system */\n provider?: string;\n /** Additional attributes to add to the span */\n attributes?: Attributes;\n}\n\n/**\n * Configuration for database operations\n *\n * Follows DB semantic conventions:\n * https://opentelemetry.io/docs/specs/semconv/database/\n */\nexport interface DBConfig {\n /** Database system (e.g., 'postgresql', 'mongodb', 'redis') */\n system: string;\n /** Operation type (e.g., 'SELECT', 'INSERT', 'find', 'get') */\n operation?: string;\n /** Database name */\n database?: string;\n /** Collection/table name */\n collection?: string;\n /** Additional attributes to add to the span */\n attributes?: Attributes;\n}\n\n/**\n * Configuration for HTTP client operations\n *\n * Follows HTTP semantic conventions:\n * https://opentelemetry.io/docs/specs/semconv/http/\n */\nexport interface HTTPConfig {\n /** HTTP method (e.g., 'GET', 'POST') */\n method?: string;\n /** Target URL or URL template */\n url?: string;\n /** Additional attributes to add to the span */\n attributes?: Attributes;\n}\n\n/**\n * Configuration for messaging operations\n *\n * Follows Messaging semantic conventions:\n * https://opentelemetry.io/docs/specs/semconv/messaging/\n */\nexport interface MessagingConfig {\n /** Messaging system (e.g., 'kafka', 'rabbitmq', 'sqs') */\n system: string;\n /** Operation type */\n operation?: 'publish' | 'receive' | 'process';\n /** Destination name (queue/topic) */\n destination?: string;\n /** Additional attributes to add to the span */\n attributes?: Attributes;\n}\n\n/**\n * Trace LLM operations with Gen AI semantic conventions\n *\n * Automatically adds standard attributes for LLM operations:\n * - gen.ai.request.model\n * - gen.ai.operation.name\n * - gen.ai.system\n *\n * **Use Cases:**\n * - Chat completions\n * - Text generation\n * - Embeddings\n * - Multi-step LLM workflows\n *\n * @param config - LLM operation configuration\n * @returns Traced function factory with Gen AI attributes\n *\n * @example Chat completion with OpenAI\n * ```typescript\n * import { traceLLM } from 'autotel/semantic-helpers'\n * import OpenAI from 'openai'\n *\n * const openai = new OpenAI()\n *\n * export const generateResponse = traceLLM({\n * model: 'gpt-4-turbo',\n * operation: 'chat',\n * provider: 'openai'\n * })(ctx => async (prompt: string) => {\n * const response = await openai.chat.completions.create({\n * model: 'gpt-4-turbo',\n * messages: [{ role: 'user', content: prompt }]\n * })\n *\n * // Add usage metrics to span\n * ctx.setAttribute('gen.ai.usage.completion_tokens', response.usage?.completion_tokens)\n * ctx.setAttribute('gen.ai.usage.prompt_tokens', response.usage?.prompt_tokens)\n *\n * return response.choices[0].message.content\n * })\n * ```\n *\n * @example Anthropic Claude with streaming\n * ```typescript\n * import { traceLLM } from 'autotel/semantic-helpers'\n * import Anthropic from '@anthropic-ai/sdk'\n *\n * const anthropic = new Anthropic()\n *\n * export const streamResponse = traceLLM({\n * model: 'claude-3-opus-20240229',\n * operation: 'chat',\n * provider: 'anthropic'\n * })(ctx => async function* (prompt: string) {\n * const stream = await anthropic.messages.create({\n * model: 'claude-3-opus-20240229',\n * messages: [{ role: 'user', content: prompt }],\n * stream: true,\n * max_tokens: 1024\n * })\n *\n * let totalTokens = 0\n * for await (const event of stream) {\n * if (event.type === 'content_block_delta') {\n * yield event.delta.text\n * }\n * if (event.type === 'message_stop') {\n * ctx.setAttribute('gen.ai.usage.completion_tokens', event.message.usage.output_tokens)\n * totalTokens = event.message.usage.output_tokens\n * }\n * }\n *\n * return totalTokens\n * })\n * ```\n *\n * @example Embeddings\n * ```typescript\n * import { traceLLM } from 'autotel/semantic-helpers'\n * import { OpenAIEmbeddings } from '@langchain/openai'\n *\n * const embeddings = new OpenAIEmbeddings()\n *\n * export const embed = traceLLM({\n * model: 'text-embedding-3-small',\n * operation: 'embedding',\n * provider: 'openai'\n * })(ctx => async (text: string) => {\n * const result = await embeddings.embedQuery(text)\n * ctx.setAttribute('gen.ai.response.embedding_length', result.length)\n * return result\n * })\n * ```\n *\n * @public\n */\nexport function traceLLM<TArgs extends unknown[], TReturn>(config: LLMConfig) {\n return (\n fnFactory: (ctx: TraceContext) => (...args: TArgs) => Promise<TReturn>,\n ): ((...args: TArgs) => Promise<TReturn>) => {\n return trace<TArgs, TReturn>((ctx) => {\n // Set semantic convention attributes\n ctx.setAttribute('gen.ai.request.model', config.model);\n ctx.setAttribute('gen.ai.operation.name', config.operation || 'chat');\n if (config.provider) {\n ctx.setAttribute('gen.ai.system', config.provider);\n }\n if (config.attributes) {\n for (const [key, value] of Object.entries(config.attributes)) {\n if (value !== undefined && value !== null) {\n // setAttribute only accepts primitives (string | number | boolean)\n // Arrays and objects should be serialized\n const attrValue =\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ? value\n : JSON.stringify(value);\n ctx.setAttribute(key, attrValue);\n }\n }\n }\n\n // Call the user's factory to get their function and return it\n return fnFactory(ctx);\n });\n };\n}\n\n/**\n * Trace database operations with DB semantic conventions\n *\n * Automatically adds standard attributes for database operations:\n * - db.system\n * - db.operation\n * - db.name\n * - db.collection.name (for NoSQL)\n *\n * **Use Cases:**\n * - SQL queries (PostgreSQL, MySQL, SQLite)\n * - NoSQL operations (MongoDB, DynamoDB, Redis)\n * - ORM queries (Prisma, TypeORM, Drizzle)\n *\n * @param config - Database operation configuration\n * @returns Traced function factory with DB attributes\n *\n * @example PostgreSQL query\n * ```typescript\n * import { traceDB } from 'autotel/semantic-helpers'\n * import { pool } from './db'\n *\n * export const getUser = traceDB({\n * system: 'postgresql',\n * operation: 'SELECT',\n * database: 'app_db',\n * collection: 'users'\n * })(ctx => async (userId: string) => {\n * const query = 'SELECT * FROM users WHERE id = $1'\n * ctx.setAttribute('db.statement', query)\n *\n * const result = await pool.query(query, [userId])\n * ctx.setAttribute('db.rows_affected', result.rowCount)\n *\n * return result.rows[0]\n * })\n * ```\n *\n * @example MongoDB with Mongoose\n * ```typescript\n * import { traceDB } from 'autotel/semantic-helpers'\n * import { User } from './models/User'\n *\n * export const findUsers = traceDB({\n * system: 'mongodb',\n * operation: 'find',\n * database: 'app_db',\n * collection: 'users'\n * })(ctx => async (filter: object) => {\n * ctx.setAttribute('db.mongodb.filter', JSON.stringify(filter))\n *\n * const users = await User.find(filter).limit(100)\n * ctx.setAttribute('db.response.count', users.length)\n *\n * return users\n * })\n * ```\n *\n * @example Redis operations\n * ```typescript\n * import { traceDB } from 'autotel/semantic-helpers'\n * import { redis } from './redis'\n *\n * export const cacheGet = traceDB({\n * system: 'redis',\n * operation: 'GET'\n * })(ctx => async (key: string) => {\n * ctx.setAttribute('db.redis.key', key)\n *\n * const value = await redis.get(key)\n * ctx.setAttribute('db.response.cache_hit', value !== null)\n *\n * return value\n * })\n * ```\n *\n * @example Prisma with detailed query info\n * ```typescript\n * import { traceDB } from 'autotel/semantic-helpers'\n * import { prisma } from './prisma'\n *\n * export const createPost = traceDB({\n * system: 'postgresql',\n * operation: 'INSERT',\n * database: 'app_db',\n * collection: 'posts'\n * })(ctx => async (data: { title: string; content: string; authorId: string }) => {\n * ctx.setAttribute('db.prisma.model', 'Post')\n * ctx.setAttribute('db.prisma.action', 'create')\n *\n * const post = await prisma.post.create({ data })\n *\n * ctx.setAttribute('db.response.id', post.id)\n * return post\n * })\n * ```\n *\n * @public\n */\nexport function traceDB<TArgs extends unknown[], TReturn>(config: DBConfig) {\n return (\n fnFactory: (ctx: TraceContext) => (...args: TArgs) => Promise<TReturn>,\n ): ((...args: TArgs) => Promise<TReturn>) => {\n return trace<TArgs, TReturn>((ctx) => {\n // Set semantic convention attributes\n ctx.setAttribute('db.system', config.system);\n if (config.operation) {\n ctx.setAttribute('db.operation', config.operation);\n }\n if (config.database) {\n ctx.setAttribute('db.name', config.database);\n }\n if (config.collection) {\n ctx.setAttribute('db.collection.name', config.collection);\n }\n if (config.attributes) {\n for (const [key, value] of Object.entries(config.attributes)) {\n if (value !== undefined && value !== null) {\n // setAttribute only accepts primitives (string | number | boolean)\n // Arrays and objects should be serialized\n const attrValue =\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ? value\n : JSON.stringify(value);\n ctx.setAttribute(key, attrValue);\n }\n }\n }\n\n // Call the user's factory to get their function and return it\n return fnFactory(ctx);\n });\n };\n}\n\n/**\n * Trace HTTP client operations with HTTP semantic conventions\n *\n * Automatically adds standard attributes for HTTP requests:\n * - http.request.method\n * - url.full\n *\n * **Use Cases:**\n * - External API calls\n * - Microservice communication\n * - Third-party integrations\n *\n * @param config - HTTP operation configuration\n * @returns Traced function factory with HTTP attributes\n *\n * @example Fetch API\n * ```typescript\n * import { traceHTTP } from 'autotel/semantic-helpers'\n *\n * export const fetchUser = traceHTTP({\n * method: 'GET',\n * url: 'https://api.example.com/users/:id'\n * })(ctx => async (userId: string) => {\n * const url = `https://api.example.com/users/${userId}`\n * ctx.setAttribute('url.full', url)\n *\n * const response = await fetch(url)\n * ctx.setAttribute('http.response.status_code', response.status)\n *\n * if (!response.ok) {\n * ctx.setAttribute('error', true)\n * throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n * }\n *\n * return response.json()\n * })\n * ```\n *\n * @example Axios with retry logic\n * ```typescript\n * import { traceHTTP } from 'autotel/semantic-helpers'\n * import axios from 'axios'\n *\n * export const sendWebhook = traceHTTP({\n * method: 'POST',\n * url: 'https://webhook.example.com/events'\n * })(ctx => async (payload: object) => {\n * let attempts = 0\n * const maxAttempts = 3\n *\n * while (attempts < maxAttempts) {\n * try {\n * attempts++\n * ctx.setAttribute('http.request.resend_count', attempts - 1)\n *\n * const response = await axios.post('https://webhook.example.com/events', payload)\n * ctx.setAttribute('http.response.status_code', response.status)\n * return response.data\n * } catch (error) {\n * if (attempts >= maxAttempts) throw error\n * await new Promise(resolve => setTimeout(resolve, 1000 * attempts))\n * }\n * }\n * })\n * ```\n *\n * @public\n */\nexport function traceHTTP<TArgs extends unknown[], TReturn>(\n config: HTTPConfig,\n) {\n return (\n fnFactory: (ctx: TraceContext) => (...args: TArgs) => Promise<TReturn>,\n ): ((...args: TArgs) => Promise<TReturn>) => {\n return trace<TArgs, TReturn>((ctx) => {\n // Set semantic convention attributes\n if (config.method) {\n ctx.setAttribute('http.request.method', config.method);\n }\n if (config.url) {\n ctx.setAttribute('url.full', config.url);\n }\n if (config.attributes) {\n for (const [key, value] of Object.entries(config.attributes)) {\n if (value !== undefined && value !== null) {\n // setAttribute only accepts primitives (string | number | boolean)\n // Arrays and objects should be serialized\n const attrValue =\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ? value\n : JSON.stringify(value);\n ctx.setAttribute(key, attrValue);\n }\n }\n }\n\n // Call the user's factory to get their function and return it\n return fnFactory(ctx);\n });\n };\n}\n\n/**\n * Trace messaging operations with Messaging semantic conventions\n *\n * Automatically adds standard attributes for messaging:\n * - messaging.system\n * - messaging.operation\n * - messaging.destination.name\n *\n * **Use Cases:**\n * - Publishing messages to queues/topics\n * - Consuming messages from queues/topics\n * - Event-driven architectures\n *\n * @param config - Messaging operation configuration\n * @returns Traced function factory with Messaging attributes\n *\n * @example Publishing to Kafka\n * ```typescript\n * import { traceMessaging } from 'autotel/semantic-helpers'\n * import { kafka } from './kafka'\n *\n * const producer = kafka.producer()\n *\n * export const publishEvent = traceMessaging({\n * system: 'kafka',\n * operation: 'publish',\n * destination: 'user-events'\n * })(ctx => async (event: { type: string; userId: string; data: object }) => {\n * ctx.setAttribute('messaging.message.type', event.type)\n * ctx.setAttribute('messaging.kafka.partition', 0)\n *\n * await producer.send({\n * topic: 'user-events',\n * messages: [\n * {\n * key: event.userId,\n * value: JSON.stringify(event.data)\n * }\n * ]\n * })\n *\n * ctx.setAttribute('messaging.message.id', event.userId)\n * })\n * ```\n *\n * @example Consuming from RabbitMQ\n * ```typescript\n * import { traceMessaging } from 'autotel/semantic-helpers'\n * import { channel } from './rabbitmq'\n *\n * export const processOrder = traceMessaging({\n * system: 'rabbitmq',\n * operation: 'process',\n * destination: 'orders'\n * })(ctx => async (message: { orderId: string; items: object[] }) => {\n * ctx.setAttribute('messaging.message.id', message.orderId)\n * ctx.setAttribute('messaging.message.body.size', JSON.stringify(message).length)\n *\n * // Process order logic\n * const result = await processOrderInternal(message)\n *\n * ctx.setAttribute('messaging.operation.result', 'success')\n * return result\n * })\n * ```\n *\n * @example AWS SQS with batch processing\n * ```typescript\n * import { traceMessaging } from 'autotel/semantic-helpers'\n * import { SQS } from '@aws-sdk/client-sqs'\n *\n * const sqs = new SQS()\n *\n * export const sendBatch = traceMessaging({\n * system: 'aws_sqs',\n * operation: 'publish',\n * destination: 'notifications-queue'\n * })(ctx => async (messages: Array<{ id: string; body: object }>) => {\n * ctx.setAttribute('messaging.batch.message_count', messages.length)\n *\n * const result = await sqs.sendMessageBatch({\n * QueueUrl: process.env.QUEUE_URL,\n * Entries: messages.map(msg => ({\n * Id: msg.id,\n * MessageBody: JSON.stringify(msg.body)\n * }))\n * })\n *\n * ctx.setAttribute('messaging.operation.success_count', result.Successful?.length || 0)\n * ctx.setAttribute('messaging.operation.failed_count', result.Failed?.length || 0)\n *\n * return result\n * })\n * ```\n *\n * @public\n */\nexport function traceMessaging<TArgs extends unknown[], TReturn>(\n config: MessagingConfig,\n) {\n return (\n fnFactory: (ctx: TraceContext) => (...args: TArgs) => Promise<TReturn>,\n ): ((...args: TArgs) => Promise<TReturn>) => {\n return trace<TArgs, TReturn>((ctx) => {\n // Set semantic convention attributes\n ctx.setAttribute('messaging.system', config.system);\n if (config.operation) {\n ctx.setAttribute('messaging.operation', config.operation);\n }\n if (config.destination) {\n ctx.setAttribute('messaging.destination.name', config.destination);\n }\n if (config.attributes) {\n for (const [key, value] of Object.entries(config.attributes)) {\n if (value !== undefined && value !== null) {\n // setAttribute only accepts primitives (string | number | boolean)\n // Arrays and objects should be serialized\n const attrValue =\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ? value\n : JSON.stringify(value);\n ctx.setAttribute(key, attrValue);\n }\n }\n }\n\n // Call the user's factory to get their function and return it\n return fnFactory(ctx);\n });\n };\n}\n"]}
|
|
@@ -295,6 +295,7 @@ var logger = silentLogger;
|
|
|
295
295
|
var validationConfig = null;
|
|
296
296
|
var eventsConfig = null;
|
|
297
297
|
var _stringRedactor = null;
|
|
298
|
+
var _optionalRequire = safeRequire;
|
|
298
299
|
function resolveMetricsFlag(configFlag = "auto") {
|
|
299
300
|
const envFlag = process.env.AUTOTEL_METRICS;
|
|
300
301
|
if (envFlag === "on" || envFlag === "true") return true;
|
|
@@ -543,7 +544,7 @@ function init(cfg) {
|
|
|
543
544
|
}
|
|
544
545
|
sdk.start();
|
|
545
546
|
if (mergedConfig.openllmetry?.enabled) {
|
|
546
|
-
const traceloop =
|
|
547
|
+
const traceloop = _optionalRequire("@traceloop/node-server-sdk");
|
|
547
548
|
if (traceloop) {
|
|
548
549
|
const initOptions = {
|
|
549
550
|
...mergedConfig.openllmetry.options
|
|
@@ -743,5 +744,5 @@ function getSdk() {
|
|
|
743
744
|
}
|
|
744
745
|
|
|
745
746
|
export { BaggageSpanProcessor, createStringRedactor, getConfig, getEventsConfig, getLogger, getSdk, getValidationConfig, init, isInitialized, warnIfNotInitialized };
|
|
746
|
-
//# sourceMappingURL=chunk-
|
|
747
|
-
//# sourceMappingURL=chunk-
|
|
747
|
+
//# sourceMappingURL=chunk-7RV6L24E.js.map
|
|
748
|
+
//# sourceMappingURL=chunk-7RV6L24E.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/baggage-span-processor.ts","../src/redact-values.ts","../src/posthog-logs.ts","../src/env-config.ts","../src/init.ts"],"names":["otelContext","config","OTLPTraceExporterHTTP","OTLPMetricExporterHTTP"],"mappings":"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","file":"chunk-7RV6L24E.js","sourcesContent":["/**\n * Span processor that copies baggage entries to span attributes\n *\n * This makes baggage visible in trace UIs without manual attribute setting.\n * Enabled via init({ baggage: true }) or init({ baggage: 'custom-prefix' })\n */\n\nimport type { Span, Context } from '@opentelemetry/api';\nimport { propagation, context as otelContext } from '@opentelemetry/api';\nimport type { SpanProcessor } from '@opentelemetry/sdk-trace-base';\nimport type { ReadableSpan } from '@opentelemetry/sdk-trace-base';\nimport { requireModule } from './node-require';\n\nexport interface BaggageSpanProcessorOptions {\n /**\n * Prefix for baggage attributes\n * @default 'baggage.'\n */\n prefix?: string;\n}\n\n/**\n * Span processor that automatically copies baggage entries to span attributes\n *\n * This makes baggage visible in trace UIs (Jaeger, Grafana, DataDog, etc.)\n * without manually calling ctx.setAttribute() for each baggage entry.\n *\n * @example Enable in init()\n * ```typescript\n * init({\n * service: 'my-app',\n * baggage: true // Uses default 'baggage.' prefix\n * });\n *\n * // Now baggage automatically appears as span attributes\n * await withBaggage({\n * baggage: { 'tenant.id': 't1', 'user.id': 'u1' },\n * fn: async () => {\n * // Span has baggage.tenant.id and baggage.user.id attributes!\n * }\n * });\n * ```\n *\n * @example Custom prefix\n * ```typescript\n * init({\n * service: 'my-app',\n * baggage: 'ctx' // Uses 'ctx.' prefix\n * });\n * // Creates attributes: ctx.tenant.id, ctx.user.id\n * ```\n */\nexport class BaggageSpanProcessor implements SpanProcessor {\n private readonly prefix: string;\n\n constructor(options: BaggageSpanProcessorOptions = {}) {\n this.prefix = options.prefix ?? 'baggage.';\n }\n\n onStart(span: Span, parentContext: Context): void {\n // Read baggage from parentContext first (spans created with explicit context)\n // Then fall back to active context (spans created without explicit context)\n // Also check getActiveContextWithBaggage() to see baggage set via ctx.setBaggage()\n let baggage = propagation.getBaggage(parentContext);\n if (!baggage) {\n baggage = propagation.getBaggage(otelContext.active());\n }\n // Check stored context from ctx.setBaggage() if still no baggage\n if (!baggage) {\n try {\n const { getActiveContextWithBaggage } = requireModule<{\n getActiveContextWithBaggage: () => Context;\n }>('./trace-context');\n const storedContext = getActiveContextWithBaggage();\n baggage = propagation.getBaggage(storedContext);\n } catch {\n // Fallback if trace-context isn't available\n }\n }\n if (!baggage) return;\n\n // Copy all baggage entries to span attributes\n for (const [key, entry] of baggage.getAllEntries()) {\n span.setAttribute(`${this.prefix}${key}`, entry.value);\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onEnd(_span: ReadableSpan): void {\n // No-op - required by SpanProcessor interface\n }\n\n async shutdown(): Promise<void> {\n // No-op\n }\n\n async forceFlush(): Promise<void> {\n // No-op\n }\n}\n","/** Standalone string redaction for use outside the span processor pipeline. */\n\nimport {\n REDACTOR_PRESETS,\n type AttributeRedactorConfig,\n type AttributeRedactorPreset,\n type ValuePatternConfig,\n} from './attribute-redacting-processor';\n\nexport type StringRedactor = (value: string) => string;\nexport function createStringRedactor(\n config: AttributeRedactorConfig | AttributeRedactorPreset,\n): StringRedactor {\n const resolved =\n typeof config === 'string' ? REDACTOR_PRESETS[config] : config;\n const valuePatterns: ValuePatternConfig[] = resolved.valuePatterns ?? [];\n const defaultReplacement = resolved.replacement ?? '[REDACTED]';\n\n return (value: string): string => {\n let result = value;\n for (const { pattern, replacement } of valuePatterns) {\n pattern.lastIndex = 0;\n result = result.replaceAll(pattern, replacement ?? defaultReplacement);\n }\n return result;\n };\n}\n","import type { LogRecordProcessor } from '@opentelemetry/sdk-logs';\nimport { safeRequire } from './node-require';\nimport type { StringRedactor } from './redact-values';\n\nclass RedactingLogRecordProcessor implements LogRecordProcessor {\n constructor(\n private wrapped: LogRecordProcessor,\n private redact: StringRedactor,\n ) {}\n\n onEmit(logRecord: any, context?: any): void {\n if (logRecord.body && typeof logRecord.body === 'string') {\n logRecord.body = this.redact(logRecord.body);\n }\n if (logRecord.attributes) {\n for (const [key, value] of Object.entries(logRecord.attributes)) {\n if (typeof value === 'string') {\n logRecord.attributes[key] = this.redact(value);\n } else if (Array.isArray(value)) {\n logRecord.attributes[key] = value.map((item: unknown) =>\n typeof item === 'string' ? this.redact(item) : item,\n );\n }\n }\n }\n this.wrapped.onEmit(logRecord, context);\n }\n\n shutdown(): Promise<void> {\n return this.wrapped.shutdown();\n }\n\n forceFlush(): Promise<void> {\n return this.wrapped.forceFlush();\n }\n}\n\nexport interface PostHogConfig {\n /** OTLP logs endpoint URL (e.g., https://us.i.posthog.com/i/v1/logs?token=phc_xxx) */\n url: string;\n}\n\n/**\n * Build log record processors for PostHog OTLP logs integration.\n *\n * Resolution order:\n * 1. config.url if provided\n * 2. POSTHOG_LOGS_URL env var\n * 3. Empty array (disabled)\n */\nexport function buildPostHogLogProcessors(\n config: PostHogConfig | undefined,\n stringRedactor?: StringRedactor | null,\n): LogRecordProcessor[] {\n const url = config?.url || process.env.POSTHOG_LOGS_URL;\n if (!url) return [];\n\n const sdkLogs = safeRequire<{\n BatchLogRecordProcessor: new (exporter: unknown) => LogRecordProcessor;\n }>('@opentelemetry/sdk-logs');\n\n const exporterModule = safeRequire<{\n OTLPLogExporter: new (config: { url: string }) => unknown;\n }>('@opentelemetry/exporter-logs-otlp-http');\n\n if (!sdkLogs || !exporterModule) return [];\n\n const exporter = new exporterModule.OTLPLogExporter({ url });\n let processor: LogRecordProcessor = new sdkLogs.BatchLogRecordProcessor(\n exporter,\n );\n if (stringRedactor) {\n processor = new RedactingLogRecordProcessor(processor, stringRedactor);\n }\n\n return [processor];\n}\n","/**\n * Standard OpenTelemetry environment variables\n */\nexport interface OtelEnvVars {\n OTEL_SERVICE_NAME?: string;\n OTEL_EXPORTER_OTLP_ENDPOINT?: string;\n OTEL_EXPORTER_OTLP_HEADERS?: string;\n OTEL_RESOURCE_ATTRIBUTES?: string;\n OTEL_EXPORTER_OTLP_PROTOCOL?: 'http' | 'grpc';\n}\n\n/**\n * Parsed resource attributes as key-value pairs\n */\nexport interface ResourceAttributes {\n [key: string]: string;\n}\n\n/**\n * Parsed OTLP headers as key-value pairs\n */\nexport interface OtlpHeaders {\n [key: string]: string;\n}\n\n/**\n * Environment-resolved configuration (subset of AutotelConfig)\n * Defined locally to avoid circular dependency with init.ts\n */\nexport interface EnvConfig {\n service?: string;\n endpoint?: string;\n protocol?: 'http' | 'grpc';\n headers?: Record<string, string>;\n resourceAttributes?: Record<string, string>;\n}\n\n/**\n * Validate URL format\n */\nfunction isValidUrl(urlString: string): boolean {\n try {\n const url = new URL(urlString);\n return url.protocol === 'http:' || url.protocol === 'https:';\n } catch {\n return false;\n }\n}\n\n/**\n * Resolve OpenTelemetry environment variables from process.env\n */\nexport function resolveOtelEnv(): OtelEnvVars {\n const env: OtelEnvVars = {};\n\n // OTEL_SERVICE_NAME - optional string\n if (process.env.OTEL_SERVICE_NAME) {\n const value = process.env.OTEL_SERVICE_NAME.trim();\n if (value) {\n env.OTEL_SERVICE_NAME = value;\n }\n }\n\n // OTEL_EXPORTER_OTLP_ENDPOINT - optional URL\n if (process.env.OTEL_EXPORTER_OTLP_ENDPOINT) {\n const value = process.env.OTEL_EXPORTER_OTLP_ENDPOINT.trim();\n if (value && isValidUrl(value)) {\n env.OTEL_EXPORTER_OTLP_ENDPOINT = value;\n }\n }\n\n // OTEL_EXPORTER_OTLP_HEADERS - optional string\n if (process.env.OTEL_EXPORTER_OTLP_HEADERS) {\n const value = process.env.OTEL_EXPORTER_OTLP_HEADERS.trim();\n if (value) {\n env.OTEL_EXPORTER_OTLP_HEADERS = value;\n }\n }\n\n // OTEL_RESOURCE_ATTRIBUTES - optional string\n if (process.env.OTEL_RESOURCE_ATTRIBUTES) {\n const value = process.env.OTEL_RESOURCE_ATTRIBUTES.trim();\n if (value) {\n env.OTEL_RESOURCE_ATTRIBUTES = value;\n }\n }\n\n // OTEL_EXPORTER_OTLP_PROTOCOL - optional enum ('http' | 'grpc')\n if (process.env.OTEL_EXPORTER_OTLP_PROTOCOL) {\n const value = process.env.OTEL_EXPORTER_OTLP_PROTOCOL.trim().toLowerCase();\n if (value === 'http' || value === 'grpc') {\n env.OTEL_EXPORTER_OTLP_PROTOCOL = value;\n }\n }\n\n return env;\n}\n\n/**\n * Parse OTEL_RESOURCE_ATTRIBUTES from comma-separated key=value pairs\n * Example: \"service.version=1.0.0,deployment.environment=production\"\n */\nexport function parseResourceAttributes(\n input: string | undefined,\n): ResourceAttributes {\n if (!input || input.trim() === '') {\n return {};\n }\n\n const attributes: ResourceAttributes = {};\n const pairs = input.split(',');\n\n for (const pair of pairs) {\n const trimmedPair = pair.trim();\n if (!trimmedPair) continue;\n\n const equalIndex = trimmedPair.indexOf('=');\n if (equalIndex === -1) {\n // Invalid format, skip this pair\n continue;\n }\n\n const key = trimmedPair.slice(0, equalIndex).trim();\n const value = trimmedPair.slice(equalIndex + 1).trim();\n\n if (key && value) {\n attributes[key] = value;\n }\n }\n\n return attributes;\n}\n\n/**\n * Parse OTEL_EXPORTER_OTLP_HEADERS from comma-separated key=value pairs\n * Example: \"api-key=secret123,x-custom-header=value\"\n */\nexport function parseOtlpHeaders(input: string | undefined): OtlpHeaders {\n if (!input || input.trim() === '') {\n return {};\n }\n\n const headers: OtlpHeaders = {};\n const pairs = input.split(',');\n\n for (const pair of pairs) {\n const trimmedPair = pair.trim();\n if (!trimmedPair) continue;\n\n const equalIndex = trimmedPair.indexOf('=');\n if (equalIndex === -1) {\n // Invalid format, skip this pair\n continue;\n }\n\n const key = trimmedPair.slice(0, equalIndex).trim();\n const value = trimmedPair.slice(equalIndex + 1).trim();\n\n if (key && value) {\n headers[key] = value;\n }\n }\n\n return headers;\n}\n\n/**\n * Convert resolved environment variables to config\n */\nexport function envToConfig(env: OtelEnvVars): EnvConfig {\n const config: EnvConfig = {};\n\n if (env.OTEL_SERVICE_NAME) {\n config.service = env.OTEL_SERVICE_NAME;\n }\n\n if (env.OTEL_EXPORTER_OTLP_ENDPOINT) {\n config.endpoint = env.OTEL_EXPORTER_OTLP_ENDPOINT;\n }\n\n if (env.OTEL_EXPORTER_OTLP_PROTOCOL) {\n config.protocol = env.OTEL_EXPORTER_OTLP_PROTOCOL;\n }\n\n if (env.OTEL_EXPORTER_OTLP_HEADERS) {\n config.headers = parseOtlpHeaders(env.OTEL_EXPORTER_OTLP_HEADERS);\n }\n\n const resourceAttrs = parseResourceAttributes(env.OTEL_RESOURCE_ATTRIBUTES);\n if (Object.keys(resourceAttrs).length > 0) {\n config.resourceAttributes = resourceAttrs;\n }\n\n return config;\n}\n\n/**\n * Main function to resolve config from environment variables\n */\nexport function resolveConfigFromEnv(): EnvConfig {\n const env = resolveOtelEnv();\n return envToConfig(env);\n}\n","/**\n * Simplified initialization for autotel\n *\n * Single init() function with sensible defaults.\n * Replaces initInstrumentation() and separate events config.\n */\n\nimport { NodeSDK } from '@opentelemetry/sdk-node';\nimport type { NodeSDKConfiguration } from '@opentelemetry/sdk-node';\nimport {\n BatchSpanProcessor,\n type SpanProcessor,\n SimpleSpanProcessor,\n ConsoleSpanExporter,\n} from '@opentelemetry/sdk-trace-base';\nimport type { SpanExporter } from '@opentelemetry/sdk-trace-base';\nimport {\n resourceFromAttributes,\n type Resource,\n} from '@opentelemetry/resources';\nimport {\n ATTR_SERVICE_NAME,\n ATTR_SERVICE_VERSION,\n} from '@opentelemetry/semantic-conventions';\nimport type { Sampler } from './sampling';\nimport { AdaptiveSampler } from './sampling';\nimport type { EventSubscriber } from './event-subscriber';\nimport type { Logger } from './logger';\nimport type { Attributes } from '@opentelemetry/api';\nimport type { ValidationConfig } from './validation';\nimport {\n PeriodicExportingMetricReader,\n type MetricReader,\n} from '@opentelemetry/sdk-metrics';\nimport { OTLPMetricExporter as OTLPMetricExporterHTTP } from '@opentelemetry/exporter-metrics-otlp-http';\nimport { OTLPTraceExporter as OTLPTraceExporterHTTP } from '@opentelemetry/exporter-trace-otlp-http';\nimport type { PushMetricExporter } from '@opentelemetry/sdk-metrics';\nimport type { LogRecordProcessor } from '@opentelemetry/sdk-logs';\nimport { buildPostHogLogProcessors } from './posthog-logs';\nimport { TailSamplingSpanProcessor } from './tail-sampling-processor';\nimport { BaggageSpanProcessor } from './baggage-span-processor';\nimport {\n FilteringSpanProcessor,\n type SpanFilterPredicate,\n} from './filtering-span-processor';\nimport {\n SpanNameNormalizingProcessor,\n type SpanNameNormalizerConfig,\n} from './span-name-normalizer';\nimport {\n AttributeRedactingProcessor,\n type AttributeRedactorConfig,\n type AttributeRedactorPreset,\n} from './attribute-redacting-processor';\nimport { createStringRedactor, type StringRedactor } from './redact-values';\nimport { PrettyConsoleExporter } from './pretty-console-exporter';\nimport { resolveConfigFromEnv } from './env-config';\nimport { loadYamlConfig } from './yaml-config';\nimport { requireModule, safeRequire } from './node-require';\nimport {\n CanonicalLogLineProcessor,\n type CanonicalLogLineOptions,\n} from './processors/canonical-log-line-processor';\nimport type { EventsConfig } from './events-config';\n\n/**\n * Silent logger (no-op) - used as default when user doesn't provide one.\n * Internal autotel logs are silent by default to avoid spam.\n * Users can import { autotelLogger } from 'autotel/logger' to create their own.\n */\nconst silentLogger: Logger = {\n info: () => {},\n warn: () => {},\n error: () => {},\n debug: () => {},\n};\n\n// Type imports for exporters\ntype OTLPExporterConfig = {\n url?: string;\n headers?: Record<string, string>;\n timeoutMillis?: number;\n concurrencyLimit?: number;\n};\n\n// Lazy-load gRPC exporters (optional peer dependencies)\nlet OTLPTraceExporterGRPC:\n | (new (config: OTLPExporterConfig) => SpanExporter)\n | undefined;\nlet OTLPMetricExporterGRPC:\n | (new (config: OTLPExporterConfig) => PushMetricExporter)\n | undefined;\n\n/**\n * Helper: Lazy-load gRPC trace exporter\n */\nfunction loadGRPCTraceExporter(): new (\n config: OTLPExporterConfig,\n) => SpanExporter {\n if (OTLPTraceExporterGRPC) return OTLPTraceExporterGRPC;\n\n try {\n // Dynamic import for optional peer dependency\n const grpcModule = requireModule<{\n OTLPTraceExporter: new (config: OTLPExporterConfig) => SpanExporter;\n }>('@opentelemetry/exporter-trace-otlp-grpc');\n OTLPTraceExporterGRPC = grpcModule.OTLPTraceExporter;\n return OTLPTraceExporterGRPC;\n } catch {\n throw new Error(\n 'gRPC trace exporter not found. Install @opentelemetry/exporter-trace-otlp-grpc',\n );\n }\n}\n\n/**\n * Helper: Lazy-load gRPC metric exporter\n */\nfunction loadGRPCMetricExporter(): new (\n config: OTLPExporterConfig,\n) => PushMetricExporter {\n if (OTLPMetricExporterGRPC) return OTLPMetricExporterGRPC;\n\n try {\n // Dynamic import for optional peer dependency\n const grpcModule = requireModule<{\n OTLPMetricExporter: new (\n config: OTLPExporterConfig,\n ) => PushMetricExporter;\n }>('@opentelemetry/exporter-metrics-otlp-grpc');\n OTLPMetricExporterGRPC = grpcModule.OTLPMetricExporter;\n return OTLPMetricExporterGRPC;\n } catch {\n throw new Error(\n 'gRPC metric exporter not found. Install @opentelemetry/exporter-metrics-otlp-grpc',\n );\n }\n}\n\n/**\n * Helper: Create trace exporter based on protocol\n */\nfunction createTraceExporter(\n protocol: 'http' | 'grpc',\n config: OTLPExporterConfig,\n): SpanExporter {\n if (protocol === 'grpc') {\n const Exporter = loadGRPCTraceExporter();\n return new Exporter(config);\n }\n\n // Default: HTTP\n return new OTLPTraceExporterHTTP(config);\n}\n\n/**\n * Helper: Create metric exporter based on protocol\n */\nfunction createMetricExporter(\n protocol: 'http' | 'grpc',\n config: OTLPExporterConfig,\n): PushMetricExporter {\n if (protocol === 'grpc') {\n const Exporter = loadGRPCMetricExporter();\n return new Exporter(config);\n }\n\n // Default: HTTP\n return new OTLPMetricExporterHTTP(config);\n}\n\n/**\n * Helper: Resolve protocol from config and environment\n */\nfunction resolveProtocol(configProtocol?: 'http' | 'grpc'): 'http' | 'grpc' {\n // 1. Check config parameter (highest priority)\n if (configProtocol === 'grpc' || configProtocol === 'http') {\n return configProtocol;\n }\n\n // 2. Check OTEL_EXPORTER_OTLP_PROTOCOL env var\n const envProtocol = process.env.OTEL_EXPORTER_OTLP_PROTOCOL;\n if (envProtocol === 'grpc') return 'grpc';\n if (envProtocol === 'http/protobuf' || envProtocol === 'http') return 'http';\n\n // 3. Default to HTTP\n return 'http';\n}\n\n/**\n * Helper: Adjust endpoint URL for protocol\n * gRPC exporters don't need the /v1/traces or /v1/metrics path\n * HTTP exporters need the full path\n */\nfunction formatEndpointUrl(\n endpoint: string,\n signal: 'traces' | 'metrics',\n protocol: 'http' | 'grpc',\n): string {\n if (protocol === 'grpc') {\n // gRPC: strip any paths, return base endpoint\n return endpoint.replace(/\\/(v1\\/)?(traces|metrics|logs)$/, '');\n }\n\n // HTTP: append signal path if not present\n if (!endpoint.endsWith(`/v1/${signal}`)) {\n return `${endpoint}/v1/${signal}`;\n }\n\n return endpoint;\n}\n\n// Built-in logger is created dynamically in init() with service name\n\nexport interface AutotelConfig {\n /** Service name (required) */\n service: string;\n\n /** Event subscribers - bring your own (PostHog, Mixpanel, etc.) */\n subscribers?: EventSubscriber[];\n\n /**\n * Additional OpenTelemetry instrumentations to register (raw OTel classes).\n * Useful when you need custom instrumentation configs or instrumentations\n * not covered by autoInstrumentations.\n *\n * **Important:** If you need custom instrumentation configs (like `requireParentSpan: false`),\n * use EITHER manual instrumentations OR autoInstrumentations, not both for the same library.\n * Manual instrumentations always take precedence over auto-instrumentations.\n *\n * @example Manual instrumentations with custom config\n * ```typescript\n * import { MongoDBInstrumentation } from '@opentelemetry/instrumentation-mongodb'\n *\n * init({\n * service: 'my-app',\n * autoInstrumentations: false, // Disable auto-instrumentations\n * instrumentations: [\n * new MongoDBInstrumentation({\n * requireParentSpan: false // Custom config\n * })\n * ]\n * })\n * ```\n *\n * @example Mix auto + manual (auto for most, manual for specific configs)\n * ```typescript\n * import { MongoDBInstrumentation } from '@opentelemetry/instrumentation-mongodb'\n *\n * init({\n * service: 'my-app',\n * autoInstrumentations: ['http', 'express'], // Auto for these\n * instrumentations: [\n * new MongoDBInstrumentation({\n * requireParentSpan: false // Manual config for MongoDB\n * })\n * ]\n * })\n * ```\n */\n instrumentations?: NodeSDKConfiguration['instrumentations'];\n\n /**\n * Simple names for auto-instrumentation.\n * Uses @opentelemetry/auto-instrumentations-node (peer dependency).\n *\n * **Important:** If you provide manual instrumentations for the same library,\n * the manual config takes precedence and auto-instrumentation for that library is disabled.\n *\n * @example Enable all auto-instrumentations (simple approach)\n * ```typescript\n * init({\n * service: 'my-app',\n * autoInstrumentations: true // Enable all with defaults\n * })\n * ```\n *\n * @example Enable specific auto-instrumentations\n * ```typescript\n * init({\n * service: 'my-app',\n * autoInstrumentations: ['express', 'pino', 'http']\n * })\n * ```\n *\n * @example Configure specific auto-instrumentations\n * ```typescript\n * init({\n * service: 'my-app',\n * autoInstrumentations: {\n * express: { enabled: true },\n * pino: { enabled: true },\n * http: { enabled: false }\n * }\n * })\n * ```\n *\n * @example Manual config when you need custom settings\n * ```typescript\n * import { MongoDBInstrumentation } from '@opentelemetry/instrumentation-mongodb'\n *\n * init({\n * service: 'my-app',\n * autoInstrumentations: false, // Use manual control\n * instrumentations: [\n * new MongoDBInstrumentation({\n * requireParentSpan: false // Custom config not available with auto\n * })\n * ]\n * })\n * ```\n */\n autoInstrumentations?:\n | string[]\n | boolean\n | Record<string, { enabled?: boolean }>;\n\n /**\n * OTLP endpoint for traces/metrics/logs\n * Only used if you don't provide custom exporters/processors\n * @default process.env.OTLP_ENDPOINT || 'http://localhost:4318'\n */\n endpoint?: string;\n\n /**\n * Custom span processors for traces (supports multiple processors)\n * Allows you to use any backend: Jaeger, Zipkin, Datadog, New Relic, etc.\n * If not provided, defaults to OTLP with tail sampling\n *\n * @example Multiple processors\n * ```typescript\n * import { JaegerExporter } from '@opentelemetry/exporter-jaeger'\n * import { BatchSpanProcessor, SimpleSpanProcessor, ConsoleSpanExporter } from '@opentelemetry/sdk-trace-base'\n *\n * init({\n * service: 'my-app',\n * spanProcessors: [\n * new BatchSpanProcessor(new JaegerExporter()),\n * new SimpleSpanProcessor(new ConsoleSpanExporter()) // Debug alongside production\n * ]\n * })\n * ```\n *\n * @example Single processor\n * ```typescript\n * import { ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'\n *\n * init({\n * service: 'my-app',\n * spanProcessors: [new SimpleSpanProcessor(new ConsoleSpanExporter())]\n * })\n * ```\n */\n spanProcessors?: SpanProcessor[];\n\n /**\n * Custom span exporters for traces (alternative to spanProcessors, supports multiple exporters)\n * Provide either spanProcessors OR spanExporters, not both\n * Each exporter will be wrapped in TailSamplingSpanProcessor + BatchSpanProcessor\n *\n * @example Multiple exporters\n * ```typescript\n * import { ZipkinExporter } from '@opentelemetry/exporter-zipkin'\n * import { JaegerExporter } from '@opentelemetry/exporter-jaeger'\n *\n * init({\n * service: 'my-app',\n * spanExporters: [\n * new ZipkinExporter({ url: 'http://localhost:9411/api/v2/spans' }),\n * new JaegerExporter() // Send to multiple backends simultaneously\n * ]\n * })\n * ```\n *\n * @example Single exporter\n * ```typescript\n * import { ZipkinExporter } from '@opentelemetry/exporter-zipkin'\n *\n * init({\n * service: 'my-app',\n * spanExporters: [new ZipkinExporter({ url: 'http://localhost:9411/api/v2/spans' })]\n * })\n * ```\n */\n spanExporters?: SpanExporter[];\n\n /**\n * Custom metric readers (supports multiple readers)\n * Allows sending metrics to multiple backends: OTLP, Prometheus, custom readers\n * Defaults to OTLP metrics exporter when metrics are enabled.\n *\n * @example Multiple metric readers\n * ```typescript\n * import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics'\n * import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http'\n * import { PrometheusExporter } from '@opentelemetry/exporter-prometheus'\n *\n * init({\n * service: 'my-app',\n * metricReaders: [\n * new PeriodicExportingMetricReader({ exporter: new OTLPMetricExporter() }),\n * new PrometheusExporter() // Export to multiple backends\n * ]\n * })\n * ```\n */\n metricReaders?: MetricReader[];\n\n /**\n * Custom log record processors. When omitted, logs are not configured.\n */\n logRecordProcessors?: LogRecordProcessor[];\n\n /**\n * PostHog integration - auto-configures OTLP log exporter.\n *\n * @example\n * ```typescript\n * init({\n * service: 'my-app',\n * posthog: { url: 'https://us.i.posthog.com/i/v1/logs?token=phc_xxx' }\n * });\n * ```\n *\n * Also reads from POSTHOG_LOGS_URL environment variable as fallback.\n */\n posthog?: { url: string };\n\n /** Additional resource attributes to merge with defaults. */\n resourceAttributes?: Attributes;\n\n /** Provide a fully custom Resource to merge (advanced use case). */\n resource?: Resource;\n\n /**\n * Headers for OTLP exporters. Accepts either an object map or\n * a \"key=value\" comma separated string.\n *\n * @example\n * ```typescript\n * init({\n * service: 'my-app',\n * endpoint: 'https://api.honeycomb.io',\n * headers: { 'x-honeycomb-team': 'YOUR_API_KEY' }\n * })\n * ```\n */\n headers?: Record<string, string> | string;\n\n /**\n * OTLP protocol to use for traces, metrics, and logs\n * - 'http': HTTP/protobuf (default, uses port 4318)\n * - 'grpc': gRPC (uses port 4317)\n *\n * Can be overridden with OTEL_EXPORTER_OTLP_PROTOCOL env var.\n *\n * Note: gRPC exporters are optional peer dependencies. Install them with:\n * ```bash\n * pnpm add @opentelemetry/exporter-trace-otlp-grpc @opentelemetry/exporter-metrics-otlp-grpc\n * ```\n *\n * @example HTTP (default)\n * ```typescript\n * init({\n * service: 'my-app',\n * protocol: 'http', // or omit (defaults to http)\n * endpoint: 'http://localhost:4318'\n * })\n * ```\n *\n * @example gRPC\n * ```typescript\n * init({\n * service: 'my-app',\n * protocol: 'grpc',\n * endpoint: 'grpc://localhost:4317'\n * })\n * ```\n *\n * @default 'http'\n */\n protocol?: 'http' | 'grpc';\n\n /**\n * Optional factory to build a customised NodeSDK instance from our defaults.\n */\n sdkFactory?: (defaults: Partial<NodeSDKConfiguration>) => NodeSDK;\n\n /**\n * Infrastructure metrics configuration\n * - true: always enabled (default)\n * - false: always disabled\n * - 'auto': always enabled (same as true)\n *\n * Can be overridden with AUTOTEL_METRICS=on|off env var\n */\n metrics?: boolean | 'auto';\n\n /** Sampling strategy (default: AdaptiveSampler with 10% baseline) */\n sampler?: Sampler;\n\n /** Service version (default: auto-detect from package.json or '1.0.0') */\n version?: string;\n\n /** Environment (default: process.env.NODE_ENV || 'development') */\n environment?: string;\n\n /**\n * Logger instance for internal autotel diagnostic messages\n *\n * This logger is used by autotel internally to log initialization, warnings,\n * and debug information. Any logger with info/warn/error/debug methods works.\n *\n * **For OTel instrumentation of your application logs**, use the `autoInstrumentations` option:\n * - `autoInstrumentations: ['pino']` - Injects traceId/spanId into Pino logs\n * - `autoInstrumentations: ['winston']` - Injects traceId/spanId into Winston logs\n *\n * Default: silent logger (no-op)\n *\n * @example Pino with OTel instrumentation\n * ```typescript\n * import pino from 'pino'\n * import { init } from 'autotel'\n *\n * const logger = pino({ level: 'info' })\n * init({\n * service: 'my-app',\n * logger, // For autotel's internal logs\n * autoInstrumentations: ['pino'] // For OTel trace context in YOUR logs\n * })\n * ```\n *\n * @example Custom logger for autotel diagnostics\n * ```typescript\n * const logger = {\n * info: (msg, extra) => console.log(msg, extra),\n * warn: (msg, extra) => console.warn(msg, extra),\n * error: (msg, err, extra) => console.error(msg, err, extra),\n * debug: (msg, extra) => console.debug(msg, extra),\n * }\n * init({ service: 'my-app', logger })\n * ```\n */\n logger?: Logger;\n\n /**\n * Flush events queue when root spans end\n * - true: Flush on root span completion (default)\n * - false: Use batching (events flush every 10 seconds automatically)\n *\n * Only flushes on root spans to avoid excessive network calls.\n * Default is true for serverless/short-lived processes. Set to false\n * for long-running services where batching is more efficient.\n */\n flushOnRootSpanEnd?: boolean;\n\n /**\n * Force-flush OpenTelemetry spans on shutdown (default: false)\n *\n * When enabled, spans are force-flushed along with events on root\n * span completion. This is useful for serverless/short-lived processes where\n * spans may not export before the process ends.\n *\n * - true: Force-flush spans on root span completion (~50-200ms latency)\n * - false: Spans export via normal batch processor (default behavior)\n *\n * Only applies when flushOnRootSpanEnd is also enabled.\n *\n * Note: For edge runtimes (Cloudflare Workers, Vercel Edge), use the\n * 'autotel-edge' package instead, which handles this automatically.\n *\n * @example Serverless with force-flush\n * ```typescript\n * init({\n * service: 'my-lambda',\n * flushOnRootSpanEnd: true,\n * forceFlushOnShutdown: true, // Force-flush spans\n * });\n * ```\n */\n forceFlushOnShutdown?: boolean;\n\n /**\n * Automatically copy baggage entries to span attributes\n *\n * When enabled, all baggage entries are automatically added as span attributes,\n * making them visible in trace UIs (Jaeger, Grafana, DataDog, etc.) without\n * manually calling ctx.setAttribute() for each entry.\n *\n * - `true`: adds baggage with 'baggage.' prefix (e.g. baggage.tenant.id)\n * - `string`: uses custom prefix (e.g. 'ctx' → ctx.tenant.id, '' → tenant.id)\n * - `false` or omit: disabled (default)\n *\n * @default false\n *\n * @example Enable with default prefix\n * ```typescript\n * init({\n * service: 'my-app',\n * baggage: true\n * });\n *\n * // Now baggage automatically appears as span attributes\n * await withBaggage({\n * baggage: { 'tenant.id': 't1', 'user.id': 'u1' },\n * fn: async () => {\n * // Span has baggage.tenant.id and baggage.user.id attributes!\n * }\n * });\n * ```\n *\n * @example Custom prefix\n * ```typescript\n * init({\n * service: 'my-app',\n * baggage: 'ctx' // Uses 'ctx.' prefix\n * });\n * // Creates attributes: ctx.tenant.id, ctx.user.id\n * ```\n *\n * @example No prefix\n * ```typescript\n * init({\n * service: 'my-app',\n * baggage: '' // No prefix\n * });\n * // Creates attributes: tenant.id, user.id\n * ```\n */\n baggage?: boolean | string;\n\n /**\n * Validation configuration for events events\n * - Override default sensitive field patterns for redaction\n * - Customize max lengths, nesting depth, etc.\n *\n * @example Disable redaction for development\n * ```typescript\n * init({\n * service: 'my-app',\n * validation: {\n * sensitivePatterns: [] // Disable all redaction\n * }\n * })\n * ```\n *\n * @example Add custom patterns\n * ```typescript\n * init({\n * service: 'my-app',\n * validation: {\n * sensitivePatterns: [\n * /password/i,\n * /apiKey/i,\n * /customSecret/i // Your custom pattern\n * ]\n * }\n * })\n * ```\n */\n validation?: Partial<ValidationConfig>;\n\n /**\n * Events configuration for trace context, correlation IDs, and enrichment\n *\n * Controls how product events integrate with distributed tracing:\n * - `includeTraceContext`: Automatically include trace context in events\n * - `includeLinkedTraceIds`: Include full array of linked trace IDs (for batch/fan-in)\n * - `traceUrl`: Generate clickable trace URLs in events\n * - `enrichFromBaggage`: Auto-enrich events from baggage with guardrails\n *\n * @example Basic trace context\n * ```typescript\n * init({\n * service: 'my-app',\n * events: {\n * includeTraceContext: true\n * }\n * });\n * // Events now include autotel.trace_id, autotel.span_id, autotel.correlation_id\n * ```\n *\n * @example With clickable trace URLs\n * ```typescript\n * init({\n * service: 'my-app',\n * events: {\n * includeTraceContext: true,\n * traceUrl: (ctx) => `https://grafana.internal/explore?traceId=${ctx.traceId}`\n * }\n * });\n * ```\n *\n * @example With baggage enrichment\n * ```typescript\n * init({\n * service: 'my-app',\n * events: {\n * includeTraceContext: true,\n * enrichFromBaggage: {\n * allow: ['tenant.id', 'user.id'],\n * prefix: 'ctx.',\n * maxKeys: 10,\n * maxBytes: 1024\n * }\n * }\n * });\n * ```\n */\n events?: EventsConfig;\n\n /**\n * Debug mode for local span inspection.\n * Enables console output to help you see spans as they're created.\n *\n * - `true`: Raw JSON output (ConsoleSpanExporter)\n * - `'pretty'`: Colorized, hierarchical output (PrettyConsoleExporter)\n * - `false`/undefined: No console output (default)\n *\n * When enabled: Outputs spans to console AND sends to backend (if endpoint/exporter configured)\n *\n * Perfect for progressive development:\n * - Start with debug: 'pretty' (no endpoint) → see traces immediately with nice formatting\n * - Add endpoint later → console + backend, verify before choosing provider\n * - Remove debug in production → backend only, clean production config\n *\n * Can be overridden with AUTOTEL_DEBUG environment variable.\n *\n * @example Pretty debug output (recommended for development)\n * ```typescript\n * init({\n * service: 'my-app',\n * debug: 'pretty' // Colorized, hierarchical output\n * })\n * ```\n *\n * @example Raw JSON output (verbose)\n * ```typescript\n * init({\n * service: 'my-app',\n * debug: true // Raw ConsoleSpanExporter output\n * })\n * ```\n *\n * @example Environment variable\n * ```bash\n * AUTOTEL_DEBUG=pretty node server.js\n * AUTOTEL_DEBUG=true node server.js\n * ```\n */\n debug?: boolean | 'pretty';\n\n /**\n * Filter predicate to drop unwanted spans before processing.\n *\n * Useful for filtering out noisy spans from specific instrumentations\n * (e.g., Next.js internal spans, health check endpoints).\n *\n * The filter runs on completed spans (onEnd), so you have access to:\n * - `span.name` - Span name\n * - `span.attributes` - All span attributes\n * - `span.instrumentationScope` - `{ name, version }` of the instrumentation\n * - `span.status` - Span status code and message\n * - `span.duration` - Span duration as `[seconds, nanoseconds]`\n *\n * Return `true` to keep the span, `false` to drop it.\n *\n * @example Filter out Next.js instrumentation spans\n * ```typescript\n * init({\n * service: 'my-app',\n * spanFilter: (span) => span.instrumentationScope.name !== 'next.js'\n * })\n * ```\n *\n * @example Filter out health check spans\n * ```typescript\n * init({\n * service: 'my-app',\n * spanFilter: (span) => !span.name.includes('/health')\n * })\n * ```\n *\n * @example Complex filtering (multiple conditions)\n * ```typescript\n * init({\n * service: 'my-app',\n * spanFilter: (span) => {\n * // Drop Next.js internal spans\n * if (span.instrumentationScope.name === 'next.js') return false;\n * // Drop health checks\n * if (span.name.includes('/health')) return false;\n * // Drop very short spans (less than 1ms)\n * const [secs, nanos] = span.duration;\n * if (secs === 0 && nanos < 1_000_000) return false;\n * return true;\n * }\n * })\n * ```\n */\n spanFilter?: SpanFilterPredicate;\n\n /**\n * Normalize span names to reduce cardinality from dynamic path segments.\n *\n * High-cardinality span names (e.g., `/users/123/posts/456`) cause issues:\n * - Cost explosions in observability backends\n * - Cardinality limits exceeded\n * - Poor UX when searching/filtering traces\n *\n * The normalizer transforms dynamic segments into placeholders:\n * - `/users/123` → `/users/:id`\n * - `/items/550e8400-e29b-...` → `/items/:uuid`\n *\n * Provide either a custom function or use a built-in preset:\n * - `'rest-api'` - Numeric IDs, UUIDs, ObjectIds, dates, timestamps, emails\n * - `'graphql'` - GraphQL operation name normalization\n * - `'minimal'` - Only numeric IDs and UUIDs\n *\n * @example Custom normalizer function\n * ```typescript\n * init({\n * service: 'my-app',\n * spanNameNormalizer: (name) => {\n * return name\n * .replace(/\\/[0-9]+/g, '/:id')\n * .replace(/\\/[a-f0-9-]{36}/gi, '/:uuid');\n * }\n * })\n * ```\n *\n * @example Using built-in preset\n * ```typescript\n * init({\n * service: 'my-app',\n * spanNameNormalizer: 'rest-api'\n * })\n * ```\n *\n * @example Combining with spanFilter\n * ```typescript\n * init({\n * service: 'my-app',\n * spanNameNormalizer: 'rest-api',\n * spanFilter: (span) => span.instrumentationScope.name !== 'next.js'\n * })\n * ```\n */\n spanNameNormalizer?: SpanNameNormalizerConfig;\n\n /**\n * Automatically redact PII and sensitive data from span attributes before export.\n * Critical for compliance (GDPR, PCI-DSS, HIPAA) and data security.\n *\n * Can be a preset name or custom configuration:\n * - `'default'`: Emails, phones, SSNs, credit cards, sensitive keys (password, secret, token)\n * - `'strict'`: Default + Bearer tokens, JWTs, API keys in values\n * - `'pci-dss'`: Payment card industry focus (credit cards, CVV, card-related keys)\n *\n * @example Use default preset\n * ```typescript\n * init({\n * service: 'my-app',\n * attributeRedactor: 'default'\n * })\n * ```\n *\n * @example Custom patterns\n * ```typescript\n * init({\n * service: 'my-app',\n * attributeRedactor: {\n * keyPatterns: [/password/i, /secret/i],\n * valuePatterns: [\n * { name: 'customerId', pattern: /CUST-\\d{8}/g, replacement: 'CUST-***' }\n * ]\n * }\n * })\n * ```\n *\n * @example Custom redactor function\n * ```typescript\n * init({\n * service: 'my-app',\n * attributeRedactor: {\n * redactor: (key, value) => {\n * if (key === 'user.email' && typeof value === 'string') {\n * return value.replace(/@.+/, '@[REDACTED]');\n * }\n * return value;\n * }\n * }\n * })\n * ```\n */\n attributeRedactor?: AttributeRedactorConfig | AttributeRedactorPreset;\n\n /**\n * OpenLLMetry integration for LLM observability.\n * Requires @traceloop/node-server-sdk as an optional peer dependency.\n *\n * @example Enable OpenLLMetry with default settings\n * ```typescript\n * init({\n * service: 'my-app',\n * openllmetry: { enabled: true }\n * })\n * ```\n *\n * @example Enable with custom options\n * ```typescript\n * init({\n * service: 'my-app',\n * openllmetry: {\n * enabled: true,\n * options: {\n * disableBatch: process.env.NODE_ENV !== 'production',\n * apiKey: process.env.TRACELOOP_API_KEY\n * }\n * }\n * })\n * ```\n */\n openllmetry?: {\n enabled: boolean;\n options?: Record<string, unknown>;\n };\n\n /**\n * Canonical log lines - automatically emit spans as wide events (canonical log lines)\n *\n * When enabled, each span (or root span only) is automatically emitted as a\n * comprehensive log record with ALL span attributes. This implements the\n * \"canonical log line\" pattern: one comprehensive event per request with all context.\n *\n * **Benefits:**\n * - One log line per request with all context (wide event)\n * - High-cardinality, high-dimensionality data for powerful queries\n * - Automatic - no manual logging needed\n * - Queryable as structured data instead of string search\n *\n * @example Basic usage (one canonical log line per request)\n * ```typescript\n * init({\n * service: 'checkout-api',\n * canonicalLogLines: {\n * enabled: true,\n * rootSpansOnly: true, // One canonical log line per request\n * },\n * });\n * ```\n *\n * @example With custom logger\n * ```typescript\n * import pino from 'pino';\n * const logger = pino();\n * init({\n * service: 'my-app',\n * logger,\n * canonicalLogLines: {\n * enabled: true,\n * logger, // Use Pino for canonical log lines\n * rootSpansOnly: true,\n * },\n * });\n * ```\n *\n * @example Custom message format\n * ```typescript\n * init({\n * service: 'my-app',\n * canonicalLogLines: {\n * enabled: true,\n * messageFormat: (span) => {\n * const status = span.status.code === 2 ? 'ERROR' : 'SUCCESS';\n * return `${span.name} [${status}]`;\n * },\n * },\n * });\n * ```\n */\n canonicalLogLines?: {\n enabled: boolean;\n /** Logger to use for emitting canonical log lines (defaults to OTel Logs API) */\n logger?: Logger;\n /** Only emit canonical log lines for root spans (default: false) */\n rootSpansOnly?: boolean;\n /** Minimum log level for canonical log lines (default: 'info') */\n minLevel?: 'debug' | 'info' | 'warn' | 'error';\n /** Custom message format (default: uses span name) */\n messageFormat?: (\n span: import('@opentelemetry/sdk-trace-base').ReadableSpan,\n ) => string;\n /** Whether to include resource attributes (default: true) */\n includeResourceAttributes?: boolean;\n /** Predicate to decide whether to emit (runs after event is built). */\n shouldEmit?: CanonicalLogLineOptions['shouldEmit'];\n /**\n * Declarative tail sampling conditions (OR logic).\n * Ignored when `shouldEmit` is provided.\n * @example keep: [{ status: 500 }, { durationMs: 1000 }]\n */\n keep?: CanonicalLogLineOptions['keep'];\n /** Callback invoked after emit for custom fan-out. */\n drain?: CanonicalLogLineOptions['drain'];\n /** Handler for drain failures. */\n onDrainError?: CanonicalLogLineOptions['onDrainError'];\n /**\n * Pretty-print canonical log lines to console.\n * Defaults to true when NODE_ENV is 'development'.\n */\n pretty?: boolean;\n };\n}\n\n// Internal state\nlet initialized = false;\nlet config: AutotelConfig | null = null;\nlet sdk: NodeSDK | null = null;\nlet warnedOnce = false;\nlet logger: Logger = silentLogger; // Silent by default - no spam\nlet validationConfig: Partial<ValidationConfig> | null = null;\nlet eventsConfig: EventsConfig | null = null;\nlet _stringRedactor: StringRedactor | null = null;\nlet _optionalRequire: typeof safeRequire = safeRequire;\n\n/**\n * Resolve metrics flag with env var override support\n */\nexport function resolveMetricsFlag(\n configFlag: boolean | 'auto' = 'auto',\n): boolean {\n // 1. Check env var override (highest priority)\n const envFlag = process.env.AUTOTEL_METRICS;\n if (envFlag === 'on' || envFlag === 'true') return true;\n if (envFlag === 'off' || envFlag === 'false') return false;\n\n // 2. Check config flag\n if (configFlag === true) return true;\n if (configFlag === false) return false;\n\n // 3. Default: enabled in all environments (simpler)\n return true;\n}\n\n/**\n * Resolve debug flag with env var override support\n *\n * Supports:\n * - `'pretty'`: Colorized, hierarchical output (PrettyConsoleExporter)\n * - `true` / `'true'` / `'1'`: Raw JSON output (ConsoleSpanExporter)\n * - `false` / `'false'` / `'0'`: Disabled\n */\nexport function resolveDebugFlag(\n configFlag?: boolean | 'pretty',\n): boolean | 'pretty' {\n // 1. Check env var override (highest priority)\n const envFlag = process.env.AUTOTEL_DEBUG;\n if (envFlag === 'pretty') return 'pretty';\n if (envFlag === 'true' || envFlag === '1') return true;\n if (envFlag === 'false' || envFlag === '0') return false;\n\n // 2. Return config flag (defaults to false)\n return configFlag ?? false;\n}\n\nfunction normalizeOtlpHeaders(\n headers?: Record<string, string> | string,\n): Record<string, string> | undefined {\n if (!headers) return undefined;\n if (typeof headers !== 'string') return headers;\n\n const parsed: Record<string, string> = {};\n for (const pair of headers.split(',')) {\n const [key, ...valueParts] = pair.split('=');\n if (!key || valueParts.length === 0) continue;\n parsed[key.trim()] = valueParts.join('=').trim();\n }\n return parsed;\n}\n\n/**\n * Initialize autotel - Write Once, Observe Everywhere\n *\n * Follows OpenTelemetry standards: opinionated defaults with full flexibility\n * Idempotent: multiple calls are safe, last one wins\n *\n * @example Minimal setup (OTLP default)\n * ```typescript\n * init({ service: 'my-app' })\n * ```\n *\n * @example With events (observe in PostHog, Mixpanel, etc.)\n * ```typescript\n * import { PostHogSubscriber } from 'autotel-subscribers/posthog';\n *\n * init({\n * service: 'my-app',\n * subscribers: [new PostHogSubscriber({ apiKey: '...' })]\n * })\n * ```\n *\n * @example Observe in Jaeger\n * ```typescript\n * import { JaegerExporter } from '@opentelemetry/exporter-jaeger'\n *\n * init({\n * service: 'my-app',\n * spanExporter: new JaegerExporter({ endpoint: 'http://localhost:14268/api/traces' })\n * })\n * ```\n *\n * @example Observe in Zipkin\n * ```typescript\n * import { ZipkinExporter } from '@opentelemetry/exporter-zipkin'\n *\n * init({\n * service: 'my-app',\n * spanExporter: new ZipkinExporter({ url: 'http://localhost:9411/api/v2/spans' })\n * })\n * ```\n *\n * @example Observe in Datadog\n * ```typescript\n * import { DatadogSpanProcessor } from '@opentelemetry/exporter-datadog'\n *\n * init({\n * service: 'my-app',\n * spanProcessor: new DatadogSpanProcessor({ ... })\n * })\n * ```\n *\n * @example Console output (dev)\n * ```typescript\n * import { ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'\n *\n * init({\n * service: 'my-app',\n * spanProcessor: new SimpleSpanProcessor(new ConsoleSpanExporter())\n * })\n * ```\n */\n\nexport function init(cfg: AutotelConfig): void {\n // Resolve configs in priority order: explicit > yaml > env > defaults\n const envConfig = resolveConfigFromEnv();\n const yamlConfig = loadYamlConfig() ?? {};\n\n // Merge configs: explicit config > yaml file > env vars > defaults\n const mergedConfig: AutotelConfig = {\n ...envConfig, // Environment variables (lowest priority)\n ...yamlConfig, // YAML file (middle priority)\n ...cfg, // Explicit config (highest priority)\n // Deep merge for resourceAttributes\n resourceAttributes: {\n ...envConfig.resourceAttributes,\n ...yamlConfig.resourceAttributes,\n ...cfg.resourceAttributes,\n },\n // Handle headers merge (can be string or object)\n headers: cfg.headers ?? yamlConfig.headers ?? envConfig.headers,\n } as AutotelConfig;\n\n // Set logger (use provided or default to silent - no spam)\n logger = mergedConfig.logger || silentLogger;\n\n // Warn if re-initializing (same behavior in all environments)\n if (initialized) {\n logger.warn(\n {},\n '[autotel] init() called again - last config wins. This may cause unexpected behavior.',\n );\n }\n\n config = mergedConfig;\n validationConfig = mergedConfig.validation || null;\n eventsConfig = mergedConfig.events || null;\n\n // Initialize OpenTelemetry\n // Only use endpoint if explicitly configured (no default fallback)\n const endpoint = mergedConfig.endpoint;\n const otlpHeaders = normalizeOtlpHeaders(mergedConfig.headers);\n const version = mergedConfig.version || detectVersion();\n const environment =\n mergedConfig.environment || process.env.NODE_ENV || 'development';\n const metricsEnabled = resolveMetricsFlag(mergedConfig.metrics);\n\n // Detect hostname for proper Datadog correlation and Service Catalog discovery\n const hostname = detectHostname();\n\n let resource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: mergedConfig.service,\n [ATTR_SERVICE_VERSION]: version,\n // Support both old and new OpenTelemetry semantic conventions for environment\n 'deployment.environment': environment, // Deprecated but widely supported\n 'deployment.environment.name': environment, // OTel v1.27.0+ standard\n });\n\n // Add hostname attributes for Datadog Service Catalog and infrastructure correlation\n if (hostname) {\n resource = resource.merge(\n resourceFromAttributes({\n 'host.name': hostname, // OpenTelemetry standard\n 'datadog.host.name': hostname, // Datadog-specific, highest priority for Datadog\n }),\n );\n }\n\n if (mergedConfig.resource) {\n resource = resource.merge(mergedConfig.resource);\n }\n\n if (mergedConfig.resourceAttributes) {\n resource = resource.merge(\n resourceFromAttributes(mergedConfig.resourceAttributes),\n );\n }\n\n // Resolve OTLP protocol (http or grpc)\n const protocol = resolveProtocol(mergedConfig.protocol);\n\n // Build array of span processors (supports multiple)\n let spanProcessors: SpanProcessor[] = [];\n\n if (mergedConfig.spanProcessors && mergedConfig.spanProcessors.length > 0) {\n // User provided custom processors (full control)\n spanProcessors.push(...mergedConfig.spanProcessors);\n } else if (\n mergedConfig.spanExporters &&\n mergedConfig.spanExporters.length > 0\n ) {\n // User provided custom exporters (wrap each with tail sampling)\n for (const exporter of mergedConfig.spanExporters) {\n spanProcessors.push(\n new TailSamplingSpanProcessor(new BatchSpanProcessor(exporter)),\n );\n }\n } else if (endpoint) {\n // Default: OTLP with tail sampling (only if endpoint is configured)\n const traceExporter = createTraceExporter(protocol, {\n url: formatEndpointUrl(endpoint, 'traces', protocol),\n headers: otlpHeaders,\n });\n\n spanProcessors.push(\n new TailSamplingSpanProcessor(new BatchSpanProcessor(traceExporter)),\n );\n }\n // If no endpoint and no custom processors/exporters, array remains empty\n // SDK will still work but won't export traces\n\n // Add baggage span processor if enabled\n if (mergedConfig.baggage) {\n const prefix =\n typeof mergedConfig.baggage === 'string'\n ? mergedConfig.baggage\n ? `${mergedConfig.baggage}.`\n : ''\n : 'baggage.';\n spanProcessors.push(new BaggageSpanProcessor({ prefix }));\n }\n\n // Apply debug mode configuration\n const debugMode = resolveDebugFlag(mergedConfig.debug);\n\n if (debugMode === 'pretty') {\n // Pretty debug: colorized, hierarchical output\n spanProcessors.push(new SimpleSpanProcessor(new PrettyConsoleExporter()));\n } else if (debugMode === true) {\n // Raw debug: JSON output\n spanProcessors.push(new SimpleSpanProcessor(new ConsoleSpanExporter()));\n }\n\n // Add canonical log line processor BEFORE wrapping processors\n // This ensures it gets wrapped with the same filter/normalizer/redactor as other processors,\n // so canonical logs respect spanFilter (filtered spans aren't logged), spanNameNormalizer\n // (normalized names are used), and attributeRedactor (sensitive data is redacted).\n if (mergedConfig.canonicalLogLines?.enabled) {\n const canonicalOptions: CanonicalLogLineOptions = {\n logger: mergedConfig.canonicalLogLines.logger || mergedConfig.logger,\n rootSpansOnly: mergedConfig.canonicalLogLines.rootSpansOnly,\n minLevel: mergedConfig.canonicalLogLines.minLevel,\n messageFormat: mergedConfig.canonicalLogLines.messageFormat,\n includeResourceAttributes:\n mergedConfig.canonicalLogLines.includeResourceAttributes,\n shouldEmit: mergedConfig.canonicalLogLines.shouldEmit,\n keep: mergedConfig.canonicalLogLines.keep,\n drain: mergedConfig.canonicalLogLines.drain,\n onDrainError: mergedConfig.canonicalLogLines.onDrainError,\n pretty: mergedConfig.canonicalLogLines.pretty,\n };\n spanProcessors.push(new CanonicalLogLineProcessor(canonicalOptions));\n }\n\n // Wrap processors in order: redactor (innermost) → normalizer → filter (outermost)\n // This ensures onEnd() execution order is: filter → normalizer → redactor\n // So filtering sees original attributes, and redaction happens last before export.\n\n // Step 1: Wrap with AttributeRedactingProcessor (innermost - executes last in onEnd)\n if (mergedConfig.attributeRedactor && spanProcessors.length > 0) {\n spanProcessors = spanProcessors.map(\n (processor) =>\n new AttributeRedactingProcessor(processor, {\n redactor: mergedConfig.attributeRedactor!,\n }),\n );\n }\n\n // Store string redactor for use by PostHog log/subscriber paths\n if (mergedConfig.attributeRedactor) {\n _stringRedactor = createStringRedactor(mergedConfig.attributeRedactor);\n }\n\n // Wire string redactor to subscribers that support it (e.g., PostHogSubscriber)\n if (_stringRedactor && mergedConfig.subscribers) {\n for (const subscriber of mergedConfig.subscribers) {\n if (\n 'setStringRedactor' in subscriber &&\n typeof (subscriber as any).setStringRedactor === 'function'\n ) {\n (subscriber as any).setStringRedactor(_stringRedactor);\n }\n }\n }\n\n // Step 2: Wrap with SpanNameNormalizingProcessor (middle)\n // Normalizer runs in onStart(), so span names are normalized before any onEnd processing\n if (mergedConfig.spanNameNormalizer && spanProcessors.length > 0) {\n spanProcessors = spanProcessors.map(\n (processor) =>\n new SpanNameNormalizingProcessor(processor, {\n normalizer: mergedConfig.spanNameNormalizer!,\n }),\n );\n }\n\n // Step 3: Wrap with FilteringSpanProcessor (outermost - executes first in onEnd)\n // Filter sees original (unredacted) attributes, so it can match on sensitive values\n if (mergedConfig.spanFilter && spanProcessors.length > 0) {\n spanProcessors = spanProcessors.map(\n (processor) =>\n new FilteringSpanProcessor(processor, {\n filter: mergedConfig.spanFilter!,\n }),\n );\n }\n\n // Build array of metric readers (supports multiple)\n const metricReaders: MetricReader[] = [];\n\n if (mergedConfig.metricReaders && mergedConfig.metricReaders.length > 0) {\n // User provided custom metric readers\n metricReaders.push(...mergedConfig.metricReaders);\n } else if (metricsEnabled && endpoint) {\n // Default: OTLP metrics exporter (only if endpoint is configured)\n const metricExporter = createMetricExporter(protocol, {\n url: formatEndpointUrl(endpoint, 'metrics', protocol),\n headers: otlpHeaders,\n });\n\n metricReaders.push(\n new PeriodicExportingMetricReader({\n exporter: metricExporter,\n }),\n );\n }\n\n let logRecordProcessors: LogRecordProcessor[] | undefined;\n if (\n mergedConfig.logRecordProcessors &&\n mergedConfig.logRecordProcessors.length > 0\n ) {\n logRecordProcessors = [...mergedConfig.logRecordProcessors];\n }\n\n // PostHog OTLP logs integration\n const posthogProcessors = buildPostHogLogProcessors(\n mergedConfig.posthog,\n _stringRedactor,\n );\n if (posthogProcessors.length > 0) {\n if (!logRecordProcessors) {\n logRecordProcessors = [];\n }\n logRecordProcessors.push(...posthogProcessors);\n logger.info({}, '[autotel] PostHog OTLP logs configured');\n }\n\n // Handle instrumentations: merge manual instrumentations with auto-instrumentations\n let finalInstrumentations: NodeSDKConfiguration['instrumentations'] =\n mergedConfig.instrumentations ? [...mergedConfig.instrumentations] : [];\n\n if (\n mergedConfig.autoInstrumentations !== undefined &&\n mergedConfig.autoInstrumentations !== false\n ) {\n // Check for ESM mode and provide guidance\n const isESM = isESMMode();\n if (isESM) {\n logger.info(\n {},\n '[autotel] ESM mode detected. For auto-instrumentation to work:\\n' +\n ' 1. Install @opentelemetry/auto-instrumentations-node as a direct dependency\\n' +\n ' 2. Import autotel/register FIRST in your instrumentation file\\n' +\n ' 3. Use getNodeAutoInstrumentations() directly instead of autoInstrumentations\\n' +\n ' See: https://github.com/jagreehal/autotel#esm-setup',\n );\n }\n\n try {\n // Detect manual instrumentations to avoid conflicts\n const manualInstrumentationNames = getInstrumentationNames(\n mergedConfig.instrumentations ?? [],\n );\n\n // Warn if both autoInstrumentations and manual instrumentations are provided\n if (manualInstrumentationNames.size > 0) {\n const manualNames = [...manualInstrumentationNames].join(', ');\n logger.info(\n {},\n `[autotel] Detected manual instrumentations (${manualNames}). ` +\n 'These will take precedence over auto-instrumentations. ' +\n 'Tip: Set autoInstrumentations:false if you want full manual control, or remove manual configs to use auto-instrumentations.',\n );\n }\n\n const autoInstrumentations = getAutoInstrumentations(\n mergedConfig.autoInstrumentations,\n manualInstrumentationNames,\n );\n if (autoInstrumentations && autoInstrumentations.length > 0) {\n // Cast to proper type - getNodeAutoInstrumentations returns the correct type\n finalInstrumentations = [\n ...finalInstrumentations,\n ...(autoInstrumentations as NodeSDKConfiguration['instrumentations']),\n ];\n }\n } catch (error) {\n logger.warn(\n {},\n `[autotel] Failed to configure auto-instrumentations: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n const sdkOptions: Partial<NodeSDKConfiguration> = {\n resource,\n instrumentations: finalInstrumentations,\n };\n\n if (spanProcessors.length > 0) {\n sdkOptions.spanProcessors = spanProcessors;\n }\n\n if (metricReaders.length > 0) {\n sdkOptions.metricReaders = metricReaders;\n }\n\n if (logRecordProcessors && logRecordProcessors.length > 0) {\n sdkOptions.logRecordProcessors = logRecordProcessors;\n }\n\n sdk = mergedConfig.sdkFactory\n ? mergedConfig.sdkFactory(sdkOptions)\n : new NodeSDK(sdkOptions);\n\n if (!sdk) {\n throw new Error('[autotel] sdkFactory must return a NodeSDK instance');\n }\n\n sdk.start();\n\n // Initialize OpenLLMetry if enabled (after SDK starts to reuse tracer provider)\n if (mergedConfig.openllmetry?.enabled) {\n const traceloop = _optionalRequire<{\n initialize?: (options?: Record<string, unknown>) => void;\n }>('@traceloop/node-server-sdk');\n\n if (traceloop) {\n const initOptions: Record<string, unknown> = {\n ...mergedConfig.openllmetry.options,\n };\n\n // Reuse autotel's tracer provider\n try {\n // Type assertion needed as getTracerProvider is not in the public NodeSDK interface\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const tracerProvider = (sdk as any).getTracerProvider();\n initOptions.tracerProvider = tracerProvider;\n } catch {\n // Ignore if tracer provider not available\n }\n\n // Pass span exporter to OpenLLMetry if provided\n if (mergedConfig.spanExporters?.[0]) {\n initOptions.exporter = mergedConfig.spanExporters[0];\n }\n\n if (typeof traceloop.initialize === 'function') {\n traceloop.initialize(initOptions);\n logger.info({}, '[autotel] OpenLLMetry initialized successfully');\n } else {\n logger.warn(\n {},\n '[autotel] OpenLLMetry initialize function not found. Check @traceloop/node-server-sdk version.',\n );\n }\n } else {\n logger.warn(\n {},\n '[autotel] OpenLLMetry enabled but @traceloop/node-server-sdk is not installed. ' +\n 'Install it as a peer dependency to use OpenLLMetry integration.',\n );\n }\n }\n\n initialized = true;\n}\n\n/**\n * Extract instrumentation class names from instrumentation instances\n * Used to detect duplicates between manual and auto instrumentations\n */\nfunction getInstrumentationNames(\n instrumentations: NodeSDKConfiguration['instrumentations'],\n): Set<string> {\n const names = new Set<string>();\n\n if (!instrumentations) return names;\n\n for (const instrumentation of instrumentations) {\n if (instrumentation && typeof instrumentation === 'object') {\n names.add(instrumentation.constructor.name);\n }\n }\n\n return names;\n}\n\n/**\n * Map common instrumentation class names to their package names\n * Used to disable auto-instrumentations when user provides manual configs\n */\nconst INSTRUMENTATION_CLASS_TO_PACKAGE: Record<string, string> = {\n HttpInstrumentation: '@opentelemetry/instrumentation-http',\n HttpsInstrumentation: '@opentelemetry/instrumentation-http',\n ExpressInstrumentation: '@opentelemetry/instrumentation-express',\n FastifyInstrumentation: '@opentelemetry/instrumentation-fastify',\n MongoDBInstrumentation: '@opentelemetry/instrumentation-mongodb',\n MongooseInstrumentation: '@opentelemetry/instrumentation-mongoose',\n PrismaInstrumentation: '@opentelemetry/instrumentation-prisma',\n PinoInstrumentation: '@opentelemetry/instrumentation-pino',\n WinstonInstrumentation: '@opentelemetry/instrumentation-winston',\n RedisInstrumentation: '@opentelemetry/instrumentation-redis',\n GraphQLInstrumentation: '@opentelemetry/instrumentation-graphql',\n GrpcInstrumentation: '@opentelemetry/instrumentation-grpc',\n IORedisInstrumentation: '@opentelemetry/instrumentation-ioredis',\n KnexInstrumentation: '@opentelemetry/instrumentation-knex',\n NestJsInstrumentation: '@opentelemetry/instrumentation-nestjs-core',\n PgInstrumentation: '@opentelemetry/instrumentation-pg',\n MySQLInstrumentation: '@opentelemetry/instrumentation-mysql',\n MySQL2Instrumentation: '@opentelemetry/instrumentation-mysql2',\n};\n\n/**\n * Type for the auto-instrumentations loader function\n * @internal Used for testing injection\n */\nexport type AutoInstrumentationsLoader = (\n config?: Record<string, { enabled?: boolean }>,\n) => unknown[];\n\n/**\n * Detect if we're running in ESM mode\n */\nfunction isESMMode(): boolean {\n // Check if we're in an ESM context by looking for common ESM indicators\n try {\n // In ESM, module.exports doesn't exist in the global scope the same way\n // Also check if the package.json type is \"module\"\n const fs = requireModule<typeof import('node:fs')>('node:fs');\n try {\n const pkg = JSON.parse(\n fs.readFileSync(`${process.cwd()}/package.json`, 'utf8'),\n );\n return pkg.type === 'module';\n } catch {\n return false;\n }\n } catch {\n return false;\n }\n}\n\n/**\n * Lazy-load auto-instrumentations (optional peer dependency)\n * Only loads when integrations config is truthy, avoiding ~40+ package imports at startup.\n */\nfunction loadNodeAutoInstrumentations(): AutoInstrumentationsLoader {\n try {\n const mod = requireModule<{\n getNodeAutoInstrumentations: AutoInstrumentationsLoader;\n }>('@opentelemetry/auto-instrumentations-node');\n return mod.getNodeAutoInstrumentations;\n } catch {\n const isESM = isESMMode();\n const baseMessage = '@opentelemetry/auto-instrumentations-node not found.';\n\n if (isESM) {\n throw new Error(\n `${baseMessage}\\n\\n` +\n 'ESM Setup Required:\\n' +\n '1. Install as a direct dependency: pnpm add @opentelemetry/auto-instrumentations-node\\n' +\n '2. Create instrumentation.mjs with:\\n' +\n \" import 'autotel/register'; // MUST be first!\\n\" +\n \" import { init } from 'autotel';\\n\" +\n \" import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';\\n\" +\n ' init({ service: \"my-app\", instrumentations: getNodeAutoInstrumentations() });\\n' +\n '3. Run with: tsx --import ./instrumentation.mjs src/index.ts\\n\\n' +\n 'See: https://github.com/jagreehal/autotel#esm-setup',\n );\n }\n\n throw new Error(\n `${baseMessage} Install it: pnpm add @opentelemetry/auto-instrumentations-node`,\n );\n }\n}\n\n/**\n * Injectable loader for testing. Set to override the default loader.\n * @internal\n */\nlet _autoInstrumentationsLoader: (() => AutoInstrumentationsLoader) | null =\n null;\n\n/**\n * @internal Set custom loader (for testing)\n */\nexport function _setAutoInstrumentationsLoader(\n loader: (() => AutoInstrumentationsLoader) | null,\n): void {\n _autoInstrumentationsLoader = loader;\n}\n\n/**\n * @internal Reset loader to default (for testing cleanup)\n */\nexport function _resetAutoInstrumentationsLoader(): void {\n _autoInstrumentationsLoader = null;\n}\n\n/**\n * Get auto-instrumentations based on simple integration names\n * Excludes instrumentations that are manually provided to avoid conflicts\n */\nfunction getAutoInstrumentations(\n integrations: string[] | boolean | Record<string, { enabled?: boolean }>,\n manualInstrumentationNames: Set<string> = new Set(),\n): unknown[] {\n if (integrations === false) {\n return [];\n }\n\n // Use injected loader if set (for testing), otherwise lazy-load\n const getNodeAutoInstrumentations = _autoInstrumentationsLoader\n ? _autoInstrumentationsLoader()\n : loadNodeAutoInstrumentations();\n\n // Build exclusion config for manual instrumentations\n const exclusionConfig: Record<string, { enabled: boolean }> = {};\n for (const className of manualInstrumentationNames) {\n const packageName = INSTRUMENTATION_CLASS_TO_PACKAGE[className];\n if (packageName) {\n exclusionConfig[packageName] = { enabled: false };\n }\n }\n\n if (integrations === true) {\n // If exclusions exist, pass them to getNodeAutoInstrumentations\n if (Object.keys(exclusionConfig).length > 0) {\n return getNodeAutoInstrumentations(exclusionConfig);\n }\n return getNodeAutoInstrumentations();\n }\n\n if (Array.isArray(integrations)) {\n const config: Record<string, { enabled: boolean }> = { ...exclusionConfig };\n for (const name of integrations) {\n const packageName = `@opentelemetry/instrumentation-${name}`;\n // Don't override exclusions\n if (!exclusionConfig[packageName]) {\n config[packageName] = { enabled: true };\n }\n }\n return getNodeAutoInstrumentations(config);\n }\n\n const config: Record<string, { enabled?: boolean }> = {\n ...exclusionConfig,\n ...integrations,\n };\n\n // Override any integrations that conflict with manual instrumentations\n for (const packageName of Object.keys(exclusionConfig)) {\n const integrationsKey = Object.keys(integrations).find((key) =>\n packageName.includes(key),\n );\n if (integrationsKey) {\n // Manual instrumentation takes precedence\n config[packageName] = { enabled: false };\n }\n }\n\n return getNodeAutoInstrumentations(config);\n}\n\n/**\n * Check if autotel has been initialized\n */\nexport function isInitialized(): boolean {\n return initialized;\n}\n\n/**\n * Get current config (internal use)\n */\nexport function getConfig(): AutotelConfig | null {\n return config;\n}\n\n/**\n * Get current logger (internal use)\n */\nexport function getLogger(): Logger {\n return logger;\n}\n\n/**\n * Get validation config (internal use)\n */\nexport function getValidationConfig(): Partial<ValidationConfig> | null {\n return validationConfig;\n}\n\n/**\n * Get events config (internal use)\n */\nexport function getEventsConfig(): EventsConfig | null {\n return eventsConfig;\n}\n\n/**\n * Warn once if not initialized (same behavior in all environments)\n */\nexport function warnIfNotInitialized(context: string): void {\n if (!initialized && !warnedOnce) {\n logger.warn(\n {},\n `[autotel] ${context} used before init() called. ` +\n 'Call init({ service: \"...\" }) first. See: https://docs.autotel.dev/quickstart',\n );\n warnedOnce = true;\n }\n}\n\n/**\n * Get default sampler\n */\nexport function getDefaultSampler(): Sampler {\n return (\n config?.sampler ||\n new AdaptiveSampler({\n baselineSampleRate: 0.1,\n alwaysSampleErrors: true,\n alwaysSampleSlow: true,\n })\n );\n}\n\n/**\n * Auto-detect version from package.json\n */\nfunction detectVersion(): string {\n try {\n // Try to read package.json from cwd using fs\n const fs = requireModule<typeof import('node:fs')>('node:fs');\n const pkg = JSON.parse(\n fs.readFileSync(`${process.cwd()}/package.json`, 'utf8'),\n );\n return pkg.version || '1.0.0';\n } catch {\n return '1.0.0';\n }\n}\n\n/**\n * Detect hostname for resource attributes.\n * Supports Datadog conventions (DD_HOSTNAME) and falls back to system hostname.\n *\n * Priority order:\n * 1. DD_HOSTNAME environment variable (Datadog convention)\n * 2. HOSTNAME environment variable (common Unix convention)\n * 3. os.hostname() (system hostname)\n *\n * @returns hostname string or undefined if detection fails\n */\nfunction detectHostname(): string | undefined {\n // Priority 1: DD_HOSTNAME (Datadog convention)\n if (process.env.DD_HOSTNAME) {\n return process.env.DD_HOSTNAME;\n }\n\n // Priority 2: HOSTNAME (common in containers and Unix systems)\n if (process.env.HOSTNAME) {\n return process.env.HOSTNAME;\n }\n\n // Priority 3: System hostname\n try {\n const os = requireModule<typeof import('node:os')>('node:os');\n return os.hostname();\n } catch {\n // os module not available (edge runtime, browser, etc.)\n return undefined;\n }\n}\n\n/**\n * Get the string redactor configured via init({ attributeRedactor }).\n * Returns null if no redactor was configured.\n */\nexport function getStringRedactor(): StringRedactor | null {\n return _stringRedactor;\n}\n\n/**\n * @internal Override optional require for deterministic tests.\n */\nexport function _setOptionalRequireForTesting(\n loader: typeof safeRequire,\n): void {\n _optionalRequire = loader;\n}\n\n/**\n * @internal Reset optional require override.\n */\nexport function _resetOptionalRequireForTesting(): void {\n _optionalRequire = safeRequire;\n}\n\n/**\n * Get SDK instance (for shutdown)\n */\nexport function getSdk(): NodeSDK | null {\n return sdk;\n}\n"]}
|