autotel
Version:
Write Once, Observe Anywhere
1 lines • 21.7 kB
Source Map (JSON)
{"version":3,"sources":["../src/semantic-helpers.ts"],"names":["trace"],"mappings":"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","file":"chunk-YEVCD6DR.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"]}