UNPKG

autotel

Version:
330 lines (328 loc) 9.86 kB
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); const require_functional = require('./functional-C8B0Qa7o.cjs'); //#region src/semantic-helpers.ts /** * Semantic convention helpers for OpenTelemetry * * Pre-configured trace helpers that follow OpenTelemetry semantic conventions * for common operation types. Reduces boilerplate and ensures consistency. * * Based on: https://opentelemetry.io/docs/specs/semconv/ */ /** * Trace database operations with DB semantic conventions * * Automatically adds standard attributes for database operations: * - db.system * - db.operation * - db.name * - db.collection.name (for NoSQL) * * **Use Cases:** * - SQL queries (PostgreSQL, MySQL, SQLite) * - NoSQL operations (MongoDB, DynamoDB, Redis) * - ORM queries (Prisma, TypeORM, Drizzle) * * @param config - Database operation configuration * @returns Traced function factory with DB attributes * * @example PostgreSQL query * ```typescript * import { traceDB } from 'autotel/semantic-helpers' * import { pool } from './db' * * export const getUser = traceDB({ * system: 'postgresql', * operation: 'SELECT', * database: 'app_db', * collection: 'users' * })(ctx => async (userId: string) => { * const query = 'SELECT * FROM users WHERE id = $1' * ctx.setAttribute('db.statement', query) * * const result = await pool.query(query, [userId]) * ctx.setAttribute('db.rows_affected', result.rowCount) * * return result.rows[0] * }) * ``` * * @example MongoDB with Mongoose * ```typescript * import { traceDB } from 'autotel/semantic-helpers' * import { User } from './models/User' * * export const findUsers = traceDB({ * system: 'mongodb', * operation: 'find', * database: 'app_db', * collection: 'users' * })(ctx => async (filter: object) => { * ctx.setAttribute('db.mongodb.filter', JSON.stringify(filter)) * * const users = await User.find(filter).limit(100) * ctx.setAttribute('db.response.count', users.length) * * return users * }) * ``` * * @example Redis operations * ```typescript * import { traceDB } from 'autotel/semantic-helpers' * import { redis } from './redis' * * export const cacheGet = traceDB({ * system: 'redis', * operation: 'GET' * })(ctx => async (key: string) => { * ctx.setAttribute('db.redis.key', key) * * const value = await redis.get(key) * ctx.setAttribute('db.response.cache_hit', value !== null) * * return value * }) * ``` * * @example Prisma with detailed query info * ```typescript * import { traceDB } from 'autotel/semantic-helpers' * import { prisma } from './prisma' * * export const createPost = traceDB({ * system: 'postgresql', * operation: 'INSERT', * database: 'app_db', * collection: 'posts' * })(ctx => async (data: { title: string; content: string; authorId: string }) => { * ctx.setAttribute('db.prisma.model', 'Post') * ctx.setAttribute('db.prisma.action', 'create') * * const post = await prisma.post.create({ data }) * * ctx.setAttribute('db.response.id', post.id) * return post * }) * ``` * * @public */ function traceDB(config) { return (fnFactory) => { return require_functional.trace((ctx) => { ctx.setAttribute("db.system", config.system); if (config.operation) ctx.setAttribute("db.operation", config.operation); if (config.database) ctx.setAttribute("db.name", config.database); if (config.collection) ctx.setAttribute("db.collection.name", config.collection); if (config.attributes) { for (const [key, value] of Object.entries(config.attributes)) if (value !== void 0 && value !== null) { const attrValue = typeof value === "string" || typeof value === "number" || typeof value === "boolean" ? value : JSON.stringify(value); ctx.setAttribute(key, attrValue); } } return fnFactory(ctx); }); }; } /** * Trace HTTP client operations with HTTP semantic conventions * * Automatically adds standard attributes for HTTP requests: * - http.request.method * - url.full * * **Use Cases:** * - External API calls * - Microservice communication * - Third-party integrations * * @param config - HTTP operation configuration * @returns Traced function factory with HTTP attributes * * @example Fetch API * ```typescript * import { traceHTTP } from 'autotel/semantic-helpers' * * export const fetchUser = traceHTTP({ * method: 'GET', * url: 'https://api.example.com/users/:id' * })(ctx => async (userId: string) => { * const url = `https://api.example.com/users/${userId}` * ctx.setAttribute('url.full', url) * * const response = await fetch(url) * ctx.setAttribute('http.response.status_code', response.status) * * if (!response.ok) { * ctx.setAttribute('error', true) * throw new Error(`HTTP ${response.status}: ${response.statusText}`) * } * * return response.json() * }) * ``` * * @example Axios with retry logic * ```typescript * import { traceHTTP } from 'autotel/semantic-helpers' * import axios from 'axios' * * export const sendWebhook = traceHTTP({ * method: 'POST', * url: 'https://webhook.example.com/events' * })(ctx => async (payload: object) => { * let attempts = 0 * const maxAttempts = 3 * * while (attempts < maxAttempts) { * try { * attempts++ * ctx.setAttribute('http.request.resend_count', attempts - 1) * * const response = await axios.post('https://webhook.example.com/events', payload) * ctx.setAttribute('http.response.status_code', response.status) * return response.data * } catch (error) { * if (attempts >= maxAttempts) throw error * await new Promise(resolve => setTimeout(resolve, 1000 * attempts)) * } * } * }) * ``` * * @public */ function traceHTTP(config) { return (fnFactory) => { return require_functional.trace((ctx) => { if (config.method) ctx.setAttribute("http.request.method", config.method); if (config.url) ctx.setAttribute("url.full", config.url); if (config.attributes) { for (const [key, value] of Object.entries(config.attributes)) if (value !== void 0 && value !== null) { const attrValue = typeof value === "string" || typeof value === "number" || typeof value === "boolean" ? value : JSON.stringify(value); ctx.setAttribute(key, attrValue); } } return fnFactory(ctx); }); }; } /** * Trace messaging operations with Messaging semantic conventions * * Automatically adds standard attributes for messaging: * - messaging.system * - messaging.operation * - messaging.destination.name * * **Use Cases:** * - Publishing messages to queues/topics * - Consuming messages from queues/topics * - Event-driven architectures * * @param config - Messaging operation configuration * @returns Traced function factory with Messaging attributes * * @example Publishing to Kafka * ```typescript * import { traceMessaging } from 'autotel/semantic-helpers' * import { kafka } from './kafka' * * const producer = kafka.producer() * * export const publishEvent = traceMessaging({ * system: 'kafka', * operation: 'publish', * destination: 'user-events' * })(ctx => async (event: { type: string; userId: string; data: object }) => { * ctx.setAttribute('messaging.message.type', event.type) * ctx.setAttribute('messaging.kafka.partition', 0) * * await producer.send({ * topic: 'user-events', * messages: [ * { * key: event.userId, * value: JSON.stringify(event.data) * } * ] * }) * * ctx.setAttribute('messaging.message.id', event.userId) * }) * ``` * * @example Consuming from RabbitMQ * ```typescript * import { traceMessaging } from 'autotel/semantic-helpers' * import { channel } from './rabbitmq' * * export const processOrder = traceMessaging({ * system: 'rabbitmq', * operation: 'process', * destination: 'orders' * })(ctx => async (message: { orderId: string; items: object[] }) => { * ctx.setAttribute('messaging.message.id', message.orderId) * ctx.setAttribute('messaging.message.body.size', JSON.stringify(message).length) * * // Process order logic * const result = await processOrderInternal(message) * * ctx.setAttribute('messaging.operation.result', 'success') * return result * }) * ``` * * @example AWS SQS with batch processing * ```typescript * import { traceMessaging } from 'autotel/semantic-helpers' * import { SQS } from '@aws-sdk/client-sqs' * * const sqs = new SQS() * * export const sendBatch = traceMessaging({ * system: 'aws_sqs', * operation: 'publish', * destination: 'notifications-queue' * })(ctx => async (messages: Array<{ id: string; body: object }>) => { * ctx.setAttribute('messaging.batch.message_count', messages.length) * * const result = await sqs.sendMessageBatch({ * QueueUrl: process.env.QUEUE_URL, * Entries: messages.map(msg => ({ * Id: msg.id, * MessageBody: JSON.stringify(msg.body) * })) * }) * * ctx.setAttribute('messaging.operation.success_count', result.Successful?.length || 0) * ctx.setAttribute('messaging.operation.failed_count', result.Failed?.length || 0) * * return result * }) * ``` * * @public */ function traceMessaging(config) { return (fnFactory) => { return require_functional.trace((ctx) => { ctx.setAttribute("messaging.system", config.system); if (config.operation) ctx.setAttribute("messaging.operation", config.operation); if (config.destination) ctx.setAttribute("messaging.destination.name", config.destination); if (config.attributes) { for (const [key, value] of Object.entries(config.attributes)) if (value !== void 0 && value !== null) { const attrValue = typeof value === "string" || typeof value === "number" || typeof value === "boolean" ? value : JSON.stringify(value); ctx.setAttribute(key, attrValue); } } return fnFactory(ctx); }); }; } //#endregion exports.traceDB = traceDB; exports.traceHTTP = traceHTTP; exports.traceMessaging = traceMessaging; //# sourceMappingURL=semantic-helpers.cjs.map