@geekmidas/telescope 1.1.1 → 10.0.0-alpha.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 (167) hide show
  1. package/README.md +14 -4
  2. package/dist/{hono-Bz9PQQzj.mjs → hono-Bw-ey6CT.mjs} +2 -2
  3. package/dist/{hono-Bz9PQQzj.mjs.map → hono-Bw-ey6CT.mjs.map} +1 -1
  4. package/dist/{hono-Z_JIICmt.cjs → hono-CPHX7FAm.cjs} +2 -2
  5. package/dist/{hono-Z_JIICmt.cjs.map → hono-CPHX7FAm.cjs.map} +1 -1
  6. package/dist/{http-Crsriv32.mjs → http-BIvr0YdI.mjs} +1 -1
  7. package/dist/{http-Crsriv32.mjs.map → http-BIvr0YdI.mjs.map} +1 -1
  8. package/dist/{http-JycFRfSe.cjs → http-BsqmhurZ.cjs} +1 -1
  9. package/dist/{http-JycFRfSe.cjs.map → http-BsqmhurZ.cjs.map} +1 -1
  10. package/dist/{http-D5kn3_Mt.d.mts → http-DULM1u2L.d.cts} +3 -3
  11. package/dist/{http-D5kn3_Mt.d.mts.map → http-DULM1u2L.d.cts.map} +1 -1
  12. package/dist/{http-Dvca6fC3.d.cts → http-GB-Nlvwc.d.mts} +3 -3
  13. package/dist/{http-Dvca6fC3.d.cts.map → http-GB-Nlvwc.d.mts.map} +1 -1
  14. package/dist/instrumentation/hono.cjs +2 -2
  15. package/dist/instrumentation/hono.mjs +2 -2
  16. package/dist/instrumentation/http.cjs +1 -1
  17. package/dist/instrumentation/http.d.cts +1 -1
  18. package/dist/instrumentation/http.d.mts +1 -1
  19. package/dist/instrumentation/http.mjs +1 -1
  20. package/dist/instrumentation/index.cjs +5 -5
  21. package/dist/instrumentation/index.d.cts +3 -3
  22. package/dist/instrumentation/index.d.mts +3 -3
  23. package/dist/instrumentation/index.mjs +5 -5
  24. package/dist/instrumentation/middleware.cjs +2 -2
  25. package/dist/instrumentation/middleware.d.cts +1 -1
  26. package/dist/instrumentation/middleware.d.mts +1 -1
  27. package/dist/instrumentation/middleware.mjs +2 -2
  28. package/dist/instrumentation/otel.cjs +2 -2
  29. package/dist/instrumentation/otel.d.cts +1 -1
  30. package/dist/instrumentation/otel.d.mts +1 -1
  31. package/dist/instrumentation/otel.mjs +2 -2
  32. package/dist/instrumentation/setup.cjs +1 -1
  33. package/dist/instrumentation/setup.mjs +1 -1
  34. package/dist/logger/console.d.cts.map +1 -1
  35. package/dist/{middleware-D3v5SnSs.cjs → middleware-Bcq6XXGG.cjs} +3 -3
  36. package/dist/middleware-Bcq6XXGG.cjs.map +1 -0
  37. package/dist/{middleware-Dy1ctr2N.mjs → middleware-D1XEHvyy.mjs} +3 -3
  38. package/dist/middleware-D1XEHvyy.mjs.map +1 -0
  39. package/dist/{middleware-CA84nsNN.d.cts → middleware-PeyS-9Ny.d.cts} +2 -2
  40. package/dist/{middleware-CA84nsNN.d.cts.map → middleware-PeyS-9Ny.d.cts.map} +1 -1
  41. package/dist/{middleware-DAceVpfw.d.mts → middleware-oHvo_ZgU.d.mts} +2 -2
  42. package/dist/{middleware-DAceVpfw.d.mts.map → middleware-oHvo_ZgU.d.mts.map} +1 -1
  43. package/dist/{otel-DPKNyJ0t.mjs → otel-B9bNoeZ2.mjs} +4 -4
  44. package/dist/otel-B9bNoeZ2.mjs.map +1 -0
  45. package/dist/{otel-1bJPb2Z7.d.mts → otel-Bk89b1lX.d.cts} +3 -3
  46. package/dist/{otel-1bJPb2Z7.d.mts.map → otel-Bk89b1lX.d.cts.map} +1 -1
  47. package/dist/{otel-DuiBeBPU.cjs → otel-BzjOtVxZ.cjs} +4 -4
  48. package/dist/otel-BzjOtVxZ.cjs.map +1 -0
  49. package/dist/{otel-SBVoVEHr.d.cts → otel-lPMsM26e.d.mts} +3 -3
  50. package/dist/{otel-SBVoVEHr.d.cts.map → otel-lPMsM26e.d.mts.map} +1 -1
  51. package/dist/otlp/hono.cjs +3 -3
  52. package/dist/otlp/hono.mjs +3 -3
  53. package/dist/otlp/index.cjs +3 -3
  54. package/dist/otlp/index.mjs +3 -3
  55. package/dist/otlp/receiver.cjs +3 -3
  56. package/dist/otlp/receiver.mjs +3 -3
  57. package/dist/otlp/transformer.cjs +2 -2
  58. package/dist/otlp/transformer.mjs +2 -2
  59. package/dist/otlp/types.cjs +1 -1
  60. package/dist/otlp/types.mjs +1 -1
  61. package/dist/{receiver-q4f7eirs.mjs → receiver-D46dfkYI.mjs} +2 -2
  62. package/dist/{receiver-q4f7eirs.mjs.map → receiver-D46dfkYI.mjs.map} +1 -1
  63. package/dist/{receiver-BW_4go_X.cjs → receiver-slCoAFat.cjs} +2 -2
  64. package/dist/{receiver-BW_4go_X.cjs.map → receiver-slCoAFat.cjs.map} +1 -1
  65. package/dist/{setup-BhCp2LUp.mjs → setup-DC9JMEYZ.mjs} +1 -1
  66. package/dist/{setup-BhCp2LUp.mjs.map → setup-DC9JMEYZ.mjs.map} +1 -1
  67. package/dist/{setup-_uT4L_x5.cjs → setup-DufxQpi9.cjs} +1 -1
  68. package/dist/{setup-_uT4L_x5.cjs.map → setup-DufxQpi9.cjs.map} +1 -1
  69. package/dist/storage/kysely.cjs +48 -38
  70. package/dist/storage/kysely.cjs.map +1 -1
  71. package/dist/storage/kysely.d.cts +29 -9
  72. package/dist/storage/kysely.d.cts.map +1 -1
  73. package/dist/storage/kysely.d.mts +29 -9
  74. package/dist/storage/kysely.d.mts.map +1 -1
  75. package/dist/storage/kysely.mjs +48 -38
  76. package/dist/storage/kysely.mjs.map +1 -1
  77. package/dist/tracing-BX8uY2nR.d.cts.map +1 -1
  78. package/dist/tracing-PnECEbEu.d.mts.map +1 -1
  79. package/dist/{transformer-CFrJQkUg.mjs → transformer-CFQ4WFk6.mjs} +2 -2
  80. package/dist/{transformer-CFrJQkUg.mjs.map → transformer-CFQ4WFk6.mjs.map} +1 -1
  81. package/dist/{transformer-DvDYyFRQ.cjs → transformer-Csl6fbgv.cjs} +2 -2
  82. package/dist/{transformer-DvDYyFRQ.cjs.map → transformer-Csl6fbgv.cjs.map} +1 -1
  83. package/dist/types-B1EIa_cQ.d.mts.map +1 -1
  84. package/dist/{types-BBeTDpHr.cjs → types-BMiHgsi4.cjs} +1 -1
  85. package/dist/{types-BBeTDpHr.cjs.map → types-BMiHgsi4.cjs.map} +1 -1
  86. package/dist/{types-CXYLhG_G.mjs → types-Cj59nqy7.mjs} +1 -1
  87. package/dist/{types-CXYLhG_G.mjs.map → types-Cj59nqy7.mjs.map} +1 -1
  88. package/dist/types-l7Rlb5Mg.d.cts.map +1 -1
  89. package/package.json +6 -3
  90. package/CHANGELOG.md +0 -51
  91. package/dist/middleware-D3v5SnSs.cjs.map +0 -1
  92. package/dist/middleware-Dy1ctr2N.mjs.map +0 -1
  93. package/dist/otel-DPKNyJ0t.mjs.map +0 -1
  94. package/dist/otel-DuiBeBPU.cjs.map +0 -1
  95. package/src/Telescope.ts +0 -561
  96. package/src/__tests__/Telescope.spec.ts +0 -438
  97. package/src/__tests__/pino-transport.spec.ts +0 -115
  98. package/src/__tests__/redact.spec.ts +0 -336
  99. package/src/adapters/__tests__/lambda.spec.ts +0 -318
  100. package/src/adapters/__tests__/middy.spec.ts +0 -741
  101. package/src/adapters/index.ts +0 -22
  102. package/src/adapters/lambda.ts +0 -432
  103. package/src/adapters/middy.ts +0 -402
  104. package/src/adapters/types.ts +0 -181
  105. package/src/core.ts +0 -51
  106. package/src/index.ts +0 -34
  107. package/src/instrumentation/__tests__/core.spec.ts +0 -248
  108. package/src/instrumentation/__tests__/hono.spec.ts +0 -348
  109. package/src/instrumentation/__tests__/middleware.spec.ts +0 -528
  110. package/src/instrumentation/__tests__/otel.spec.ts +0 -291
  111. package/src/instrumentation/__tests__/tracing.spec.ts +0 -208
  112. package/src/instrumentation/core.ts +0 -228
  113. package/src/instrumentation/hono.ts +0 -270
  114. package/src/instrumentation/http.ts +0 -306
  115. package/src/instrumentation/index.ts +0 -90
  116. package/src/instrumentation/middleware.ts +0 -297
  117. package/src/instrumentation/otel.ts +0 -271
  118. package/src/instrumentation/setup.ts +0 -246
  119. package/src/instrumentation/tracing.ts +0 -145
  120. package/src/logger/__tests__/console.spec.ts +0 -366
  121. package/src/logger/__tests__/pino.spec.ts +0 -217
  122. package/src/logger/console.ts +0 -230
  123. package/src/logger/pino.ts +0 -201
  124. package/src/metrics/MetricsAggregator.ts +0 -555
  125. package/src/metrics/__tests__/MetricsAggregator.spec.ts +0 -298
  126. package/src/metrics/index.ts +0 -2
  127. package/src/metrics/storage/InMemoryMetricsStorage.ts +0 -380
  128. package/src/metrics/storage/MetricsStorage.ts +0 -74
  129. package/src/metrics/storage/index.ts +0 -3
  130. package/src/metrics/storage/types.ts +0 -243
  131. package/src/otlp/__tests__/receiver.spec.ts +0 -367
  132. package/src/otlp/__tests__/transformer.spec.ts +0 -455
  133. package/src/otlp/hono.ts +0 -120
  134. package/src/otlp/index.ts +0 -22
  135. package/src/otlp/receiver.ts +0 -162
  136. package/src/otlp/transformer.ts +0 -367
  137. package/src/otlp/types.ts +0 -364
  138. package/src/redact.ts +0 -116
  139. package/src/server/__tests__/hono.spec.ts +0 -814
  140. package/src/server/hono.ts +0 -442
  141. package/src/storage/__tests__/kysely.spec.ts +0 -715
  142. package/src/storage/__tests__/memory.spec.ts +0 -694
  143. package/src/storage/kysely.ts +0 -599
  144. package/src/storage/memory.ts +0 -204
  145. package/src/types.ts +0 -447
  146. package/src/ui-assets.ts +0 -40
  147. package/tsconfig.json +0 -9
  148. package/ui/CHANGELOG.md +0 -7
  149. package/ui/index.html +0 -12
  150. package/ui/node_modules/.bin/tsc +0 -21
  151. package/ui/node_modules/.bin/tsserver +0 -21
  152. package/ui/node_modules/.bin/vite +0 -21
  153. package/ui/package.json +0 -24
  154. package/ui/src/App.tsx +0 -595
  155. package/ui/src/api.ts +0 -88
  156. package/ui/src/components/ExceptionDetail.tsx +0 -100
  157. package/ui/src/components/FilterBar.tsx +0 -162
  158. package/ui/src/components/LogDetail.tsx +0 -91
  159. package/ui/src/components/RequestDetail.tsx +0 -143
  160. package/ui/src/main.tsx +0 -10
  161. package/ui/src/styles.css +0 -11
  162. package/ui/src/types.ts +0 -63
  163. package/ui/src/vite-env.d.ts +0 -1
  164. package/ui/src/vite-plugin-gkm-config.ts +0 -54
  165. package/ui/tsconfig.json +0 -20
  166. package/ui/tsconfig.tsbuildinfo +0 -1
  167. package/ui/vite.config.ts +0 -13
