forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import {isNetworkError} from '#/lib/strings/errors'
2import {Sentry} from '#/logger/sentry/lib'
3import {LogLevel, type Transport} from '#/logger/types'
4import {prepareMetadata} from '#/logger/util'
5
6export const sentryTransport: Transport = (
7 level,
8 context,
9 message,
10 {type, tags, ...metadata},
11 timestamp,
12) => {
13 // Skip debug messages entirely for now - esb
14 if (level === LogLevel.Debug) return
15
16 const meta = {
17 __context__: context,
18 ...prepareMetadata(metadata),
19 }
20 let _tags = tags || {}
21 _tags = {
22 // use `category` to match breadcrumbs
23 category: context,
24 ...tags,
25 }
26
27 /**
28 * If a string, report a breadcrumb
29 */
30 if (typeof message === 'string') {
31 const severity = (
32 {
33 [LogLevel.Debug]: 'debug',
34 [LogLevel.Info]: 'info',
35 [LogLevel.Log]: 'log', // Sentry value here is undefined
36 [LogLevel.Warn]: 'warning',
37 [LogLevel.Error]: 'error',
38 } as const
39 )[level]
40
41 Sentry.addBreadcrumb({
42 category: context,
43 message,
44 data: meta,
45 type: type || 'default',
46 level: severity,
47 timestamp: timestamp / 1000, // Sentry expects seconds
48 })
49
50 // We don't want to send any network errors to sentry
51 if (isNetworkError(message)) {
52 return
53 }
54
55 /**
56 * Send all higher levels with `captureMessage`, with appropriate severity
57 * level
58 */
59 if (level === 'error' || level === 'warn' || level === 'log') {
60 // Defer non-critical messages so they're sent in a batch
61 queueMessageForSentry(message, {
62 level: severity,
63 tags: _tags,
64 extra: meta,
65 })
66 }
67 } else {
68 /**
69 * It's otherwise an Error and should be reported with captureException
70 */
71 Sentry.captureException(message, {
72 tags: _tags,
73 extra: meta,
74 })
75 }
76}
77
78const queuedMessages: [string, Parameters<typeof Sentry.captureMessage>[1]][] =
79 []
80let sentrySendTimeout: ReturnType<typeof setTimeout> | null = null
81
82function queueMessageForSentry(
83 message: string,
84 captureContext: Parameters<typeof Sentry.captureMessage>[1],
85) {
86 queuedMessages.push([message, captureContext])
87 if (!sentrySendTimeout) {
88 // Throttle sending messages with a leading delay
89 // so that we can get Sentry out of the critical path.
90 sentrySendTimeout = setTimeout(() => {
91 sentrySendTimeout = null
92 sendQueuedMessages()
93 }, 7000)
94 }
95}
96
97function sendQueuedMessages() {
98 while (queuedMessages.length > 0) {
99 const record = queuedMessages.shift()
100 if (record) {
101 Sentry.captureMessage(record[0], record[1])
102 }
103 }
104}