Skip to content

Commit 3b881d9

Browse files
committed
split transactionToSentryRequest into its own method, use transaction's tracestate value in envelope
1 parent 8e1e62a commit 3b881d9

File tree

2 files changed

+85
-62
lines changed

2 files changed

+85
-62
lines changed

packages/core/src/request.ts

Lines changed: 78 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,85 @@
11
import { Event, SentryRequest, Session } from '@sentry/types';
2+
import { base64ToUnicode } from '@sentry/utils';
23

34
import { API } from './api';
45

5-
/** Creates a SentryRequest from an event. */
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+
}
17+
return {
18+
body: JSON.stringify(event),
19+
type: event.type || 'event',
20+
url: api.getStoreEndpointWithUrlEncodedAuth(),
21+
};
22+
}
23+
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 {
36+
// because we send event.tracestate as a header whose values have meaningful equals signs, when we create it we have
37+
// to replace the padding character with something else; the .replace call here reverses that to make it valid
38+
// base64
39+
const tracestateJSON = event.tracestate ? base64ToUnicode(event.tracestate.replace('.', '=')) : undefined;
40+
delete event.tracestate;
41+
42+
const envelopeHeaders = JSON.stringify({
43+
event_id: event.event_id,
44+
sent_at: new Date().toISOString(),
45+
trace: tracestateJSON, // trace context for dynamic sampling on relay
46+
});
47+
48+
const itemHeaders = JSON.stringify({
49+
type: event.type,
50+
// The content-type is assumed to be 'application/json' and not part of
51+
// the current spec for transaction items, so we don't bloat the request
52+
// body with it.
53+
//
54+
// content_type: 'application/json',
55+
//
56+
// The length is optional. It must be the number of bytes in req.Body
57+
// encoded as UTF-8. Since the server can figure this out and would
58+
// otherwise refuse events that report the length incorrectly, we decided
59+
// not to send the length to avoid problems related to reporting the wrong
60+
// size and to reduce request body size.
61+
//
62+
// length: new TextEncoder().encode(req.body).length,
63+
});
64+
65+
const req: SentryRequest = {
66+
// The trailing newline is optional; leave it off to avoid sending unnecessary bytes.
67+
// body: `${envelopeHeaders}\n${itemHeaders}\n${JSON.stringify(event)\n}`,
68+
body: `${envelopeHeaders}\n${itemHeaders}\n${JSON.stringify(event)}`,
69+
type: 'transaction',
70+
url: api.getEnvelopeEndpointWithUrlEncodedAuth(),
71+
};
72+
73+
return req;
74+
}
75+
76+
/**
77+
* Create a SentryRequest from a session event.
78+
*
79+
* @param event The session event to send
80+
* @param api Helper to provide the correct url for the request
81+
* @returns SentryRequest in envelope form
82+
*/
683
export function sessionToSentryRequest(session: Session, api: API): SentryRequest {
784
const envelopeHeaders = JSON.stringify({
885
sent_at: new Date().toISOString(),
@@ -17,62 +94,3 @@ export function sessionToSentryRequest(session: Session, api: API): SentryReques
1794
url: api.getEnvelopeEndpointWithUrlEncodedAuth(),
1895
};
1996
}
20-
21-
/** Creates a SentryRequest from an event. */
22-
export function eventToSentryRequest(event: Event, api: API): SentryRequest {
23-
const useEnvelope = event.type === 'transaction';
24-
25-
const req: SentryRequest = {
26-
body: JSON.stringify(event),
27-
type: event.type || 'event',
28-
url: useEnvelope ? api.getEnvelopeEndpointWithUrlEncodedAuth() : api.getStoreEndpointWithUrlEncodedAuth(),
29-
};
30-
31-
// https://develop.sentry.dev/sdk/envelopes/
32-
33-
// Since we don't need to manipulate envelopes nor store them, there is no
34-
// exported concept of an Envelope with operations including serialization and
35-
// deserialization. Instead, we only implement a minimal subset of the spec to
36-
// serialize events inline here.
37-
if (useEnvelope) {
38-
const envelopeHeaders = JSON.stringify({
39-
event_id: event.event_id,
40-
sent_at: new Date().toISOString(),
41-
42-
// trace context for dynamic sampling on relay
43-
trace: {
44-
trace_id: event.contexts?.trace?.trace_id,
45-
// TODO: any reason we can't change this property to be called publicKey, since that's what it is?
46-
public_key: api.getDsn().user,
47-
environment: event.environment || 'no environment specified',
48-
release: event.release || 'no release specified',
49-
},
50-
});
51-
52-
const itemHeaders = JSON.stringify({
53-
type: event.type,
54-
// The content-type is assumed to be 'application/json' and not part of
55-
// the current spec for transaction items, so we don't bloat the request
56-
// body with it.
57-
//
58-
// content_type: 'application/json',
59-
//
60-
// The length is optional. It must be the number of bytes in req.Body
61-
// encoded as UTF-8. Since the server can figure this out and would
62-
// otherwise refuse events that report the length incorrectly, we decided
63-
// not to send the length to avoid problems related to reporting the wrong
64-
// size and to reduce request body size.
65-
//
66-
// length: new TextEncoder().encode(req.body).length,
67-
});
68-
69-
// The trailing newline is optional. We intentionally don't send it to avoid
70-
// sending unnecessary bytes.
71-
//
72-
// const envelope = `${envelopeHeaders}\n${itemHeaders}\n${req.body}\n`;
73-
const envelope = `${envelopeHeaders}\n${itemHeaders}\n${req.body}`;
74-
req.body = envelope;
75-
}
76-
77-
return req;
78-
}

packages/core/test/lib/request.test.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ describe('eventToSentryRequest', () => {
3030
event_id: eventId,
3131
release: 'off.leash.park',
3232
spans: [],
33+
tracestate:
34+
'ewAiAGUAbgB2AGkAcgBvAG4AbQBlAG4AdAAiADoAIgBkAG8AZwBwAGEAcgBrACIALAAiAHAAdQBiAGwAaQBjAF8AawBlAHkAIgA6ACIAZAB' +
35+
'vAGcAcwBhAHIAZQBiAGEAZABhAHQAawBlAGUAcABpAG4AZwBzAGUAYwByAGUAdABzACIALAAiAHIAZQBsAGUAYQBzAGUAIgA6ACIAbwBmAG' +
36+
'YALgBsAGUAYQBzAGgALgBwAGEAcgBrACIALAAiAHQAcgBhAGMAZQBfAGkAZAAiADoAIgAwADkAMAA4ADIAMAAxADMAMAA0ADEANQAyADAAM' +
37+
'QAzACIAfQA.',
3338
transaction: '/dogs/are/great/',
3439
type: 'transaction',
3540
user: { id: '1121', username: 'CharlieDog', ip_address: '11.21.20.12' },
@@ -52,12 +57,12 @@ describe('eventToSentryRequest', () => {
5257
expect(envelope.envelopeHeader).toEqual({
5358
event_id: eventId,
5459
sent_at: expect.any(String),
55-
trace: {
60+
trace: JSON.stringify({
5661
environment: 'dogpark',
5762
public_key: 'dogsarebadatkeepingsecrets',
5863
release: 'off.leash.park',
5964
trace_id: traceId,
60-
},
65+
}),
6166
});
6267
expect(envelope.itemHeader).toEqual({ type: 'transaction' });
6368
expect(envelope.event).toEqual(event);

0 commit comments

Comments
 (0)