From 17752463676e7e78d922a3fc6c71ad5ec3638e8d Mon Sep 17 00:00:00 2001 From: s1gr1d <32902192+s1gr1d@users.noreply.github.com> Date: Wed, 25 Feb 2026 09:52:58 +0100 Subject: [PATCH 1/7] fix(consola): Add extra keys as `consola.` attributes --- packages/core/src/integrations/consola.ts | 23 ++++++++- .../test/lib/integrations/consola.test.ts | 47 +++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/packages/core/src/integrations/consola.ts b/packages/core/src/integrations/consola.ts index 7cc2d2395416..d74aa64bf813 100644 --- a/packages/core/src/integrations/consola.ts +++ b/packages/core/src/integrations/consola.ts @@ -61,7 +61,7 @@ export interface ConsolaReporter { */ export interface ConsolaLogObject { /** - * Allows additional custom properties to be set on the log object. + * Allows additional custom properties to be set on the log object (e.g. when reporter is called directly) * These properties will be captured as log attributes with a 'consola.' prefix. * * @example @@ -194,8 +194,11 @@ export function createConsolaReporter(options: ConsolaReporterOptions = {}): Con return { log(logObj: ConsolaLogObject) { + // We need to exclude certain known properties from being added as additional attributes // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { type, level, message: consolaMessage, args, tag, date: _date, ...attributes } = logObj; + const { type, level, message: consolaMessage, args, tag, date: _date, ...rest } = logObj; + + const hasExtraLogObjKeys = Object.keys(rest).length > 0; // Get client - use provided client or current client const client = providedClient || getClient(); @@ -223,6 +226,13 @@ export function createConsolaReporter(options: ConsolaReporterOptions = {}): Con } const message = messageParts.join(' '); + // Build attributes: `rest` properties from logObj get a "consola" prefix; base attributes added below may override + const attributes: Record = {}; + + for (const [key, value] of Object.entries(rest)) { + attributes[`consola.${key}`] = value; + } + // Build attributes attributes['sentry.origin'] = 'auto.log.consola'; @@ -239,6 +249,15 @@ export function createConsolaReporter(options: ConsolaReporterOptions = {}): Con attributes['consola.level'] = level; } + // Extra keys on logObj (beyond reserved) indicate direct `reporter.log({ type, message, ...rest })` + if (hasExtraLogObjKeys && args && args.length >= 1 && typeof args[0] === 'string') { + // Use first 'string' arg as message + const message = args[0]; + + _INTERNAL_captureLog({ level: logSeverityLevel, message, attributes }); + return; + } + _INTERNAL_captureLog({ level: logSeverityLevel, message, diff --git a/packages/core/test/lib/integrations/consola.test.ts b/packages/core/test/lib/integrations/consola.test.ts index 2b9ea96edf75..6c4310c61475 100644 --- a/packages/core/test/lib/integrations/consola.test.ts +++ b/packages/core/test/lib/integrations/consola.test.ts @@ -207,4 +207,51 @@ describe('createConsolaReporter', () => { expect(_INTERNAL_captureLog).toHaveBeenCalledTimes(6); }); }); + + describe('direct reporter call (extra log keys)', () => { + it('consola-merged: args=[message] with extra keys on logObj', () => { + sentryReporter.log({ + type: 'log', + level: 2, + args: ['obj-message'], + userId: 123, + action: 'login', + time: '2026-02-24T10:24:04.477Z', + smallObj: { word: 'hi' }, + tag: '', + }); + + const call = vi.mocked(_INTERNAL_captureLog).mock.calls[0]![0]; + + // Message from args + expect(call.message).toBe('obj-message'); + expect(call.attributes).toMatchObject({ + 'consola.type': 'log', + 'consola.level': 2, + 'consola.userId': 123, + 'consola.smallObj': { word: 'hi' }, + 'consola.action': 'login', + 'consola.time': '2026-02-24T10:24:04.477Z', + 'sentry.origin': 'auto.log.consola', + }); + expect(call.attributes?.['sentry.message.parameter.0']).toBeUndefined(); + }); + + it('direct reporter.log({ type, message, userId, sessionId }) captures custom keys with consola. prefix', () => { + sentryReporter.log({ + type: 'info', + message: 'User action', + userId: 123, + sessionId: 'abc-123', + }); + + const call = vi.mocked(_INTERNAL_captureLog).mock.calls[0]![0]; + expect(call.message).toBe('User action'); + expect(call.attributes).toMatchObject({ + 'consola.type': 'info', + 'consola.userId': 123, + 'consola.sessionId': 'abc-123', + }); + }); + }); }); From 75774d99e9889627d397d7e0393c7ebf384d0d15 Mon Sep 17 00:00:00 2001 From: s1gr1d <32902192+s1gr1d@users.noreply.github.com> Date: Wed, 25 Feb 2026 09:57:40 +0100 Subject: [PATCH 2/7] move comment --- packages/core/src/integrations/consola.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/integrations/consola.ts b/packages/core/src/integrations/consola.ts index d74aa64bf813..a4c9855aa565 100644 --- a/packages/core/src/integrations/consola.ts +++ b/packages/core/src/integrations/consola.ts @@ -226,9 +226,9 @@ export function createConsolaReporter(options: ConsolaReporterOptions = {}): Con } const message = messageParts.join(' '); - // Build attributes: `rest` properties from logObj get a "consola" prefix; base attributes added below may override const attributes: Record = {}; + // Build attributes: `rest` properties from logObj get a "consola" prefix; base attributes added below may override for (const [key, value] of Object.entries(rest)) { attributes[`consola.${key}`] = value; } From d906d0d4078af061d7fc94e6636cb0f425a70d53 Mon Sep 17 00:00:00 2001 From: s1gr1d <32902192+s1gr1d@users.noreply.github.com> Date: Wed, 25 Feb 2026 10:05:12 +0100 Subject: [PATCH 3/7] add normalization --- packages/core/src/integrations/consola.ts | 3 ++- packages/core/test/lib/integrations/consola.test.ts | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/core/src/integrations/consola.ts b/packages/core/src/integrations/consola.ts index a4c9855aa565..10cfaeddbf97 100644 --- a/packages/core/src/integrations/consola.ts +++ b/packages/core/src/integrations/consola.ts @@ -3,6 +3,7 @@ import { getClient } from '../currentScopes'; import { _INTERNAL_captureLog } from '../logs/internal'; import { formatConsoleArgs } from '../logs/utils'; import type { LogSeverityLevel } from '../types-hoist/log'; +import { normalize } from '../utils/normalize'; /** * Options for the Sentry Consola reporter. @@ -230,7 +231,7 @@ export function createConsolaReporter(options: ConsolaReporterOptions = {}): Con // Build attributes: `rest` properties from logObj get a "consola" prefix; base attributes added below may override for (const [key, value] of Object.entries(rest)) { - attributes[`consola.${key}`] = value; + attributes[`consola.${key}`] = normalize(value, normalizeDepth, normalizeMaxBreadth); } // Build attributes diff --git a/packages/core/test/lib/integrations/consola.test.ts b/packages/core/test/lib/integrations/consola.test.ts index 6c4310c61475..2750419fbdeb 100644 --- a/packages/core/test/lib/integrations/consola.test.ts +++ b/packages/core/test/lib/integrations/consola.test.ts @@ -217,7 +217,7 @@ describe('createConsolaReporter', () => { userId: 123, action: 'login', time: '2026-02-24T10:24:04.477Z', - smallObj: { word: 'hi' }, + smallObj: { firstLevel: { secondLevel: { thirdLevel: { fourthLevel: 'deep' } } } }, tag: '', }); @@ -229,7 +229,7 @@ describe('createConsolaReporter', () => { 'consola.type': 'log', 'consola.level': 2, 'consola.userId': 123, - 'consola.smallObj': { word: 'hi' }, + 'consola.smallObj': { firstLevel: { secondLevel: { thirdLevel: '[Object]' } } }, // Object is normalized 'consola.action': 'login', 'consola.time': '2026-02-24T10:24:04.477Z', 'sentry.origin': 'auto.log.consola', From d840e74eaaeb30fa958f3fc18682673588454547 Mon Sep 17 00:00:00 2001 From: s1gr1d <32902192+s1gr1d@users.noreply.github.com> Date: Wed, 25 Feb 2026 10:18:41 +0100 Subject: [PATCH 4/7] remove message variable --- packages/core/src/integrations/consola.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/core/src/integrations/consola.ts b/packages/core/src/integrations/consola.ts index 10cfaeddbf97..a4ac088fc396 100644 --- a/packages/core/src/integrations/consola.ts +++ b/packages/core/src/integrations/consola.ts @@ -253,9 +253,7 @@ export function createConsolaReporter(options: ConsolaReporterOptions = {}): Con // Extra keys on logObj (beyond reserved) indicate direct `reporter.log({ type, message, ...rest })` if (hasExtraLogObjKeys && args && args.length >= 1 && typeof args[0] === 'string') { // Use first 'string' arg as message - const message = args[0]; - - _INTERNAL_captureLog({ level: logSeverityLevel, message, attributes }); + _INTERNAL_captureLog({ level: logSeverityLevel, message: args[0], attributes }); return; } From ab8587354be4b47f300a838819fa14e88678fe75 Mon Sep 17 00:00:00 2001 From: s1gr1d <32902192+s1gr1d@users.noreply.github.com> Date: Wed, 25 Feb 2026 10:24:29 +0100 Subject: [PATCH 5/7] format message --- packages/core/src/integrations/consola.ts | 7 ------- packages/core/test/lib/integrations/consola.test.ts | 4 ++-- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/core/src/integrations/consola.ts b/packages/core/src/integrations/consola.ts index a4ac088fc396..8b27824a2885 100644 --- a/packages/core/src/integrations/consola.ts +++ b/packages/core/src/integrations/consola.ts @@ -250,13 +250,6 @@ export function createConsolaReporter(options: ConsolaReporterOptions = {}): Con attributes['consola.level'] = level; } - // Extra keys on logObj (beyond reserved) indicate direct `reporter.log({ type, message, ...rest })` - if (hasExtraLogObjKeys && args && args.length >= 1 && typeof args[0] === 'string') { - // Use first 'string' arg as message - _INTERNAL_captureLog({ level: logSeverityLevel, message: args[0], attributes }); - return; - } - _INTERNAL_captureLog({ level: logSeverityLevel, message, diff --git a/packages/core/test/lib/integrations/consola.test.ts b/packages/core/test/lib/integrations/consola.test.ts index 2750419fbdeb..b1c03ef77a4e 100644 --- a/packages/core/test/lib/integrations/consola.test.ts +++ b/packages/core/test/lib/integrations/consola.test.ts @@ -213,7 +213,7 @@ describe('createConsolaReporter', () => { sentryReporter.log({ type: 'log', level: 2, - args: ['obj-message'], + args: ['Hello', 'world', { some: 'obj' }], userId: 123, action: 'login', time: '2026-02-24T10:24:04.477Z', @@ -224,7 +224,7 @@ describe('createConsolaReporter', () => { const call = vi.mocked(_INTERNAL_captureLog).mock.calls[0]![0]; // Message from args - expect(call.message).toBe('obj-message'); + expect(call.message).toBe('Hello world {"some":"obj"}'); expect(call.attributes).toMatchObject({ 'consola.type': 'log', 'consola.level': 2, From 6db152d49c4267cdcd9cbd438442b6c4dfec38b8 Mon Sep 17 00:00:00 2001 From: s1gr1d <32902192+s1gr1d@users.noreply.github.com> Date: Wed, 25 Feb 2026 13:03:32 +0100 Subject: [PATCH 6/7] fix tests --- packages/core/src/integrations/consola.ts | 18 ++++++++++-------- .../core/test/lib/integrations/consola.test.ts | 18 +++++++++--------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/packages/core/src/integrations/consola.ts b/packages/core/src/integrations/consola.ts index 8b27824a2885..26ca7b71ab4e 100644 --- a/packages/core/src/integrations/consola.ts +++ b/packages/core/src/integrations/consola.ts @@ -62,8 +62,9 @@ export interface ConsolaReporter { */ export interface ConsolaLogObject { /** - * Allows additional custom properties to be set on the log object (e.g. when reporter is called directly) - * These properties will be captured as log attributes with a 'consola.' prefix. + * Allows additional custom properties to be set on the log object. These properties will be captured as log attributes. + * + * Additional properties are set when passing a single object with a `message` (`consola.[type]({ message: '', ... })`) or if the reporter is called directly * * @example * ```ts @@ -74,7 +75,7 @@ export interface ConsolaLogObject { * userId: 123, * sessionId: 'abc-123' * }); - * // Will create attributes: consola.userId and consola.sessionId + * // Will create attributes: `userId` and `sessionId` * ``` */ [key: string]: unknown; @@ -153,6 +154,10 @@ export interface ConsolaLogObject { * * When provided, this is the final formatted message. When not provided, * the message should be constructed from the `args` array. + * + * Note: In reporters, `message` is typically undefined. It is primarily for + * `consola.[type]({ message: 'xxx' })` usage and is normalized into `args` before + * reporters receive the log object. See: https://github.com/unjs/consola/issues/406#issuecomment-3684792551 */ message?: string; } @@ -199,8 +204,6 @@ export function createConsolaReporter(options: ConsolaReporterOptions = {}): Con // eslint-disable-next-line @typescript-eslint/no-unused-vars const { type, level, message: consolaMessage, args, tag, date: _date, ...rest } = logObj; - const hasExtraLogObjKeys = Object.keys(rest).length > 0; - // Get client - use provided client or current client const client = providedClient || getClient(); if (!client) { @@ -229,12 +232,11 @@ export function createConsolaReporter(options: ConsolaReporterOptions = {}): Con const attributes: Record = {}; - // Build attributes: `rest` properties from logObj get a "consola" prefix; base attributes added below may override + // Build attributes for (const [key, value] of Object.entries(rest)) { - attributes[`consola.${key}`] = normalize(value, normalizeDepth, normalizeMaxBreadth); + attributes[key] = normalize(value, normalizeDepth, normalizeMaxBreadth); } - // Build attributes attributes['sentry.origin'] = 'auto.log.consola'; if (tag) { diff --git a/packages/core/test/lib/integrations/consola.test.ts b/packages/core/test/lib/integrations/consola.test.ts index b1c03ef77a4e..727f9c4b07d1 100644 --- a/packages/core/test/lib/integrations/consola.test.ts +++ b/packages/core/test/lib/integrations/consola.test.ts @@ -208,8 +208,8 @@ describe('createConsolaReporter', () => { }); }); - describe('direct reporter call (extra log keys)', () => { - it('consola-merged: args=[message] with extra keys on logObj', () => { + describe('message and args handling', () => { + it('consola-merged: args=[message] with extra keys on log object', () => { sentryReporter.log({ type: 'log', level: 2, @@ -228,16 +228,16 @@ describe('createConsolaReporter', () => { expect(call.attributes).toMatchObject({ 'consola.type': 'log', 'consola.level': 2, - 'consola.userId': 123, - 'consola.smallObj': { firstLevel: { secondLevel: { thirdLevel: '[Object]' } } }, // Object is normalized - 'consola.action': 'login', - 'consola.time': '2026-02-24T10:24:04.477Z', + userId: 123, + smallObj: { firstLevel: { secondLevel: { thirdLevel: '[Object]' } } }, // Object is normalized + action: 'login', + time: '2026-02-24T10:24:04.477Z', 'sentry.origin': 'auto.log.consola', }); expect(call.attributes?.['sentry.message.parameter.0']).toBeUndefined(); }); - it('direct reporter.log({ type, message, userId, sessionId }) captures custom keys with consola. prefix', () => { + it('capturing custom keys mimicking `log({ message: "", ... })` or direct reporter.log({ type, message, userId, sessionId })', () => { sentryReporter.log({ type: 'info', message: 'User action', @@ -249,8 +249,8 @@ describe('createConsolaReporter', () => { expect(call.message).toBe('User action'); expect(call.attributes).toMatchObject({ 'consola.type': 'info', - 'consola.userId': 123, - 'consola.sessionId': 'abc-123', + userId: 123, + sessionId: 'abc-123', }); }); }); From 286f0096ba43e42ac57dceb055e57df42afb5666 Mon Sep 17 00:00:00 2001 From: s1gr1d <32902192+s1gr1d@users.noreply.github.com> Date: Thu, 26 Feb 2026 10:07:03 +0100 Subject: [PATCH 7/7] update tests --- .../test/lib/integrations/consola.test.ts | 94 +++++++++---------- 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/packages/core/test/lib/integrations/consola.test.ts b/packages/core/test/lib/integrations/consola.test.ts index 727f9c4b07d1..e1a32b775e54 100644 --- a/packages/core/test/lib/integrations/consola.test.ts +++ b/packages/core/test/lib/integrations/consola.test.ts @@ -62,7 +62,7 @@ describe('createConsolaReporter', () => { }); describe('message and args handling', () => { - it('should format message from args when message is not provided', () => { + it('should format message from args', () => { const logObj = { type: 'info', args: ['Hello', 'world', 123, { key: 'value' }], @@ -101,6 +101,51 @@ describe('createConsolaReporter', () => { }, }); }); + + it('consola-merged: args=[message] with extra keys on log object', () => { + sentryReporter.log({ + type: 'log', + level: 2, + args: ['Hello', 'world', { some: 'obj' }], + userId: 123, + action: 'login', + time: '2026-02-24T10:24:04.477Z', + smallObj: { firstLevel: { secondLevel: { thirdLevel: { fourthLevel: 'deep' } } } }, + tag: '', + }); + + const call = vi.mocked(_INTERNAL_captureLog).mock.calls[0]![0]; + + // Message from args + expect(call.message).toBe('Hello world {"some":"obj"}'); + expect(call.attributes).toMatchObject({ + 'consola.type': 'log', + 'consola.level': 2, + userId: 123, + smallObj: { firstLevel: { secondLevel: { thirdLevel: '[Object]' } } }, // Object is normalized + action: 'login', + time: '2026-02-24T10:24:04.477Z', + 'sentry.origin': 'auto.log.consola', + }); + expect(call.attributes?.['sentry.message.parameter.0']).toBeUndefined(); + }); + + it('capturing custom keys mimicking direct reporter.log({ type, message, userId, sessionId })', () => { + sentryReporter.log({ + type: 'info', + message: 'User action', + userId: 123, + sessionId: 'abc-123', + }); + + const call = vi.mocked(_INTERNAL_captureLog).mock.calls[0]![0]; + expect(call.message).toBe('User action'); + expect(call.attributes).toMatchObject({ + 'consola.type': 'info', + userId: 123, + sessionId: 'abc-123', + }); + }); }); describe('level mapping', () => { @@ -207,51 +252,4 @@ describe('createConsolaReporter', () => { expect(_INTERNAL_captureLog).toHaveBeenCalledTimes(6); }); }); - - describe('message and args handling', () => { - it('consola-merged: args=[message] with extra keys on log object', () => { - sentryReporter.log({ - type: 'log', - level: 2, - args: ['Hello', 'world', { some: 'obj' }], - userId: 123, - action: 'login', - time: '2026-02-24T10:24:04.477Z', - smallObj: { firstLevel: { secondLevel: { thirdLevel: { fourthLevel: 'deep' } } } }, - tag: '', - }); - - const call = vi.mocked(_INTERNAL_captureLog).mock.calls[0]![0]; - - // Message from args - expect(call.message).toBe('Hello world {"some":"obj"}'); - expect(call.attributes).toMatchObject({ - 'consola.type': 'log', - 'consola.level': 2, - userId: 123, - smallObj: { firstLevel: { secondLevel: { thirdLevel: '[Object]' } } }, // Object is normalized - action: 'login', - time: '2026-02-24T10:24:04.477Z', - 'sentry.origin': 'auto.log.consola', - }); - expect(call.attributes?.['sentry.message.parameter.0']).toBeUndefined(); - }); - - it('capturing custom keys mimicking `log({ message: "", ... })` or direct reporter.log({ type, message, userId, sessionId })', () => { - sentryReporter.log({ - type: 'info', - message: 'User action', - userId: 123, - sessionId: 'abc-123', - }); - - const call = vi.mocked(_INTERNAL_captureLog).mock.calls[0]![0]; - expect(call.message).toBe('User action'); - expect(call.attributes).toMatchObject({ - 'consola.type': 'info', - userId: 123, - sessionId: 'abc-123', - }); - }); - }); });