From 79d409f8e08f61ac4d3dfe782e8b93e629a080de Mon Sep 17 00:00:00 2001 From: Sergiy Dybskiy Date: Thu, 12 Feb 2026 19:09:44 -0500 Subject: [PATCH 1/4] feat(deno): Export logs API from Deno SDK Re-export `logger` and `consoleLoggingIntegration` from `@sentry/core` so Deno users can use `Sentry.logger.info(...)` and capture console calls as Sentry logs. Co-Authored-By: Claude Opus 4.6 --- packages/deno/src/index.ts | 2 ++ packages/deno/test/mod.test.ts | 43 +++++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/packages/deno/src/index.ts b/packages/deno/src/index.ts index 5f987b4459aa..9680ad8bd934 100644 --- a/packages/deno/src/index.ts +++ b/packages/deno/src/index.ts @@ -89,6 +89,8 @@ export { updateSpanName, wrapMcpServerWithSentry, featureFlagsIntegration, + logger, + consoleLoggingIntegration, } from '@sentry/core'; export { DenoClient } from './client'; diff --git a/packages/deno/test/mod.test.ts b/packages/deno/test/mod.test.ts index 0d40945951c4..4132d7567c54 100644 --- a/packages/deno/test/mod.test.ts +++ b/packages/deno/test/mod.test.ts @@ -1,8 +1,8 @@ -import type { Event } from '@sentry/core'; -import { createStackParser, nodeStackLineParser } from '@sentry/core'; +import type { Envelope, Event } from '@sentry/core'; +import { createStackParser, forEachEnvelopeItem, nodeStackLineParser } from '@sentry/core'; import { assertEquals } from 'https://deno.land/std@0.202.0/assert/assert_equals.ts'; import { assertSnapshot } from 'https://deno.land/std@0.202.0/testing/snapshot.ts'; -import { DenoClient, getCurrentScope, getDefaultIntegrations } from '../build/esm/index.js'; +import { DenoClient, getCurrentScope, getDefaultIntegrations, logger, Scope } from '../build/esm/index.js'; import { getNormalizedEvent } from './normalize.ts'; import { makeTestTransport } from './transport.ts'; @@ -74,6 +74,43 @@ Deno.test('captureMessage twice', async t => { await assertSnapshot(t, ev); }); +Deno.test('logger.info captures a log envelope item', async () => { + const envelopes: Array = []; + const client = new DenoClient({ + dsn: 'https://233a45e5efe34c47a3536797ce15dafa@nothing.here/5650507', + enableLogs: true, + integrations: getDefaultIntegrations({}), + stackParser: createStackParser(nodeStackLineParser()), + transport: makeTestTransport(envelope => { + envelopes.push(envelope); + }), + }); + + client.init(); + const scope = new Scope(); + scope.setClient(client); + + logger.info('test log message', { key: 'value' }, { scope }); + + await client.flush(2000); + + // deno-lint-ignore no-explicit-any + let logItem: any = undefined; + for (const envelope of envelopes) { + forEachEnvelopeItem(envelope, item => { + const [headers, body] = item; + if (headers.type === 'log') { + logItem = body; + } + }); + } + + assertEquals(logItem !== undefined, true); + assertEquals(logItem.items.length, 1); + assertEquals(logItem.items[0].level, 'info'); + assertEquals(logItem.items[0].body, 'test log message'); +}); + Deno.test('App runs without errors', async _ => { const cmd = new Deno.Command('deno', { args: ['run', '--allow-net=some-domain.com', './test/example.ts'], From bf2e63929c4d6c0a352eb8989edbae6036502581 Mon Sep 17 00:00:00 2001 From: Sergiy Dybskiy Date: Fri, 13 Feb 2026 09:05:52 -0500 Subject: [PATCH 2/4] feat(deno): Export Log types and add log flushing on exit Export `Log` and `LogSeverityLevel` types from the Deno SDK. Add log buffer flushing on server exit via `globalThis.addEventListener('unload')` (Deno's equivalent of Node's `process.on('beforeExit')`), matching the pattern in node-core's NodeClient. Also adds `server.address` attribute to logs via `beforeCaptureLog` hook. Co-Authored-By: Claude Opus 4.6 --- packages/deno/src/client.ts | 35 +++++++++++++++++++++++++++++++++-- packages/deno/src/index.ts | 2 ++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/packages/deno/src/client.ts b/packages/deno/src/client.ts index b87cfcc6e163..36886c8d5a5e 100644 --- a/packages/deno/src/client.ts +++ b/packages/deno/src/client.ts @@ -1,5 +1,5 @@ import type { ServerRuntimeClientOptions } from '@sentry/core'; -import { SDK_VERSION, ServerRuntimeClient } from '@sentry/core'; +import { _INTERNAL_flushLogsBuffer, SDK_VERSION, ServerRuntimeClient } from '@sentry/core'; import type { DenoClientOptions } from './types'; function getHostName(): string | undefined { @@ -19,6 +19,8 @@ function getHostName(): string | undefined { * @see SentryClient for usage documentation. */ export class DenoClient extends ServerRuntimeClient { + private _logOnExitFlushListener: (() => void) | undefined; + /** * Creates a new Deno SDK instance. * @param options Configuration options for this SDK. @@ -36,13 +38,42 @@ export class DenoClient extends ServerRuntimeClient { version: SDK_VERSION, }; + const serverName = options.serverName || getHostName(); + const clientOptions: ServerRuntimeClientOptions = { ...options, platform: 'javascript', runtime: { name: 'deno', version: Deno.version.deno }, - serverName: options.serverName || getHostName(), + serverName, }; super(clientOptions); + + if (this.getOptions().enableLogs) { + this._logOnExitFlushListener = () => { + _INTERNAL_flushLogsBuffer(this); + }; + + if (serverName) { + this.on('beforeCaptureLog', log => { + log.attributes = { + ...log.attributes, + 'server.address': serverName, + }; + }); + } + + globalThis.addEventListener('unload', this._logOnExitFlushListener); + } + } + + /** @inheritDoc */ + // @ts-expect-error - PromiseLike is a subset of Promise + public async close(timeout?: number | undefined): PromiseLike { + if (this._logOnExitFlushListener) { + globalThis.removeEventListener('unload', this._logOnExitFlushListener); + } + + return super.close(timeout); } } diff --git a/packages/deno/src/index.ts b/packages/deno/src/index.ts index 9680ad8bd934..e698b461c4fc 100644 --- a/packages/deno/src/index.ts +++ b/packages/deno/src/index.ts @@ -1,6 +1,8 @@ export type { Breadcrumb, BreadcrumbHint, + Log, + LogSeverityLevel, PolymorphicRequest, RequestEventData, SdkInfo, From ef6f5b0dab67e4174bb494987bfe545b5a82ad95 Mon Sep 17 00:00:00 2001 From: Sergiy Dybskiy Date: Fri, 13 Feb 2026 09:11:24 -0500 Subject: [PATCH 3/4] test(deno): Add tests for log flushing and server.address attribute Co-Authored-By: Claude Opus 4.6 --- packages/deno/test/mod.test.ts | 61 +++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/packages/deno/test/mod.test.ts b/packages/deno/test/mod.test.ts index 4132d7567c54..891cd107b7b7 100644 --- a/packages/deno/test/mod.test.ts +++ b/packages/deno/test/mod.test.ts @@ -1,4 +1,4 @@ -import type { Envelope, Event } from '@sentry/core'; +import type { Envelope, Event, Log } from '@sentry/core'; import { createStackParser, forEachEnvelopeItem, nodeStackLineParser } from '@sentry/core'; import { assertEquals } from 'https://deno.land/std@0.202.0/assert/assert_equals.ts'; import { assertSnapshot } from 'https://deno.land/std@0.202.0/testing/snapshot.ts'; @@ -111,6 +111,65 @@ Deno.test('logger.info captures a log envelope item', async () => { assertEquals(logItem.items[0].body, 'test log message'); }); +Deno.test('adds server.address to log attributes', () => { + const client = new DenoClient({ + dsn: 'https://233a45e5efe34c47a3536797ce15dafa@nothing.here/5650507', + enableLogs: true, + serverName: 'test-server', + integrations: getDefaultIntegrations({}), + stackParser: createStackParser(nodeStackLineParser()), + transport: makeTestTransport(() => {}), + }); + + const log: Log = { level: 'info', message: 'test message', attributes: {} }; + client.emit('beforeCaptureLog', log); + + assertEquals(log.attributes?.['server.address'], 'test-server'); +}); + +Deno.test('preserves existing log attributes when adding server.address', () => { + const client = new DenoClient({ + dsn: 'https://233a45e5efe34c47a3536797ce15dafa@nothing.here/5650507', + enableLogs: true, + serverName: 'test-server', + integrations: getDefaultIntegrations({}), + stackParser: createStackParser(nodeStackLineParser()), + transport: makeTestTransport(() => {}), + }); + + const log: Log = { level: 'info', message: 'test message', attributes: { 'existing.attr': 'value' } }; + client.emit('beforeCaptureLog', log); + + assertEquals(log.attributes?.['existing.attr'], 'value'); + assertEquals(log.attributes?.['server.address'], 'test-server'); +}); + +Deno.test('close() removes unload listener when enableLogs is true', async () => { + const removeEventListenerCalls: Array = []; + const originalRemoveEventListener = globalThis.removeEventListener; + globalThis.removeEventListener = ((event: string, ...args: unknown[]) => { + removeEventListenerCalls.push(event); + // deno-lint-ignore no-explicit-any + return originalRemoveEventListener.call(globalThis, event, ...(args as [any])); + }) as typeof globalThis.removeEventListener; + + try { + const client = new DenoClient({ + dsn: 'https://233a45e5efe34c47a3536797ce15dafa@nothing.here/5650507', + enableLogs: true, + integrations: getDefaultIntegrations({}), + stackParser: createStackParser(nodeStackLineParser()), + transport: makeTestTransport(() => {}), + }); + + await client.close(); + + assertEquals(removeEventListenerCalls.includes('unload'), true); + } finally { + globalThis.removeEventListener = originalRemoveEventListener; + } +}); + Deno.test('App runs without errors', async _ => { const cmd = new Deno.Command('deno', { args: ['run', '--allow-net=some-domain.com', './test/example.ts'], From 322b135fb3f602a064bf5a9a722f2936ff2cfd3b Mon Sep 17 00:00:00 2001 From: Sergiy Dybskiy Date: Fri, 13 Feb 2026 09:45:38 -0500 Subject: [PATCH 4/4] chore(deno): Ignore deno package in codecov and gitignore deno.lock Deno tests run via Deno's native test runner, not vitest, so codecov cannot see their coverage. Adding packages/deno/** to the codecov ignore list prevents false coverage drops. Also gitignore deno.lock which is generated when running deno test. Co-Authored-By: Claude Opus 4.6 --- .gitignore | 1 + codecov.yml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index ce8be2090e76..8e48f9bff776 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,7 @@ tmp.js packages/deno/build-types packages/deno/build-test packages/deno/lib.deno.d.ts +deno.lock # gatsby packages/gatsby/gatsby-node.d.ts diff --git a/codecov.yml b/codecov.yml index fcc0885b060b..3a0292ae5d3c 100644 --- a/codecov.yml +++ b/codecov.yml @@ -14,3 +14,6 @@ coverage: patch: default: enabled: no + +ignore: + - 'packages/deno/**'