|
| 1 | +import { trace } from '@opentelemetry/api'; |
| 2 | + |
| 3 | +/** |
| 4 | + * OpenTelemetry tracer for creating custom spans in the Catalyst application. |
| 5 | + * |
| 6 | + * Use this tracer to instrument important operations and track their performance. |
| 7 | + * Spans created with this tracer will appear in your observability dashboard |
| 8 | + * nested under the appropriate HTTP request trace. |
| 9 | + * |
| 10 | + * @see https://nextjs.org/docs/app/guides/open-telemetry#custom-spans |
| 11 | + * @see OPENTELEMETRY.md for detailed usage guide |
| 12 | + * |
| 13 | + * @example Basic usage |
| 14 | + * import { tracer } from '~/lib/otel/tracer'; |
| 15 | + * |
| 16 | + * export async function fetchProductRecommendations(productId: string) { |
| 17 | + * return await tracer.startActiveSpan('fetchProductRecommendations', async (span) => { |
| 18 | + * try { |
| 19 | + * // Add attributes for context |
| 20 | + * span.setAttribute('product.id', productId); |
| 21 | + * |
| 22 | + * const recommendations = await fetch(`/api/recommendations/${productId}`); |
| 23 | + * |
| 24 | + * span.setAttribute('recommendations.count', recommendations.length); |
| 25 | + * |
| 26 | + * return recommendations; |
| 27 | + * } finally { |
| 28 | + * // Always end the span, even if an error occurs |
| 29 | + * span.end(); |
| 30 | + * } |
| 31 | + * }); |
| 32 | + * } |
| 33 | + * |
| 34 | + * @example With error handling |
| 35 | + * import { tracer } from '~/lib/otel/tracer'; |
| 36 | + * import { SpanStatusCode } from '@opentelemetry/api'; |
| 37 | + * |
| 38 | + * export async function processOrder(orderId: string) { |
| 39 | + * return await tracer.startActiveSpan('processOrder', async (span) => { |
| 40 | + * try { |
| 41 | + * span.setAttribute('order.id', orderId); |
| 42 | + * |
| 43 | + * const result = await submitOrder(orderId); |
| 44 | + * |
| 45 | + * return result; |
| 46 | + * } catch (error) { |
| 47 | + * // Record the exception and mark span as error |
| 48 | + * span.recordException(error); |
| 49 | + * span.setStatus({ code: SpanStatusCode.ERROR }); |
| 50 | + * throw error; |
| 51 | + * } finally { |
| 52 | + * span.end(); |
| 53 | + * } |
| 54 | + * }); |
| 55 | + * } |
| 56 | + * |
| 57 | + * @example Data transformation |
| 58 | + * export async function transformCartData(rawCart: RawCart) { |
| 59 | + * return await tracer.startActiveSpan('transformCartData', async (span) => { |
| 60 | + * try { |
| 61 | + * span.setAttribute('cart.itemCount', rawCart.lineItems.length); |
| 62 | + * |
| 63 | + * const transformed = { |
| 64 | + * items: rawCart.lineItems.map(transformLineItem), |
| 65 | + * total: calculateTotal(rawCart), |
| 66 | + * }; |
| 67 | + * |
| 68 | + * return transformed; |
| 69 | + * } finally { |
| 70 | + * span.end(); |
| 71 | + * } |
| 72 | + * }); |
| 73 | + * } |
| 74 | + * |
| 75 | + * When to use custom spans: |
| 76 | + * - Operations that might be slow (> 100ms) |
| 77 | + * - Critical business logic (pricing, inventory, checkout) |
| 78 | + * - External API integrations |
| 79 | + * - Data transformations with variable performance |
| 80 | + * - Any operation you want to monitor and optimize |
| 81 | + * |
| 82 | + * When NOT to use custom spans: |
| 83 | + * - Operations already instrumented by Next.js (fetch calls, route rendering) |
| 84 | + * - Simple utility functions |
| 85 | + * - Trivial operations (< 10ms) |
| 86 | + * |
| 87 | + * Best practices: |
| 88 | + * - Use descriptive span names (e.g., 'cart.calculateDiscounts') |
| 89 | + * - Add meaningful attributes for filtering and debugging |
| 90 | + * - Always end spans in finally blocks |
| 91 | + * - Use hierarchical naming (e.g., 'cart.validate', 'cart.addItem') |
| 92 | + */ |
| 93 | +export const tracer = trace.getTracer('default'); |
0 commit comments