Skip to content

Commit fc180d5

Browse files
committed
cleanup
Signed-off-by: flakey5 <[email protected]>
1 parent c0468e2 commit fc180d5

4 files changed

Lines changed: 309 additions & 290 deletions

File tree

apps/site/cloudflare/worker-entrypoint.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,13 @@ export default withSentry(
5050
},
5151
tail: createSentryTail({
5252
samplingRate: 1,
53-
headersToRedact: ['authorization', 'cookie'],
53+
headersToRedact: [
54+
'authorization',
55+
'cookie',
56+
'cf-connecting-ip',
57+
'x-forwarded-for',
58+
'x-real-ip',
59+
],
5460
}),
5561
}
5662
);
Lines changed: 1 addition & 289 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
import {
2-
type Event,
3-
type SeverityLevel,
4-
captureEvent,
5-
} from '@sentry/cloudflare';
1+
import { processTraceItem } from './utils/processTailItem';
62

73
export type SentryTailWorkerOptions = {
84
samplingRate: number;
@@ -18,287 +14,3 @@ export function createSentryTail<Env = unknown>(
1814
}
1915
};
2016
}
21-
22-
function processTraceItem(
23-
options: SentryTailWorkerOptions,
24-
item: TraceItem
25-
): void {
26-
const severityLevel = determineSeverityLevel(item);
27-
if (!severityLevel) {
28-
// Not an error
29-
return;
30-
}
31-
32-
if (
33-
options.samplingRate !== 1 &&
34-
!shouldSampleTraceItem(options.samplingRate)
35-
) {
36-
return;
37-
}
38-
39-
const event: Event = {
40-
level: severityLevel,
41-
timestamp: item.eventTimestamp ?? Date.now(),
42-
logger: '@node-core/cloudflare-sentry-tail',
43-
message: workerOutcomeToEventMessage(item.outcome),
44-
fingerprint: [],
45-
breadcrumbs: [],
46-
exception: {
47-
values: [],
48-
},
49-
tags: {
50-
outcome: item.outcome,
51-
script_name: item.scriptName,
52-
script_version: item.scriptVersion?.tag,
53-
cpu_time: item.cpuTime,
54-
wall_time: item.wallTime,
55-
},
56-
};
57-
58-
// Populate data specific to the type of trace event we got
59-
handleTraceItemEvent(options, item, event);
60-
61-
// Populate breadcrumbs with any relevant data
62-
addRemainingBreadcrumbs(item, event);
63-
64-
// Sort breadcrumbs by their timestamps
65-
event.breadcrumbs?.sort((a, b) => {
66-
if (!a.timestamp || !b.timestamp) {
67-
return 0;
68-
}
69-
70-
return a.timestamp - b.timestamp;
71-
});
72-
73-
captureEvent(event);
74-
}
75-
76-
function determineSeverityLevel(item: TraceItem): SeverityLevel | undefined {
77-
// Two scenarios where we want to report back to Sentry:
78-
// 1. Trace item outcome isn't 'ok'
79-
// 2. We have a status code >= 500
80-
//
81-
// Note that outcome is determined by if the worker executed to completion,
82-
// not if it returned a successful status code
83-
84-
if (item.outcome === 'ok') {
85-
const response =
86-
item.event && 'response' in item.event ? item.event.response : undefined;
87-
88-
if (response?.status && response?.status >= 500) {
89-
return 'error';
90-
} else {
91-
// Don't care
92-
return undefined;
93-
}
94-
}
95-
96-
return workerOutcomeToSeverityLevel(item.outcome);
97-
}
98-
99-
/**
100-
* Determines what kind of trace item event we received and adds any
101-
* event-specific properties to the Sentry event to be reported.
102-
*/
103-
function handleTraceItemEvent(
104-
options: SentryTailWorkerOptions,
105-
item: TraceItem,
106-
sentryEvent: Event
107-
): void {
108-
if (!item.event) {
109-
return;
110-
}
111-
112-
if ('request' in item.event) {
113-
const request = item.event.request;
114-
const response = item.event.response;
115-
116-
const redactedHeaders: Record<string, string> = {};
117-
for (let [key, value] of Object.entries(request.headers)) {
118-
key = key.toLowerCase();
119-
120-
if (options.headersToRedact && options.headersToRedact.includes(key)) {
121-
value = 'redacted';
122-
}
123-
124-
redactedHeaders[key] = value;
125-
}
126-
127-
sentryEvent.request = {
128-
method: request.method,
129-
url: request.url,
130-
headers: redactedHeaders,
131-
env: {
132-
asn: request.cf?.asn,
133-
colo: request.cf?.colo,
134-
continent: request.cf?.continent,
135-
country: request.cf?.country,
136-
timezone: request.cf?.timezone,
137-
httpProtocol: request.cf?.httpProtocol,
138-
requestPriority: request.cf?.requestPriority,
139-
tlsCipher: request.cf?.tlsCipher,
140-
tlsClientAuth: request.cf?.tlsClientAuth,
141-
tlsExportedAuthenticator: request.cf?.tlsExportedAuthenticator,
142-
tlsVersion: request.cf?.tlsVersion,
143-
},
144-
};
145-
146-
const responseStatusCode = response?.status ?? 'Unknown';
147-
148-
sentryEvent.message = response
149-
? `${responseStatusCode} Response`
150-
: 'No response';
151-
152-
sentryEvent.breadcrumbs?.push({
153-
type: 'http',
154-
category: 'request',
155-
timestamp: item.eventTimestamp ?? Date.now(),
156-
data: {
157-
url: request.url,
158-
method: request.method,
159-
status_code: responseStatusCode,
160-
},
161-
});
162-
163-
const requestUrl = new URL(request.url);
164-
sentryEvent.fingerprint?.push(
165-
requestUrl.origin,
166-
requestUrl.pathname,
167-
request.method,
168-
`${responseStatusCode}`
169-
);
170-
171-
sentryEvent.tags!.event = 'fetch';
172-
sentryEvent.tags!.ray_id = redactedHeaders['cf-ray'];
173-
} else if ('rpcMethod' in item.event) {
174-
sentryEvent.tags!.event = 'js-rpc';
175-
sentryEvent.tags!.rpc_method = item.event.rpcMethod;
176-
} else if ('scheduledTime' in item.event) {
177-
if ('cron' in item.event) {
178-
sentryEvent.tags!.event = 'scheduled';
179-
sentryEvent.tags!.scheduled_time = item.event.scheduledTime;
180-
sentryEvent.tags!.cron = item.event.cron;
181-
return;
182-
}
183-
184-
sentryEvent.tags!.event = 'alarm';
185-
sentryEvent.tags!.scheduled_time = item.event.scheduledTime.toUTCString();
186-
} else if ('queue' in item.event) {
187-
sentryEvent.tags!.event = 'queue';
188-
sentryEvent.tags!.queue = item.event.queue;
189-
sentryEvent.tags!.batchSize = item.event.batchSize;
190-
} else if ('mailFrom' in item.event) {
191-
sentryEvent.tags!.event = 'email';
192-
sentryEvent.tags!.rawSize = item.event.rawSize;
193-
}
194-
}
195-
196-
function addRemainingBreadcrumbs(item: TraceItem, sentryEvent: Event) {
197-
if (!sentryEvent.breadcrumbs) {
198-
return;
199-
}
200-
201-
let breadcrumbsIdx = sentryEvent.breadcrumbs.length;
202-
203-
// Allocate space for the elements we're gonna add
204-
sentryEvent.breadcrumbs.length +=
205-
item.logs.length +
206-
item.diagnosticsChannelEvents.length +
207-
item.exceptions.length;
208-
209-
for (const log of item.logs) {
210-
sentryEvent.breadcrumbs[breadcrumbsIdx++] = {
211-
type: 'debug',
212-
category: `console.${log.level}`,
213-
message: consoleLogToString(log.message),
214-
level: consoleLogLevelToSentryLevel(log.level),
215-
timestamp: log.timestamp,
216-
};
217-
}
218-
219-
for (const payload of item.diagnosticsChannelEvents) {
220-
sentryEvent.breadcrumbs[breadcrumbsIdx++] = {
221-
type: 'debug',
222-
category: `channel.${payload.channel}`,
223-
message: consoleLogToString(payload.message),
224-
level: 'debug',
225-
timestamp: payload.timestamp,
226-
};
227-
}
228-
229-
let fingerprintIdx = sentryEvent.fingerprint!.length;
230-
let exceptionValueIdx = sentryEvent.exception!.values!.length;
231-
232-
sentryEvent.fingerprint!.length += item.exceptions.length;
233-
sentryEvent.exception!.values!.length += item.exceptions.length;
234-
235-
for (const exception of item.exceptions) {
236-
sentryEvent.breadcrumbs[breadcrumbsIdx++] = {
237-
type: 'error',
238-
level: 'error',
239-
category: exception.name,
240-
message: exception.message,
241-
timestamp: exception.timestamp,
242-
data: {
243-
stack: exception.stack,
244-
},
245-
};
246-
247-
sentryEvent.fingerprint![fingerprintIdx++] = exception.name;
248-
sentryEvent.exception!.values![exceptionValueIdx++] = {
249-
type: exception.name,
250-
value: exception.message,
251-
};
252-
}
253-
}
254-
255-
function shouldSampleTraceItem(sampleRate: number) {
256-
const buffer = new Uint32Array(1);
257-
crypto.getRandomValues(buffer);
258-
259-
const random = buffer[0] / 4294967295;
260-
261-
return random <= sampleRate;
262-
}
263-
264-
function workerOutcomeToSeverityLevel(outcome: string): SeverityLevel {
265-
const map: Record<string, SeverityLevel> = {
266-
exceededCpu: 'fatal',
267-
exceededMemory: 'fatal',
268-
exception: 'error',
269-
ok: 'info',
270-
};
271-
272-
return map[outcome] ?? 'warning';
273-
}
274-
275-
function workerOutcomeToEventMessage(outcome: string): string {
276-
const map: Record<string, string> = {
277-
exceededCpu: 'Exceeded CPU',
278-
exceededMemory: 'Exceeded Memory',
279-
exception: 'Script Threw Exception',
280-
canceled: 'Client Disconnected',
281-
ok: 'Success',
282-
};
283-
284-
return map[outcome] ?? 'Internal';
285-
}
286-
287-
function consoleLogLevelToSentryLevel(logLevel: string): SeverityLevel {
288-
const map: Record<string, SeverityLevel> = {
289-
debug: 'debug',
290-
log: 'info',
291-
error: 'error',
292-
warn: 'warning',
293-
trace: 'debug',
294-
};
295-
296-
return map[logLevel] ?? 'debug';
297-
}
298-
299-
function consoleLogToString(logMessage: unknown): string {
300-
const pieces = Array.isArray(logMessage) ? logMessage : [logMessage];
301-
return pieces
302-
.map(p => (typeof p === 'string' ? p : JSON.stringify(p)))
303-
.join(', ');
304-
}

0 commit comments

Comments
 (0)