autotel 2.1.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/LICENSE +21 -0
- package/README.md +1946 -0
- package/dist/chunk-2LNRY4QK.js +273 -0
- package/dist/chunk-2LNRY4QK.js.map +1 -0
- package/dist/chunk-3HENGDW2.js +587 -0
- package/dist/chunk-3HENGDW2.js.map +1 -0
- package/dist/chunk-4OAT42CA.cjs +73 -0
- package/dist/chunk-4OAT42CA.cjs.map +1 -0
- package/dist/chunk-5GWX5LFW.js +70 -0
- package/dist/chunk-5GWX5LFW.js.map +1 -0
- package/dist/chunk-5R2M36QB.js +195 -0
- package/dist/chunk-5R2M36QB.js.map +1 -0
- package/dist/chunk-5ZN622AO.js +73 -0
- package/dist/chunk-5ZN622AO.js.map +1 -0
- package/dist/chunk-77MSMAUQ.cjs +498 -0
- package/dist/chunk-77MSMAUQ.cjs.map +1 -0
- package/dist/chunk-ABPEQ6RK.cjs +596 -0
- package/dist/chunk-ABPEQ6RK.cjs.map +1 -0
- package/dist/chunk-BWYGJKRB.js +95 -0
- package/dist/chunk-BWYGJKRB.js.map +1 -0
- package/dist/chunk-BZHG5IZ4.js +73 -0
- package/dist/chunk-BZHG5IZ4.js.map +1 -0
- package/dist/chunk-G7VZBCD6.cjs +35 -0
- package/dist/chunk-G7VZBCD6.cjs.map +1 -0
- package/dist/chunk-GVLK7YUU.cjs +30 -0
- package/dist/chunk-GVLK7YUU.cjs.map +1 -0
- package/dist/chunk-HCCXC7XG.js +205 -0
- package/dist/chunk-HCCXC7XG.js.map +1 -0
- package/dist/chunk-HE6T6FIX.cjs +203 -0
- package/dist/chunk-HE6T6FIX.cjs.map +1 -0
- package/dist/chunk-KIXWPOCO.cjs +100 -0
- package/dist/chunk-KIXWPOCO.cjs.map +1 -0
- package/dist/chunk-KVGNW3FC.js +87 -0
- package/dist/chunk-KVGNW3FC.js.map +1 -0
- package/dist/chunk-LITNXTTT.js +3 -0
- package/dist/chunk-LITNXTTT.js.map +1 -0
- package/dist/chunk-M4ANN7RL.js +114 -0
- package/dist/chunk-M4ANN7RL.js.map +1 -0
- package/dist/chunk-NC52UBR2.cjs +32 -0
- package/dist/chunk-NC52UBR2.cjs.map +1 -0
- package/dist/chunk-NHCNRQD3.cjs +212 -0
- package/dist/chunk-NHCNRQD3.cjs.map +1 -0
- package/dist/chunk-NZ72VDNY.cjs +4 -0
- package/dist/chunk-NZ72VDNY.cjs.map +1 -0
- package/dist/chunk-P6JUDYNO.js +57 -0
- package/dist/chunk-P6JUDYNO.js.map +1 -0
- package/dist/chunk-RJYY7BWX.js +1349 -0
- package/dist/chunk-RJYY7BWX.js.map +1 -0
- package/dist/chunk-TRI4V5BF.cjs +126 -0
- package/dist/chunk-TRI4V5BF.cjs.map +1 -0
- package/dist/chunk-UL33I6IS.js +139 -0
- package/dist/chunk-UL33I6IS.js.map +1 -0
- package/dist/chunk-URRW6M2C.cjs +61 -0
- package/dist/chunk-URRW6M2C.cjs.map +1 -0
- package/dist/chunk-UY3UYPBZ.cjs +77 -0
- package/dist/chunk-UY3UYPBZ.cjs.map +1 -0
- package/dist/chunk-W3253FGB.cjs +277 -0
- package/dist/chunk-W3253FGB.cjs.map +1 -0
- package/dist/chunk-W7LHZVQF.js +26 -0
- package/dist/chunk-W7LHZVQF.js.map +1 -0
- package/dist/chunk-WBWNM6LB.cjs +1360 -0
- package/dist/chunk-WBWNM6LB.cjs.map +1 -0
- package/dist/chunk-WFJ7L2RV.js +494 -0
- package/dist/chunk-WFJ7L2RV.js.map +1 -0
- package/dist/chunk-X4RMFFMR.js +28 -0
- package/dist/chunk-X4RMFFMR.js.map +1 -0
- package/dist/chunk-Y4Y2S7BM.cjs +92 -0
- package/dist/chunk-Y4Y2S7BM.cjs.map +1 -0
- package/dist/chunk-YLPNXZFI.cjs +143 -0
- package/dist/chunk-YLPNXZFI.cjs.map +1 -0
- package/dist/chunk-YTXEZ4SD.cjs +77 -0
- package/dist/chunk-YTXEZ4SD.cjs.map +1 -0
- package/dist/chunk-Z6ZWNWWR.js +30 -0
- package/dist/chunk-Z6ZWNWWR.js.map +1 -0
- package/dist/config.cjs +26 -0
- package/dist/config.cjs.map +1 -0
- package/dist/config.d.cts +75 -0
- package/dist/config.d.ts +75 -0
- package/dist/config.js +5 -0
- package/dist/config.js.map +1 -0
- package/dist/db.cjs +233 -0
- package/dist/db.cjs.map +1 -0
- package/dist/db.d.cts +123 -0
- package/dist/db.d.ts +123 -0
- package/dist/db.js +228 -0
- package/dist/db.js.map +1 -0
- package/dist/decorators.cjs +67 -0
- package/dist/decorators.cjs.map +1 -0
- package/dist/decorators.d.cts +91 -0
- package/dist/decorators.d.ts +91 -0
- package/dist/decorators.js +65 -0
- package/dist/decorators.js.map +1 -0
- package/dist/event-subscriber.cjs +6 -0
- package/dist/event-subscriber.cjs.map +1 -0
- package/dist/event-subscriber.d.cts +116 -0
- package/dist/event-subscriber.d.ts +116 -0
- package/dist/event-subscriber.js +3 -0
- package/dist/event-subscriber.js.map +1 -0
- package/dist/event-testing.cjs +21 -0
- package/dist/event-testing.cjs.map +1 -0
- package/dist/event-testing.d.cts +110 -0
- package/dist/event-testing.d.ts +110 -0
- package/dist/event-testing.js +4 -0
- package/dist/event-testing.js.map +1 -0
- package/dist/event.cjs +30 -0
- package/dist/event.cjs.map +1 -0
- package/dist/event.d.cts +282 -0
- package/dist/event.d.ts +282 -0
- package/dist/event.js +13 -0
- package/dist/event.js.map +1 -0
- package/dist/exporters.cjs +17 -0
- package/dist/exporters.cjs.map +1 -0
- package/dist/exporters.d.cts +1 -0
- package/dist/exporters.d.ts +1 -0
- package/dist/exporters.js +4 -0
- package/dist/exporters.js.map +1 -0
- package/dist/functional.cjs +46 -0
- package/dist/functional.cjs.map +1 -0
- package/dist/functional.d.cts +478 -0
- package/dist/functional.d.ts +478 -0
- package/dist/functional.js +13 -0
- package/dist/functional.js.map +1 -0
- package/dist/http.cjs +189 -0
- package/dist/http.cjs.map +1 -0
- package/dist/http.d.cts +169 -0
- package/dist/http.d.ts +169 -0
- package/dist/http.js +184 -0
- package/dist/http.js.map +1 -0
- package/dist/index.cjs +333 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +758 -0
- package/dist/index.d.ts +758 -0
- package/dist/index.js +143 -0
- package/dist/index.js.map +1 -0
- package/dist/instrumentation.cjs +182 -0
- package/dist/instrumentation.cjs.map +1 -0
- package/dist/instrumentation.d.cts +49 -0
- package/dist/instrumentation.d.ts +49 -0
- package/dist/instrumentation.js +179 -0
- package/dist/instrumentation.js.map +1 -0
- package/dist/logger.cjs +19 -0
- package/dist/logger.cjs.map +1 -0
- package/dist/logger.d.cts +146 -0
- package/dist/logger.d.ts +146 -0
- package/dist/logger.js +6 -0
- package/dist/logger.js.map +1 -0
- package/dist/metric-helpers.cjs +31 -0
- package/dist/metric-helpers.cjs.map +1 -0
- package/dist/metric-helpers.d.cts +13 -0
- package/dist/metric-helpers.d.ts +13 -0
- package/dist/metric-helpers.js +6 -0
- package/dist/metric-helpers.js.map +1 -0
- package/dist/metric-testing.cjs +21 -0
- package/dist/metric-testing.cjs.map +1 -0
- package/dist/metric-testing.d.cts +110 -0
- package/dist/metric-testing.d.ts +110 -0
- package/dist/metric-testing.js +4 -0
- package/dist/metric-testing.js.map +1 -0
- package/dist/metric.cjs +26 -0
- package/dist/metric.cjs.map +1 -0
- package/dist/metric.d.cts +240 -0
- package/dist/metric.d.ts +240 -0
- package/dist/metric.js +9 -0
- package/dist/metric.js.map +1 -0
- package/dist/processors.cjs +17 -0
- package/dist/processors.cjs.map +1 -0
- package/dist/processors.d.cts +1 -0
- package/dist/processors.d.ts +1 -0
- package/dist/processors.js +4 -0
- package/dist/processors.js.map +1 -0
- package/dist/sampling.cjs +40 -0
- package/dist/sampling.cjs.map +1 -0
- package/dist/sampling.d.cts +260 -0
- package/dist/sampling.d.ts +260 -0
- package/dist/sampling.js +7 -0
- package/dist/sampling.js.map +1 -0
- package/dist/semantic-helpers.cjs +35 -0
- package/dist/semantic-helpers.cjs.map +1 -0
- package/dist/semantic-helpers.d.cts +442 -0
- package/dist/semantic-helpers.d.ts +442 -0
- package/dist/semantic-helpers.js +14 -0
- package/dist/semantic-helpers.js.map +1 -0
- package/dist/tail-sampling-processor.cjs +13 -0
- package/dist/tail-sampling-processor.cjs.map +1 -0
- package/dist/tail-sampling-processor.d.cts +27 -0
- package/dist/tail-sampling-processor.d.ts +27 -0
- package/dist/tail-sampling-processor.js +4 -0
- package/dist/tail-sampling-processor.js.map +1 -0
- package/dist/testing.cjs +286 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +291 -0
- package/dist/testing.d.ts +291 -0
- package/dist/testing.js +263 -0
- package/dist/testing.js.map +1 -0
- package/dist/trace-context-DRZdUvVY.d.cts +181 -0
- package/dist/trace-context-DRZdUvVY.d.ts +181 -0
- package/dist/trace-helpers.cjs +54 -0
- package/dist/trace-helpers.cjs.map +1 -0
- package/dist/trace-helpers.d.cts +524 -0
- package/dist/trace-helpers.d.ts +524 -0
- package/dist/trace-helpers.js +5 -0
- package/dist/trace-helpers.js.map +1 -0
- package/dist/tracer-provider.cjs +21 -0
- package/dist/tracer-provider.cjs.map +1 -0
- package/dist/tracer-provider.d.cts +169 -0
- package/dist/tracer-provider.d.ts +169 -0
- package/dist/tracer-provider.js +4 -0
- package/dist/tracer-provider.js.map +1 -0
- package/package.json +280 -0
- package/src/baggage-span-processor.test.ts +202 -0
- package/src/baggage-span-processor.ts +98 -0
- package/src/circuit-breaker.test.ts +341 -0
- package/src/circuit-breaker.ts +184 -0
- package/src/config.test.ts +94 -0
- package/src/config.ts +169 -0
- package/src/db.test.ts +252 -0
- package/src/db.ts +447 -0
- package/src/decorators.test.ts +203 -0
- package/src/decorators.ts +188 -0
- package/src/env-config.test.ts +246 -0
- package/src/env-config.ts +158 -0
- package/src/event-queue.test.ts +222 -0
- package/src/event-queue.ts +203 -0
- package/src/event-subscriber.ts +136 -0
- package/src/event-testing.ts +197 -0
- package/src/event.test.ts +718 -0
- package/src/event.ts +556 -0
- package/src/exporters.ts +96 -0
- package/src/functional.test.ts +1059 -0
- package/src/functional.ts +2295 -0
- package/src/http.test.ts +487 -0
- package/src/http.ts +424 -0
- package/src/index.ts +158 -0
- package/src/init.customization.test.ts +210 -0
- package/src/init.integrations.test.ts +366 -0
- package/src/init.openllmetry.test.ts +282 -0
- package/src/init.protocol.test.ts +215 -0
- package/src/init.ts +1426 -0
- package/src/instrumentation.test.ts +108 -0
- package/src/instrumentation.ts +308 -0
- package/src/logger.test.ts +117 -0
- package/src/logger.ts +246 -0
- package/src/metric-helpers.ts +47 -0
- package/src/metric-testing.ts +197 -0
- package/src/metric.ts +434 -0
- package/src/metrics.test.ts +205 -0
- package/src/operation-context.ts +93 -0
- package/src/processors.ts +106 -0
- package/src/rate-limiter.test.ts +199 -0
- package/src/rate-limiter.ts +98 -0
- package/src/sampling.test.ts +513 -0
- package/src/sampling.ts +428 -0
- package/src/semantic-helpers.test.ts +311 -0
- package/src/semantic-helpers.ts +584 -0
- package/src/shutdown.test.ts +311 -0
- package/src/shutdown.ts +222 -0
- package/src/stub.integration.test.ts +361 -0
- package/src/tail-sampling-processor.test.ts +226 -0
- package/src/tail-sampling-processor.ts +51 -0
- package/src/testing.ts +670 -0
- package/src/trace-context.ts +470 -0
- package/src/trace-helpers.new.test.ts +278 -0
- package/src/trace-helpers.test.ts +242 -0
- package/src/trace-helpers.ts +690 -0
- package/src/tracer-provider.test.ts +183 -0
- package/src/tracer-provider.ts +266 -0
- package/src/track.test.ts +153 -0
- package/src/track.ts +120 -0
- package/src/validation.test.ts +306 -0
- package/src/validation.ts +239 -0
- package/src/variable-name-inference.test.ts +178 -0
- package/src/variable-name-inference.ts +242 -0
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic convention helpers for OpenTelemetry
|
|
3
|
+
*
|
|
4
|
+
* Pre-configured trace helpers that follow OpenTelemetry semantic conventions
|
|
5
|
+
* for common operation types. Reduces boilerplate and ensures consistency.
|
|
6
|
+
*
|
|
7
|
+
* Based on: https://opentelemetry.io/docs/specs/semconv/
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { trace } from './functional';
|
|
11
|
+
import type { TraceContext } from './trace-context';
|
|
12
|
+
import type { Attributes } from '@opentelemetry/api';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Configuration for LLM (Large Language Model) operations
|
|
16
|
+
*
|
|
17
|
+
* Follows Gen AI semantic conventions:
|
|
18
|
+
* https://opentelemetry.io/docs/specs/semconv/gen-ai/
|
|
19
|
+
*/
|
|
20
|
+
export interface LLMConfig {
|
|
21
|
+
/** Model name (e.g., 'gpt-4', 'claude-3-opus') */
|
|
22
|
+
model: string;
|
|
23
|
+
/** Operation type */
|
|
24
|
+
operation?: 'chat' | 'completion' | 'embedding';
|
|
25
|
+
/** Model provider (e.g., 'openai', 'anthropic', 'cohere') */
|
|
26
|
+
system?: string;
|
|
27
|
+
/** Additional attributes to add to the span */
|
|
28
|
+
attributes?: Attributes;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Configuration for database operations
|
|
33
|
+
*
|
|
34
|
+
* Follows DB semantic conventions:
|
|
35
|
+
* https://opentelemetry.io/docs/specs/semconv/database/
|
|
36
|
+
*/
|
|
37
|
+
export interface DBConfig {
|
|
38
|
+
/** Database system (e.g., 'postgresql', 'mongodb', 'redis') */
|
|
39
|
+
system: string;
|
|
40
|
+
/** Operation type (e.g., 'SELECT', 'INSERT', 'find', 'get') */
|
|
41
|
+
operation?: string;
|
|
42
|
+
/** Database name */
|
|
43
|
+
dbName?: string;
|
|
44
|
+
/** Collection/table name */
|
|
45
|
+
collection?: string;
|
|
46
|
+
/** Additional attributes to add to the span */
|
|
47
|
+
attributes?: Attributes;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Configuration for HTTP client operations
|
|
52
|
+
*
|
|
53
|
+
* Follows HTTP semantic conventions:
|
|
54
|
+
* https://opentelemetry.io/docs/specs/semconv/http/
|
|
55
|
+
*/
|
|
56
|
+
export interface HTTPConfig {
|
|
57
|
+
/** HTTP method (e.g., 'GET', 'POST') */
|
|
58
|
+
method?: string;
|
|
59
|
+
/** Target URL or URL template */
|
|
60
|
+
url?: string;
|
|
61
|
+
/** Additional attributes to add to the span */
|
|
62
|
+
attributes?: Attributes;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Configuration for messaging operations
|
|
67
|
+
*
|
|
68
|
+
* Follows Messaging semantic conventions:
|
|
69
|
+
* https://opentelemetry.io/docs/specs/semconv/messaging/
|
|
70
|
+
*/
|
|
71
|
+
export interface MessagingConfig {
|
|
72
|
+
/** Messaging system (e.g., 'kafka', 'rabbitmq', 'sqs') */
|
|
73
|
+
system: string;
|
|
74
|
+
/** Operation type */
|
|
75
|
+
operation?: 'publish' | 'receive' | 'process';
|
|
76
|
+
/** Destination name (queue/topic) */
|
|
77
|
+
destination?: string;
|
|
78
|
+
/** Additional attributes to add to the span */
|
|
79
|
+
attributes?: Attributes;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Trace LLM operations with Gen AI semantic conventions
|
|
84
|
+
*
|
|
85
|
+
* Automatically adds standard attributes for LLM operations:
|
|
86
|
+
* - gen.ai.request.model
|
|
87
|
+
* - gen.ai.operation.name
|
|
88
|
+
* - gen.ai.system
|
|
89
|
+
*
|
|
90
|
+
* **Use Cases:**
|
|
91
|
+
* - Chat completions
|
|
92
|
+
* - Text generation
|
|
93
|
+
* - Embeddings
|
|
94
|
+
* - Multi-step LLM workflows
|
|
95
|
+
*
|
|
96
|
+
* @param config - LLM operation configuration
|
|
97
|
+
* @returns Traced function factory with Gen AI attributes
|
|
98
|
+
*
|
|
99
|
+
* @example Chat completion with OpenAI
|
|
100
|
+
* ```typescript
|
|
101
|
+
* import { traceLLM } from 'autotel/semantic-helpers'
|
|
102
|
+
* import OpenAI from 'openai'
|
|
103
|
+
*
|
|
104
|
+
* const openai = new OpenAI()
|
|
105
|
+
*
|
|
106
|
+
* export const generateResponse = traceLLM({
|
|
107
|
+
* model: 'gpt-4-turbo',
|
|
108
|
+
* operation: 'chat',
|
|
109
|
+
* system: 'openai'
|
|
110
|
+
* })(ctx => async (prompt: string) => {
|
|
111
|
+
* const response = await openai.chat.completions.create({
|
|
112
|
+
* model: 'gpt-4-turbo',
|
|
113
|
+
* messages: [{ role: 'user', content: prompt }]
|
|
114
|
+
* })
|
|
115
|
+
*
|
|
116
|
+
* // Add usage metrics to span
|
|
117
|
+
* ctx.setAttribute('gen.ai.usage.completion_tokens', response.usage?.completion_tokens)
|
|
118
|
+
* ctx.setAttribute('gen.ai.usage.prompt_tokens', response.usage?.prompt_tokens)
|
|
119
|
+
*
|
|
120
|
+
* return response.choices[0].message.content
|
|
121
|
+
* })
|
|
122
|
+
* ```
|
|
123
|
+
*
|
|
124
|
+
* @example Anthropic Claude with streaming
|
|
125
|
+
* ```typescript
|
|
126
|
+
* import { traceLLM } from 'autotel/semantic-helpers'
|
|
127
|
+
* import Anthropic from '@anthropic-ai/sdk'
|
|
128
|
+
*
|
|
129
|
+
* const anthropic = new Anthropic()
|
|
130
|
+
*
|
|
131
|
+
* export const streamResponse = traceLLM({
|
|
132
|
+
* model: 'claude-3-opus-20240229',
|
|
133
|
+
* operation: 'chat',
|
|
134
|
+
* system: 'anthropic'
|
|
135
|
+
* })(ctx => async function* (prompt: string) {
|
|
136
|
+
* const stream = await anthropic.messages.create({
|
|
137
|
+
* model: 'claude-3-opus-20240229',
|
|
138
|
+
* messages: [{ role: 'user', content: prompt }],
|
|
139
|
+
* stream: true,
|
|
140
|
+
* max_tokens: 1024
|
|
141
|
+
* })
|
|
142
|
+
*
|
|
143
|
+
* let totalTokens = 0
|
|
144
|
+
* for await (const event of stream) {
|
|
145
|
+
* if (event.type === 'content_block_delta') {
|
|
146
|
+
* yield event.delta.text
|
|
147
|
+
* }
|
|
148
|
+
* if (event.type === 'message_stop') {
|
|
149
|
+
* ctx.setAttribute('gen.ai.usage.completion_tokens', event.message.usage.output_tokens)
|
|
150
|
+
* totalTokens = event.message.usage.output_tokens
|
|
151
|
+
* }
|
|
152
|
+
* }
|
|
153
|
+
*
|
|
154
|
+
* return totalTokens
|
|
155
|
+
* })
|
|
156
|
+
* ```
|
|
157
|
+
*
|
|
158
|
+
* @example Embeddings
|
|
159
|
+
* ```typescript
|
|
160
|
+
* import { traceLLM } from 'autotel/semantic-helpers'
|
|
161
|
+
* import { OpenAIEmbeddings } from '@langchain/openai'
|
|
162
|
+
*
|
|
163
|
+
* const embeddings = new OpenAIEmbeddings()
|
|
164
|
+
*
|
|
165
|
+
* export const embed = traceLLM({
|
|
166
|
+
* model: 'text-embedding-3-small',
|
|
167
|
+
* operation: 'embedding',
|
|
168
|
+
* system: 'openai'
|
|
169
|
+
* })(ctx => async (text: string) => {
|
|
170
|
+
* const result = await embeddings.embedQuery(text)
|
|
171
|
+
* ctx.setAttribute('gen.ai.response.embedding_length', result.length)
|
|
172
|
+
* return result
|
|
173
|
+
* })
|
|
174
|
+
* ```
|
|
175
|
+
*
|
|
176
|
+
* @public
|
|
177
|
+
*/
|
|
178
|
+
export function traceLLM<TArgs extends unknown[], TReturn>(config: LLMConfig) {
|
|
179
|
+
return (
|
|
180
|
+
fnFactory: (ctx: TraceContext) => (...args: TArgs) => Promise<TReturn>,
|
|
181
|
+
): ((...args: TArgs) => Promise<TReturn>) => {
|
|
182
|
+
return trace<TArgs, TReturn>((ctx) => {
|
|
183
|
+
// Set semantic convention attributes
|
|
184
|
+
ctx.setAttribute('gen.ai.request.model', config.model);
|
|
185
|
+
ctx.setAttribute('gen.ai.operation.name', config.operation || 'chat');
|
|
186
|
+
if (config.system) {
|
|
187
|
+
ctx.setAttribute('gen.ai.system', config.system);
|
|
188
|
+
}
|
|
189
|
+
if (config.attributes) {
|
|
190
|
+
for (const [key, value] of Object.entries(config.attributes)) {
|
|
191
|
+
if (value !== undefined && value !== null) {
|
|
192
|
+
// setAttribute only accepts primitives (string | number | boolean)
|
|
193
|
+
// Arrays and objects should be serialized
|
|
194
|
+
const attrValue =
|
|
195
|
+
typeof value === 'string' ||
|
|
196
|
+
typeof value === 'number' ||
|
|
197
|
+
typeof value === 'boolean'
|
|
198
|
+
? value
|
|
199
|
+
: JSON.stringify(value);
|
|
200
|
+
ctx.setAttribute(key, attrValue);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Call the user's factory to get their function and return it
|
|
206
|
+
return fnFactory(ctx);
|
|
207
|
+
});
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Trace database operations with DB semantic conventions
|
|
213
|
+
*
|
|
214
|
+
* Automatically adds standard attributes for database operations:
|
|
215
|
+
* - db.system
|
|
216
|
+
* - db.operation
|
|
217
|
+
* - db.name
|
|
218
|
+
* - db.collection.name (for NoSQL)
|
|
219
|
+
*
|
|
220
|
+
* **Use Cases:**
|
|
221
|
+
* - SQL queries (PostgreSQL, MySQL, SQLite)
|
|
222
|
+
* - NoSQL operations (MongoDB, DynamoDB, Redis)
|
|
223
|
+
* - ORM queries (Prisma, TypeORM, Drizzle)
|
|
224
|
+
*
|
|
225
|
+
* @param config - Database operation configuration
|
|
226
|
+
* @returns Traced function factory with DB attributes
|
|
227
|
+
*
|
|
228
|
+
* @example PostgreSQL query
|
|
229
|
+
* ```typescript
|
|
230
|
+
* import { traceDB } from 'autotel/semantic-helpers'
|
|
231
|
+
* import { pool } from './db'
|
|
232
|
+
*
|
|
233
|
+
* export const getUser = traceDB({
|
|
234
|
+
* system: 'postgresql',
|
|
235
|
+
* operation: 'SELECT',
|
|
236
|
+
* dbName: 'app_db',
|
|
237
|
+
* collection: 'users'
|
|
238
|
+
* })(ctx => async (userId: string) => {
|
|
239
|
+
* const query = 'SELECT * FROM users WHERE id = $1'
|
|
240
|
+
* ctx.setAttribute('db.statement', query)
|
|
241
|
+
*
|
|
242
|
+
* const result = await pool.query(query, [userId])
|
|
243
|
+
* ctx.setAttribute('db.rows_affected', result.rowCount)
|
|
244
|
+
*
|
|
245
|
+
* return result.rows[0]
|
|
246
|
+
* })
|
|
247
|
+
* ```
|
|
248
|
+
*
|
|
249
|
+
* @example MongoDB with Mongoose
|
|
250
|
+
* ```typescript
|
|
251
|
+
* import { traceDB } from 'autotel/semantic-helpers'
|
|
252
|
+
* import { User } from './models/User'
|
|
253
|
+
*
|
|
254
|
+
* export const findUsers = traceDB({
|
|
255
|
+
* system: 'mongodb',
|
|
256
|
+
* operation: 'find',
|
|
257
|
+
* dbName: 'app_db',
|
|
258
|
+
* collection: 'users'
|
|
259
|
+
* })(ctx => async (filter: object) => {
|
|
260
|
+
* ctx.setAttribute('db.mongodb.filter', JSON.stringify(filter))
|
|
261
|
+
*
|
|
262
|
+
* const users = await User.find(filter).limit(100)
|
|
263
|
+
* ctx.setAttribute('db.response.count', users.length)
|
|
264
|
+
*
|
|
265
|
+
* return users
|
|
266
|
+
* })
|
|
267
|
+
* ```
|
|
268
|
+
*
|
|
269
|
+
* @example Redis operations
|
|
270
|
+
* ```typescript
|
|
271
|
+
* import { traceDB } from 'autotel/semantic-helpers'
|
|
272
|
+
* import { redis } from './redis'
|
|
273
|
+
*
|
|
274
|
+
* export const cacheGet = traceDB({
|
|
275
|
+
* system: 'redis',
|
|
276
|
+
* operation: 'GET'
|
|
277
|
+
* })(ctx => async (key: string) => {
|
|
278
|
+
* ctx.setAttribute('db.redis.key', key)
|
|
279
|
+
*
|
|
280
|
+
* const value = await redis.get(key)
|
|
281
|
+
* ctx.setAttribute('db.response.cache_hit', value !== null)
|
|
282
|
+
*
|
|
283
|
+
* return value
|
|
284
|
+
* })
|
|
285
|
+
* ```
|
|
286
|
+
*
|
|
287
|
+
* @example Prisma with detailed query info
|
|
288
|
+
* ```typescript
|
|
289
|
+
* import { traceDB } from 'autotel/semantic-helpers'
|
|
290
|
+
* import { prisma } from './prisma'
|
|
291
|
+
*
|
|
292
|
+
* export const createPost = traceDB({
|
|
293
|
+
* system: 'postgresql',
|
|
294
|
+
* operation: 'INSERT',
|
|
295
|
+
* dbName: 'app_db',
|
|
296
|
+
* collection: 'posts'
|
|
297
|
+
* })(ctx => async (data: { title: string; content: string; authorId: string }) => {
|
|
298
|
+
* ctx.setAttribute('db.prisma.model', 'Post')
|
|
299
|
+
* ctx.setAttribute('db.prisma.action', 'create')
|
|
300
|
+
*
|
|
301
|
+
* const post = await prisma.post.create({ data })
|
|
302
|
+
*
|
|
303
|
+
* ctx.setAttribute('db.response.id', post.id)
|
|
304
|
+
* return post
|
|
305
|
+
* })
|
|
306
|
+
* ```
|
|
307
|
+
*
|
|
308
|
+
* @public
|
|
309
|
+
*/
|
|
310
|
+
export function traceDB<TArgs extends unknown[], TReturn>(config: DBConfig) {
|
|
311
|
+
return (
|
|
312
|
+
fnFactory: (ctx: TraceContext) => (...args: TArgs) => Promise<TReturn>,
|
|
313
|
+
): ((...args: TArgs) => Promise<TReturn>) => {
|
|
314
|
+
return trace<TArgs, TReturn>((ctx) => {
|
|
315
|
+
// Set semantic convention attributes
|
|
316
|
+
ctx.setAttribute('db.system', config.system);
|
|
317
|
+
if (config.operation) {
|
|
318
|
+
ctx.setAttribute('db.operation', config.operation);
|
|
319
|
+
}
|
|
320
|
+
if (config.dbName) {
|
|
321
|
+
ctx.setAttribute('db.name', config.dbName);
|
|
322
|
+
}
|
|
323
|
+
if (config.collection) {
|
|
324
|
+
ctx.setAttribute('db.collection.name', config.collection);
|
|
325
|
+
}
|
|
326
|
+
if (config.attributes) {
|
|
327
|
+
for (const [key, value] of Object.entries(config.attributes)) {
|
|
328
|
+
if (value !== undefined && value !== null) {
|
|
329
|
+
// setAttribute only accepts primitives (string | number | boolean)
|
|
330
|
+
// Arrays and objects should be serialized
|
|
331
|
+
const attrValue =
|
|
332
|
+
typeof value === 'string' ||
|
|
333
|
+
typeof value === 'number' ||
|
|
334
|
+
typeof value === 'boolean'
|
|
335
|
+
? value
|
|
336
|
+
: JSON.stringify(value);
|
|
337
|
+
ctx.setAttribute(key, attrValue);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Call the user's factory to get their function and return it
|
|
343
|
+
return fnFactory(ctx);
|
|
344
|
+
});
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Trace HTTP client operations with HTTP semantic conventions
|
|
350
|
+
*
|
|
351
|
+
* Automatically adds standard attributes for HTTP requests:
|
|
352
|
+
* - http.request.method
|
|
353
|
+
* - url.full
|
|
354
|
+
*
|
|
355
|
+
* **Use Cases:**
|
|
356
|
+
* - External API calls
|
|
357
|
+
* - Microservice communication
|
|
358
|
+
* - Third-party integrations
|
|
359
|
+
*
|
|
360
|
+
* @param config - HTTP operation configuration
|
|
361
|
+
* @returns Traced function factory with HTTP attributes
|
|
362
|
+
*
|
|
363
|
+
* @example Fetch API
|
|
364
|
+
* ```typescript
|
|
365
|
+
* import { traceHTTP } from 'autotel/semantic-helpers'
|
|
366
|
+
*
|
|
367
|
+
* export const fetchUser = traceHTTP({
|
|
368
|
+
* method: 'GET',
|
|
369
|
+
* url: 'https://api.example.com/users/:id'
|
|
370
|
+
* })(ctx => async (userId: string) => {
|
|
371
|
+
* const url = `https://api.example.com/users/${userId}`
|
|
372
|
+
* ctx.setAttribute('url.full', url)
|
|
373
|
+
*
|
|
374
|
+
* const response = await fetch(url)
|
|
375
|
+
* ctx.setAttribute('http.response.status_code', response.status)
|
|
376
|
+
*
|
|
377
|
+
* if (!response.ok) {
|
|
378
|
+
* ctx.setAttribute('error', true)
|
|
379
|
+
* throw new Error(`HTTP ${response.status}: ${response.statusText}`)
|
|
380
|
+
* }
|
|
381
|
+
*
|
|
382
|
+
* return response.json()
|
|
383
|
+
* })
|
|
384
|
+
* ```
|
|
385
|
+
*
|
|
386
|
+
* @example Axios with retry logic
|
|
387
|
+
* ```typescript
|
|
388
|
+
* import { traceHTTP } from 'autotel/semantic-helpers'
|
|
389
|
+
* import axios from 'axios'
|
|
390
|
+
*
|
|
391
|
+
* export const sendWebhook = traceHTTP({
|
|
392
|
+
* method: 'POST',
|
|
393
|
+
* url: 'https://webhook.example.com/events'
|
|
394
|
+
* })(ctx => async (payload: object) => {
|
|
395
|
+
* let attempts = 0
|
|
396
|
+
* const maxAttempts = 3
|
|
397
|
+
*
|
|
398
|
+
* while (attempts < maxAttempts) {
|
|
399
|
+
* try {
|
|
400
|
+
* attempts++
|
|
401
|
+
* ctx.setAttribute('http.request.resend_count', attempts - 1)
|
|
402
|
+
*
|
|
403
|
+
* const response = await axios.post('https://webhook.example.com/events', payload)
|
|
404
|
+
* ctx.setAttribute('http.response.status_code', response.status)
|
|
405
|
+
* return response.data
|
|
406
|
+
* } catch (error) {
|
|
407
|
+
* if (attempts >= maxAttempts) throw error
|
|
408
|
+
* await new Promise(resolve => setTimeout(resolve, 1000 * attempts))
|
|
409
|
+
* }
|
|
410
|
+
* }
|
|
411
|
+
* })
|
|
412
|
+
* ```
|
|
413
|
+
*
|
|
414
|
+
* @public
|
|
415
|
+
*/
|
|
416
|
+
export function traceHTTP<TArgs extends unknown[], TReturn>(
|
|
417
|
+
config: HTTPConfig,
|
|
418
|
+
) {
|
|
419
|
+
return (
|
|
420
|
+
fnFactory: (ctx: TraceContext) => (...args: TArgs) => Promise<TReturn>,
|
|
421
|
+
): ((...args: TArgs) => Promise<TReturn>) => {
|
|
422
|
+
return trace<TArgs, TReturn>((ctx) => {
|
|
423
|
+
// Set semantic convention attributes
|
|
424
|
+
if (config.method) {
|
|
425
|
+
ctx.setAttribute('http.request.method', config.method);
|
|
426
|
+
}
|
|
427
|
+
if (config.url) {
|
|
428
|
+
ctx.setAttribute('url.full', config.url);
|
|
429
|
+
}
|
|
430
|
+
if (config.attributes) {
|
|
431
|
+
for (const [key, value] of Object.entries(config.attributes)) {
|
|
432
|
+
if (value !== undefined && value !== null) {
|
|
433
|
+
// setAttribute only accepts primitives (string | number | boolean)
|
|
434
|
+
// Arrays and objects should be serialized
|
|
435
|
+
const attrValue =
|
|
436
|
+
typeof value === 'string' ||
|
|
437
|
+
typeof value === 'number' ||
|
|
438
|
+
typeof value === 'boolean'
|
|
439
|
+
? value
|
|
440
|
+
: JSON.stringify(value);
|
|
441
|
+
ctx.setAttribute(key, attrValue);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Call the user's factory to get their function and return it
|
|
447
|
+
return fnFactory(ctx);
|
|
448
|
+
});
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Trace messaging operations with Messaging semantic conventions
|
|
454
|
+
*
|
|
455
|
+
* Automatically adds standard attributes for messaging:
|
|
456
|
+
* - messaging.system
|
|
457
|
+
* - messaging.operation
|
|
458
|
+
* - messaging.destination.name
|
|
459
|
+
*
|
|
460
|
+
* **Use Cases:**
|
|
461
|
+
* - Publishing messages to queues/topics
|
|
462
|
+
* - Consuming messages from queues/topics
|
|
463
|
+
* - Event-driven architectures
|
|
464
|
+
*
|
|
465
|
+
* @param config - Messaging operation configuration
|
|
466
|
+
* @returns Traced function factory with Messaging attributes
|
|
467
|
+
*
|
|
468
|
+
* @example Publishing to Kafka
|
|
469
|
+
* ```typescript
|
|
470
|
+
* import { traceMessaging } from 'autotel/semantic-helpers'
|
|
471
|
+
* import { kafka } from './kafka'
|
|
472
|
+
*
|
|
473
|
+
* const producer = kafka.producer()
|
|
474
|
+
*
|
|
475
|
+
* export const publishEvent = traceMessaging({
|
|
476
|
+
* system: 'kafka',
|
|
477
|
+
* operation: 'publish',
|
|
478
|
+
* destination: 'user-events'
|
|
479
|
+
* })(ctx => async (event: { type: string; userId: string; data: object }) => {
|
|
480
|
+
* ctx.setAttribute('messaging.message.type', event.type)
|
|
481
|
+
* ctx.setAttribute('messaging.kafka.partition', 0)
|
|
482
|
+
*
|
|
483
|
+
* await producer.send({
|
|
484
|
+
* topic: 'user-events',
|
|
485
|
+
* messages: [
|
|
486
|
+
* {
|
|
487
|
+
* key: event.userId,
|
|
488
|
+
* value: JSON.stringify(event.data)
|
|
489
|
+
* }
|
|
490
|
+
* ]
|
|
491
|
+
* })
|
|
492
|
+
*
|
|
493
|
+
* ctx.setAttribute('messaging.message.id', event.userId)
|
|
494
|
+
* })
|
|
495
|
+
* ```
|
|
496
|
+
*
|
|
497
|
+
* @example Consuming from RabbitMQ
|
|
498
|
+
* ```typescript
|
|
499
|
+
* import { traceMessaging } from 'autotel/semantic-helpers'
|
|
500
|
+
* import { channel } from './rabbitmq'
|
|
501
|
+
*
|
|
502
|
+
* export const processOrder = traceMessaging({
|
|
503
|
+
* system: 'rabbitmq',
|
|
504
|
+
* operation: 'process',
|
|
505
|
+
* destination: 'orders'
|
|
506
|
+
* })(ctx => async (message: { orderId: string; items: object[] }) => {
|
|
507
|
+
* ctx.setAttribute('messaging.message.id', message.orderId)
|
|
508
|
+
* ctx.setAttribute('messaging.message.body.size', JSON.stringify(message).length)
|
|
509
|
+
*
|
|
510
|
+
* // Process order logic
|
|
511
|
+
* const result = await processOrderInternal(message)
|
|
512
|
+
*
|
|
513
|
+
* ctx.setAttribute('messaging.operation.result', 'success')
|
|
514
|
+
* return result
|
|
515
|
+
* })
|
|
516
|
+
* ```
|
|
517
|
+
*
|
|
518
|
+
* @example AWS SQS with batch processing
|
|
519
|
+
* ```typescript
|
|
520
|
+
* import { traceMessaging } from 'autotel/semantic-helpers'
|
|
521
|
+
* import { SQS } from '@aws-sdk/client-sqs'
|
|
522
|
+
*
|
|
523
|
+
* const sqs = new SQS()
|
|
524
|
+
*
|
|
525
|
+
* export const sendBatch = traceMessaging({
|
|
526
|
+
* system: 'aws_sqs',
|
|
527
|
+
* operation: 'publish',
|
|
528
|
+
* destination: 'notifications-queue'
|
|
529
|
+
* })(ctx => async (messages: Array<{ id: string; body: object }>) => {
|
|
530
|
+
* ctx.setAttribute('messaging.batch.message_count', messages.length)
|
|
531
|
+
*
|
|
532
|
+
* const result = await sqs.sendMessageBatch({
|
|
533
|
+
* QueueUrl: process.env.QUEUE_URL,
|
|
534
|
+
* Entries: messages.map(msg => ({
|
|
535
|
+
* Id: msg.id,
|
|
536
|
+
* MessageBody: JSON.stringify(msg.body)
|
|
537
|
+
* }))
|
|
538
|
+
* })
|
|
539
|
+
*
|
|
540
|
+
* ctx.setAttribute('messaging.operation.success_count', result.Successful?.length || 0)
|
|
541
|
+
* ctx.setAttribute('messaging.operation.failed_count', result.Failed?.length || 0)
|
|
542
|
+
*
|
|
543
|
+
* return result
|
|
544
|
+
* })
|
|
545
|
+
* ```
|
|
546
|
+
*
|
|
547
|
+
* @public
|
|
548
|
+
*/
|
|
549
|
+
export function traceMessaging<TArgs extends unknown[], TReturn>(
|
|
550
|
+
config: MessagingConfig,
|
|
551
|
+
) {
|
|
552
|
+
return (
|
|
553
|
+
fnFactory: (ctx: TraceContext) => (...args: TArgs) => Promise<TReturn>,
|
|
554
|
+
): ((...args: TArgs) => Promise<TReturn>) => {
|
|
555
|
+
return trace<TArgs, TReturn>((ctx) => {
|
|
556
|
+
// Set semantic convention attributes
|
|
557
|
+
ctx.setAttribute('messaging.system', config.system);
|
|
558
|
+
if (config.operation) {
|
|
559
|
+
ctx.setAttribute('messaging.operation', config.operation);
|
|
560
|
+
}
|
|
561
|
+
if (config.destination) {
|
|
562
|
+
ctx.setAttribute('messaging.destination.name', config.destination);
|
|
563
|
+
}
|
|
564
|
+
if (config.attributes) {
|
|
565
|
+
for (const [key, value] of Object.entries(config.attributes)) {
|
|
566
|
+
if (value !== undefined && value !== null) {
|
|
567
|
+
// setAttribute only accepts primitives (string | number | boolean)
|
|
568
|
+
// Arrays and objects should be serialized
|
|
569
|
+
const attrValue =
|
|
570
|
+
typeof value === 'string' ||
|
|
571
|
+
typeof value === 'number' ||
|
|
572
|
+
typeof value === 'boolean'
|
|
573
|
+
? value
|
|
574
|
+
: JSON.stringify(value);
|
|
575
|
+
ctx.setAttribute(key, attrValue);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Call the user's factory to get their function and return it
|
|
581
|
+
return fnFactory(ctx);
|
|
582
|
+
});
|
|
583
|
+
};
|
|
584
|
+
}
|