Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ref(core): Use helpers with event envelopes #4656

Merged
merged 5 commits into from
Mar 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 23 additions & 32 deletions packages/core/src/request.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -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<EventEnvelope>(envelopeHeaders, [eventItem]);
req.body = serializeEnvelope(envelope);
}

return req;
Expand Down
11 changes: 9 additions & 2 deletions packages/types/src/envelope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ClientReport } from './clientreport';
import { Event } from './event';
import { SdkInfo } from './sdkinfo';
import { Session, SessionAggregates } from './session';
import { TransactionSamplingMethod } from './transaction';
import { UserFeedback } from './user';

// Based on: https://develop.sentry.dev/sdk/envelopes/
Expand All @@ -25,14 +26,20 @@ type BaseEnvelope<EH extends BaseEnvelopeHeaders, I extends BaseEnvelopeItem<Bas
I[],
];

type EventItemHeaders = { type: 'event' | 'transaction' };
type EventItemHeaders = {
type: 'event' | 'transaction';
sample_rates: [{ id?: TransactionSamplingMethod; rate?: number }];
};
type AttachmentItemHeaders = { type: 'attachment'; filename: string };
type UserFeedbackItemHeaders = { type: 'user_report' };
type SessionItemHeaders = { type: 'session' };
type SessionAggregatesItemHeaders = { type: 'sessions' };
type ClientReportItemHeaders = { type: 'client_report' };

export type EventItem = BaseEnvelopeItem<EventItemHeaders, Event>;
// 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<EventItemHeaders, Event | string>;
export type AttachmentItem = BaseEnvelopeItem<AttachmentItemHeaders, unknown>;
export type UserFeedbackItem = BaseEnvelopeItem<UserFeedbackItemHeaders, UserFeedback>;
export type SessionItem =
Expand Down
6 changes: 5 additions & 1 deletion packages/utils/src/envelope.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Envelope } from '@sentry/types';

import { isPrimitive } from './is';

/**
* Creates an envelope.
* Make sure to always explicitly provide the generic to this function
Expand Down Expand Up @@ -33,6 +35,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 primitives
const serializedPayload = isPrimitive(payload) ? String(payload) : JSON.stringify(payload);
return `${acc}\n${JSON.stringify(itemHeaders)}\n${serializedPayload}`;
}, serializedHeaders);
}