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 7075cdf3a979..e3aabc0d2fe6 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 @@ -1,5 +1,5 @@ import { expect } from '@playwright/test'; -import type { OtelLogEnvelope } from '@sentry/core'; +import type { LogEnvelope } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; import { getFirstSentryEnvelopeRequest, properFullEnvelopeRequestParser } from '../../../../utils/helpers'; @@ -12,237 +12,114 @@ sentryTest('should capture console object calls', async ({ getLocalTestUrl, page const url = await getLocalTestUrl({ testDir: __dirname }); - const event = await getFirstSentryEnvelopeRequest(page, url, properFullEnvelopeRequestParser); + const event = await getFirstSentryEnvelopeRequest(page, url, properFullEnvelopeRequestParser); const envelopeItems = event[1]; expect(envelopeItems[0]).toEqual([ { - type: 'otel_log', + type: 'log', + item_count: 8, + content_type: 'application/vnd.sentry.items.log+json', }, { - severityText: 'trace', - body: { stringValue: 'console.trace 123 false' }, - attributes: [ + items: [ { - key: 'sentry.origin', - value: { - stringValue: 'auto.console.logging', + timestamp: expect.any(Number), + level: 'trace', + severity_number: 1, + trace_id: expect.any(String), + body: 'console.trace 123 false', + 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' }, }, }, { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.browser', + timestamp: expect.any(Number), + level: 'debug', + severity_number: 5, + trace_id: expect.any(String), + body: 'console.debug 123 false', + 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' }, }, }, { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), + timestamp: expect.any(Number), + level: 'info', + severity_number: 10, + trace_id: expect.any(String), + body: 'console.log 123 false', + 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' }, }, }, - ], - timeUnixNano: expect.any(String), - traceId: expect.any(String), - severityNumber: 1, - }, - ]); - - expect(envelopeItems[1]).toEqual([ - { - type: 'otel_log', - }, - { - severityText: 'debug', - body: { stringValue: 'console.debug 123 false' }, - attributes: [ { - key: 'sentry.origin', - value: { - stringValue: 'auto.console.logging', + timestamp: expect.any(Number), + level: 'info', + severity_number: 9, + trace_id: expect.any(String), + body: 'console.info 123 false', + 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' }, }, }, { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.browser', + timestamp: expect.any(Number), + level: 'warn', + severity_number: 13, + trace_id: expect.any(String), + body: 'console.warn 123 false', + 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' }, }, }, { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), - }, - }, - ], - timeUnixNano: expect.any(String), - traceId: expect.any(String), - severityNumber: 5, - }, - ]); - - expect(envelopeItems[2]).toEqual([ - { - type: 'otel_log', - }, - { - severityText: 'info', - body: { stringValue: 'console.log 123 false' }, - attributes: [ - { - key: 'sentry.origin', - value: { - stringValue: 'auto.console.logging', - }, - }, - { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.browser', - }, - }, - { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), - }, - }, - ], - timeUnixNano: expect.any(String), - traceId: expect.any(String), - severityNumber: 10, - }, - ]); - - expect(envelopeItems[3]).toEqual([ - { - type: 'otel_log', - }, - { - severityText: 'info', - body: { stringValue: 'console.info 123 false' }, - attributes: [ - { - key: 'sentry.origin', - value: { - stringValue: 'auto.console.logging', - }, - }, - { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.browser', - }, - }, - { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), - }, - }, - ], - timeUnixNano: expect.any(String), - traceId: expect.any(String), - severityNumber: 9, - }, - ]); - - expect(envelopeItems[4]).toEqual([ - { - type: 'otel_log', - }, - { - severityText: 'warn', - body: { stringValue: 'console.warn 123 false' }, - attributes: [ - { - key: 'sentry.origin', - value: { - stringValue: 'auto.console.logging', - }, - }, - { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.browser', - }, - }, - { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), - }, - }, - ], - timeUnixNano: expect.any(String), - traceId: expect.any(String), - severityNumber: 13, - }, - ]); - - expect(envelopeItems[5]).toEqual([ - { - type: 'otel_log', - }, - { - severityText: 'error', - body: { stringValue: 'console.error 123 false' }, - attributes: [ - { - key: 'sentry.origin', - value: { - stringValue: 'auto.console.logging', - }, - }, - { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.browser', - }, - }, - { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), - }, - }, - ], - timeUnixNano: expect.any(String), - traceId: expect.any(String), - severityNumber: 17, - }, - ]); - - expect(envelopeItems[6]).toEqual([ - { - type: 'otel_log', - }, - { - severityText: 'error', - body: { stringValue: 'Assertion failed: console.assert 123 false' }, - attributes: [ - { - key: 'sentry.origin', - value: { - stringValue: 'auto.console.logging', + timestamp: expect.any(Number), + level: 'error', + severity_number: 17, + trace_id: expect.any(String), + body: 'console.error 123 false', + 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' }, }, }, { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.browser', + timestamp: expect.any(Number), + level: 'error', + severity_number: 17, + trace_id: expect.any(String), + body: 'Assertion failed: console.assert 123 false', + 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' }, }, }, { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), + timestamp: expect.any(Number), + level: 'info', + severity_number: 10, + trace_id: expect.any(String), + body: '', + 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' }, }, }, ], - timeUnixNano: expect.any(String), - traceId: expect.any(String), - severityNumber: 17, }, ]); }); diff --git a/dev-packages/browser-integration-tests/suites/public-api/logger/simple/test.ts b/dev-packages/browser-integration-tests/suites/public-api/logger/simple/test.ts index de011cbf46a9..7fc4e02bad07 100644 --- a/dev-packages/browser-integration-tests/suites/public-api/logger/simple/test.ts +++ b/dev-packages/browser-integration-tests/suites/public-api/logger/simple/test.ts @@ -1,5 +1,5 @@ import { expect } from '@playwright/test'; -import type { OtelLogEnvelope } from '@sentry/core'; +import type { LogEnvelope } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; import { getFirstSentryEnvelopeRequest, properFullEnvelopeRequestParser } from '../../../../utils/helpers'; @@ -12,510 +12,180 @@ sentryTest('should capture all logging methods', async ({ getLocalTestUrl, page const url = await getLocalTestUrl({ testDir: __dirname }); - const event = await getFirstSentryEnvelopeRequest(page, url, properFullEnvelopeRequestParser); + const event = await getFirstSentryEnvelopeRequest(page, url, properFullEnvelopeRequestParser); const envelopeItems = event[1]; expect(envelopeItems[0]).toEqual([ { - type: 'otel_log', + type: 'log', + item_count: 12, + content_type: 'application/vnd.sentry.items.log+json', }, { - severityText: 'trace', - body: { stringValue: 'test trace' }, - attributes: [ + items: [ { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.browser', + timestamp: expect.any(Number), + level: 'trace', + body: 'test trace', + severity_number: 1, + trace_id: expect.any(String), + attributes: { + 'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'debug', + body: 'test debug', + severity_number: 5, + trace_id: expect.any(String), + attributes: { + 'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'info', + body: 'test info', + severity_number: 9, + trace_id: expect.any(String), + attributes: { + 'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, }, - }, - { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), - }, - }, - ], - timeUnixNano: expect.any(String), - traceId: expect.any(String), - severityNumber: 1, - }, - ]); - - expect(envelopeItems[1]).toEqual([ - { - type: 'otel_log', - }, - { - severityText: 'debug', - body: { stringValue: 'test debug' }, - attributes: [ - { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.browser', - }, - }, - { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), - }, - }, - ], - timeUnixNano: expect.any(String), - traceId: expect.any(String), - severityNumber: 5, - }, - ]); - - expect(envelopeItems[2]).toEqual([ - { - type: 'otel_log', - }, - { - severityText: 'info', - body: { stringValue: 'test info' }, - attributes: [ - { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.browser', - }, - }, - { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), - }, - }, - ], - timeUnixNano: expect.any(String), - traceId: expect.any(String), - severityNumber: 9, - }, - ]); - - expect(envelopeItems[3]).toEqual([ - { - type: 'otel_log', - }, - { - severityText: 'warn', - body: { stringValue: 'test warn' }, - attributes: [ - { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.browser', - }, - }, - { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), - }, - }, - ], - timeUnixNano: expect.any(String), - traceId: expect.any(String), - severityNumber: 13, - }, - ]); - - expect(envelopeItems[4]).toEqual([ - { - type: 'otel_log', - }, - { - severityText: 'error', - body: { stringValue: 'test error' }, - attributes: [ - { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.browser', - }, - }, - { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), - }, - }, - ], - timeUnixNano: expect.any(String), - traceId: expect.any(String), - severityNumber: 17, - }, - ]); - - expect(envelopeItems[5]).toEqual([ - { - type: 'otel_log', - }, - { - severityText: 'fatal', - body: { stringValue: 'test fatal' }, - attributes: [ - { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.browser', - }, - }, - { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), - }, - }, - ], - timeUnixNano: expect.any(String), - traceId: expect.any(String), - severityNumber: 21, - }, - ]); - - expect(envelopeItems[6]).toEqual([ - { - type: 'otel_log', - }, - { - severityText: 'trace', - body: { stringValue: 'test trace stringArg false 123' }, - attributes: [ - { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.browser', - }, - }, - { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), - }, - }, - { - key: 'sentry.message.template', - value: { - stringValue: 'test %s %s %s %s', - }, - }, - { - key: 'sentry.message.parameter.0', - value: { - stringValue: 'trace', - }, - }, - { - key: 'sentry.message.parameter.1', - value: { - stringValue: 'stringArg', - }, - }, - { - key: 'sentry.message.parameter.2', - value: { - boolValue: false, - }, - }, - { - key: 'sentry.message.parameter.3', - value: { - doubleValue: 123, - }, - }, - ], - timeUnixNano: expect.any(String), - traceId: expect.any(String), - severityNumber: 1, - }, - ]); - - expect(envelopeItems[7]).toEqual([ - { - type: 'otel_log', - }, - { - severityText: 'debug', - body: { stringValue: 'test debug stringArg false 123' }, - attributes: [ - { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.browser', - }, - }, - { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), - }, - }, - { - key: 'sentry.message.template', - value: { - stringValue: 'test %s %s %s %s', - }, - }, - { - key: 'sentry.message.parameter.0', - value: { - stringValue: 'debug', - }, - }, - { - key: 'sentry.message.parameter.1', - value: { - stringValue: 'stringArg', - }, - }, - { - key: 'sentry.message.parameter.2', - value: { - boolValue: false, - }, - }, - { - key: 'sentry.message.parameter.3', - value: { - doubleValue: 123, - }, - }, - ], - timeUnixNano: expect.any(String), - traceId: expect.any(String), - severityNumber: 5, - }, - ]); - - expect(envelopeItems[8]).toEqual([ - { - type: 'otel_log', - }, - { - severityText: 'info', - body: { stringValue: 'test info stringArg false 123' }, - attributes: [ - { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.browser', - }, - }, - { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), - }, - }, - { - key: 'sentry.message.template', - value: { - stringValue: 'test %s %s %s %s', - }, - }, - { - key: 'sentry.message.parameter.0', - value: { - stringValue: 'info', - }, - }, - { - key: 'sentry.message.parameter.1', - value: { - stringValue: 'stringArg', - }, - }, - { - key: 'sentry.message.parameter.2', - value: { - boolValue: false, - }, - }, - { - key: 'sentry.message.parameter.3', - value: { - doubleValue: 123, - }, - }, - ], - timeUnixNano: expect.any(String), - traceId: expect.any(String), - severityNumber: 9, - }, - ]); - - expect(envelopeItems[9]).toEqual([ - { - type: 'otel_log', - }, - { - severityText: 'warn', - body: { stringValue: 'test warn stringArg false 123' }, - attributes: [ - { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.browser', - }, - }, - { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), - }, - }, - { - key: 'sentry.message.template', - value: { - stringValue: 'test %s %s %s %s', - }, - }, - { - key: 'sentry.message.parameter.0', - value: { - stringValue: 'warn', - }, - }, - { - key: 'sentry.message.parameter.1', - value: { - stringValue: 'stringArg', - }, - }, - { - key: 'sentry.message.parameter.2', - value: { - boolValue: false, - }, - }, - { - key: 'sentry.message.parameter.3', - value: { - doubleValue: 123, - }, - }, - ], - timeUnixNano: expect.any(String), - traceId: expect.any(String), - severityNumber: 13, - }, - ]); - - expect(envelopeItems[10]).toEqual([ - { - type: 'otel_log', - }, - { - severityText: 'error', - body: { stringValue: 'test error stringArg false 123' }, - attributes: [ - { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.browser', - }, - }, - { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), - }, - }, - { - key: 'sentry.message.template', - value: { - stringValue: 'test %s %s %s %s', - }, - }, - { - key: 'sentry.message.parameter.0', - value: { - stringValue: 'error', - }, - }, - { - key: 'sentry.message.parameter.1', - value: { - stringValue: 'stringArg', - }, - }, - { - key: 'sentry.message.parameter.2', - value: { - boolValue: false, - }, - }, - { - key: 'sentry.message.parameter.3', - value: { - doubleValue: 123, - }, - }, - ], - timeUnixNano: expect.any(String), - traceId: expect.any(String), - severityNumber: 17, - }, - ]); - - expect(envelopeItems[11]).toEqual([ - { - type: 'otel_log', - }, - { - severityText: 'fatal', - body: { stringValue: 'test fatal stringArg false 123' }, - attributes: [ - { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.browser', - }, - }, - { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), - }, - }, - { - key: 'sentry.message.template', - value: { - stringValue: 'test %s %s %s %s', - }, - }, - { - key: 'sentry.message.parameter.0', - value: { - stringValue: 'fatal', - }, - }, - { - key: 'sentry.message.parameter.1', - value: { - stringValue: 'stringArg', - }, - }, - { - key: 'sentry.message.parameter.2', - value: { - boolValue: false, - }, - }, - { - key: 'sentry.message.parameter.3', - value: { - doubleValue: 123, + }, + { + timestamp: expect.any(Number), + level: 'warn', + body: 'test warn', + severity_number: 13, + trace_id: expect.any(String), + attributes: { + 'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'error', + body: 'test error', + severity_number: 17, + trace_id: expect.any(String), + attributes: { + 'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'fatal', + body: 'test fatal', + severity_number: 21, + trace_id: expect.any(String), + attributes: { + 'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'trace', + body: 'test trace stringArg false 123', + severity_number: 1, + trace_id: expect.any(String), + attributes: { + 'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'sentry.message.template': { value: 'test %s %s %s %s', type: 'string' }, + 'sentry.message.parameter.0': { value: 'trace', type: 'string' }, + 'sentry.message.parameter.1': { value: 'stringArg', type: 'string' }, + 'sentry.message.parameter.2': { value: false, type: 'boolean' }, + 'sentry.message.parameter.3': { value: 123, type: 'integer' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'debug', + body: 'test debug stringArg false 123', + severity_number: 5, + trace_id: expect.any(String), + attributes: { + 'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'sentry.message.template': { value: 'test %s %s %s %s', type: 'string' }, + 'sentry.message.parameter.0': { value: 'debug', type: 'string' }, + 'sentry.message.parameter.1': { value: 'stringArg', type: 'string' }, + 'sentry.message.parameter.2': { value: false, type: 'boolean' }, + 'sentry.message.parameter.3': { value: 123, type: 'integer' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'info', + body: 'test info stringArg false 123', + severity_number: 9, + trace_id: expect.any(String), + attributes: { + 'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'sentry.message.template': { value: 'test %s %s %s %s', type: 'string' }, + 'sentry.message.parameter.0': { value: 'info', type: 'string' }, + 'sentry.message.parameter.1': { value: 'stringArg', type: 'string' }, + 'sentry.message.parameter.2': { value: false, type: 'boolean' }, + 'sentry.message.parameter.3': { value: 123, type: 'integer' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'warn', + body: 'test warn stringArg false 123', + severity_number: 13, + trace_id: expect.any(String), + attributes: { + 'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'sentry.message.template': { value: 'test %s %s %s %s', type: 'string' }, + 'sentry.message.parameter.0': { value: 'warn', type: 'string' }, + 'sentry.message.parameter.1': { value: 'stringArg', type: 'string' }, + 'sentry.message.parameter.2': { value: false, type: 'boolean' }, + 'sentry.message.parameter.3': { value: 123, type: 'integer' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'error', + body: 'test error stringArg false 123', + severity_number: 17, + trace_id: expect.any(String), + attributes: { + 'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'sentry.message.template': { value: 'test %s %s %s %s', type: 'string' }, + 'sentry.message.parameter.0': { value: 'error', type: 'string' }, + 'sentry.message.parameter.1': { value: 'stringArg', type: 'string' }, + 'sentry.message.parameter.2': { value: false, type: 'boolean' }, + 'sentry.message.parameter.3': { value: 123, type: 'integer' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'fatal', + body: 'test fatal stringArg false 123', + severity_number: 21, + trace_id: expect.any(String), + attributes: { + 'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'sentry.message.template': { value: 'test %s %s %s %s', type: 'string' }, + 'sentry.message.parameter.0': { value: 'fatal', type: 'string' }, + 'sentry.message.parameter.1': { value: 'stringArg', type: 'string' }, + 'sentry.message.parameter.2': { value: false, type: 'boolean' }, + 'sentry.message.parameter.3': { value: 123, type: 'integer' }, }, }, ], - timeUnixNano: expect.any(String), - traceId: expect.any(String), - severityNumber: 21, }, ]); }); diff --git a/dev-packages/node-integration-tests/suites/winston/test.ts b/dev-packages/node-integration-tests/suites/winston/test.ts index e28ec8f586ff..034210f8690b 100644 --- a/dev-packages/node-integration-tests/suites/winston/test.ts +++ b/dev-packages/node-integration-tests/suites/winston/test.ts @@ -9,92 +9,36 @@ describe('winston integration', () => { test('should capture winston logs with default levels', async () => { const runner = createRunner(__dirname, 'subject.ts') .expect({ - otel_log: { - severityText: 'info', - body: { - stringValue: 'Test info message', - }, - attributes: [ - { - key: 'sentry.origin', - value: { - stringValue: 'auto.logging.winston', - }, - }, - { - key: 'sentry.release', - value: { - stringValue: '1.0.0', - }, - }, - { - key: 'sentry.environment', - value: { - stringValue: 'test', - }, - }, - { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.node', - }, - }, - { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), - }, - }, - { - key: 'server.address', - value: { - stringValue: expect.any(String), - }, - }, - ], - }, - }) - .expect({ - otel_log: { - severityText: 'error', - body: { - stringValue: 'Test error message', - }, - attributes: [ - { - key: 'sentry.origin', - value: { - stringValue: 'auto.logging.winston', - }, - }, - { - key: 'sentry.release', - value: { - stringValue: '1.0.0', - }, - }, - { - key: 'sentry.environment', - value: { - stringValue: 'test', - }, - }, - { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.node', - }, - }, - { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), - }, - }, - { - key: 'server.address', - value: { - stringValue: expect.any(String), + log: { + items: [ + { + timestamp: expect.any(Number), + level: 'info', + body: 'Test info message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.winston', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'error', + body: 'Test error message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.winston', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, }, }, ], @@ -109,92 +53,66 @@ describe('winston integration', () => { const runner = createRunner(__dirname, 'subject.ts') .withEnv({ CUSTOM_LEVELS: 'true' }) .expect({ - otel_log: { - severityText: 'info', - body: { - stringValue: 'Test info message', - }, - attributes: [ - { - key: 'sentry.origin', - value: { - stringValue: 'auto.logging.winston', - }, - }, - { - key: 'sentry.release', - value: { - stringValue: '1.0.0', - }, - }, - { - key: 'sentry.environment', - value: { - stringValue: 'test', - }, - }, - { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.node', - }, - }, - { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), - }, - }, - { - key: 'server.address', - value: { - stringValue: expect.any(String), - }, - }, - ], - }, - }) - .expect({ - otel_log: { - severityText: 'error', - body: { - stringValue: 'Test error message', - }, - attributes: [ - { - key: 'sentry.origin', - value: { - stringValue: 'auto.logging.winston', - }, - }, - { - key: 'sentry.release', - value: { - stringValue: '1.0.0', - }, - }, - { - key: 'sentry.environment', - value: { - stringValue: 'test', - }, - }, - { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.node', - }, - }, - { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), - }, - }, - { - key: 'server.address', - value: { - stringValue: expect.any(String), + log: { + items: [ + { + timestamp: expect.any(Number), + level: 'info', + body: 'Test info message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.winston', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'error', + body: 'Test error message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.winston', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'info', + body: 'Test info message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.winston', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'error', + body: 'Test error message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.winston', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, }, }, ], @@ -209,92 +127,53 @@ describe('winston integration', () => { const runner = createRunner(__dirname, 'subject.ts') .withEnv({ WITH_METADATA: 'true' }) .expect({ - otel_log: { - severityText: 'info', - body: { - stringValue: 'Test info message', - }, - attributes: [ - { - key: 'sentry.origin', - value: { - stringValue: 'auto.logging.winston', - }, - }, - { - key: 'sentry.release', - value: { - stringValue: '1.0.0', - }, - }, - { - key: 'sentry.environment', - value: { - stringValue: 'test', - }, - }, - { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.node', - }, - }, - { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), - }, - }, - { - key: 'server.address', - value: { - stringValue: expect.any(String), - }, - }, - ], - }, - }) - .expect({ - otel_log: { - severityText: 'error', - body: { - stringValue: 'Test error message', - }, - attributes: [ - { - key: 'sentry.origin', - value: { - stringValue: 'auto.logging.winston', - }, - }, - { - key: 'sentry.release', - value: { - stringValue: '1.0.0', - }, - }, - { - key: 'sentry.environment', - value: { - stringValue: 'test', - }, - }, - { - key: 'sentry.sdk.name', - value: { - stringValue: 'sentry.javascript.node', - }, - }, - { - key: 'sentry.sdk.version', - value: { - stringValue: expect.any(String), - }, - }, - { - key: 'server.address', - value: { - stringValue: expect.any(String), + log: { + items: [ + { + timestamp: expect.any(Number), + level: 'info', + body: 'Test info message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.winston', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'error', + body: 'Test error message', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.winston', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + }, + }, + { + timestamp: expect.any(Number), + level: 'info', + body: 'Test message with metadata', + severity_number: expect.any(Number), + trace_id: expect.any(String), + attributes: { + 'sentry.origin': { value: 'auto.logging.winston', type: 'string' }, + 'sentry.release': { value: '1.0.0', type: 'string' }, + 'sentry.environment': { value: 'test', type: 'string' }, + 'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, + 'sentry.sdk.version': { value: expect.any(String), type: 'string' }, + 'server.address': { value: expect.any(String), type: 'string' }, + foo: { value: 'bar', type: 'string' }, + number: { value: 42, type: 'integer' }, }, }, ], diff --git a/dev-packages/node-integration-tests/utils/assertions.ts b/dev-packages/node-integration-tests/utils/assertions.ts index 9eee29e36ecd..296bdc608bb4 100644 --- a/dev-packages/node-integration-tests/utils/assertions.ts +++ b/dev-packages/node-integration-tests/utils/assertions.ts @@ -3,7 +3,7 @@ import type { Envelope, Event, SerializedCheckIn, - SerializedOtelLog, + SerializedLogContainer, SerializedSession, SessionAggregates, TransactionEvent, @@ -67,7 +67,10 @@ export function assertSentryClientReport(actual: ClientReport, expected: Partial }); } -export function assertSentryOtelLog(actual: SerializedOtelLog, expected: Partial): void { +export function assertSentryLogContainer( + actual: SerializedLogContainer, + expected: Partial, +): void { expect(actual).toMatchObject({ ...expected, }); diff --git a/dev-packages/node-integration-tests/utils/runner.ts b/dev-packages/node-integration-tests/utils/runner.ts index f327dd759bfb..79e43d056ce6 100644 --- a/dev-packages/node-integration-tests/utils/runner.ts +++ b/dev-packages/node-integration-tests/utils/runner.ts @@ -6,7 +6,7 @@ import type { Event, EventEnvelope, SerializedCheckIn, - SerializedOtelLog, + SerializedLogContainer, SerializedSession, SessionAggregates, TransactionEvent, @@ -21,7 +21,7 @@ import { assertSentryCheckIn, assertSentryClientReport, assertSentryEvent, - assertSentryOtelLog, + assertSentryLogContainer, assertSentrySession, assertSentrySessions, assertSentryTransaction, @@ -121,7 +121,7 @@ type ExpectedSession = Partial | ((event: SerializedSession) type ExpectedSessions = Partial | ((event: SessionAggregates) => void); type ExpectedCheckIn = Partial | ((event: SerializedCheckIn) => void); type ExpectedClientReport = Partial | ((event: ClientReport) => void); -type ExpectedOtelLog = Partial | ((event: SerializedOtelLog) => void); +type ExpectedLogContainer = Partial | ((event: SerializedLogContainer) => void); type Expected = | { @@ -143,7 +143,7 @@ type Expected = client_report: ExpectedClientReport; } | { - otel_log: ExpectedOtelLog; + log: ExpectedLogContainer; }; type ExpectedEnvelopeHeader = @@ -151,7 +151,7 @@ type ExpectedEnvelopeHeader = | { transaction: Partial } | { session: Partial } | { sessions: Partial } - | { otel_log: Partial }; + | { log: Partial }; type StartResult = { completed(): Promise; @@ -332,8 +332,8 @@ export function createRunner(...paths: string[]) { } else if ('client_report' in expected) { expectClientReport(item[1] as ClientReport, expected.client_report); expectCallbackCalled(); - } else if ('otel_log' in expected) { - expectOtelLog(item[1] as SerializedOtelLog, expected.otel_log); + } else if ('log' in expected) { + expectLog(item[1] as SerializedLogContainer, expected.log); expectCallbackCalled(); } else { throw new Error( @@ -558,10 +558,10 @@ function expectClientReport(item: ClientReport, expected: ExpectedClientReport): } } -function expectOtelLog(item: SerializedOtelLog, expected: ExpectedOtelLog): void { +function expectLog(item: SerializedLogContainer, expected: ExpectedLogContainer): void { if (typeof expected === 'function') { expected(item); } else { - assertSentryOtelLog(item, expected); + assertSentryLogContainer(item, expected); } } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 5b25d9d8497e..6c35ea212b94 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -323,8 +323,7 @@ export type { ProfileChunkItem, SpanEnvelope, SpanItem, - OtelLogEnvelope, - OtelLogItem, + LogEnvelope, } from './types-hoist/envelope'; export type { ExtendedError } from './types-hoist/error'; export type { Event, EventHint, EventType, ErrorEvent, TransactionEvent } from './types-hoist/event'; @@ -385,13 +384,7 @@ export type { TraceFlag, } from './types-hoist/span'; export type { SpanStatus } from './types-hoist/spanStatus'; -export type { - Log, - LogSeverityLevel, - SerializedOtelLog, - SerializedLogAttribute, - SerializedLogAttributeValueType, -} from './types-hoist/log'; +export type { Log, LogSeverityLevel } from './types-hoist/log'; export type { TimedEvent } from './types-hoist/timedEvent'; export type { StackFrame } from './types-hoist/stackframe'; export type { Stacktrace, StackParser, StackLineParser, StackLineParserFn } from './types-hoist/stacktrace'; @@ -443,3 +436,4 @@ export type { ParameterizedString } from './types-hoist/parameterize'; export type { ContinuousProfiler, ProfilingIntegration, Profiler } from './types-hoist/profiling'; export type { ViewHierarchyData, ViewHierarchyWindow } from './types-hoist/view-hierarchy'; export type { LegacyCSPReport } from './types-hoist/csp'; +export type { SerializedLog, SerializedLogContainer } from './types-hoist/log'; diff --git a/packages/core/src/logs/envelope.ts b/packages/core/src/logs/envelope.ts index 49f2a445b150..c909a9140de1 100644 --- a/packages/core/src/logs/envelope.ts +++ b/packages/core/src/logs/envelope.ts @@ -1,41 +1,47 @@ import type { DsnComponents } from '../types-hoist/dsn'; -import type { OtelLogEnvelope, OtelLogItem } from '../types-hoist/envelope'; -import type { SerializedOtelLog } from '../types-hoist/log'; +import type { LogContainerItem, LogEnvelope } from '../types-hoist/envelope'; +import type { SerializedLog } from '../types-hoist/log'; import type { SdkMetadata } from '../types-hoist/sdkmetadata'; import { dsnToString } from '../utils-hoist/dsn'; import { createEnvelope } from '../utils-hoist/envelope'; /** - * Creates OTEL log envelope item for a serialized OTEL log. + * Creates a log container envelope item for a list of logs. * - * @param log - The serialized OTEL log to include in the envelope. - * @returns The created OTEL log envelope item. + * @param items - The logs to include in the envelope. + * @returns The created log container envelope item. */ -export function createOtelLogEnvelopeItem(log: SerializedOtelLog): OtelLogItem { +export function createLogContainerEnvelopeItem(items: Array): LogContainerItem { return [ { - type: 'otel_log', + type: 'log', + item_count: items.length, + content_type: 'application/vnd.sentry.items.log+json', + }, + { + items, }, - log, ]; } /** * Creates an envelope for a list of logs. * + * Logs from multiple traces can be included in the same envelope. + * * @param logs - The logs to include in the envelope. * @param metadata - The metadata to include in the envelope. * @param tunnel - The tunnel to include in the envelope. * @param dsn - The DSN to include in the envelope. * @returns The created envelope. */ -export function createOtelLogEnvelope( - logs: Array, +export function createLogEnvelope( + logs: Array, metadata?: SdkMetadata, tunnel?: string, dsn?: DsnComponents, -): OtelLogEnvelope { - const headers: OtelLogEnvelope[0] = {}; +): LogEnvelope { + const headers: LogEnvelope[0] = {}; if (metadata?.sdk) { headers.sdk = { @@ -48,5 +54,5 @@ export function createOtelLogEnvelope( headers.dsn = dsnToString(dsn); } - return createEnvelope(headers, logs.map(createOtelLogEnvelopeItem)); + return createEnvelope(headers, [createLogContainerEnvelopeItem(logs)]); } diff --git a/packages/core/src/logs/exports.ts b/packages/core/src/logs/exports.ts index df5726a81256..8496f7fba595 100644 --- a/packages/core/src/logs/exports.ts +++ b/packages/core/src/logs/exports.ts @@ -2,16 +2,17 @@ import type { Client } from '../client'; import { _getTraceInfoFromScope } from '../client'; import { getClient, getCurrentScope } from '../currentScopes'; import { DEBUG_BUILD } from '../debug-build'; -import type { Log, SerializedLogAttribute, SerializedOtelLog } from '../types-hoist/log'; +import type { Log, SerializedLog, SerializedLogAttributeValue } from '../types-hoist/log'; import { _getSpanForScope } from '../utils/spanOnScope'; import { isParameterizedString } from '../utils-hoist/is'; import { logger } from '../utils-hoist/logger'; +import { timestampInSeconds } from '../utils-hoist/time'; import { SEVERITY_TEXT_TO_SEVERITY_NUMBER } from './constants'; -import { createOtelLogEnvelope } from './envelope'; +import { createLogEnvelope } from './envelope'; const MAX_LOG_BUFFER_SIZE = 100; -const CLIENT_TO_LOG_BUFFER_MAP = new WeakMap>(); +const CLIENT_TO_LOG_BUFFER_MAP = new WeakMap>(); /** * Converts a log attribute to a serialized log attribute. @@ -20,22 +21,28 @@ const CLIENT_TO_LOG_BUFFER_MAP = new WeakMap>() * @param value - The value of the log attribute. * @returns The serialized log attribute. */ -export function logAttributeToSerializedLogAttribute(key: string, value: unknown): SerializedLogAttribute { +export function logAttributeToSerializedLogAttribute(value: unknown): SerializedLogAttributeValue { switch (typeof value) { case 'number': + if (Number.isInteger(value)) { + return { + value, + type: 'integer', + }; + } return { - key, - value: { doubleValue: value }, + value, + type: 'double', }; case 'boolean': return { - key, - value: { boolValue: value }, + value, + type: 'boolean', }; case 'string': return { - key, - value: { stringValue: value }, + value, + type: 'string', }; default: { let stringValue = ''; @@ -45,8 +52,8 @@ export function logAttributeToSerializedLogAttribute(key: string, value: unknown // Do nothing } return { - key, - value: { stringValue }, + value: stringValue, + type: 'string', }; } } @@ -127,23 +134,27 @@ export function _INTERNAL_captureLog( const { level, message, attributes = {}, severityNumber } = log; - const serializedLog: SerializedOtelLog = { - severityText: level, - body: { - stringValue: message, - }, - attributes: Object.entries(attributes).map(([key, value]) => logAttributeToSerializedLogAttribute(key, value)), - timeUnixNano: `${new Date().getTime().toString()}000000`, - traceId: traceContext?.trace_id, - severityNumber: severityNumber ?? SEVERITY_TEXT_TO_SEVERITY_NUMBER[level], + const serializedLog: SerializedLog = { + timestamp: timestampInSeconds(), + level, + body: message, + trace_id: traceContext?.trace_id, + severity_number: severityNumber ?? SEVERITY_TEXT_TO_SEVERITY_NUMBER[level], + attributes: Object.keys(attributes).reduce( + (acc, key) => { + acc[key] = logAttributeToSerializedLogAttribute(attributes[key]); + return acc; + }, + {} as Record, + ), }; const logBuffer = CLIENT_TO_LOG_BUFFER_MAP.get(client); if (logBuffer === undefined) { CLIENT_TO_LOG_BUFFER_MAP.set(client, [serializedLog]); } else { - logBuffer.push(serializedLog); - if (logBuffer.length > MAX_LOG_BUFFER_SIZE) { + CLIENT_TO_LOG_BUFFER_MAP.set(client, [...logBuffer, serializedLog]); + if (logBuffer.length >= MAX_LOG_BUFFER_SIZE) { _INTERNAL_flushLogsBuffer(client, logBuffer); } } @@ -160,17 +171,17 @@ export function _INTERNAL_captureLog( * @experimental This method will experience breaking changes. This is not yet part of * the stable Sentry SDK API and can be changed or removed without warning. */ -export function _INTERNAL_flushLogsBuffer(client: Client, maybeLogBuffer?: Array): void { - const logBuffer = maybeLogBuffer ?? CLIENT_TO_LOG_BUFFER_MAP.get(client) ?? []; +export function _INTERNAL_flushLogsBuffer(client: Client, maybeLogBuffer?: Array): void { + const logBuffer = maybeLogBuffer ?? _INTERNAL_getLogBuffer(client) ?? []; if (logBuffer.length === 0) { return; } const clientOptions = client.getOptions(); - const envelope = createOtelLogEnvelope(logBuffer, clientOptions._metadata, clientOptions.tunnel, client.getDsn()); + const envelope = createLogEnvelope(logBuffer, clientOptions._metadata, clientOptions.tunnel, client.getDsn()); // Clear the log buffer after envelopes have been constructed. - logBuffer.length = 0; + CLIENT_TO_LOG_BUFFER_MAP.set(client, []); client.emit('flushLogs'); @@ -187,6 +198,6 @@ export function _INTERNAL_flushLogsBuffer(client: Client, maybeLogBuffer?: Array * @param client - The client to get the log buffer for. * @returns The log buffer for the given client. */ -export function _INTERNAL_getLogBuffer(client: Client): Array | undefined { +export function _INTERNAL_getLogBuffer(client: Client): Array | undefined { return CLIENT_TO_LOG_BUFFER_MAP.get(client); } diff --git a/packages/core/src/types-hoist/envelope.ts b/packages/core/src/types-hoist/envelope.ts index b77f22493c92..d874a4e65800 100644 --- a/packages/core/src/types-hoist/envelope.ts +++ b/packages/core/src/types-hoist/envelope.ts @@ -5,7 +5,7 @@ import type { LegacyCSPReport } from './csp'; import type { DsnComponents } from './dsn'; import type { Event } from './event'; import type { FeedbackEvent, UserFeedback } from './feedback'; -import type { SerializedOtelLog } from './log'; +import type { SerializedLogContainer } from './log'; import type { Profile, ProfileChunk } from './profiling'; import type { ReplayEvent, ReplayRecordingData } from './replay'; import type { SdkInfo } from './sdkinfo'; @@ -44,7 +44,7 @@ export type EnvelopeItemType = | 'replay_recording' | 'check_in' | 'span' - | 'otel_log' + | 'log' | 'raw_security'; export type BaseEnvelopeHeaders = { @@ -87,7 +87,17 @@ type CheckInItemHeaders = { type: 'check_in' }; type ProfileItemHeaders = { type: 'profile' }; type ProfileChunkItemHeaders = { type: 'profile_chunk' }; type SpanItemHeaders = { type: 'span' }; -type OtelLogItemHeaders = { type: 'otel_log' }; +type LogContainerItemHeaders = { + type: 'log'; + /** + * The number of log items in the container. This must be the same as the number of log items in the payload. + */ + item_count: number; + /** + * The content type of the log items. This must be `application/vnd.sentry.items.log+json`. + */ + content_type: 'application/vnd.sentry.items.log+json'; +}; type RawSecurityHeaders = { type: 'raw_security'; sentry_release?: string; sentry_environment?: string }; export type EventItem = BaseEnvelopeItem; @@ -104,7 +114,7 @@ export type FeedbackItem = BaseEnvelopeItem; export type ProfileItem = BaseEnvelopeItem; export type ProfileChunkItem = BaseEnvelopeItem; export type SpanItem = BaseEnvelopeItem>; -export type OtelLogItem = BaseEnvelopeItem; +export type LogContainerItem = BaseEnvelopeItem; export type RawSecurityItem = BaseEnvelopeItem; export type EventEnvelopeHeaders = { event_id: string; sent_at: string; trace?: Partial }; @@ -113,8 +123,7 @@ type CheckInEnvelopeHeaders = { trace?: DynamicSamplingContext }; type ClientReportEnvelopeHeaders = BaseEnvelopeHeaders; type ReplayEnvelopeHeaders = BaseEnvelopeHeaders; type SpanEnvelopeHeaders = BaseEnvelopeHeaders & { trace?: DynamicSamplingContext }; -type OtelLogEnvelopeHeaders = BaseEnvelopeHeaders & { trace?: DynamicSamplingContext }; - +type LogEnvelopeHeaders = BaseEnvelopeHeaders; export type EventEnvelope = BaseEnvelope< EventEnvelopeHeaders, EventItem | AttachmentItem | UserFeedbackItem | FeedbackItem | ProfileItem @@ -126,7 +135,7 @@ export type CheckInEnvelope = BaseEnvelope; export type SpanEnvelope = BaseEnvelope; export type ProfileChunkEnvelope = BaseEnvelope; export type RawSecurityEnvelope = BaseEnvelope; -export type OtelLogEnvelope = BaseEnvelope; +export type LogEnvelope = BaseEnvelope; export type Envelope = | EventEnvelope @@ -137,6 +146,5 @@ export type Envelope = | CheckInEnvelope | SpanEnvelope | RawSecurityEnvelope - | OtelLogEnvelope; - + | LogEnvelope; export type EnvelopeItem = Envelope[1][number]; diff --git a/packages/core/src/types-hoist/log.ts b/packages/core/src/types-hoist/log.ts index 05f8e05ad228..1a6e3974e91e 100644 --- a/packages/core/src/types-hoist/log.ts +++ b/packages/core/src/types-hoist/log.ts @@ -2,27 +2,6 @@ import type { ParameterizedString } from './parameterize'; export type LogSeverityLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal'; -export type SerializedLogAttributeValueType = - | { - stringValue: string; - } - | { - // integers must be represented as a string - // because JSON cannot differentiate between integers and floats - intValue: string; - } - | { - boolValue: boolean; - } - | { - doubleValue: number; - }; - -export type SerializedLogAttribute = { - key: string; - value: SerializedLogAttributeValueType; -}; - export interface Log { /** * The severity level of the log. @@ -46,36 +25,49 @@ export interface Log { attributes?: Record; /** - * The severity number - generally higher severity are levels like 'error' and lower are levels like 'debug' + * The severity number. */ severityNumber?: number; } -export interface SerializedOtelLog { - severityText?: Log['level']; +export type SerializedLogAttributeValue = + | { value: string; type: 'string' } + | { value: number; type: 'integer' } + | { value: number; type: 'double' } + | { value: boolean; type: 'boolean' }; +export interface SerializedLog { /** - * The trace ID for this log + * Timestamp in seconds (epoch time) indicating when the log occurred. */ - traceId?: string; + timestamp: number; + + /** + * The severity level of the log. One of `trace`, `debug`, `info`, `warn`, `error`, `fatal`. + */ + level: LogSeverityLevel; - severityNumber?: Log['severityNumber']; + /** + * The log body. + */ + body: Log['message']; - body: { - stringValue: Log['message']; - }; + /** + * The trace ID for this log + */ + trace_id?: string; /** * Arbitrary structured data that stores information about the log - e.g., userId: 100. */ - attributes?: SerializedLogAttribute[]; + attributes?: Record; /** - * This doesn't have to be explicitly specified most of the time. If you need to set it, the value - * is the number of seconds since midnight on January 1, 1970 ("unix epoch time") - * - * @summary A timestamp representing when the log occurred. - * @link https://develop.sentry.dev/sdk/event-payloads/breadcrumbs/#:~:text=is%20info.-,timestamp,-(recommended) + * The severity number. */ - timeUnixNano?: string; + severity_number?: Log['severityNumber']; } + +export type SerializedLogContainer = { + items: Array; +}; diff --git a/packages/core/src/utils-hoist/envelope.ts b/packages/core/src/utils-hoist/envelope.ts index 0bb3d12f76b9..0f6af9440643 100644 --- a/packages/core/src/utils-hoist/envelope.ts +++ b/packages/core/src/utils-hoist/envelope.ts @@ -90,7 +90,6 @@ function decodeUTF8(input: Uint8Array): string { */ export function serializeEnvelope(envelope: Envelope): string | Uint8Array { const [envHeaders, items] = envelope; - // Initially we construct our envelope as a string and only convert to binary chunks if we encounter binary data let parts: string | Uint8Array[] = JSON.stringify(envHeaders); @@ -221,7 +220,7 @@ const ITEM_TYPE_TO_DATA_CATEGORY_MAP: Record = { feedback: 'feedback', span: 'span', raw_security: 'security', - otel_log: 'log_item', + log: 'log_item', }; /** diff --git a/packages/core/test/lib/logs/envelope.test.ts b/packages/core/test/lib/logs/envelope.test.ts index 5f98719bbb44..cd765cf018bc 100644 --- a/packages/core/test/lib/logs/envelope.test.ts +++ b/packages/core/test/lib/logs/envelope.test.ts @@ -1,7 +1,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { createOtelLogEnvelope, createOtelLogEnvelopeItem } from '../../../src/logs/envelope'; +import { createLogContainerEnvelopeItem, createLogEnvelope } from '../../../src/logs/envelope'; import type { DsnComponents } from '../../../src/types-hoist/dsn'; -import type { SerializedOtelLog } from '../../../src/types-hoist/log'; +import type { SerializedLog } from '../../../src/types-hoist/log'; import type { SdkMetadata } from '../../../src/types-hoist/sdkmetadata'; import * as utilsDsn from '../../../src/utils-hoist/dsn'; import * as utilsEnvelope from '../../../src/utils-hoist/envelope'; @@ -14,24 +14,23 @@ vi.mock('../../../src/utils-hoist/envelope', () => ({ createEnvelope: vi.fn((_headers, items) => [_headers, items]), })); -describe('createOtelLogEnvelopeItem', () => { +describe('createLogContainerEnvelopeItem', () => { it('creates an envelope item with correct structure', () => { - const mockLog: SerializedOtelLog = { - severityText: 'error', - body: { - stringValue: 'Test error message', - }, + const mockLog: SerializedLog = { + timestamp: 1713859200, + level: 'error', + body: 'Test error message', }; - const result = createOtelLogEnvelopeItem(mockLog); + const result = createLogContainerEnvelopeItem([mockLog, mockLog]); expect(result).toHaveLength(2); - expect(result[0]).toEqual({ type: 'otel_log' }); - expect(result[1]).toBe(mockLog); + expect(result[0]).toEqual({ type: 'log', item_count: 2, content_type: 'application/vnd.sentry.items.log+json' }); + expect(result[1]).toEqual({ items: [mockLog, mockLog] }); }); }); -describe('createOtelLogEnvelope', () => { +describe('createLogEnvelope', () => { beforeEach(() => { vi.useFakeTimers(); vi.setSystemTime(new Date('2023-01-01T12:00:00Z')); @@ -46,14 +45,15 @@ describe('createOtelLogEnvelope', () => { }); it('creates an envelope with basic headers', () => { - const mockLogs: SerializedOtelLog[] = [ + const mockLogs: SerializedLog[] = [ { - severityText: 'info', - body: { stringValue: 'Test log message' }, + timestamp: 1713859200, + level: 'info', + body: 'Test log message', }, ]; - const result = createOtelLogEnvelope(mockLogs); + const result = createLogEnvelope(mockLogs); expect(result[0]).toEqual({}); @@ -62,10 +62,11 @@ describe('createOtelLogEnvelope', () => { }); it('includes SDK info when metadata is provided', () => { - const mockLogs: SerializedOtelLog[] = [ + const mockLogs: SerializedLog[] = [ { - severityText: 'info', - body: { stringValue: 'Test log message' }, + timestamp: 1713859200, + level: 'info', + body: 'Test log message', }, ]; @@ -76,7 +77,7 @@ describe('createOtelLogEnvelope', () => { }, }; - const result = createOtelLogEnvelope(mockLogs, metadata); + const result = createLogEnvelope(mockLogs, metadata); expect(result[0]).toEqual({ sdk: { @@ -87,10 +88,11 @@ describe('createOtelLogEnvelope', () => { }); it('includes DSN when tunnel and DSN are provided', () => { - const mockLogs: SerializedOtelLog[] = [ + const mockLogs: SerializedLog[] = [ { - severityText: 'info', - body: { stringValue: 'Test log message' }, + timestamp: 1713859200, + level: 'info', + body: 'Test log message', }, ]; @@ -103,88 +105,35 @@ describe('createOtelLogEnvelope', () => { publicKey: 'abc123', }; - const result = createOtelLogEnvelope(mockLogs, undefined, 'https://tunnel.example.com', dsn); + const result = createLogEnvelope(mockLogs, undefined, 'https://tunnel.example.com', dsn); expect(result[0]).toHaveProperty('dsn'); expect(utilsDsn.dsnToString).toHaveBeenCalledWith(dsn); }); it('maps each log to an envelope item', () => { - const mockLogs: SerializedOtelLog[] = [ + const mockLogs: SerializedLog[] = [ { - severityText: 'info', - body: { stringValue: 'First log message' }, + timestamp: 1713859200, + level: 'info', + body: 'First log message', }, { - severityText: 'error', - body: { stringValue: 'Second log message' }, + timestamp: 1713859200, + level: 'error', + body: 'Second log message', }, ]; - createOtelLogEnvelope(mockLogs); - - // Check that createEnvelope was called with an array of envelope items - expect(utilsEnvelope.createEnvelope).toHaveBeenCalledWith( - expect.anything(), - expect.arrayContaining([ - expect.arrayContaining([{ type: 'otel_log' }, mockLogs[0]]), - expect.arrayContaining([{ type: 'otel_log' }, mockLogs[1]]), - ]), - ); - }); -}); - -describe('Trace context in logs', () => { - it('correctly sets parent_span_id in trace context', () => { - // Create a log with trace context - const mockParentSpanId = 'abcdef1234567890'; - const mockTraceId = '00112233445566778899aabbccddeeff'; - - const mockLog: SerializedOtelLog = { - severityText: 'info', - body: { stringValue: 'Test log with trace context' }, - traceId: mockTraceId, - attributes: [ - { - key: 'sentry.trace.parent_span_id', - value: { stringValue: mockParentSpanId }, - }, - { - key: 'some.other.attribute', - value: { stringValue: 'test value' }, - }, - ], - }; - - // Create an envelope item from this log - const envelopeItem = createOtelLogEnvelopeItem(mockLog); - - // Verify the parent_span_id is preserved in the envelope item - expect(envelopeItem[1]).toBe(mockLog); - expect(envelopeItem[1].traceId).toBe(mockTraceId); - expect(envelopeItem[1].attributes).toContainEqual({ - key: 'sentry.trace.parent_span_id', - value: { stringValue: mockParentSpanId }, - }); - - // Create an envelope with this log - createOtelLogEnvelope([mockLog]); + createLogEnvelope(mockLogs); - // Verify the envelope preserves the trace information + // Check that createEnvelope was called with a single container item containing all logs expect(utilsEnvelope.createEnvelope).toHaveBeenCalledWith( expect.anything(), expect.arrayContaining([ expect.arrayContaining([ - { type: 'otel_log' }, - expect.objectContaining({ - traceId: mockTraceId, - attributes: expect.arrayContaining([ - { - key: 'sentry.trace.parent_span_id', - value: { stringValue: mockParentSpanId }, - }, - ]), - }), + { type: 'log', item_count: 2, content_type: 'application/vnd.sentry.items.log+json' }, + { items: mockLogs }, ]), ]), ); diff --git a/packages/core/test/lib/logs/exports.test.ts b/packages/core/test/lib/logs/exports.test.ts index 9c4795d8fc53..1ae570bc5968 100644 --- a/packages/core/test/lib/logs/exports.test.ts +++ b/packages/core/test/lib/logs/exports.test.ts @@ -13,61 +13,69 @@ import { getDefaultTestClientOptions, TestClient } from '../../mocks/client'; const PUBLIC_DSN = 'https://username@domain/123'; describe('logAttributeToSerializedLogAttribute', () => { - it('serializes number values', () => { - const result = logAttributeToSerializedLogAttribute('count', 42); + it('serializes integer values', () => { + const result = logAttributeToSerializedLogAttribute(42); expect(result).toEqual({ - key: 'count', - value: { doubleValue: 42 }, + value: 42, + type: 'integer', + }); + }); + + it('serializes double values', () => { + const result = logAttributeToSerializedLogAttribute(42.34); + expect(result).toEqual({ + value: 42.34, + type: 'double', }); }); it('serializes boolean values', () => { - const result = logAttributeToSerializedLogAttribute('enabled', true); + const result = logAttributeToSerializedLogAttribute(true); expect(result).toEqual({ - key: 'enabled', - value: { boolValue: true }, + value: true, + type: 'boolean', }); }); it('serializes string values', () => { - const result = logAttributeToSerializedLogAttribute('username', 'john_doe'); + const result = logAttributeToSerializedLogAttribute('username'); expect(result).toEqual({ - key: 'username', - value: { stringValue: 'john_doe' }, + value: 'username', + type: 'string', }); }); it('serializes object values as JSON strings', () => { const obj = { name: 'John', age: 30 }; - const result = logAttributeToSerializedLogAttribute('user', obj); + const result = logAttributeToSerializedLogAttribute(obj); expect(result).toEqual({ - key: 'user', - value: { stringValue: JSON.stringify(obj) }, + value: JSON.stringify(obj), + type: 'string', }); }); it('serializes array values as JSON strings', () => { const array = [1, 2, 3, 'test']; - const result = logAttributeToSerializedLogAttribute('items', array); + const result = logAttributeToSerializedLogAttribute(array); expect(result).toEqual({ - key: 'items', - value: { stringValue: JSON.stringify(array) }, + value: JSON.stringify(array), + type: 'string', }); }); it('serializes undefined values as empty strings', () => { - const result = logAttributeToSerializedLogAttribute('missing', undefined); + const result = logAttributeToSerializedLogAttribute(undefined); expect(result).toEqual({ - key: 'missing', - value: { stringValue: '' }, + value: '', + type: 'string', }); }); it('serializes null values as JSON strings', () => { - const result = logAttributeToSerializedLogAttribute('empty', null); + const result = logAttributeToSerializedLogAttribute(null); expect(result).toEqual({ - key: 'empty', - value: { stringValue: 'null' }, + value: 'null', + type: 'string', }); }); }); @@ -81,11 +89,12 @@ describe('_INTERNAL_captureLog', () => { expect(_INTERNAL_getLogBuffer(client)).toHaveLength(1); expect(_INTERNAL_getLogBuffer(client)?.[0]).toEqual( expect.objectContaining({ - severityText: 'info', - body: { - stringValue: 'test log message', - }, - timeUnixNano: expect.any(String), + level: 'info', + body: 'test log message', + timestamp: expect.any(Number), + trace_id: expect.any(String), + severity_number: 9, + attributes: {}, }), ); }); @@ -116,8 +125,8 @@ describe('_INTERNAL_captureLog', () => { expect(_INTERNAL_getLogBuffer(client)?.[0]).toEqual( expect.objectContaining({ - traceId: '3d9355f71e9c444b81161599adac6e29', - severityNumber: 17, // error level maps to 17 + trace_id: '3d9355f71e9c444b81161599adac6e29', + severity_number: 17, // error level maps to 17 }), ); }); @@ -134,12 +143,16 @@ describe('_INTERNAL_captureLog', () => { _INTERNAL_captureLog({ level: 'info', message: 'test log with metadata' }, client, undefined); const logAttributes = _INTERNAL_getLogBuffer(client)?.[0]?.attributes; - expect(logAttributes).toEqual( - expect.arrayContaining([ - expect.objectContaining({ key: 'sentry.release', value: { stringValue: '1.0.0' } }), - expect.objectContaining({ key: 'sentry.environment', value: { stringValue: 'test' } }), - ]), - ); + expect(logAttributes).toEqual({ + 'sentry.release': { + value: '1.0.0', + type: 'string', + }, + 'sentry.environment': { + value: 'test', + type: 'string', + }, + }); }); it('includes SDK metadata in log attributes when available', () => { @@ -159,12 +172,16 @@ describe('_INTERNAL_captureLog', () => { _INTERNAL_captureLog({ level: 'info', message: 'test log with SDK metadata' }, client, undefined); const logAttributes = _INTERNAL_getLogBuffer(client)?.[0]?.attributes; - expect(logAttributes).toEqual( - expect.arrayContaining([ - expect.objectContaining({ key: 'sentry.sdk.name', value: { stringValue: 'sentry.javascript.node' } }), - expect.objectContaining({ key: 'sentry.sdk.version', value: { stringValue: '7.0.0' } }), - ]), - ); + expect(logAttributes).toEqual({ + 'sentry.sdk.name': { + value: 'sentry.javascript.node', + type: 'string', + }, + 'sentry.sdk.version': { + value: '7.0.0', + type: 'string', + }, + }); }); it('does not include SDK metadata in log attributes when not available', () => { @@ -202,12 +219,16 @@ describe('_INTERNAL_captureLog', () => { ); const logAttributes = _INTERNAL_getLogBuffer(client)?.[0]?.attributes; - expect(logAttributes).toEqual( - expect.arrayContaining([ - expect.objectContaining({ key: 'userId', value: { stringValue: '123' } }), - expect.objectContaining({ key: 'component', value: { stringValue: 'auth' } }), - ]), - ); + expect(logAttributes).toEqual({ + userId: { + value: '123', + type: 'string', + }, + component: { + value: 'auth', + type: 'string', + }, + }); }); it('flushes logs buffer when it reaches max size', () => { @@ -244,22 +265,20 @@ describe('_INTERNAL_captureLog', () => { _INTERNAL_captureLog({ level: 'info', message: parameterizedMessage }, client, undefined); const logAttributes = _INTERNAL_getLogBuffer(client)?.[0]?.attributes; - expect(logAttributes).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - key: 'sentry.message.template', - value: { stringValue: 'Hello %s, welcome to %s' }, - }), - expect.objectContaining({ - key: 'sentry.message.parameter.0', - value: { stringValue: 'John' }, - }), - expect.objectContaining({ - key: 'sentry.message.parameter.1', - value: { stringValue: 'Sentry' }, - }), - ]), - ); + expect(logAttributes).toEqual({ + 'sentry.message.template': { + value: 'Hello %s, welcome to %s', + type: 'string', + }, + 'sentry.message.parameter.0': { + value: 'John', + type: 'string', + }, + 'sentry.message.parameter.1': { + value: 'Sentry', + type: 'string', + }, + }); }); it('processes logs through beforeSendLog when provided', () => { @@ -295,13 +314,17 @@ describe('_INTERNAL_captureLog', () => { expect(logBuffer).toBeDefined(); expect(logBuffer?.[0]).toEqual( expect.objectContaining({ - body: { - stringValue: 'Modified: original message', + body: 'Modified: original message', + attributes: { + processed: { + value: true, + type: 'boolean', + }, + original: { + value: true, + type: 'boolean', + }, }, - attributes: expect.arrayContaining([ - expect.objectContaining({ key: 'processed', value: { boolValue: true } }), - expect.objectContaining({ key: 'original', value: { boolValue: true } }), - ]), }), ); });