Skip to content

Commit 9a4b9b2

Browse files
authored
feat(deno): Add processSegmentSpan to Deno context integration (#20613)
Adds `processSegmentSpan` to the `denoContextIntegration` for span streaming support. Attributes added in getsentry/sentry-conventions#347 closes #20381
1 parent 5637aa0 commit 9a4b9b2

3 files changed

Lines changed: 70 additions & 32 deletions

File tree

dev-packages/e2e-tests/test-applications/deno-streamed/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"version": "1.0.0",
44
"private": true,
55
"scripts": {
6-
"start": "deno run --allow-net --allow-env --allow-read src/app.ts",
6+
"start": "deno run --allow-net --allow-env --allow-read --allow-sys src/app.ts",
77
"test": "playwright test",
88
"clean": "npx rimraf node_modules pnpm-lock.yaml",
99
"test:build": "pnpm install",

dev-packages/e2e-tests/test-applications/deno-streamed/tests/spans.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import { waitForStreamedSpans, getSpanOp } from '@sentry-internal/test-utils';
33

44
const SEGMENT_SPAN = {
55
attributes: {
6+
'app.start_time': {
7+
type: 'string',
8+
value: expect.any(String),
9+
},
610
'client.address': {
711
type: 'string',
812
value: expect.any(String),
@@ -11,6 +15,11 @@ const SEGMENT_SPAN = {
1115
type: 'integer',
1216
value: expect.any(Number),
1317
},
18+
// TODO: 'device.archs' is set but arrays are not yet serialized in span attributes
19+
'device.processor_count': {
20+
type: 'integer',
21+
value: expect.any(Number),
22+
},
1423
'http.request.header.accept': {
1524
type: 'string',
1625
value: '*/*',
@@ -51,6 +60,14 @@ const SEGMENT_SPAN = {
5160
type: 'integer',
5261
value: expect.any(Number),
5362
},
63+
'os.name': {
64+
type: 'string',
65+
value: expect.any(String),
66+
},
67+
'os.version': {
68+
type: 'string',
69+
value: expect.any(String),
70+
},
5471
'sentry.environment': {
5572
type: 'string',
5673
value: 'qa',
@@ -115,6 +132,14 @@ const SEGMENT_SPAN = {
115132
type: 'string',
116133
value: 'node',
117134
},
135+
'process.runtime.engine.name': {
136+
type: 'string',
137+
value: 'v8',
138+
},
139+
'process.runtime.engine.version': {
140+
type: 'string',
141+
value: expect.any(String),
142+
},
118143
},
119144
end_timestamp: expect.any(Number),
120145
is_segment: true,

packages/deno/src/integrations/context.ts

Lines changed: 44 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import type { Event, IntegrationFn } from '@sentry/core';
2-
import { defineIntegration } from '@sentry/core';
1+
import type { IntegrationFn } from '@sentry/core';
2+
import { defineIntegration, safeSetSpanJSONAttributes } from '@sentry/core';
33

44
const INTEGRATION_NAME = 'DenoContext';
55

@@ -22,41 +22,54 @@ async function getOSRelease(): Promise<string | undefined> {
2222
: undefined;
2323
}
2424

25-
async function addDenoRuntimeContext(event: Event): Promise<Event> {
26-
event.contexts = {
27-
...{
28-
app: {
29-
app_start_time: new Date(Date.now() - performance.now()).toISOString(),
30-
},
31-
device: {
32-
arch: Deno.build.arch,
33-
// eslint-disable-next-line no-restricted-globals
34-
processor_count: navigator.hardwareConcurrency,
35-
},
36-
os: {
37-
name: getOSName(),
38-
version: await getOSRelease(),
39-
},
40-
v8: {
41-
name: 'v8',
42-
version: Deno.version.v8,
43-
},
44-
typescript: {
45-
name: 'TypeScript',
46-
version: Deno.version.typescript,
47-
},
48-
},
49-
...event.contexts,
25+
const _denoContextIntegration = (() => {
26+
const appStartTime = new Date(Date.now() - performance.now()).toISOString();
27+
const osName = getOSName();
28+
const arch = Deno.build.arch;
29+
// eslint-disable-next-line no-restricted-globals
30+
const processorCount = navigator.hardwareConcurrency;
31+
const v8Version = Deno.version.v8;
32+
const tsVersion = Deno.version.typescript;
33+
34+
const cachedContext = {
35+
app: { app_start_time: appStartTime },
36+
device: { arch, processor_count: processorCount },
37+
os: { name: osName } as { name: string; version?: string },
38+
v8: { name: 'v8', version: v8Version },
39+
typescript: { name: 'TypeScript', version: tsVersion },
5040
};
5141

52-
return event;
53-
}
42+
const cachedSpanAttributes: Record<string, unknown> = {
43+
'app.start_time': appStartTime,
44+
// Convention uses 'device.archs' (string[]), but array attributes are not yet serialized.
45+
// Once array serialization lands, this will start appearing on spans automatically.
46+
'device.archs': [arch],
47+
'device.processor_count': processorCount,
48+
'os.name': osName,
49+
'process.runtime.engine.name': 'v8',
50+
'process.runtime.engine.version': v8Version,
51+
};
52+
53+
getOSRelease()
54+
.then(release => {
55+
cachedContext.os.version = release;
56+
cachedSpanAttributes['os.version'] = release;
57+
})
58+
.catch(() => {
59+
// Ignore - os.version will be undefined
60+
});
5461

55-
const _denoContextIntegration = (() => {
5662
return {
5763
name: INTEGRATION_NAME,
5864
processEvent(event) {
59-
return addDenoRuntimeContext(event);
65+
event.contexts = {
66+
...cachedContext,
67+
...event.contexts,
68+
};
69+
return event;
70+
},
71+
processSegmentSpan(span) {
72+
safeSetSpanJSONAttributes(span, cachedSpanAttributes);
6073
},
6174
};
6275
}) satisfies IntegrationFn;

0 commit comments

Comments
 (0)