Skip to content

Commit cccdb42

Browse files
committed
feat(cloudflare): Allow interop with OpenTelemetry emitted spans
1 parent c37d4e5 commit cccdb42

File tree

4 files changed

+91
-18
lines changed

4 files changed

+91
-18
lines changed

packages/cloudflare/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@
4949
"access": "public"
5050
},
5151
"dependencies": {
52-
"@sentry/core": "9.31.0"
52+
"@sentry/core": "9.31.0",
53+
"@opentelemetry/api": "^1.9.0"
5354
},
5455
"peerDependencies": {
5556
"@cloudflare/workers-types": "^4.x"
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import type { Context, Span, SpanOptions, Tracer, TracerProvider } from '@opentelemetry/api';
2+
import { trace } from '@opentelemetry/api';
3+
import { startInactiveSpan, startSpan } from '@sentry/core';
4+
5+
/**
6+
* Set up a mock OTEL tracer to allow inter-op with OpenTelemetry emitted spans.
7+
* This is not perfect but handles easy/common use cases.
8+
*/
9+
export function setupOpenTelemetryTracer(): void {
10+
trace.setGlobalTracerProvider(new SentryCloudflareTraceProvider());
11+
}
12+
13+
class SentryCloudflareTraceProvider implements TracerProvider {
14+
private readonly _tracers: Map<string, Tracer> = new Map();
15+
16+
public getTracer(name: string, version?: string, options?: { schemaUrl?: string }): Tracer {
17+
const key = `${name}@${version || ''}:${options?.schemaUrl || ''}`;
18+
if (!this._tracers.has(key)) {
19+
this._tracers.set(key, new SentryCloudflareTracer());
20+
}
21+
22+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
23+
return this._tracers.get(key)!;
24+
}
25+
}
26+
27+
class SentryCloudflareTracer implements Tracer {
28+
public startSpan(name: string, options?: SpanOptions): Span {
29+
return startInactiveSpan({ name, ...options });
30+
}
31+
32+
/**
33+
* NOTE: This does not handle `context` being passed in. It will always put spans on the current scope.
34+
*/
35+
public startActiveSpan<F extends (span: Span) => unknown>(name: string, fn: F): ReturnType<F>;
36+
public startActiveSpan<F extends (span: Span) => unknown>(name: string, options: SpanOptions, fn: F): ReturnType<F>;
37+
public startActiveSpan<F extends (span: Span) => unknown>(
38+
name: string,
39+
options: SpanOptions,
40+
context: Context,
41+
fn: F,
42+
): ReturnType<F>;
43+
public startActiveSpan<F extends (span: Span) => unknown>(
44+
name: string,
45+
options: unknown,
46+
context?: unknown,
47+
fn?: F,
48+
): ReturnType<F> {
49+
const opts = typeof options === 'object' && options !== null ? options : {};
50+
51+
const spanOpts = {
52+
name,
53+
...opts,
54+
};
55+
56+
const callback = (
57+
typeof options === 'function'
58+
? options
59+
: typeof context === 'function'
60+
? context
61+
: typeof fn === 'function'
62+
? fn
63+
: // eslint-disable-next-line @typescript-eslint/no-empty-function
64+
() => {}
65+
) as F;
66+
67+
return startSpan(spanOpts, callback) as ReturnType<F>;
68+
}
69+
}

packages/cloudflare/src/sdk.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
import type { CloudflareClientOptions, CloudflareOptions } from './client';
1414
import { CloudflareClient } from './client';
1515
import { fetchIntegration } from './integrations/fetch';
16+
import { setupOpenTelemetryTracer } from './opentelemetry/tracer';
1617
import { makeCloudflareTransport } from './transport';
1718
import { defaultStackParser } from './vendor/stacktrace';
1819

@@ -50,5 +51,7 @@ export function init(options: CloudflareOptions): CloudflareClient | undefined {
5051
transport: options.transport || makeCloudflareTransport,
5152
};
5253

54+
setupOpenTelemetryTracer();
55+
5356
return initAndBind(CloudflareClient, clientOptions) as CloudflareClient;
5457
}

yarn.lock

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26920,7 +26920,7 @@ string-template@~0.2.1:
2692026920
is-fullwidth-code-point "^3.0.0"
2692126921
strip-ansi "^6.0.1"
2692226922

26923-
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
26923+
string-width@4.2.3, "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
2692426924
version "4.2.3"
2692526925
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
2692626926
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -27030,6 +27030,13 @@ stringify-object@^3.2.1:
2703027030
dependencies:
2703127031
ansi-regex "^5.0.1"
2703227032

27033+
strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1:
27034+
version "6.0.1"
27035+
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
27036+
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
27037+
dependencies:
27038+
ansi-regex "^5.0.1"
27039+
2703327040
strip-ansi@^3.0.0:
2703427041
version "3.0.1"
2703527042
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
@@ -27051,13 +27058,6 @@ strip-ansi@^5.1.0, strip-ansi@^5.2.0:
2705127058
dependencies:
2705227059
ansi-regex "^4.1.0"
2705327060

27054-
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
27055-
version "6.0.1"
27056-
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
27057-
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
27058-
dependencies:
27059-
ansi-regex "^5.0.1"
27060-
2706127061
strip-ansi@^7.0.1, strip-ansi@^7.1.0:
2706227062
version "7.1.0"
2706327063
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
@@ -27193,7 +27193,7 @@ stylus@0.59.0, stylus@^0.59.0:
2719327193
sax "~1.2.4"
2719427194
source-map "^0.7.3"
2719527195

27196-
sucrase@^3.27.0, sucrase@^3.35.0:
27196+
sucrase@^3.27.0, sucrase@^3.35.0, sucrase@getsentry/sucrase#es2020-polyfills:
2719727197
version "3.36.0"
2719827198
resolved "https://codeload.github.com/getsentry/sucrase/tar.gz/fd682f6129e507c00bb4e6319cc5d6b767e36061"
2719927199
dependencies:
@@ -29828,19 +29828,19 @@ wrangler@^3.67.1:
2982829828
string-width "^4.1.0"
2982929829
strip-ansi "^6.0.0"
2983029830

29831-
wrap-ansi@^6.0.1:
29832-
version "6.2.0"
29833-
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
29834-
integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
29831+
wrap-ansi@7.0.0, wrap-ansi@^7.0.0:
29832+
version "7.0.0"
29833+
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
29834+
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
2983529835
dependencies:
2983629836
ansi-styles "^4.0.0"
2983729837
string-width "^4.1.0"
2983829838
strip-ansi "^6.0.0"
2983929839

29840-
wrap-ansi@^7.0.0:
29841-
version "7.0.0"
29842-
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
29843-
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
29840+
wrap-ansi@^6.0.1:
29841+
version "6.2.0"
29842+
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
29843+
integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
2984429844
dependencies:
2984529845
ansi-styles "^4.0.0"
2984629846
string-width "^4.1.0"

0 commit comments

Comments
 (0)