Skip to content

Commit 571a961

Browse files
committed
initial StreamingSpanExporter implementation for otel
1 parent d9cd2f3 commit 571a961

File tree

5 files changed

+36
-16
lines changed

5 files changed

+36
-16
lines changed

packages/core/src/spans/captureSpan.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { safeSetSpanAttributes } from './spanFirstUtils';
2525
/**
2626
* Captures a span and returns it to the caller, to be enqueued for sending.
2727
*/
28-
export function captureSpan(span: Span, client = getClient()): void {
28+
export function captureSpan(span: Span, client = getClient()): Span | void {
2929
if (!client) {
3030
DEBUG_BUILD && debug.warn('No client available to capture span.');
3131
return;
@@ -59,6 +59,8 @@ export function captureSpan(span: Span, client = getClient()): void {
5959
// or construct a fully new span object. The latter is risky because users (or we) could hold
6060
// references to the original span instance.
6161
client.emit('enqueueSpan', span);
62+
63+
return span;
6264
}
6365

6466
function applyScopeToSegmentSpan(

packages/node/src/sdk/initOtel.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ export function setupOtel(
107107
spanProcessors: [
108108
new SentrySpanProcessor({
109109
timeout: _clampSpanProcessorTimeout(client.getOptions().maxSpanWaitDuration),
110+
client,
110111
}),
111112
...(options.spanProcessors || []),
112113
],

packages/opentelemetry/src/spanExporter.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable max-lines */
22
import type { Span } from '@opentelemetry/api';
33
import { SpanKind } from '@opentelemetry/api';
4-
import type { ReadableSpan } from '@opentelemetry/sdk-trace-base';
4+
import type { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base';
55
import { ATTR_HTTP_RESPONSE_STATUS_CODE, SEMATTRS_HTTP_STATUS_CODE } from '@opentelemetry/semantic-conventions';
66
import type {
77
SpanAttributes,
@@ -46,10 +46,16 @@ interface FinishedSpanBucket {
4646
spans: Set<ReadableSpan>;
4747
}
4848

49+
export interface ISentrySpanExporter {
50+
export(span: ReadableSpan): void;
51+
flush(): void;
52+
clear(): void;
53+
}
54+
4955
/**
5056
* A Sentry-specific exporter that converts OpenTelemetry Spans to Sentry Spans & Transactions.
5157
*/
52-
export class SentrySpanExporter {
58+
export class SentrySpanExporter implements ISentrySpanExporter {
5359
/*
5460
* A quick explanation on the buckets: We do bucketing of finished spans for efficiency. This span exporter is
5561
* accumulating spans until a root span is encountered and then it flushes all the spans that are descendants of that

packages/opentelemetry/src/spanProcessor.ts

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import type { Context } from '@opentelemetry/api';
22
import { ROOT_CONTEXT, trace } from '@opentelemetry/api';
33
import type { ReadableSpan, Span, SpanProcessor as SpanProcessorInterface } from '@opentelemetry/sdk-trace-base';
4+
import type { Client } from '@sentry/core';
45
import {
56
addChildSpanToSpan,
7+
captureSpan,
68
getClient,
79
getDefaultCurrentScope,
810
getDefaultIsolationScope,
@@ -11,7 +13,9 @@ import {
1113
setCapturedScopesOnSpan,
1214
} from '@sentry/core';
1315
import { SEMANTIC_ATTRIBUTE_SENTRY_PARENT_IS_REMOTE } from './semanticAttributes';
16+
import type { ISentrySpanExporter } from './spanExporter';
1417
import { SentrySpanExporter } from './spanExporter';
18+
import { StreamingSpanExporter } from './streamedSpanExporter';
1519
import { getScopesFromContext } from './utils/contextData';
1620
import { setIsSetup } from './utils/setupCheck';
1721

@@ -51,24 +55,22 @@ function onSpanStart(span: Span, parentContext: Context): void {
5155
client?.emit('spanStart', span);
5256
}
5357

54-
function onSpanEnd(span: Span): void {
55-
logSpanEnd(span);
56-
57-
const client = getClient();
58-
client?.emit('spanEnd', span);
59-
client?.emit('afterSpanEnd', span);
60-
}
61-
6258
/**
6359
* Converts OpenTelemetry Spans to Sentry Spans and sends them to Sentry via
6460
* the Sentry SDK.
6561
*/
6662
export class SentrySpanProcessor implements SpanProcessorInterface {
67-
private _exporter: SentrySpanExporter;
63+
private _exporter: ISentrySpanExporter;
64+
private _client: Client | undefined;
6865

69-
public constructor(options?: { timeout?: number }) {
66+
public constructor(options?: { timeout?: number; client?: Client }) {
7067
setIsSetup('SentrySpanProcessor');
71-
this._exporter = new SentrySpanExporter(options);
68+
this._client = options?.client ?? getClient();
69+
if (this._client?.getOptions().traceLifecycle === 'stream') {
70+
this._exporter = new StreamingSpanExporter(this._client, { flushInterval: options?.timeout });
71+
} else {
72+
this._exporter = new SentrySpanExporter(options);
73+
}
7274
}
7375

7476
/**
@@ -94,8 +96,16 @@ export class SentrySpanProcessor implements SpanProcessorInterface {
9496

9597
/** @inheritDoc */
9698
public onEnd(span: Span & ReadableSpan): void {
97-
onSpanEnd(span);
99+
logSpanEnd(span);
100+
101+
this._client?.emit('spanEnd', span);
98102

99-
this._exporter.export(span);
103+
if (this._client?.getOptions().traceLifecycle === 'stream') {
104+
// we probably don't need to emit afterSpanEnd here but can call captureSpan directly.
105+
// might need to revisit but let's see.
106+
captureSpan(span, this._client);
107+
} else {
108+
this._exporter.export(span);
109+
}
100110
}
101111
}

packages/vercel-edge/src/sdk.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ export function setupOtel(client: VercelEdgeClient): void {
170170
spanProcessors: [
171171
new SentrySpanProcessor({
172172
timeout: client.getOptions().maxSpanWaitDuration,
173+
client,
173174
}),
174175
],
175176
});

0 commit comments

Comments
 (0)