diff --git a/dev-packages/browser-integration-tests/suites/public-api/logger/integration/subject.js b/dev-packages/browser-integration-tests/suites/public-api/logger/integration/subject.js index 6c2e9cfdde7a..6974f191b76b 100644 --- a/dev-packages/browser-integration-tests/suites/public-api/logger/integration/subject.js +++ b/dev-packages/browser-integration-tests/suites/public-api/logger/integration/subject.js @@ -6,6 +6,11 @@ console.warn('console.warn', 123, false); console.error('console.error', 123, false); console.assert(false, 'console.assert', 123, false); +// Test object and array truncation +console.log('Object:', { key: 'value', nested: { prop: 123 } }); +console.log('Array:', [1, 2, 3, 'string']); +console.log('Mixed:', 'prefix', { obj: true }, [4, 5, 6], 'suffix'); + console.log(''); Sentry.flush(); diff --git a/dev-packages/browser-integration-tests/suites/public-api/logger/integration/test.ts b/dev-packages/browser-integration-tests/suites/public-api/logger/integration/test.ts index e3aabc0d2fe6..7561b76e8b72 100644 --- a/dev-packages/browser-integration-tests/suites/public-api/logger/integration/test.ts +++ b/dev-packages/browser-integration-tests/suites/public-api/logger/integration/test.ts @@ -18,7 +18,7 @@ sentryTest('should capture console object calls', async ({ getLocalTestUrl, page expect(envelopeItems[0]).toEqual([ { type: 'log', - item_count: 8, + item_count: 11, content_type: 'application/vnd.sentry.items.log+json', }, { @@ -107,6 +107,42 @@ sentryTest('should capture console object calls', async ({ getLocalTestUrl, page 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, }, }, + { + timestamp: expect.any(Number), + level: 'info', + severity_number: 10, + trace_id: expect.any(String), + body: 'Object: {"key":"value","nested":{"prop":123}}', + attributes: { + 'sentry.origin': { value: 'auto.console.logging', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'info', + severity_number: 10, + trace_id: expect.any(String), + body: 'Array: [1,2,3,"string"]', + attributes: { + 'sentry.origin': { value: 'auto.console.logging', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'info', + severity_number: 10, + trace_id: expect.any(String), + body: 'Mixed: prefix {"obj":true} [4,5,6] suffix', + attributes: { + 'sentry.origin': { value: 'auto.console.logging', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + }, + }, { timestamp: expect.any(Number), level: 'info', diff --git a/packages/core/src/logs/console-integration.ts b/packages/core/src/logs/console-integration.ts index 58460f59d9ab..677532c36346 100644 --- a/packages/core/src/logs/console-integration.ts +++ b/packages/core/src/logs/console-integration.ts @@ -5,8 +5,9 @@ import { defineIntegration } from '../integration'; import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '../semanticAttributes'; import type { ConsoleLevel } from '../types-hoist/instrument'; import type { IntegrationFn } from '../types-hoist/integration'; +import { isPrimitive } from '../utils/is'; import { CONSOLE_LEVELS, logger } from '../utils/logger'; -import { safeJoin } from '../utils/string'; +import { normalize } from '../utils/normalize'; import { GLOBAL_OBJ } from '../utils/worldwide'; import { _INTERNAL_captureLog } from './exports'; @@ -32,7 +33,8 @@ const _consoleLoggingIntegration = ((options: Partial = { return { name: INTEGRATION_NAME, setup(client) { - if (!client.getOptions()._experiments?.enableLogs) { + const { _experiments, normalizeDepth = 3, normalizeMaxBreadth = 1_000 } = client.getOptions(); + if (!_experiments?.enableLogs) { DEBUG_BUILD && logger.warn('`_experiments.enableLogs` is not enabled, ConsoleLogs integration disabled'); return; } @@ -45,9 +47,11 @@ const _consoleLoggingIntegration = ((options: Partial = { if (level === 'assert') { if (!args[0]) { const followingArgs = args.slice(1); - const message = - followingArgs.length > 0 ? `Assertion failed: ${formatConsoleArgs(followingArgs)}` : 'Assertion failed'; - _INTERNAL_captureLog({ level: 'error', message, attributes: DEFAULT_ATTRIBUTES }); + const assertionMessage = + followingArgs.length > 0 + ? `Assertion failed: ${formatConsoleArgs(followingArgs, normalizeDepth, normalizeMaxBreadth)}` + : 'Assertion failed'; + _INTERNAL_captureLog({ level: 'error', message: assertionMessage, attributes: DEFAULT_ATTRIBUTES }); } return; } @@ -55,7 +59,7 @@ const _consoleLoggingIntegration = ((options: Partial = { const isLevelLog = level === 'log'; _INTERNAL_captureLog({ level: isLevelLog ? 'info' : level, - message: formatConsoleArgs(args), + message: formatConsoleArgs(args, normalizeDepth, normalizeMaxBreadth), severityNumber: isLevelLog ? 10 : undefined, attributes: DEFAULT_ATTRIBUTES, }); @@ -85,8 +89,16 @@ const _consoleLoggingIntegration = ((options: Partial = { */ export const consoleLoggingIntegration = defineIntegration(_consoleLoggingIntegration); -function formatConsoleArgs(values: unknown[]): string { +function formatConsoleArgs(values: unknown[], normalizeDepth: number, normalizeMaxBreadth: number): string { return 'util' in GLOBAL_OBJ && typeof (GLOBAL_OBJ as GlobalObjectWithUtil).util.format === 'function' ? (GLOBAL_OBJ as GlobalObjectWithUtil).util.format(...values) - : safeJoin(values, ' '); + : safeJoinConsoleArgs(values, normalizeDepth, normalizeMaxBreadth); +} + +function safeJoinConsoleArgs(values: unknown[], normalizeDepth: number, normalizeMaxBreadth: number): string { + return values + .map(value => + isPrimitive(value) ? String(value) : JSON.stringify(normalize(value, normalizeDepth, normalizeMaxBreadth)), + ) + .join(' '); }