|
1 | 1 | import { Event, SentryRequest, Session } from '@sentry/types';
|
| 2 | +import { base64ToUnicode, logger } from '@sentry/utils'; |
2 | 3 |
|
3 | 4 | import { API } from './api';
|
4 | 5 |
|
5 |
| -/** Creates a SentryRequest from an event. */ |
6 |
| -export function sessionToSentryRequest(session: Session, api: API): SentryRequest { |
7 |
| - const envelopeHeaders = JSON.stringify({ |
8 |
| - sent_at: new Date().toISOString(), |
9 |
| - }); |
10 |
| - const itemHeaders = JSON.stringify({ |
11 |
| - type: 'session', |
12 |
| - }); |
13 |
| - |
| 6 | +/** |
| 7 | + * Create a SentryRequest from an error, message, or transaction event. |
| 8 | + * |
| 9 | + * @param event The event to send |
| 10 | + * @param api Helper to provide the correct url for the request |
| 11 | + * @returns SentryRequest representing the event |
| 12 | + */ |
| 13 | +export function eventToSentryRequest(event: Event, api: API): SentryRequest { |
| 14 | + if (event.type === 'transaction') { |
| 15 | + return transactionToSentryRequest(event, api); |
| 16 | + } |
14 | 17 | return {
|
15 |
| - body: `${envelopeHeaders}\n${itemHeaders}\n${JSON.stringify(session)}`, |
16 |
| - type: 'session', |
17 |
| - url: api.getEnvelopeEndpointWithUrlEncodedAuth(), |
| 18 | + body: JSON.stringify(event), |
| 19 | + type: event.type || 'event', |
| 20 | + url: api.getStoreEndpointWithUrlEncodedAuth(), |
18 | 21 | };
|
19 | 22 | }
|
20 | 23 |
|
21 |
| -/** Creates a SentryRequest from an event. */ |
22 |
| -export function eventToSentryRequest(event: Event, api: API): SentryRequest { |
| 24 | +/** |
| 25 | + * Create a SentryRequest from a transaction event. |
| 26 | + * |
| 27 | + * Since we don't need to manipulate envelopes nor store them, there is no exported concept of an Envelope with |
| 28 | + * operations including serialization and deserialization. Instead, we only implement a minimal subset of the spec to |
| 29 | + * serialize events inline here. See https://develop.sentry.dev/sdk/envelopes/. |
| 30 | + * |
| 31 | + * @param event The transaction event to send |
| 32 | + * @param api Helper to provide the correct url for the request |
| 33 | + * @returns SentryRequest in envelope form |
| 34 | + */ |
| 35 | +export function transactionToSentryRequest(event: Event, api: API): SentryRequest { |
23 | 36 | // since JS has no Object.prototype.pop()
|
24 | 37 | const { __sentry_samplingMethod: samplingMethod, __sentry_sampleRate: sampleRate, ...otherTags } = event.tags || {};
|
25 | 38 | event.tags = otherTags;
|
26 | 39 |
|
27 |
| - const useEnvelope = event.type === 'transaction'; |
28 |
| - |
29 |
| - const req: SentryRequest = { |
30 |
| - body: JSON.stringify(event), |
31 |
| - type: event.type || 'event', |
32 |
| - url: useEnvelope ? api.getEnvelopeEndpointWithUrlEncodedAuth() : api.getStoreEndpointWithUrlEncodedAuth(), |
33 |
| - }; |
| 40 | + // because we send event.tracestate as a header whose values have meaningful equals signs, when we create it we have |
| 41 | + // to replace the padding character with something else; the `.replace` call here reverses that to make it valid |
| 42 | + // base64 before converting to unicode for parsing |
| 43 | + let tracestateJSON; |
| 44 | + try { |
| 45 | + tracestateJSON = event.tracestate ? base64ToUnicode(event.tracestate.replace('.', '=')) : undefined; |
| 46 | + } catch (err) { |
| 47 | + logger.warn(err); |
| 48 | + tracestateJSON = ''; |
| 49 | + } |
| 50 | + delete event.tracestate; |
34 | 51 |
|
35 |
| - // https://develop.sentry.dev/sdk/envelopes/ |
| 52 | + const envelopeHeaders = JSON.stringify({ |
| 53 | + event_id: event.event_id, |
| 54 | + sent_at: new Date().toISOString(), |
| 55 | + trace: tracestateJSON, // trace context for dynamic sampling on relay |
| 56 | + }); |
36 | 57 |
|
37 |
| - // Since we don't need to manipulate envelopes nor store them, there is no |
38 |
| - // exported concept of an Envelope with operations including serialization and |
39 |
| - // deserialization. Instead, we only implement a minimal subset of the spec to |
40 |
| - // serialize events inline here. |
41 |
| - if (useEnvelope) { |
42 |
| - const envelopeHeaders = JSON.stringify({ |
43 |
| - event_id: event.event_id, |
44 |
| - sent_at: new Date().toISOString(), |
| 58 | + const itemHeaders = JSON.stringify({ |
| 59 | + type: event.type, |
45 | 60 |
|
46 |
| - // trace context for dynamic sampling on relay |
47 |
| - trace: { |
48 |
| - trace_id: event.contexts?.trace?.trace_id, |
49 |
| - // TODO: any reason we can't change this property to be called publicKey, since that's what it is? |
50 |
| - public_key: api.getDsn().user, |
51 |
| - environment: event.environment || 'no environment specified', |
52 |
| - release: event.release || 'no release specified', |
53 |
| - }, |
54 |
| - }); |
| 61 | + // TODO: Right now, sampleRate won't be defined in the cases of inheritance and explicitly-set sampling decisions. |
| 62 | + sample_rates: [{ id: samplingMethod, rate: sampleRate }], |
55 | 63 |
|
56 |
| - const itemHeaders = JSON.stringify({ |
57 |
| - type: event.type, |
| 64 | + // The content-type is assumed to be 'application/json' and not part of |
| 65 | + // the current spec for transaction items, so we don't bloat the request |
| 66 | + // body with it. |
| 67 | + // |
| 68 | + // content_type: 'application/json', |
| 69 | + // |
| 70 | + // The length is optional. It must be the number of bytes in req.Body |
| 71 | + // encoded as UTF-8. Since the server can figure this out and would |
| 72 | + // otherwise refuse events that report the length incorrectly, we decided |
| 73 | + // not to send the length to avoid problems related to reporting the wrong |
| 74 | + // size and to reduce request body size. |
| 75 | + // |
| 76 | + // length: new TextEncoder().encode(req.body).length, |
| 77 | + }); |
58 | 78 |
|
59 |
| - // TODO: Right now, sampleRate may or may not be defined (it won't be in the cases of inheritance and |
60 |
| - // explicitly-set sampling decisions). Are we good with that? |
61 |
| - sample_rates: [{ id: samplingMethod, rate: sampleRate }], |
| 79 | + const req: SentryRequest = { |
| 80 | + // The trailing newline is optional; leave it off to avoid sending unnecessary bytes. |
| 81 | + // body: `${envelopeHeaders}\n${itemHeaders}\n${JSON.stringify(event)\n}`, |
| 82 | + body: `${envelopeHeaders}\n${itemHeaders}\n${JSON.stringify(event)}`, |
| 83 | + type: 'transaction', |
| 84 | + url: api.getEnvelopeEndpointWithUrlEncodedAuth(), |
| 85 | + }; |
62 | 86 |
|
63 |
| - // The content-type is assumed to be 'application/json' and not part of |
64 |
| - // the current spec for transaction items, so we don't bloat the request |
65 |
| - // body with it. |
66 |
| - // |
67 |
| - // content_type: 'application/json', |
68 |
| - // |
69 |
| - // The length is optional. It must be the number of bytes in req.Body |
70 |
| - // encoded as UTF-8. Since the server can figure this out and would |
71 |
| - // otherwise refuse events that report the length incorrectly, we decided |
72 |
| - // not to send the length to avoid problems related to reporting the wrong |
73 |
| - // size and to reduce request body size. |
74 |
| - // |
75 |
| - // length: new TextEncoder().encode(req.body).length, |
76 |
| - }); |
| 87 | + return req; |
| 88 | +} |
77 | 89 |
|
78 |
| - // The trailing newline is optional. We intentionally don't send it to avoid |
79 |
| - // sending unnecessary bytes. |
80 |
| - // |
81 |
| - // const envelope = `${envelopeHeaders}\n${itemHeaders}\n${req.body}\n`; |
82 |
| - const envelope = `${envelopeHeaders}\n${itemHeaders}\n${req.body}`; |
83 |
| - req.body = envelope; |
84 |
| - } |
| 90 | +/** |
| 91 | + * Create a SentryRequest from a session event. |
| 92 | + * |
| 93 | + * @param event The session event to send |
| 94 | + * @param api Helper to provide the correct url for the request |
| 95 | + * @returns SentryRequest in envelope form |
| 96 | + */ |
| 97 | +export function sessionToSentryRequest(session: Session, api: API): SentryRequest { |
| 98 | + const envelopeHeaders = JSON.stringify({ |
| 99 | + sent_at: new Date().toISOString(), |
| 100 | + }); |
| 101 | + const itemHeaders = JSON.stringify({ |
| 102 | + type: 'session', |
| 103 | + }); |
85 | 104 |
|
86 |
| - return req; |
| 105 | + return { |
| 106 | + body: `${envelopeHeaders}\n${itemHeaders}\n${JSON.stringify(session)}`, |
| 107 | + type: 'session', |
| 108 | + url: api.getEnvelopeEndpointWithUrlEncodedAuth(), |
| 109 | + }; |
87 | 110 | }
|
0 commit comments