diff --git a/dev-packages/e2e-tests/test-applications/deno-streamed/package.json b/dev-packages/e2e-tests/test-applications/deno-streamed/package.json index 7bbaeaf631f8..282f8abb6492 100644 --- a/dev-packages/e2e-tests/test-applications/deno-streamed/package.json +++ b/dev-packages/e2e-tests/test-applications/deno-streamed/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "private": true, "scripts": { - "start": "deno run --allow-net --allow-env --allow-read src/app.ts", + "start": "deno run --allow-net --allow-env --allow-read --allow-sys src/app.ts", "test": "playwright test", "clean": "npx rimraf node_modules pnpm-lock.yaml", "test:build": "pnpm install", diff --git a/dev-packages/e2e-tests/test-applications/deno-streamed/tests/spans.test.ts b/dev-packages/e2e-tests/test-applications/deno-streamed/tests/spans.test.ts index 023429b07f41..15d7eaf99d9a 100644 --- a/dev-packages/e2e-tests/test-applications/deno-streamed/tests/spans.test.ts +++ b/dev-packages/e2e-tests/test-applications/deno-streamed/tests/spans.test.ts @@ -3,6 +3,10 @@ import { waitForStreamedSpans, getSpanOp } from '@sentry-internal/test-utils'; const SEGMENT_SPAN = { attributes: { + 'app.start_time': { + type: 'string', + value: expect.any(String), + }, 'client.address': { type: 'string', value: expect.any(String), @@ -11,6 +15,11 @@ const SEGMENT_SPAN = { type: 'integer', value: expect.any(Number), }, + // TODO: 'device.archs' is set but arrays are not yet serialized in span attributes + 'device.processor_count': { + type: 'integer', + value: expect.any(Number), + }, 'http.request.header.accept': { type: 'string', value: '*/*', @@ -51,6 +60,14 @@ const SEGMENT_SPAN = { type: 'integer', value: expect.any(Number), }, + 'os.name': { + type: 'string', + value: expect.any(String), + }, + 'os.version': { + type: 'string', + value: expect.any(String), + }, 'sentry.environment': { type: 'string', value: 'qa', @@ -115,6 +132,14 @@ const SEGMENT_SPAN = { type: 'string', value: 'node', }, + 'process.runtime.engine.name': { + type: 'string', + value: 'v8', + }, + 'process.runtime.engine.version': { + type: 'string', + value: expect.any(String), + }, }, end_timestamp: expect.any(Number), is_segment: true, diff --git a/packages/deno/src/integrations/context.ts b/packages/deno/src/integrations/context.ts index 979ffff7d0e8..62ea66807171 100644 --- a/packages/deno/src/integrations/context.ts +++ b/packages/deno/src/integrations/context.ts @@ -1,5 +1,5 @@ -import type { Event, IntegrationFn } from '@sentry/core'; -import { defineIntegration } from '@sentry/core'; +import type { IntegrationFn } from '@sentry/core'; +import { defineIntegration, safeSetSpanJSONAttributes } from '@sentry/core'; const INTEGRATION_NAME = 'DenoContext'; @@ -22,41 +22,54 @@ async function getOSRelease(): Promise { : undefined; } -async function addDenoRuntimeContext(event: Event): Promise { - event.contexts = { - ...{ - app: { - app_start_time: new Date(Date.now() - performance.now()).toISOString(), - }, - device: { - arch: Deno.build.arch, - // eslint-disable-next-line no-restricted-globals - processor_count: navigator.hardwareConcurrency, - }, - os: { - name: getOSName(), - version: await getOSRelease(), - }, - v8: { - name: 'v8', - version: Deno.version.v8, - }, - typescript: { - name: 'TypeScript', - version: Deno.version.typescript, - }, - }, - ...event.contexts, +const _denoContextIntegration = (() => { + const appStartTime = new Date(Date.now() - performance.now()).toISOString(); + const osName = getOSName(); + const arch = Deno.build.arch; + // eslint-disable-next-line no-restricted-globals + const processorCount = navigator.hardwareConcurrency; + const v8Version = Deno.version.v8; + const tsVersion = Deno.version.typescript; + + const cachedContext = { + app: { app_start_time: appStartTime }, + device: { arch, processor_count: processorCount }, + os: { name: osName } as { name: string; version?: string }, + v8: { name: 'v8', version: v8Version }, + typescript: { name: 'TypeScript', version: tsVersion }, }; - return event; -} + const cachedSpanAttributes: Record = { + 'app.start_time': appStartTime, + // Convention uses 'device.archs' (string[]), but array attributes are not yet serialized. + // Once array serialization lands, this will start appearing on spans automatically. + 'device.archs': [arch], + 'device.processor_count': processorCount, + 'os.name': osName, + 'process.runtime.engine.name': 'v8', + 'process.runtime.engine.version': v8Version, + }; + + getOSRelease() + .then(release => { + cachedContext.os.version = release; + cachedSpanAttributes['os.version'] = release; + }) + .catch(() => { + // Ignore - os.version will be undefined + }); -const _denoContextIntegration = (() => { return { name: INTEGRATION_NAME, processEvent(event) { - return addDenoRuntimeContext(event); + event.contexts = { + ...cachedContext, + ...event.contexts, + }; + return event; + }, + processSegmentSpan(span) { + safeSetSpanJSONAttributes(span, cachedSpanAttributes); }, }; }) satisfies IntegrationFn;