about summary refs log tree commit diff
path: root/src/logger/transports/sentry.ts
diff options
context:
space:
mode:
authorEric Bailey <git@esb.lol>2025-02-28 12:09:36 -0600
committerGitHub <noreply@github.com>2025-02-28 12:09:36 -0600
commit7c36ea115855050f319be19bb74d6f7fd80f8eed (patch)
treeed32d674b1b74dca813ad9cac44a621313431270 /src/logger/transports/sentry.ts
parent9e9ffd5c6e9e5c672f60aa10d60c6628a15ae783 (diff)
downloadvoidsky-7c36ea115855050f319be19bb74d6f7fd80f8eed.tar.zst
Logger improvements (#7729)
* Remove enablement

* Refactor context and filtering

* Fix imports, simplify transports config

* Migrate usages of debug context

* Re-org, add colors and grouping to console logging

* Remove temp default context

* Remove manual prefix

* Move colorizing out of console transport body

* Reduce reuse

* Pass through context

* Ensure bitdrift is enabled in dev

* Enable Sentry on web only

* Clean up types

* Docs

* Format

* Update tests

* Clean up tests

* No positional args

* Revert Sentry changes

* Clean up context, use it, pass metadata through to Bitdrift

* Fix up debugging

* Clean up metadata before passing to Bitdrift

* Correct transports

* Reserve context prop on metadata and include in transports

* Update tests
Diffstat (limited to 'src/logger/transports/sentry.ts')
-rw-r--r--src/logger/transports/sentry.ts102
1 files changed, 102 insertions, 0 deletions
diff --git a/src/logger/transports/sentry.ts b/src/logger/transports/sentry.ts
new file mode 100644
index 000000000..890918d67
--- /dev/null
+++ b/src/logger/transports/sentry.ts
@@ -0,0 +1,102 @@
+import {isNetworkError} from '#/lib/strings/errors'
+import {Sentry} from '#/logger/sentry/lib'
+import {LogLevel, Transport} from '#/logger/types'
+import {prepareMetadata} from '#/logger/util'
+
+export const sentryTransport: Transport = (
+  level,
+  context,
+  message,
+  {type, tags, ...metadata},
+  timestamp,
+) => {
+  const meta = {
+    // match Bitdrift payload
+    context,
+    ...prepareMetadata(metadata),
+  }
+  let _tags = tags || {}
+  _tags = {
+    // use `category` to match breadcrumbs
+    category: context,
+    ...tags,
+  }
+
+  /**
+   * If a string, report a breadcrumb
+   */
+  if (typeof message === 'string') {
+    const severity = (
+      {
+        [LogLevel.Debug]: 'debug',
+        [LogLevel.Info]: 'info',
+        [LogLevel.Log]: 'log', // Sentry value here is undefined
+        [LogLevel.Warn]: 'warning',
+        [LogLevel.Error]: 'error',
+      } as const
+    )[level]
+
+    Sentry.addBreadcrumb({
+      category: context,
+      message,
+      data: meta,
+      type: type || 'default',
+      level: severity,
+      timestamp: timestamp / 1000, // Sentry expects seconds
+    })
+
+    // We don't want to send any network errors to sentry
+    if (isNetworkError(message)) {
+      return
+    }
+
+    /**
+     * Send all higher levels with `captureMessage`, with appropriate severity
+     * level
+     */
+    if (level === 'error' || level === 'warn' || level === 'log') {
+      // Defer non-critical messages so they're sent in a batch
+      queueMessageForSentry(message, {
+        level: severity,
+        tags: _tags,
+        extra: meta,
+      })
+    }
+  } else {
+    /**
+     * It's otherwise an Error and should be reported with captureException
+     */
+    Sentry.captureException(message, {
+      tags: _tags,
+      extra: meta,
+    })
+  }
+}
+
+const queuedMessages: [string, Parameters<typeof Sentry.captureMessage>[1]][] =
+  []
+let sentrySendTimeout: ReturnType<typeof setTimeout> | null = null
+
+function queueMessageForSentry(
+  message: string,
+  captureContext: Parameters<typeof Sentry.captureMessage>[1],
+) {
+  queuedMessages.push([message, captureContext])
+  if (!sentrySendTimeout) {
+    // Throttle sending messages with a leading delay
+    // so that we can get Sentry out of the critical path.
+    sentrySendTimeout = setTimeout(() => {
+      sentrySendTimeout = null
+      sendQueuedMessages()
+    }, 7000)
+  }
+}
+
+function sendQueuedMessages() {
+  while (queuedMessages.length > 0) {
+    const record = queuedMessages.shift()
+    if (record) {
+      Sentry.captureMessage(record[0], record[1])
+    }
+  }
+}