diff --git a/packages/core/src/request.ts b/packages/core/src/request.ts index e5050c04d070..36952e66d461 100644 --- a/packages/core/src/request.ts +++ b/packages/core/src/request.ts @@ -1,5 +1,14 @@ -import { Event, SdkInfo, SentryRequest, SentryRequestType, Session, SessionAggregates } from '@sentry/types'; -import { dsnToString, normalize } from '@sentry/utils'; +import { + Event, + EventEnvelope, + EventItem, + SdkInfo, + SentryRequest, + SentryRequestType, + Session, + SessionAggregates, +} from '@sentry/types'; +import { createEnvelope, dsnToString, normalize, serializeEnvelope } from '@sentry/utils'; import { APIDetails, getEnvelopeEndpointWithUrlEncodedAuth, getStoreEndpointWithUrlEncodedAuth } from './api'; @@ -128,39 +137,21 @@ export function eventToSentryRequest(event: Event, api: APIDetails): SentryReque // deserialization. Instead, we only implement a minimal subset of the spec to // serialize events inline here. if (useEnvelope) { - const envelopeHeaders = JSON.stringify({ - event_id: event.event_id, + const envelopeHeaders = { + event_id: event.event_id as string, sent_at: new Date().toISOString(), ...(sdkInfo && { sdk: sdkInfo }), ...(!!api.tunnel && { dsn: dsnToString(api.dsn) }), - }); - const itemHeaders = JSON.stringify({ - type: eventType, - - // TODO: Right now, sampleRate may or may not be defined (it won't be in the cases of inheritance and - // explicitly-set sampling decisions). Are we good with that? - sample_rates: [{ id: samplingMethod, rate: sampleRate }], - - // The content-type is assumed to be 'application/json' and not part of - // the current spec for transaction items, so we don't bloat the request - // body with it. - // - // content_type: 'application/json', - // - // The length is optional. It must be the number of bytes in req.Body - // encoded as UTF-8. Since the server can figure this out and would - // otherwise refuse events that report the length incorrectly, we decided - // not to send the length to avoid problems related to reporting the wrong - // size and to reduce request body size. - // - // length: new TextEncoder().encode(req.body).length, - }); - // The trailing newline is optional. We intentionally don't send it to avoid - // sending unnecessary bytes. - // - // const envelope = `${envelopeHeaders}\n${itemHeaders}\n${req.body}\n`; - const envelope = `${envelopeHeaders}\n${itemHeaders}\n${req.body}`; - req.body = envelope; + }; + const eventItem: EventItem = [ + { + type: eventType, + sample_rates: [{ id: samplingMethod, rate: sampleRate }], + }, + req.body, + ]; + const envelope = createEnvelope(envelopeHeaders, [eventItem]); + req.body = serializeEnvelope(envelope); } return req; diff --git a/packages/types/src/envelope.ts b/packages/types/src/envelope.ts index aa7655db9cea..5e3f07b6b1e2 100644 --- a/packages/types/src/envelope.ts +++ b/packages/types/src/envelope.ts @@ -25,14 +25,17 @@ type BaseEnvelope; +// TODO(v7): Remove the string union from `Event | string` +// We have to allow this hack for now as we pre-serialize events because we support +// both store and envelope endpoints. +export type EventItem = BaseEnvelopeItem; export type AttachmentItem = BaseEnvelopeItem; export type UserFeedbackItem = BaseEnvelopeItem; export type SessionItem = diff --git a/packages/utils/src/envelope.ts b/packages/utils/src/envelope.ts index 7552bd339784..68d8f91cb67a 100644 --- a/packages/utils/src/envelope.ts +++ b/packages/utils/src/envelope.ts @@ -33,6 +33,8 @@ export function serializeEnvelope(envelope: Envelope): string { // eslint-disable-next-line @typescript-eslint/no-explicit-any return (items as any[]).reduce((acc, item: typeof items[number]) => { const [itemHeaders, payload] = item; - return `${acc}\n${JSON.stringify(itemHeaders)}\n${JSON.stringify(payload)}`; + // We do not serialize payloads that are strings + const serializedPayload = typeof payload === 'string' ? payload : JSON.stringify(payload); + return `${acc}\n${JSON.stringify(itemHeaders)}\n${serializedPayload}`; }, serializedHeaders); }