Skip to content

Commit fad1045

Browse files
authored
Fix frontend spans (#2269)
1 parent 00356c1 commit fad1045

File tree

1 file changed

+46
-25
lines changed
  • src/Elastic.Documentation.Site/Assets/telemetry

1 file changed

+46
-25
lines changed
Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,61 @@
11
/**
22
* React utilities for OpenTelemetry tracing in components.
33
*/
4-
import { trace, context, SpanStatusCode, Span } from '@opentelemetry/api'
4+
import { trace, SpanStatusCode, Span } from '@opentelemetry/api'
55

6+
/**
7+
* Creates a span and executes a function within its context.
8+
*
9+
* This ensures that:
10+
* - The span is automatically set as the active context
11+
* - Any automatic instrumentation (e.g., fetch, XHR) creates child spans
12+
* - Nested calls to traceSpan() properly create child spans
13+
* - The span is properly ended even if errors occur
14+
*
15+
* @param spanName - Name of the span to create
16+
* @param fn - Async function to execute within the span context
17+
* @param attributes - Optional attributes to set on the span
18+
* @returns Promise resolving to the function's return value
19+
*/
620
export async function traceSpan<T>(
721
spanName: string,
822
fn: (span: Span) => Promise<T>,
923
attributes?: Record<string, string | number | boolean>
1024
): Promise<T> {
1125
const tracer = trace.getTracer('docs-frontend')
12-
const span = tracer.startSpan(spanName, undefined, context.active())
1326

14-
if (attributes) {
15-
span.setAttributes(attributes)
16-
}
27+
// startActiveSpan automatically:
28+
// 1. Creates the span
29+
// 2. Sets it as the active context
30+
// 3. Executes the callback within that context
31+
// 4. Ends the span after execution
32+
return tracer.startActiveSpan(spanName, async (span) => {
33+
if (attributes) {
34+
span.setAttributes(attributes)
35+
}
1736

18-
try {
19-
const result = await fn(span)
20-
span.setStatus({ code: SpanStatusCode.OK })
21-
return result
22-
} catch (error) {
23-
// Check if this is an AbortError (user cancelled/typed more)
24-
if (error instanceof Error && error.name === 'AbortError') {
25-
// Cancellation is NOT an error - it's expected behavior
26-
span.setAttribute('cancelled', true)
37+
try {
38+
const result = await fn(span)
2739
span.setStatus({ code: SpanStatusCode.OK })
28-
} else {
29-
// Real error - mark as ERROR
30-
span.setStatus({
31-
code: SpanStatusCode.ERROR,
32-
message: error instanceof Error ? error.message : String(error),
33-
})
34-
span.recordException(error as Error)
40+
return result
41+
} catch (error) {
42+
// Check if this is an AbortError (user cancelled/typed more)
43+
if (error instanceof Error && error.name === 'AbortError') {
44+
// Cancellation is NOT an error - it's expected behavior
45+
span.setAttribute('cancelled', true)
46+
span.setStatus({ code: SpanStatusCode.OK })
47+
} else {
48+
// Real error - mark as ERROR
49+
span.setStatus({
50+
code: SpanStatusCode.ERROR,
51+
message:
52+
error instanceof Error ? error.message : String(error),
53+
})
54+
span.recordException(error as Error)
55+
}
56+
throw error
57+
} finally {
58+
span.end()
3559
}
36-
throw error
37-
} finally {
38-
span.end()
39-
}
60+
})
4061
}

0 commit comments

Comments
 (0)