From adf801f671ef1e3c2d149dad2b35c73b4fcbff7e Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 25 Nov 2025 12:58:22 +0100 Subject: [PATCH 1/6] fix(node): Always use custom scope when calling `Sentry.logger.*` --- packages/node-core/src/logs/capture.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/node-core/src/logs/capture.ts b/packages/node-core/src/logs/capture.ts index 4c2fdc73a34c..8c77186785ff 100644 --- a/packages/node-core/src/logs/capture.ts +++ b/packages/node-core/src/logs/capture.ts @@ -34,7 +34,8 @@ export type CaptureLogArgs = CaptureLogArgWithTemplate | CaptureLogArgWithoutTem export function captureLog(level: LogSeverityLevel, ...args: CaptureLogArgs): void { const [messageOrMessageTemplate, paramsOrAttributes, maybeAttributesOrMetadata, maybeMetadata] = args; if (Array.isArray(paramsOrAttributes)) { - const attributes = { ...(maybeAttributesOrMetadata as Log['attributes']) }; + // type-casting here because from the type definitions we know that `maybeAttributesOrMetadata` is an attributes object (or undefined) + const attributes = { ...(maybeAttributesOrMetadata as Log['attributes'] | undefined) }; attributes['sentry.message.template'] = messageOrMessageTemplate; paramsOrAttributes.forEach((param, index) => { attributes[`sentry.message.parameter.${index}`] = param; @@ -44,7 +45,8 @@ export function captureLog(level: LogSeverityLevel, ...args: CaptureLogArgs): vo } else { _INTERNAL_captureLog( { level, message: messageOrMessageTemplate, attributes: paramsOrAttributes }, - maybeMetadata?.scope, + // type-casting here because from the type definitions we know that `maybeAttributesOrMetadata` is a metadata object (or undefined) + (maybeAttributesOrMetadata as CaptureLogMetadata | undefined)?.scope ?? maybeMetadata?.scope, ); } } From 75744da04becc4aededc51b9b6490d419af62d74 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 25 Nov 2025 13:27:22 +0100 Subject: [PATCH 2/6] add node integration test --- .../suites/public-api/logs/subject.ts | 27 ++++ .../suites/public-api/logs/test.ts | 127 ++++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 dev-packages/node-core-integration-tests/suites/public-api/logs/subject.ts create mode 100644 dev-packages/node-core-integration-tests/suites/public-api/logs/test.ts diff --git a/dev-packages/node-core-integration-tests/suites/public-api/logs/subject.ts b/dev-packages/node-core-integration-tests/suites/public-api/logs/subject.ts new file mode 100644 index 000000000000..3396e2b501e6 --- /dev/null +++ b/dev-packages/node-core-integration-tests/suites/public-api/logs/subject.ts @@ -0,0 +1,27 @@ +import * as Sentry from '@sentry/node-core'; +import { loggingTransport } from '@sentry-internal/node-integration-tests'; + +const client = new Sentry.NodeClient({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + transport: loggingTransport, + stackParser: Sentry.defaultStackParser, + integrations: [], + enableLogs: true, + sendDefaultPii: true, +}); + +const customScope = new Sentry.Scope(); +customScope.setClient(client); +customScope.update({ user: { username: 'h4cktor' } }); +client.init(); + +async function run(): Promise { + Sentry.logger.info('test info', { foo: 'bar1' }, { scope: customScope }); + Sentry.logger.info('test info with {param}', [1], { foo: 'bar2' }, { scope: customScope }); + Sentry.logger.info(Sentry.logger.fmt`test info with fm ${1}`, [1], { foo: 'bar3' }, { scope: customScope }); + + await Sentry.flush(); +} + +// eslint-disable-next-line @typescript-eslint/no-floating-promises +void run(); diff --git a/dev-packages/node-core-integration-tests/suites/public-api/logs/test.ts b/dev-packages/node-core-integration-tests/suites/public-api/logs/test.ts new file mode 100644 index 000000000000..5eaf457e6d33 --- /dev/null +++ b/dev-packages/node-core-integration-tests/suites/public-api/logs/test.ts @@ -0,0 +1,127 @@ +import { afterAll, describe, expect, test } from 'vitest'; +import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; + +describe('logger public API', () => { + afterAll(() => { + cleanupChildProcesses(); + }); + + test('captures logs with parameters in different forms', async () => { + const runner = createRunner(__dirname, 'subject.ts') + .expect({ + log: { + items: [ + { + attributes: { + foo: { + type: 'string', + value: 'bar1', + }, + 'sentry.sdk.name': { + type: 'string', + value: 'sentry.javascript.node', + }, + 'sentry.sdk.version': { + type: 'string', + value: expect.any(String), + }, + 'server.address': { + type: 'string', + value: 'M6QX4Q5HKV.local', + }, + 'user.name': { + type: 'string', + value: 'h4cktor', + }, + }, + body: 'test info', + level: 'info', + severity_number: 9, + timestamp: expect.any(Number), + trace_id: expect.stringMatching(/^[\da-f]{32}$/), + }, + { + attributes: { + foo: { + type: 'string', + value: 'bar2', + }, + 'sentry.message.parameter.0': { + type: 'integer', + value: 1, + }, + 'sentry.message.template': { + type: 'string', + value: 'test info with {param}', + }, + 'sentry.sdk.name': { + type: 'string', + value: 'sentry.javascript.node', + }, + 'sentry.sdk.version': { + type: 'string', + value: expect.any(String), + }, + 'server.address': { + type: 'string', + value: 'M6QX4Q5HKV.local', + }, + 'user.name': { + type: 'string', + value: 'h4cktor', + }, + }, + body: 'test info with {param} 1', + level: 'info', + severity_number: 9, + timestamp: expect.any(Number), + trace_id: expect.stringMatching(/^[\da-f]{32}$/), + }, + { + attributes: { + foo: { + type: 'string', + value: 'bar3', + }, + 'sentry.message.parameter.0': { + type: 'integer', + value: 1, + }, + 'sentry.message.template': { + type: 'string', + value: '"test info with fm 1"', + }, + 'sentry.sdk.name': { + type: 'string', + value: 'sentry.javascript.node', + }, + 'sentry.sdk.version': { + type: 'string', + value: expect.any(String), + }, + 'server.address': { + type: 'string', + value: 'M6QX4Q5HKV.local', + }, + 'user.name': { + type: 'string', + value: 'h4cktor', + }, + }, + body: `[String: 'test info with fm 1'] { + __sentry_template_string__: 'test info with fm %s', + __sentry_template_values__: [ 1 ] +} 1`, + level: 'info', + severity_number: 9, + timestamp: expect.any(Number), + trace_id: expect.stringMatching(/^[\da-f]{32}$/), + }, + ], + }, + }) + .start(); + + await runner.completed(); + }); +}); From c166bbce357269a90019253f745bab1d7a61175e Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 25 Nov 2025 13:37:00 +0100 Subject: [PATCH 3/6] add unit tests --- packages/node-core/test/logs/exports.test.ts | 47 ++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/packages/node-core/test/logs/exports.test.ts b/packages/node-core/test/logs/exports.test.ts index 45da1722abc8..3863620fa3ff 100644 --- a/packages/node-core/test/logs/exports.test.ts +++ b/packages/node-core/test/logs/exports.test.ts @@ -1,6 +1,7 @@ import * as sentryCore from '@sentry/core'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import * as nodeLogger from '../../src/logs/exports'; +import { Scope } from '@sentry/core'; // Mock the core functions vi.mock('@sentry/core', async () => { @@ -172,4 +173,50 @@ describe('Node Logger', () => { ); }); }); + + describe('scustom cope', () => { + it('calls _INTERNAL_captureLog with custom scope for basic log message', () => { + const customScope = new Scope(); + nodeLogger.debug('User logged in', undefined, { scope: customScope }); + expect(mockCaptureLog).toHaveBeenCalledWith( + { + level: 'debug', + message: 'User logged in', + }, + customScope, + ); + }); + + it('calls _INTERNAL_captureLog with custom scope for parametrized log message', () => { + const customScope = new Scope(); + nodeLogger.debug('User %s logged in from %s', ['Alice', 'mobile'], undefined, { scope: customScope }); + expect(mockCaptureLog).toHaveBeenCalledWith( + { + level: 'debug', + message: 'User Alice logged in from mobile', + attributes: { + 'sentry.message.template': 'User %s logged in from %s', + 'sentry.message.parameter.0': 'Alice', + 'sentry.message.parameter.1': 'mobile', + }, + }, + customScope, + ); + }); + + it('calls _INTERNAL_captureLog with custom scope for fmt log message', () => { + const customScope = new Scope(); + nodeLogger.debug(nodeLogger.fmt`User ${'Alice'} logged in from ${'mobile'}`, undefined, { scope: customScope }); + expect(mockCaptureLog).toHaveBeenCalledWith( + { + level: 'debug', + message: expect.objectContaining({ + __sentry_template_string__: 'User %s logged in from %s', + __sentry_template_values__: ['Alice', 'mobile'], + }), + }, + customScope, + ); + }); + }); }); From 915ff9adfe7bf341ecbce9d865a76dd3f39d2bc7 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 25 Nov 2025 13:42:32 +0100 Subject: [PATCH 4/6] fix incorrect usage in integration test --- .../suites/public-api/logs/subject.ts | 4 ++-- .../suites/public-api/logs/test.ts | 11 ++++------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/dev-packages/node-core-integration-tests/suites/public-api/logs/subject.ts b/dev-packages/node-core-integration-tests/suites/public-api/logs/subject.ts index 3396e2b501e6..7a1bc01025d8 100644 --- a/dev-packages/node-core-integration-tests/suites/public-api/logs/subject.ts +++ b/dev-packages/node-core-integration-tests/suites/public-api/logs/subject.ts @@ -17,8 +17,8 @@ client.init(); async function run(): Promise { Sentry.logger.info('test info', { foo: 'bar1' }, { scope: customScope }); - Sentry.logger.info('test info with {param}', [1], { foo: 'bar2' }, { scope: customScope }); - Sentry.logger.info(Sentry.logger.fmt`test info with fm ${1}`, [1], { foo: 'bar3' }, { scope: customScope }); + Sentry.logger.info('test info with %d', [1], { foo: 'bar2' }, { scope: customScope }); + Sentry.logger.info(Sentry.logger.fmt`test info with fmt ${1}`, { foo: 'bar3' }, { scope: customScope }); await Sentry.flush(); } diff --git a/dev-packages/node-core-integration-tests/suites/public-api/logs/test.ts b/dev-packages/node-core-integration-tests/suites/public-api/logs/test.ts index 5eaf457e6d33..fd283ac03be7 100644 --- a/dev-packages/node-core-integration-tests/suites/public-api/logs/test.ts +++ b/dev-packages/node-core-integration-tests/suites/public-api/logs/test.ts @@ -52,7 +52,7 @@ describe('logger public API', () => { }, 'sentry.message.template': { type: 'string', - value: 'test info with {param}', + value: 'test info with %d', }, 'sentry.sdk.name': { type: 'string', @@ -71,7 +71,7 @@ describe('logger public API', () => { value: 'h4cktor', }, }, - body: 'test info with {param} 1', + body: 'test info with 1', level: 'info', severity_number: 9, timestamp: expect.any(Number), @@ -89,7 +89,7 @@ describe('logger public API', () => { }, 'sentry.message.template': { type: 'string', - value: '"test info with fm 1"', + value: 'test info with fmt %s', }, 'sentry.sdk.name': { type: 'string', @@ -108,10 +108,7 @@ describe('logger public API', () => { value: 'h4cktor', }, }, - body: `[String: 'test info with fm 1'] { - __sentry_template_string__: 'test info with fm %s', - __sentry_template_values__: [ 1 ] -} 1`, + body: 'test info with fmt 1', level: 'info', severity_number: 9, timestamp: expect.any(Number), From efb7672f0d6895fe8612a70ed5409f61088ac5c2 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 25 Nov 2025 13:47:00 +0100 Subject: [PATCH 5/6] Update dev-packages/node-core-integration-tests/suites/public-api/logs/test.ts --- .../node-core-integration-tests/suites/public-api/logs/test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/node-core-integration-tests/suites/public-api/logs/test.ts b/dev-packages/node-core-integration-tests/suites/public-api/logs/test.ts index fd283ac03be7..9469308c1ed6 100644 --- a/dev-packages/node-core-integration-tests/suites/public-api/logs/test.ts +++ b/dev-packages/node-core-integration-tests/suites/public-api/logs/test.ts @@ -6,7 +6,7 @@ describe('logger public API', () => { cleanupChildProcesses(); }); - test('captures logs with parameters in different forms', async () => { + test('captures logs with custom scopes and parameters in different forms', async () => { const runner = createRunner(__dirname, 'subject.ts') .expect({ log: { From 4150a62897a79f76ff91989ffbe7c2efd23ce154 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 25 Nov 2025 14:52:34 +0100 Subject: [PATCH 6/6] lint --- packages/node-core/test/logs/exports.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node-core/test/logs/exports.test.ts b/packages/node-core/test/logs/exports.test.ts index 3863620fa3ff..782ba85ee2c0 100644 --- a/packages/node-core/test/logs/exports.test.ts +++ b/packages/node-core/test/logs/exports.test.ts @@ -1,7 +1,7 @@ import * as sentryCore from '@sentry/core'; +import { Scope } from '@sentry/core'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import * as nodeLogger from '../../src/logs/exports'; -import { Scope } from '@sentry/core'; // Mock the core functions vi.mock('@sentry/core', async () => {