diff --git a/packages/core/src/integrations/consola.ts b/packages/core/src/integrations/consola.ts index 4781b253b161..7cc2d2395416 100644 --- a/packages/core/src/integrations/consola.ts +++ b/packages/core/src/integrations/consola.ts @@ -123,13 +123,20 @@ export interface ConsolaLogObject { /** * The raw arguments passed to the log method. * - * When `message` is not provided, these args are typically formatted into the final message. + * These args are typically formatted into the final `message`. In Consola reporters, `message` is not provided. * * @example * ```ts * consola.info('Hello', 'world', { user: 'john' }); * // args = ['Hello', 'world', { user: 'john' }] * ``` + * + * @example + * ```ts + * // `message` is a reserved property in Consola + * consola.log({ message: 'Hello' }); + * // args = ['Hello'] + * ``` */ args?: unknown[]; diff --git a/packages/core/test/lib/integrations/consola.test.ts b/packages/core/test/lib/integrations/consola.test.ts index a32f073eeb75..2b9ea96edf75 100644 --- a/packages/core/test/lib/integrations/consola.test.ts +++ b/packages/core/test/lib/integrations/consola.test.ts @@ -11,9 +11,14 @@ vi.mock('../../../src/logs/internal', () => ({ _INTERNAL_flushLogsBuffer: vi.fn(), })); -vi.mock('../../../src/logs/utils', async actual => ({ - formatConsoleArgs: vi.fn(((await actual()) as any).formatConsoleArgs), -})); +vi.mock('../../../src/logs/utils', async importOriginal => { + // eslint-disable-next-line @typescript-eslint/consistent-type-imports + const actual: typeof import('../../../src/logs/utils') = await importOriginal(); + return { + ...actual, + formatConsoleArgs: vi.fn(actual.formatConsoleArgs), + }; +}); vi.mock('../../../src/currentScopes', () => ({ getClient: vi.fn(), @@ -22,6 +27,7 @@ vi.mock('../../../src/currentScopes', () => ({ describe('createConsolaReporter', () => { let mockClient: TestClient; + let sentryReporter: ReturnType; beforeEach(() => { vi.clearAllMocks(); @@ -34,12 +40,11 @@ describe('createConsolaReporter', () => { normalizeMaxBreadth: 1000, }); - const mockScope = { - getClient: vi.fn().mockReturnValue(mockClient), - }; - vi.mocked(getClient).mockReturnValue(mockClient); - vi.mocked(getCurrentScope).mockReturnValue(mockScope as any); + vi.mocked(getCurrentScope).mockReturnValue({ + getClient: vi.fn().mockReturnValue(mockClient), + } as any); + sentryReporter = createConsolaReporter(); }); afterEach(() => { @@ -56,126 +61,7 @@ describe('createConsolaReporter', () => { }); }); - describe('log capturing', () => { - let sentryReporter: any; - - beforeEach(() => { - sentryReporter = createConsolaReporter(); - }); - - it('should capture error logs', () => { - const logObj = { - type: 'error', - level: 0, - message: 'This is an error', - tag: 'test', - date: new Date('2023-01-01T00:00:00.000Z'), - }; - - sentryReporter.log(logObj); - - expect(_INTERNAL_captureLog).toHaveBeenCalledWith({ - level: 'error', - message: 'This is an error', - attributes: { - 'sentry.origin': 'auto.log.consola', - 'consola.tag': 'test', - 'consola.type': 'error', - 'consola.level': 0, - }, - }); - }); - - it('should capture warn logs', () => { - const logObj = { - type: 'warn', - message: 'This is a warning', - }; - - sentryReporter.log(logObj); - - expect(_INTERNAL_captureLog).toHaveBeenCalledWith({ - level: 'warn', - message: 'This is a warning', - attributes: { - 'sentry.origin': 'auto.log.consola', - 'consola.type': 'warn', - }, - }); - }); - - it('should capture info logs', () => { - const logObj = { - type: 'info', - message: 'This is info', - }; - - sentryReporter.log(logObj); - - expect(_INTERNAL_captureLog).toHaveBeenCalledWith({ - level: 'info', - message: 'This is info', - attributes: { - 'sentry.origin': 'auto.log.consola', - 'consola.type': 'info', - }, - }); - }); - - it('should capture debug logs', () => { - const logObj = { - type: 'debug', - message: 'Debug message', - }; - - sentryReporter.log(logObj); - - expect(_INTERNAL_captureLog).toHaveBeenCalledWith({ - level: 'debug', - message: 'Debug message', - attributes: { - 'sentry.origin': 'auto.log.consola', - 'consola.type': 'debug', - }, - }); - }); - - it('should capture trace logs', () => { - const logObj = { - type: 'trace', - message: 'Trace message', - }; - - sentryReporter.log(logObj); - - expect(_INTERNAL_captureLog).toHaveBeenCalledWith({ - level: 'trace', - message: 'Trace message', - attributes: { - 'sentry.origin': 'auto.log.consola', - 'consola.type': 'trace', - }, - }); - }); - - it('should capture fatal logs', () => { - const logObj = { - type: 'fatal', - message: 'Fatal error', - }; - - sentryReporter.log(logObj); - - expect(_INTERNAL_captureLog).toHaveBeenCalledWith({ - level: 'fatal', - message: 'Fatal error', - attributes: { - 'sentry.origin': 'auto.log.consola', - 'consola.type': 'fatal', - }, - }); - }); - + describe('message and args handling', () => { it('should format message from args when message is not provided', () => { const logObj = { type: 'info', @@ -215,53 +101,67 @@ describe('createConsolaReporter', () => { }, }); }); + }); - it('should map consola levels to sentry levels when type is not provided', () => { - const logObj = { - level: 0, // Fatal level - message: 'Fatal message', - }; - - sentryReporter.log(logObj); - - expect(_INTERNAL_captureLog).toHaveBeenCalledWith({ - level: 'fatal', - message: 'Fatal message', - attributes: { - 'sentry.origin': 'auto.log.consola', - 'consola.level': 0, - }, - }); + describe('level mapping', () => { + it.each([ + ['error', 'error'], + ['warn', 'warn'], + ['info', 'info'], + ['debug', 'debug'], + ['trace', 'trace'], + ['fatal', 'fatal'], + ] as const)('maps type "%s" to Sentry level "%s"', (type, expectedLevel) => { + sentryReporter.log({ type, args: [`${type} message`] }); + + expect(_INTERNAL_captureLog).toHaveBeenCalledWith( + expect.objectContaining({ + level: expectedLevel, + message: `${type} message`, + attributes: expect.objectContaining({ 'consola.type': type, 'sentry.origin': 'auto.log.consola' }), + }), + ); }); - it('should map various consola types correctly', () => { - const testCases = [ - { type: 'success', expectedLevel: 'info' }, - { type: 'fail', expectedLevel: 'error' }, - { type: 'ready', expectedLevel: 'info' }, - { type: 'start', expectedLevel: 'info' }, - { type: 'verbose', expectedLevel: 'debug' }, - { type: 'log', expectedLevel: 'info' }, - { type: 'silent', expectedLevel: 'trace' }, - ]; - - testCases.forEach(({ type, expectedLevel }) => { - vi.clearAllMocks(); - - sentryReporter.log({ - type, - message: `Test ${type} message`, - }); - - expect(_INTERNAL_captureLog).toHaveBeenCalledWith({ + it.each([ + ['success', 'info'], + ['fail', 'error'], + ['ready', 'info'], + ['start', 'info'], + ['verbose', 'debug'], + ['log', 'info'], + ['silent', 'trace'], + ] as const)('maps consola type "%s" to Sentry level "%s"', (type, expectedLevel) => { + sentryReporter.log({ type, args: [`Test ${type}`] }); + + expect(_INTERNAL_captureLog).toHaveBeenCalledWith( + expect.objectContaining({ level: expectedLevel, - message: `Test ${type} message`, - attributes: { - 'sentry.origin': 'auto.log.consola', + message: `Test ${type}`, + attributes: expect.objectContaining({ 'consola.type': type, - }, - }); + 'sentry.origin': 'auto.log.consola', + }), + }), + ); + }); + + it('uses level number when type is missing', () => { + sentryReporter.log({ + level: 0, // Fatal level + args: ['Fatal message'], }); + + expect(_INTERNAL_captureLog).toHaveBeenCalledWith( + expect.objectContaining({ + level: 'fatal', + message: 'Fatal message', + attributes: expect.objectContaining({ + 'consola.level': 0, + 'sentry.origin': 'auto.log.consola', + }), + }), + ); }); }); @@ -274,21 +174,21 @@ describe('createConsolaReporter', () => { // Should capture error filteredReporter.log({ type: 'error', - message: 'Error message', + args: ['Error message'], }); expect(_INTERNAL_captureLog).toHaveBeenCalledTimes(1); // Should capture warn filteredReporter.log({ type: 'warn', - message: 'Warn message', + args: ['Warn message'], }); expect(_INTERNAL_captureLog).toHaveBeenCalledTimes(2); // Should not capture info filteredReporter.log({ type: 'info', - message: 'Info message', + args: ['Info message'], }); expect(_INTERNAL_captureLog).toHaveBeenCalledTimes(2); }); @@ -300,7 +200,7 @@ describe('createConsolaReporter', () => { ['trace', 'debug', 'info', 'warn', 'error', 'fatal'].forEach(type => { defaultReporter.log({ type, - message: `${type} message`, + args: [`${type} message`], }); });