@@ -1,291 +0,0 @@
1
- import { trace } from '@opentelemetry/api';
2
- import {
3
- BasicTracerProvider,
4
- InMemorySpanExporter,
5
- SimpleSpanProcessor,
6
- } from '@opentelemetry/sdk-trace-base';
7
- import type { Context } from 'aws-lambda';
8
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
9
- import { OTelTelemetry } from '../otel';
10
-
11
- describe('OTelTelemetry', () => {
12
- let exporter: InMemorySpanExporter;
13
- let provider: BasicTracerProvider;
14
-
15
- const mockLambdaContext: Context = {
16
- awsRequestId: 'test-request-id',
17
- functionName: 'test-function',
18
- functionVersion: '1',
19
- invokedFunctionArn: 'arn:aws:lambda:us-east-1:123456789:function:test',
20
- memoryLimitInMB: '128',
21
- logGroupName: '/aws/lambda/test',
22
- logStreamName: '2024/01/01/[$LATEST]abc123',
23
- callbackWaitsForEmptyEventLoop: true,
24
- getRemainingTimeInMillis: () => 30000,
25
- done: () => {},
26
- fail: () => {},
27
- succeed: () => {},
28
- };
29
-
30
- beforeEach(() => {
31
- exporter = new InMemorySpanExporter();
32
- provider = new BasicTracerProvider();
33
- provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
34
- provider.register();
35
- });
36
-
37
- afterEach(async () => {
38
- exporter.reset();
39
- await provider.shutdown();
40
- trace.disable();
41
- });
42
-
43
- describe('API Gateway v2 events', () => {
44
- it('should create span for API Gateway v2 request', () => {
45
- const telemetry = new OTelTelemetry();
46
-
47
- const event = {
48
- requestContext: {
49
- http: {
50
- method: 'GET',
51
- path: '/api/users',
52
- sourceIp: '192.168.1.1',
53
- userAgent: 'test-agent',
54
- },
55
- requestId: 'apigw-request-id',
56
- },
57
- rawPath: '/api/users',
58
- rawQueryString: 'page=1',
59
- headers: { host: 'api.example.com' },
60
- };
61
-
62
- const ctx = telemetry.onRequestStart({
63
- event,
64
- context: mockLambdaContext,
65
- });
66
-
67
- telemetry.onRequestEnd(ctx, { statusCode: 200 });
68
-
69
- const spans = exporter.getFinishedSpans();
70
- expect(spans).toHaveLength(1);
71
- expect(spans[0].name).toBe('GET /api/users');
72
- expect(spans[0].attributes['http.request.method']).toBe('GET');
73
- expect(spans[0].attributes['url.path']).toBe('/api/users');
74
- expect(spans[0].attributes['faas.invocation_id']).toBe('test-request-id');
75
- expect(spans[0].attributes['faas.name']).toBe('test-function');
76
- });
77
-
78
- it('should record error on request failure', () => {
79
- const telemetry = new OTelTelemetry();
80
-
81
- const event = {
82
- requestContext: {
83
- http: { method: 'POST', path: '/api/users', sourceIp: '10.0.0.1' },
84
- requestId: 'req-123',
85
- },
86
- rawPath: '/api/users',
87
- headers: {},
88
- };
89
-
90
- const ctx = telemetry.onRequestStart({
91
- event,
92
- context: mockLambdaContext,
93
- });
94
-
95
- telemetry.onRequestError(ctx, new Error('Database connection failed'));
96
-
97
- const spans = exporter.getFinishedSpans();
98
- expect(spans).toHaveLength(1);
99
- expect(spans[0].status.code).toBe(2); // SpanStatusCode.ERROR
100
- expect(spans[0].events).toHaveLength(1);
101
- expect(spans[0].events[0].name).toBe('exception');
102
- });
103
- });
104
-
105
- describe('API Gateway v1 events', () => {
106
- it('should create span for API Gateway v1 request', () => {
107
- const telemetry = new OTelTelemetry();
108
-
109
- const event = {
110
- httpMethod: 'POST',
111
- path: '/api/orders',
112
- resource: '/api/orders',
113
- requestContext: {
114
- requestId: 'v1-request-id',
115
- identity: { sourceIp: '10.0.0.1' },
116
- },
117
- headers: {
118
- Host: 'api.example.com',
119
- 'User-Agent': 'test-client',
120
- },
121
- };
122
-
123
- const ctx = telemetry.onRequestStart({
124
- event,
125
- context: mockLambdaContext,
126
- });
127
-
128
- telemetry.onRequestEnd(ctx, { statusCode: 201 });
129
-
130
- const spans = exporter.getFinishedSpans();
131
- expect(spans).toHaveLength(1);
132
- expect(spans[0].attributes['http.request.method']).toBe('POST');
133
- expect(spans[0].attributes['url.path']).toBe('/api/orders');
134
- expect(spans[0].attributes['http.route']).toBe('/api/orders');
135
- });
136
- });
137
-
138
- describe('options', () => {
139
- it('should record request body when recordBody is true', () => {
140
- const telemetry = new OTelTelemetry({ recordBody: true });
141
-
142
- const event = {
143
- requestContext: {
144
- http: { method: 'POST', path: '/api/users', sourceIp: '10.0.0.1' },
145
- requestId: 'req-123',
146
- },
147
- rawPath: '/api/users',
148
- headers: {},
149
- body: JSON.stringify({ name: 'John', email: 'john@example.com' }),
150
- };
151
-
152
- const ctx = telemetry.onRequestStart({
153
- event,
154
- context: mockLambdaContext,
155
- });
156
-
157
- telemetry.onRequestEnd(ctx, { statusCode: 200 });
158
-
159
- const spans = exporter.getFinishedSpans();
160
- expect(spans[0].attributes['http.request.body']).toContain('John');
161
- });
162
-
163
- it('should record response body when recordResponseBody is true', () => {
164
- const telemetry = new OTelTelemetry({ recordResponseBody: true });
165
-
166
- const event = {
167
- requestContext: {
168
- http: { method: 'GET', path: '/api/users', sourceIp: '10.0.0.1' },
169
- requestId: 'req-123',
170
- },
171
- rawPath: '/api/users',
172
- headers: {},
173
- };
174
-
175
- const ctx = telemetry.onRequestStart({
176
- event,
177
- context: mockLambdaContext,
178
- });
179
-
180
- telemetry.onRequestEnd(ctx, {
181
- statusCode: 200,
182
- body: JSON.stringify({ users: [{ id: '1', name: 'John' }] }),
183
- });
184
-
185
- const spans = exporter.getFinishedSpans();
186
- expect(spans[0].attributes['http.response.body']).toContain('John');
187
- });
188
-
189
- it('should extract user ID using custom function', () => {
190
- const telemetry = new OTelTelemetry({
191
- getUserId: (event) => event.requestContext?.authorizer?.userId,
192
- });
193
-
194
- const event = {
195
- requestContext: {
196
- http: { method: 'GET', path: '/api/profile', sourceIp: '10.0.0.1' },
197
- requestId: 'req-123',
198
- authorizer: { userId: 'user-456' },
199
- },
200
- rawPath: '/api/profile',
201
- headers: {},
202
- };
203
-
204
- const ctx = telemetry.onRequestStart({
205
- event,
206
- context: mockLambdaContext,
207
- });
208
-
209
- telemetry.onRequestEnd(ctx, { statusCode: 200 });
210
-
211
- const spans = exporter.getFinishedSpans();
212
- expect(spans[0].attributes['enduser.id']).toBe('user-456');
213
- });
214
-
215
- it('should extract endpoint name using custom function', () => {
216
- const telemetry = new OTelTelemetry({
217
- getEndpointName: (event) => event.endpointName,
218
- });
219
-
220
- const event = {
221
- requestContext: {
222
- http: { method: 'GET', path: '/api/users', sourceIp: '10.0.0.1' },
223
- requestId: 'req-123',
224
- },
225
- rawPath: '/api/users',
226
- headers: {},
227
- endpointName: 'ListUsers',
228
- };
229
-
230
- const ctx = telemetry.onRequestStart({
231
- event,
232
- context: mockLambdaContext,
233
- });
234
-
235
- telemetry.onRequestEnd(ctx, { statusCode: 200 });
236
-
237
- const spans = exporter.getFinishedSpans();
238
- expect(spans[0].attributes['endpoint.name']).toBe('ListUsers');
239
- });
240
- });
241
-
242
- describe('ALB events', () => {
243
- it('should create span for ALB request', () => {
244
- const telemetry = new OTelTelemetry();
245
-
246
- const event = {
247
- httpMethod: 'GET',
248
- path: '/health',
249
- headers: {
250
- host: 'alb.example.com',
251
- 'user-agent': 'ALB-HealthChecker',
252
- 'x-forwarded-for': '10.0.0.1, 10.0.0.2',
253
- },
254
- };
255
-
256
- const ctx = telemetry.onRequestStart({
257
- event,
258
- context: mockLambdaContext,
259
- });
260
-
261
- telemetry.onRequestEnd(ctx, { statusCode: 200 });
262
-
263
- const spans = exporter.getFinishedSpans();
264
- expect(spans).toHaveLength(1);
265
- expect(spans[0].attributes['http.request.method']).toBe('GET');
266
- expect(spans[0].attributes['url.path']).toBe('/health');
267
- });
268
- });
269
-
270
- describe('direct Lambda invocation', () => {
271
- it('should create span for direct invocation', () => {
272
- const telemetry = new OTelTelemetry();
273
-
274
- const event = {
275
- action: 'processJob',
276
- data: { jobId: '123' },
277
- };
278
-
279
- const ctx = telemetry.onRequestStart({
280
- event,
281
- context: mockLambdaContext,
282
- });
283
-
284
- telemetry.onRequestEnd(ctx, { statusCode: 200 });
285
-
286
- const spans = exporter.getFinishedSpans();
287
- expect(spans).toHaveLength(1);
288
- expect(spans[0].attributes['http.request.method']).toBe('INVOKE');
289
- });
290
- });
291
- });
@@ -1,208 +0,0 @@
1
- import { trace } from '@opentelemetry/api';
2
- import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
3
- import { afterAll, beforeAll, describe, expect, it } from 'vitest';
4
- import {
5
- addSpanEvent,
6
- createSpan,
7
- getActiveSpan,
8
- getSpanId,
9
- getTraceId,
10
- recordException,
11
- setSpanAttributes,
12
- withSpan,
13
- withSpanSync,
14
- } from '../tracing';
15
-
16
- describe('Tracing Utilities', () => {
17
- let provider: NodeTracerProvider;
18
-
19
- beforeAll(() => {
20
- // Register a real tracer provider for tests
21
- provider = new NodeTracerProvider();
22
- provider.register();
23
- });
24
-
25
- afterAll(async () => {
26
- await provider.shutdown();
27
- // Reset to default noop provider
28
- trace.disable();
29
- });
30
- describe('createSpan', () => {
31
- it('should create a new span', () => {
32
- const span = createSpan('test-span');
33
- expect(span).toBeDefined();
34
- expect(span.spanContext().spanId).toBeDefined();
35
- span.end();
36
- });
37
-
38
- it('should create span with attributes', () => {
39
- const span = createSpan('test-span-attrs', { 'test.attr': 'value' });
40
- expect(span).toBeDefined();
41
- span.end();
42
- });
43
- });
44
-
45
- describe('withSpan', () => {
46
- it('should execute function within span context', async () => {
47
- const result = await withSpan('async-operation', async (span) => {
48
- expect(span).toBeDefined();
49
- expect(span.spanContext().spanId).toBeDefined();
50
- return 'result';
51
- });
52
-
53
- expect(result).toBe('result');
54
- });
55
-
56
- it('should propagate errors', async () => {
57
- await expect(
58
- withSpan('error-span', async () => {
59
- throw new Error('Test error');
60
- }),
61
- ).rejects.toThrow('Test error');
62
- });
63
-
64
- it('should pass attributes to span', async () => {
65
- let spanReceived = false;
66
- await withSpan(
67
- 'attr-span',
68
- async (span) => {
69
- spanReceived = true;
70
- expect(span).toBeDefined();
71
- return 'done';
72
- },
73
- { 'custom.attr': 123 },
74
- );
75
- expect(spanReceived).toBe(true);
76
- });
77
- });
78
-
79
- describe('withSpanSync', () => {
80
- it('should execute synchronous function within span context', () => {
81
- const result = withSpanSync('sync-operation', (span) => {
82
- expect(span).toBeDefined();
83
- return 42;
84
- });
85
-
86
- expect(result).toBe(42);
87
- });
88
-
89
- it('should handle errors in sync functions', () => {
90
- expect(() =>
91
- withSpanSync('sync-error', () => {
92
- throw new Error('Sync error');
93
- }),
94
- ).toThrow('Sync error');
95
- });
96
- });
97
-
98
- describe('getActiveSpan', () => {
99
- it('should return active span within withSpan', async () => {
100
- await withSpan('active-span', async () => {
101
- const active = getActiveSpan();
102
- expect(active).toBeDefined();
103
- });
104
- });
105
- });
106
-
107
- describe('getTraceId and getSpanId', () => {
108
- it('should return trace and span IDs within span context', async () => {
109
- let traceId: string | undefined;
110
- let spanId: string | undefined;
111
-
112
- await withSpan('id-span', async (span) => {
113
- traceId = getTraceId();
114
- spanId = getSpanId();
115
- // Verify they match the span we're in
116
- expect(traceId).toBe(span.spanContext().traceId);
117
- expect(spanId).toBe(span.spanContext().spanId);
118
- });
119
-
120
- expect(traceId).toBeDefined();
121
- expect(traceId?.length).toBe(32); // 16 bytes hex
122
- expect(spanId).toBeDefined();
123
- expect(spanId?.length).toBe(16); // 8 bytes hex
124
- });
125
-
126
- it('should return undefined outside span context', () => {
127
- // Outside of any span context
128
- const traceId = getTraceId();
129
- const spanId = getSpanId();
130
- // These may be undefined if no span is active
131
- expect(traceId === undefined || typeof traceId === 'string').toBe(true);
132
- expect(spanId === undefined || typeof spanId === 'string').toBe(true);
133
- });
134
- });
135
-
136
- describe('setSpanAttributes', () => {
137
- it('should set attributes on active span', async () => {
138
- await withSpan('attrs-span', async (span) => {
139
- setSpanAttributes({ 'custom.key': 'custom-value', 'custom.num': 42 });
140
- // Span should still be valid
141
- expect(span.isRecording()).toBe(true);
142
- });
143
- });
144
-
145
- it('should not throw when no active span', () => {
146
- // Should not throw even when called outside span context
147
- expect(() => {
148
- setSpanAttributes({ key: 'value' });
149
- }).not.toThrow();
150
- });
151
- });
152
-
153
- describe('recordException', () => {
154
- it('should record exception on active span', async () => {
155
- await withSpan('exception-span', async () => {
156
- const error = new Error('Test exception');
157
- recordException(error);
158
- // Should not throw
159
- });
160
- });
161
-
162
- it('should not throw when no active span', () => {
163
- expect(() => {
164
- recordException(new Error('No span error'));
165
- }).not.toThrow();
166
- });
167
- });
168
-
169
- describe('addSpanEvent', () => {
170
- it('should add event to active span', async () => {
171
- await withSpan('event-span', async () => {
172
- addSpanEvent('test.event', { detail: 'some detail' });
173
- // Should not throw
174
- });
175
- });
176
-
177
- it('should add event without attributes', async () => {
178
- await withSpan('event-span-simple', async () => {
179
- addSpanEvent('simple.event');
180
- // Should not throw
181
- });
182
- });
183
-
184
- it('should not throw when no active span', () => {
185
- expect(() => {
186
- addSpanEvent('orphan.event', { data: 123 });
187
- }).not.toThrow();
188
- });
189
- });
190
-
191
- describe('error handling edge cases', () => {
192
- it('should handle non-Error exceptions in withSpan', async () => {
193
- await expect(
194
- withSpan('string-error-span', async () => {
195
- throw 'string error'; // Non-Error exception
196
- }),
197
- ).rejects.toBe('string error');
198
- });
199
-
200
- it('should handle non-Error exceptions in withSpanSync', () => {
201
- expect(() =>
202
- withSpanSync('sync-string-error', () => {
203
- throw 'sync string error';
204
- }),
205
- ).toThrow();
206
- });
207
- });
208
- });