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.
Files changed (272) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1946 -0
  3. package/dist/chunk-2LNRY4QK.js +273 -0
  4. package/dist/chunk-2LNRY4QK.js.map +1 -0
  5. package/dist/chunk-3HENGDW2.js +587 -0
  6. package/dist/chunk-3HENGDW2.js.map +1 -0
  7. package/dist/chunk-4OAT42CA.cjs +73 -0
  8. package/dist/chunk-4OAT42CA.cjs.map +1 -0
  9. package/dist/chunk-5GWX5LFW.js +70 -0
  10. package/dist/chunk-5GWX5LFW.js.map +1 -0
  11. package/dist/chunk-5R2M36QB.js +195 -0
  12. package/dist/chunk-5R2M36QB.js.map +1 -0
  13. package/dist/chunk-5ZN622AO.js +73 -0
  14. package/dist/chunk-5ZN622AO.js.map +1 -0
  15. package/dist/chunk-77MSMAUQ.cjs +498 -0
  16. package/dist/chunk-77MSMAUQ.cjs.map +1 -0
  17. package/dist/chunk-ABPEQ6RK.cjs +596 -0
  18. package/dist/chunk-ABPEQ6RK.cjs.map +1 -0
  19. package/dist/chunk-BWYGJKRB.js +95 -0
  20. package/dist/chunk-BWYGJKRB.js.map +1 -0
  21. package/dist/chunk-BZHG5IZ4.js +73 -0
  22. package/dist/chunk-BZHG5IZ4.js.map +1 -0
  23. package/dist/chunk-G7VZBCD6.cjs +35 -0
  24. package/dist/chunk-G7VZBCD6.cjs.map +1 -0
  25. package/dist/chunk-GVLK7YUU.cjs +30 -0
  26. package/dist/chunk-GVLK7YUU.cjs.map +1 -0
  27. package/dist/chunk-HCCXC7XG.js +205 -0
  28. package/dist/chunk-HCCXC7XG.js.map +1 -0
  29. package/dist/chunk-HE6T6FIX.cjs +203 -0
  30. package/dist/chunk-HE6T6FIX.cjs.map +1 -0
  31. package/dist/chunk-KIXWPOCO.cjs +100 -0
  32. package/dist/chunk-KIXWPOCO.cjs.map +1 -0
  33. package/dist/chunk-KVGNW3FC.js +87 -0
  34. package/dist/chunk-KVGNW3FC.js.map +1 -0
  35. package/dist/chunk-LITNXTTT.js +3 -0
  36. package/dist/chunk-LITNXTTT.js.map +1 -0
  37. package/dist/chunk-M4ANN7RL.js +114 -0
  38. package/dist/chunk-M4ANN7RL.js.map +1 -0
  39. package/dist/chunk-NC52UBR2.cjs +32 -0
  40. package/dist/chunk-NC52UBR2.cjs.map +1 -0
  41. package/dist/chunk-NHCNRQD3.cjs +212 -0
  42. package/dist/chunk-NHCNRQD3.cjs.map +1 -0
  43. package/dist/chunk-NZ72VDNY.cjs +4 -0
  44. package/dist/chunk-NZ72VDNY.cjs.map +1 -0
  45. package/dist/chunk-P6JUDYNO.js +57 -0
  46. package/dist/chunk-P6JUDYNO.js.map +1 -0
  47. package/dist/chunk-RJYY7BWX.js +1349 -0
  48. package/dist/chunk-RJYY7BWX.js.map +1 -0
  49. package/dist/chunk-TRI4V5BF.cjs +126 -0
  50. package/dist/chunk-TRI4V5BF.cjs.map +1 -0
  51. package/dist/chunk-UL33I6IS.js +139 -0
  52. package/dist/chunk-UL33I6IS.js.map +1 -0
  53. package/dist/chunk-URRW6M2C.cjs +61 -0
  54. package/dist/chunk-URRW6M2C.cjs.map +1 -0
  55. package/dist/chunk-UY3UYPBZ.cjs +77 -0
  56. package/dist/chunk-UY3UYPBZ.cjs.map +1 -0
  57. package/dist/chunk-W3253FGB.cjs +277 -0
  58. package/dist/chunk-W3253FGB.cjs.map +1 -0
  59. package/dist/chunk-W7LHZVQF.js +26 -0
  60. package/dist/chunk-W7LHZVQF.js.map +1 -0
  61. package/dist/chunk-WBWNM6LB.cjs +1360 -0
  62. package/dist/chunk-WBWNM6LB.cjs.map +1 -0
  63. package/dist/chunk-WFJ7L2RV.js +494 -0
  64. package/dist/chunk-WFJ7L2RV.js.map +1 -0
  65. package/dist/chunk-X4RMFFMR.js +28 -0
  66. package/dist/chunk-X4RMFFMR.js.map +1 -0
  67. package/dist/chunk-Y4Y2S7BM.cjs +92 -0
  68. package/dist/chunk-Y4Y2S7BM.cjs.map +1 -0
  69. package/dist/chunk-YLPNXZFI.cjs +143 -0
  70. package/dist/chunk-YLPNXZFI.cjs.map +1 -0
  71. package/dist/chunk-YTXEZ4SD.cjs +77 -0
  72. package/dist/chunk-YTXEZ4SD.cjs.map +1 -0
  73. package/dist/chunk-Z6ZWNWWR.js +30 -0
  74. package/dist/chunk-Z6ZWNWWR.js.map +1 -0
  75. package/dist/config.cjs +26 -0
  76. package/dist/config.cjs.map +1 -0
  77. package/dist/config.d.cts +75 -0
  78. package/dist/config.d.ts +75 -0
  79. package/dist/config.js +5 -0
  80. package/dist/config.js.map +1 -0
  81. package/dist/db.cjs +233 -0
  82. package/dist/db.cjs.map +1 -0
  83. package/dist/db.d.cts +123 -0
  84. package/dist/db.d.ts +123 -0
  85. package/dist/db.js +228 -0
  86. package/dist/db.js.map +1 -0
  87. package/dist/decorators.cjs +67 -0
  88. package/dist/decorators.cjs.map +1 -0
  89. package/dist/decorators.d.cts +91 -0
  90. package/dist/decorators.d.ts +91 -0
  91. package/dist/decorators.js +65 -0
  92. package/dist/decorators.js.map +1 -0
  93. package/dist/event-subscriber.cjs +6 -0
  94. package/dist/event-subscriber.cjs.map +1 -0
  95. package/dist/event-subscriber.d.cts +116 -0
  96. package/dist/event-subscriber.d.ts +116 -0
  97. package/dist/event-subscriber.js +3 -0
  98. package/dist/event-subscriber.js.map +1 -0
  99. package/dist/event-testing.cjs +21 -0
  100. package/dist/event-testing.cjs.map +1 -0
  101. package/dist/event-testing.d.cts +110 -0
  102. package/dist/event-testing.d.ts +110 -0
  103. package/dist/event-testing.js +4 -0
  104. package/dist/event-testing.js.map +1 -0
  105. package/dist/event.cjs +30 -0
  106. package/dist/event.cjs.map +1 -0
  107. package/dist/event.d.cts +282 -0
  108. package/dist/event.d.ts +282 -0
  109. package/dist/event.js +13 -0
  110. package/dist/event.js.map +1 -0
  111. package/dist/exporters.cjs +17 -0
  112. package/dist/exporters.cjs.map +1 -0
  113. package/dist/exporters.d.cts +1 -0
  114. package/dist/exporters.d.ts +1 -0
  115. package/dist/exporters.js +4 -0
  116. package/dist/exporters.js.map +1 -0
  117. package/dist/functional.cjs +46 -0
  118. package/dist/functional.cjs.map +1 -0
  119. package/dist/functional.d.cts +478 -0
  120. package/dist/functional.d.ts +478 -0
  121. package/dist/functional.js +13 -0
  122. package/dist/functional.js.map +1 -0
  123. package/dist/http.cjs +189 -0
  124. package/dist/http.cjs.map +1 -0
  125. package/dist/http.d.cts +169 -0
  126. package/dist/http.d.ts +169 -0
  127. package/dist/http.js +184 -0
  128. package/dist/http.js.map +1 -0
  129. package/dist/index.cjs +333 -0
  130. package/dist/index.cjs.map +1 -0
  131. package/dist/index.d.cts +758 -0
  132. package/dist/index.d.ts +758 -0
  133. package/dist/index.js +143 -0
  134. package/dist/index.js.map +1 -0
  135. package/dist/instrumentation.cjs +182 -0
  136. package/dist/instrumentation.cjs.map +1 -0
  137. package/dist/instrumentation.d.cts +49 -0
  138. package/dist/instrumentation.d.ts +49 -0
  139. package/dist/instrumentation.js +179 -0
  140. package/dist/instrumentation.js.map +1 -0
  141. package/dist/logger.cjs +19 -0
  142. package/dist/logger.cjs.map +1 -0
  143. package/dist/logger.d.cts +146 -0
  144. package/dist/logger.d.ts +146 -0
  145. package/dist/logger.js +6 -0
  146. package/dist/logger.js.map +1 -0
  147. package/dist/metric-helpers.cjs +31 -0
  148. package/dist/metric-helpers.cjs.map +1 -0
  149. package/dist/metric-helpers.d.cts +13 -0
  150. package/dist/metric-helpers.d.ts +13 -0
  151. package/dist/metric-helpers.js +6 -0
  152. package/dist/metric-helpers.js.map +1 -0
  153. package/dist/metric-testing.cjs +21 -0
  154. package/dist/metric-testing.cjs.map +1 -0
  155. package/dist/metric-testing.d.cts +110 -0
  156. package/dist/metric-testing.d.ts +110 -0
  157. package/dist/metric-testing.js +4 -0
  158. package/dist/metric-testing.js.map +1 -0
  159. package/dist/metric.cjs +26 -0
  160. package/dist/metric.cjs.map +1 -0
  161. package/dist/metric.d.cts +240 -0
  162. package/dist/metric.d.ts +240 -0
  163. package/dist/metric.js +9 -0
  164. package/dist/metric.js.map +1 -0
  165. package/dist/processors.cjs +17 -0
  166. package/dist/processors.cjs.map +1 -0
  167. package/dist/processors.d.cts +1 -0
  168. package/dist/processors.d.ts +1 -0
  169. package/dist/processors.js +4 -0
  170. package/dist/processors.js.map +1 -0
  171. package/dist/sampling.cjs +40 -0
  172. package/dist/sampling.cjs.map +1 -0
  173. package/dist/sampling.d.cts +260 -0
  174. package/dist/sampling.d.ts +260 -0
  175. package/dist/sampling.js +7 -0
  176. package/dist/sampling.js.map +1 -0
  177. package/dist/semantic-helpers.cjs +35 -0
  178. package/dist/semantic-helpers.cjs.map +1 -0
  179. package/dist/semantic-helpers.d.cts +442 -0
  180. package/dist/semantic-helpers.d.ts +442 -0
  181. package/dist/semantic-helpers.js +14 -0
  182. package/dist/semantic-helpers.js.map +1 -0
  183. package/dist/tail-sampling-processor.cjs +13 -0
  184. package/dist/tail-sampling-processor.cjs.map +1 -0
  185. package/dist/tail-sampling-processor.d.cts +27 -0
  186. package/dist/tail-sampling-processor.d.ts +27 -0
  187. package/dist/tail-sampling-processor.js +4 -0
  188. package/dist/tail-sampling-processor.js.map +1 -0
  189. package/dist/testing.cjs +286 -0
  190. package/dist/testing.cjs.map +1 -0
  191. package/dist/testing.d.cts +291 -0
  192. package/dist/testing.d.ts +291 -0
  193. package/dist/testing.js +263 -0
  194. package/dist/testing.js.map +1 -0
  195. package/dist/trace-context-DRZdUvVY.d.cts +181 -0
  196. package/dist/trace-context-DRZdUvVY.d.ts +181 -0
  197. package/dist/trace-helpers.cjs +54 -0
  198. package/dist/trace-helpers.cjs.map +1 -0
  199. package/dist/trace-helpers.d.cts +524 -0
  200. package/dist/trace-helpers.d.ts +524 -0
  201. package/dist/trace-helpers.js +5 -0
  202. package/dist/trace-helpers.js.map +1 -0
  203. package/dist/tracer-provider.cjs +21 -0
  204. package/dist/tracer-provider.cjs.map +1 -0
  205. package/dist/tracer-provider.d.cts +169 -0
  206. package/dist/tracer-provider.d.ts +169 -0
  207. package/dist/tracer-provider.js +4 -0
  208. package/dist/tracer-provider.js.map +1 -0
  209. package/package.json +280 -0
  210. package/src/baggage-span-processor.test.ts +202 -0
  211. package/src/baggage-span-processor.ts +98 -0
  212. package/src/circuit-breaker.test.ts +341 -0
  213. package/src/circuit-breaker.ts +184 -0
  214. package/src/config.test.ts +94 -0
  215. package/src/config.ts +169 -0
  216. package/src/db.test.ts +252 -0
  217. package/src/db.ts +447 -0
  218. package/src/decorators.test.ts +203 -0
  219. package/src/decorators.ts +188 -0
  220. package/src/env-config.test.ts +246 -0
  221. package/src/env-config.ts +158 -0
  222. package/src/event-queue.test.ts +222 -0
  223. package/src/event-queue.ts +203 -0
  224. package/src/event-subscriber.ts +136 -0
  225. package/src/event-testing.ts +197 -0
  226. package/src/event.test.ts +718 -0
  227. package/src/event.ts +556 -0
  228. package/src/exporters.ts +96 -0
  229. package/src/functional.test.ts +1059 -0
  230. package/src/functional.ts +2295 -0
  231. package/src/http.test.ts +487 -0
  232. package/src/http.ts +424 -0
  233. package/src/index.ts +158 -0
  234. package/src/init.customization.test.ts +210 -0
  235. package/src/init.integrations.test.ts +366 -0
  236. package/src/init.openllmetry.test.ts +282 -0
  237. package/src/init.protocol.test.ts +215 -0
  238. package/src/init.ts +1426 -0
  239. package/src/instrumentation.test.ts +108 -0
  240. package/src/instrumentation.ts +308 -0
  241. package/src/logger.test.ts +117 -0
  242. package/src/logger.ts +246 -0
  243. package/src/metric-helpers.ts +47 -0
  244. package/src/metric-testing.ts +197 -0
  245. package/src/metric.ts +434 -0
  246. package/src/metrics.test.ts +205 -0
  247. package/src/operation-context.ts +93 -0
  248. package/src/processors.ts +106 -0
  249. package/src/rate-limiter.test.ts +199 -0
  250. package/src/rate-limiter.ts +98 -0
  251. package/src/sampling.test.ts +513 -0
  252. package/src/sampling.ts +428 -0
  253. package/src/semantic-helpers.test.ts +311 -0
  254. package/src/semantic-helpers.ts +584 -0
  255. package/src/shutdown.test.ts +311 -0
  256. package/src/shutdown.ts +222 -0
  257. package/src/stub.integration.test.ts +361 -0
  258. package/src/tail-sampling-processor.test.ts +226 -0
  259. package/src/tail-sampling-processor.ts +51 -0
  260. package/src/testing.ts +670 -0
  261. package/src/trace-context.ts +470 -0
  262. package/src/trace-helpers.new.test.ts +278 -0
  263. package/src/trace-helpers.test.ts +242 -0
  264. package/src/trace-helpers.ts +690 -0
  265. package/src/tracer-provider.test.ts +183 -0
  266. package/src/tracer-provider.ts +266 -0
  267. package/src/track.test.ts +153 -0
  268. package/src/track.ts +120 -0
  269. package/src/validation.test.ts +306 -0
  270. package/src/validation.ts +239 -0
  271. package/src/variable-name-inference.test.ts +178 -0
  272. 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
+ }