Reduce Sentry noise from logger transport and expected-failure call sites

This commit is contained in:
vineyardbovines
2026-06-12 13:18:55 -04:00
parent e86067986f
commit f48ecd6878
12 changed files with 75 additions and 25 deletions
-5
View File
@@ -1058,11 +1058,6 @@
"count": 1 "count": 1
} }
}, },
"src/logger/transports/sentry.ts": {
"@typescript-eslint/no-unsafe-enum-comparison": {
"count": 3
}
},
"src/logger/types.ts": { "src/logger/types.ts": {
"@typescript-eslint/no-redundant-type-constituents": { "@typescript-eslint/no-redundant-type-constituents": {
"count": 1 "count": 1
+1 -1
View File
@@ -124,7 +124,7 @@ function InnerApp() {
await features.init await features.init
} }
} catch (e) { } catch (e) {
logger.error(`session: resume failed`, {message: e}) logger.warn(`session: resume failed`, {message: e})
} finally { } finally {
setIsReady(true) setIsReady(true)
} }
+1 -1
View File
@@ -103,7 +103,7 @@ function InnerApp() {
await features.init await features.init
} }
} catch (e) { } catch (e) {
logger.error('session: resumeSession failed', {message: e}) logger.warn('session: resumeSession failed', {message: e})
} finally { } finally {
setIsReady(true) setIsReady(true)
} }
+1 -1
View File
@@ -132,7 +132,7 @@ export function Root({children}: {children: React.ReactNode}) {
const onHoverableTouchUp = useCallback((id: string) => { const onHoverableTouchUp = useCallback((id: string) => {
const hoverable = hoverables.current.get(id) const hoverable = hoverables.current.get(id)
if (!hoverable) { if (!hoverable) {
logger.warn(`No such hoverable with id ${id}`) logger.warn(`No such hoverable`, {id})
return return
} }
hoverable.onTouchUp() hoverable.onTouchUp()
@@ -189,7 +189,7 @@ export function useVideoElement(ref: RefObject<HTMLVideoElement | null>) {
`The play() request was interrupted by a call to pause()`, `The play() request was interrupted by a call to pause()`,
) )
) { ) {
logger.error('Error playing video:', {message: err}) logger.warn('Error playing video:', {message: err})
} }
}) })
} }
+1 -1
View File
@@ -55,7 +55,7 @@ async function _registerPushToken({
notyLogger.debug(`registerPushToken: success`) notyLogger.debug(`registerPushToken: success`)
} catch (error) { } catch (error) {
if (!isNetworkError(error)) { if (!isNetworkError(error)) {
notyLogger.error(`registerPushToken: failed`, {safeMessage: error}) notyLogger.warn(`registerPushToken: failed`, {safeMessage: error})
} }
} }
} }
+1 -1
View File
@@ -287,7 +287,7 @@ export function Provider({children}: React.PropsWithChildren<unknown>) {
})) }))
} catch (err) { } catch (err) {
const e = err as Error const e = err as Error
logger.error('Failed to translate text on device', {safeMessage: e}) logger.warn('Failed to translate text on device', {safeMessage: e})
// On-device translation failed (language pack missing or user // On-device translation failed (language pack missing or user
// dismissed the download prompt). // dismissed the download prompt).
ax.metric('translate:result', { ax.metric('translate:result', {
+48 -6
View File
@@ -182,11 +182,7 @@ describe('general functionality', () => {
timestamp: sentryTimestamp, timestamp: sentryTimestamp,
}) })
jest.runAllTimers() jest.runAllTimers()
expect(Sentry.captureMessage).toHaveBeenCalledWith(message, { expect(Sentry.captureMessage).not.toHaveBeenCalled()
level: 'log',
tags: {category: 'logger'},
extra: {__context__: 'logger'},
})
sentryTransport( sentryTransport(
LogLevel.Warn, LogLevel.Warn,
@@ -204,8 +200,18 @@ describe('general functionality', () => {
timestamp: sentryTimestamp, timestamp: sentryTimestamp,
}) })
jest.runAllTimers() jest.runAllTimers()
expect(Sentry.captureMessage).not.toHaveBeenCalled()
sentryTransport(
LogLevel.Error,
Logger.Context.Default,
message,
{},
timestamp,
)
jest.runAllTimers()
expect(Sentry.captureMessage).toHaveBeenCalledWith(message, { expect(Sentry.captureMessage).toHaveBeenCalledWith(message, {
level: 'warning', level: 'error',
tags: {category: 'logger'}, tags: {category: 'logger'},
extra: {__context__: 'logger'}, extra: {__context__: 'logger'},
}) })
@@ -259,6 +265,42 @@ describe('general functionality', () => {
}) })
}) })
test('sentryTransport filters network errors', () => {
jest.clearAllMocks()
const timestamp = Date.now()
// network error in the message itself
sentryTransport(
LogLevel.Error,
Logger.Context.Default,
'Network request failed',
{},
timestamp,
)
// network error in metadata, message is something else
sentryTransport(
LogLevel.Error,
Logger.Context.Default,
'poll failed',
{safeMessage: new Error('Network request failed')},
timestamp,
)
jest.runAllTimers()
expect(Sentry.captureMessage).not.toHaveBeenCalled()
// network Error object
sentryTransport(
LogLevel.Error,
Logger.Context.Default,
new Error('Network request failed'),
{},
timestamp,
)
expect(Sentry.captureException).not.toHaveBeenCalled()
})
test('add/remove transport', () => { test('add/remove transport', () => {
const timestamp = Date.now() const timestamp = Date.now()
const logger = new Logger({}) const logger = new Logger({})
+18 -5
View File
@@ -47,16 +47,24 @@ export const sentryTransport: Transport = (
timestamp: timestamp / 1000, // Sentry expects seconds timestamp: timestamp / 1000, // Sentry expects seconds
}) })
// We don't want to send any network errors to sentry // We don't want to send any network errors to sentry. The underlying
if (isNetworkError(message)) { // cause is often passed via metadata rather than the message itself, so
// check the common metadata keys too.
if (
isNetworkError(message) ||
isNetworkError(metadata.safeMessage) ||
isNetworkError(metadata.message) ||
isNetworkError(metadata.error)
) {
return return
} }
/** /**
* Send all higher levels with `captureMessage`, with appropriate severity * Only error-level strings are reported to Sentry as events. Lower levels
* level * are captured as breadcrumbs above and attached to the next event, if
* any.
*/ */
if (level === 'error' || level === 'warn' || level === 'log') { if (level === LogLevel.Error) {
// Defer non-critical messages so they're sent in a batch // Defer non-critical messages so they're sent in a batch
queueMessageForSentry(message, { queueMessageForSentry(message, {
level: severity, level: severity,
@@ -65,6 +73,11 @@ export const sentryTransport: Transport = (
}) })
} }
} else { } else {
// We don't want to send any network errors to sentry
if (isNetworkError(message)) {
return
}
/** /**
* It's otherwise an Error and should be reported with captureException * It's otherwise an Error and should be reported with captureException
*/ */
+1 -1
View File
@@ -55,7 +55,7 @@ export const ChooseAccountForm = ({
}) })
Toast.show(_(msg`Signed in as @${account.handle}`)) Toast.show(_(msg`Signed in as @${account.handle}`))
} catch (e: any) { } catch (e: any) {
logger.error('choose account: initSession failed', { logger.warn('choose account: initSession failed', {
message: e instanceof Error ? e.message : 'Unknown error', message: e instanceof Error ? e.message : 'Unknown error',
}) })
// Move to login form. // Move to login form.
+1 -1
View File
@@ -392,7 +392,7 @@ export class MessagesEventBus {
} }
} catch (e: any) { } catch (e: any) {
if (!isNetworkError(e) && !isErrorMaybeAppPasswordPermissions(e)) { if (!isNetworkError(e) && !isErrorMaybeAppPasswordPermissions(e)) {
logger.error(`poll events failed`, { logger.warn(`poll events failed`, {
safeMessage: e.message, safeMessage: e.message,
}) })
} }
+1 -1
View File
@@ -308,7 +308,7 @@ let PostFeed = ({
} }
} catch (e) { } catch (e) {
if (!isNetworkError(e)) { if (!isNetworkError(e)) {
logger.error('Poll latest failed', {feed, message: String(e)}) logger.warn('Poll latest failed', {feed, message: String(e)})
} }
} }
}) })