Skip to content

Commit de22db1

Browse files
mydeaandreiborza
authored andcommitted
feat(cloudflare): Allow interop with OpenTelemetry emitted spans
1 parent ebdd485 commit de22db1

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
@@ -27038,7 +27038,7 @@ string-template@~0.2.1:
2703827038
is-fullwidth-code-point "^3.0.0"
2703927039
strip-ansi "^6.0.1"
2704027040

27041-
"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:
27041+
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:
2704227042
version "4.2.3"
2704327043
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
2704427044
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -27148,6 +27148,13 @@ stringify-object@^3.2.1:
2714827148
dependencies:
2714927149
ansi-regex "^5.0.1"
2715027150

27151+
strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1:
27152+
version "6.0.1"
27153+
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
27154+
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
27155+
dependencies:
27156+
ansi-regex "^5.0.1"
27157+
2715127158
strip-ansi@^3.0.0:
2715227159
version "3.0.1"
2715327160
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
@@ -27169,13 +27176,6 @@ strip-ansi@^5.1.0, strip-ansi@^5.2.0:
2716927176
dependencies:
2717027177
ansi-regex "^4.1.0"
2717127178

27172-
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
27173-
version "6.0.1"
27174-
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
27175-
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
27176-
dependencies:
27177-
ansi-regex "^5.0.1"
27178-
2717927179
strip-ansi@^7.0.1, strip-ansi@^7.1.0:
2718027180
version "7.1.0"
2718127181
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
@@ -27311,7 +27311,7 @@ stylus@0.59.0, stylus@^0.59.0:
2731127311
sax "~1.2.4"
2731227312
source-map "^0.7.3"
2731327313

27314-
sucrase@^3.27.0, sucrase@^3.35.0:
27314+
sucrase@^3.27.0, sucrase@^3.35.0, sucrase@getsentry/sucrase#es2020-polyfills:
2731527315
version "3.36.0"
2731627316
resolved "https://codeload.github.com/getsentry/sucrase/tar.gz/fd682f6129e507c00bb4e6319cc5d6b767e36061"
2731727317
dependencies:
@@ -29955,19 +29955,19 @@ wrangler@^3.67.1:
2995529955
string-width "^4.1.0"
2995629956
strip-ansi "^6.0.0"
2995729957

29958-
wrap-ansi@^6.0.1:
29959-
version "6.2.0"
29960-
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
29961-
integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
29958+
wrap-ansi@7.0.0, wrap-ansi@^7.0.0:
29959+
version "7.0.0"
29960+
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
29961+
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
2996229962
dependencies:
2996329963
ansi-styles "^4.0.0"
2996429964
string-width "^4.1.0"
2996529965
strip-ansi "^6.0.0"
2996629966

29967-
wrap-ansi@^7.0.0:
29968-
version "7.0.0"
29969-
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
29970-
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
29967+
wrap-ansi@^6.0.1:
29968+
version "6.2.0"
29969+
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
29970+
integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
2997129971
dependencies:
2997229972
ansi-styles "^4.0.0"
2997329973
string-width "^4.1.0"

0 commit comments

Comments
 (0)