diff options
27 files changed, 568 insertions, 499 deletions
diff --git a/src/App.native.tsx b/src/App.native.tsx index 318258aa7..63284d5d5 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -1,5 +1,5 @@ import 'react-native-url-polyfill/auto' -import '#/lib/sentry' // must be near top +import '#/logger/sentry/setup' import '#/lib/bitdrift' // must be near top import '#/view/icons' diff --git a/src/App.web.tsx b/src/App.web.tsx index 2142c26bd..58754d8e4 100644 --- a/src/App.web.tsx +++ b/src/App.web.tsx @@ -1,4 +1,4 @@ -import '#/lib/sentry' // must be near top +import '#/logger/sentry/setup' // must be near top import '#/view/icons' import './style.css' diff --git a/src/components/FeedCard.tsx b/src/components/FeedCard.tsx index f7446bc81..1c1895418 100644 --- a/src/components/FeedCard.tsx +++ b/src/components/FeedCard.tsx @@ -271,7 +271,7 @@ function SaveButtonInner({ } Toast.show(_(msg`Feeds updated!`)) } catch (err: any) { - logger.error(err, {context: `FeedCard: failed to update feeds`, pin}) + logger.error(err, {message: `FeedCard: failed to update feeds`, pin}) Toast.show(_(msg`Failed to update feeds`), 'xmark') } }, diff --git a/src/components/dialogs/PostInteractionSettingsDialog.tsx b/src/components/dialogs/PostInteractionSettingsDialog.tsx index fdf075ca4..63b640d9f 100644 --- a/src/components/dialogs/PostInteractionSettingsDialog.tsx +++ b/src/components/dialogs/PostInteractionSettingsDialog.tsx @@ -209,7 +209,7 @@ export function PostInteractionSettingsDialogControlledInner( props.control.close() } catch (e: any) { logger.error(`Failed to save post interaction settings`, { - context: 'PostInteractionSettingsDialogControlledInner', + source: 'PostInteractionSettingsDialogControlledInner', safeMessage: e.message, }) Toast.show( diff --git a/src/lib/hooks/useNotificationHandler.ts b/src/lib/hooks/useNotificationHandler.ts index 2ec3fcb79..b5566f8a6 100644 --- a/src/lib/hooks/useNotificationHandler.ts +++ b/src/lib/hooks/useNotificationHandler.ts @@ -6,7 +6,7 @@ import {useQueryClient} from '@tanstack/react-query' import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher' import {NavigationProp} from '#/lib/routes/types' import {logEvent} from '#/lib/statsig/statsig' -import {logger} from '#/logger' +import {Logger} from '#/logger' import {isAndroid} from '#/platform/detection' import {useCurrentConvoId} from '#/state/messages/current-convo-id' import {RQKEY as RQKEY_NOTIFS} from '#/state/queries/notifications/feed' @@ -50,6 +50,8 @@ const DEFAULT_HANDLER_OPTIONS = { let storedPayload: NotificationPayload | undefined let prevDate = 0 +const logger = Logger.create(Logger.Context.Notifications) + export function useNotificationsHandler() { const queryClient = useQueryClient() const {currentAccount, accounts} = useSession() @@ -186,11 +188,7 @@ export function useNotificationsHandler() { return DEFAULT_HANDLER_OPTIONS } - logger.debug( - 'Notifications: received', - {e}, - logger.DebugContext.notifications, - ) + logger.debug('Notifications: received', {e}) const payload = e.request.trigger.payload as NotificationPayload if ( @@ -217,13 +215,9 @@ export function useNotificationsHandler() { } prevDate = e.notification.date - logger.debug( - 'Notifications: response received', - { - actionIdentifier: e.actionIdentifier, - }, - logger.DebugContext.notifications, - ) + logger.debug('Notifications: response received', { + actionIdentifier: e.actionIdentifier, + }) if ( e.actionIdentifier === Notifications.DEFAULT_ACTION_IDENTIFIER && @@ -235,7 +229,6 @@ export function useNotificationsHandler() { logger.debug( 'User pressed a notification, opening notifications tab', {}, - logger.DebugContext.notifications, ) logEvent('notifications:openApp', {}) invalidateCachedUnreadPage() diff --git a/src/lib/notifications/notifications.ts b/src/lib/notifications/notifications.ts index dfdc3b49c..ab7fc5708 100644 --- a/src/lib/notifications/notifications.ts +++ b/src/lib/notifications/notifications.ts @@ -4,7 +4,7 @@ import {getBadgeCountAsync, setBadgeCountAsync} from 'expo-notifications' import {BskyAgent} from '@atproto/api' import {logEvent} from '#/lib/statsig/statsig' -import {logger} from '#/logger' +import {Logger} from '#/logger' import {devicePlatform, isAndroid, isNative} from '#/platform/detection' import {SessionAccount, useAgent, useSession} from '#/state/session' import BackgroundNotificationHandler from '../../../modules/expo-background-notification-handler' @@ -14,6 +14,8 @@ const SERVICE_DID = (serviceUrl?: string) => ? 'did:web:api.staging.bsky.dev' : 'did:web:api.bsky.app' +const logger = Logger.create(Logger.Context.Notifications) + async function registerPushToken( agent: BskyAgent, account: SessionAccount, @@ -26,14 +28,10 @@ async function registerPushToken( token: token.data, appId: 'xyz.blueskyweb.app', }) - logger.debug( - 'Notifications: Sent push token (init)', - { - tokenType: token.type, - token: token.data, - }, - logger.DebugContext.notifications, - ) + logger.debug('Notifications: Sent push token (init)', { + tokenType: token.type, + token: token.data, + }) } catch (error) { logger.error('Notifications: Failed to set push token', {message: error}) } diff --git a/src/logger/README.md b/src/logger/README.md index 8da7deb14..e3476efdf 100644 --- a/src/logger/README.md +++ b/src/logger/README.md @@ -1,99 +1,45 @@ # Logger -Simple logger for Bluesky. Supports log levels, debug contexts, and separate -transports for production, dev, and test mode. +Simple logger for Bluesky. ## At a Glance ```typescript -import { logger } from '#/logger' +import { logger, Logger } from '#/logger' -logger.debug(message[, metadata, debugContext]) -logger.info(message[, metadata]) -logger.log(message[, metadata]) -logger.warn(message[, metadata]) -logger.error(error[, metadata]) -``` - -#### Modes +// or, optionally create new instance with custom context +// const logger = Logger.create(Logger.Context.Notifications) -The "modes" referred to here are inferred from `process.env.NODE_ENV`, -which matches how React Native sets the `__DEV__` global. - -#### Log Levels +// for dev-only logs +logger.debug(message, {}) -Log levels are used to filter which logs are either printed to the console -and/or sent to Sentry and other reporting services. To configure, set the -`EXPO_PUBLIC_LOG_LEVEL` environment variable in `.env` to one of `debug`, -`info`, `log`, `warn`, or `error`. +// for production breadcrumbs +logger.info(message, {}) -This variable should be `info` in production, and `debug` in dev. If it gets too -noisy in dev, simply set it to a higher level, such as `warn`. +// seldom used, prefer `info` +logger.log(message, {}) -## Usage - -```typescript -import { logger } from '#/logger'; -``` +// for non-error issues to look into, seldom used, prefer `error` +logger.warn(message, {}) -### `logger.error` +// for known errors without an exception, use a string +logger.error(`known error`, {}) -The `error` level is for... well, errors. These are sent to Sentry in production mode. - -`error`, along with all log levels, supports an additional parameter, `metadata: Record<string, unknown>`. Use this to provide values to the [Sentry -breadcrumb](https://docs.sentry.io/platforms/react-native/enriching-events/breadcrumbs/#manual-breadcrumbs). - -```typescript +// for unknown exceptions try { - // some async code } catch (e) { - logger.error(e, { ...metadata }); + logger.error(e, {message: `explain error`}]) } ``` -### `logger.warn` - -Warnings will be sent to Sentry as a separate Issue with level `warning`, as -well as as breadcrumbs, with a severity level of `warning` - -### `logger.log` - -Logs with level `log` will be sent to Sentry as a separate Issue with level `log`, as -well as as breadcrumbs, with a severity level of `default`. - -### `logger.info` - -The `info` level should be used for information that would be helpful in a -tracing context, like Sentry. In production mode, `info` logs are sent -to Sentry as breadcrumbs, which decorate log levels above `info` such as `log`, -`warn`, and `error`. - -### `logger.debug` - -Debug level is really only intended for local development. Use this instead of -`console.log`. - -```typescript -logger.debug(message, { ...metadata }); -``` - -Inspired by [debug](https://www.npmjs.com/package/debug), when writing debug -logs, you can optionally pass a _context_, which can be then filtered when in -debug mode. - -This value should be related to the feature, component, or screen -the code is running within, and **it should be defined in `#/logger/debugContext`**. -This way we know if a relevant context already exists, and we can trace all -active contexts in use in our app. This const enum is conveniently available on -the `logger` at `logger.DebugContext`. +#### Log Levels -For example, a debug log like this: +Log level defaults to `info`. You can set this via the `EXPO_PUBLIC_LOG_LEVEL` +env var in `.env.local`. -```typescript -logger.debug(message, {}, logger.DebugContext.composer); -``` +#### Filtering debugs by context -Would be logged to the console in dev mode if `EXPO_PUBLIC_LOG_LEVEL=debug`, _or_ if you -pass a separate environment variable `LOG_DEBUG=composer`. This variable supports -multiple contexts using commas like `LOG_DEBUG=composer,profile`, and _automatically -sets the log level to `debug`, regardless of `EXPO_PUBLIC_LOG_LEVEL`._ +Debug logs are dev-only, and not enabled by default. Once enabled, they can get +noisy. So you can filter them by setting the `EXPO_PUBLIC_LOG_DEBUG` env var +e.g. `EXPO_PUBLIC_LOG_DEBUG=notifications`. These values can be comma-separated +and include wildcards. diff --git a/src/logger/__tests__/logDump.test.ts b/src/logger/__tests__/logDump.test.ts index 135998223..165bfac08 100644 --- a/src/logger/__tests__/logDump.test.ts +++ b/src/logger/__tests__/logDump.test.ts @@ -1,13 +1,14 @@ import {expect, test} from '@jest/globals' -import {ConsoleTransportEntry, LogLevel} from '#/logger' -import {add, getEntries} from '#/logger/logDump' +import {add, ConsoleTransportEntry, getEntries} from '#/logger/logDump' +import {LogContext, LogLevel} from '#/logger/types' test('works', () => { const items: ConsoleTransportEntry[] = [ { id: '1', level: LogLevel.Debug, + context: LogContext.Default, message: 'hello', metadata: {}, timestamp: Date.now(), @@ -15,6 +16,7 @@ test('works', () => { { id: '2', level: LogLevel.Debug, + context: LogContext.Default, message: 'hello', metadata: {}, timestamp: Date.now(), @@ -22,6 +24,7 @@ test('works', () => { { id: '3', level: LogLevel.Debug, + context: LogContext.Default, message: 'hello', metadata: {}, timestamp: Date.now(), diff --git a/src/logger/__tests__/logger.test.ts b/src/logger/__tests__/logger.test.ts index be2391e12..270b0a187 100644 --- a/src/logger/__tests__/logger.test.ts +++ b/src/logger/__tests__/logger.test.ts @@ -2,16 +2,9 @@ import {beforeAll, describe, expect, jest, test} from '@jest/globals' import * as Sentry from '@sentry/react-native' import {nanoid} from 'nanoid/non-secure' -import {Logger, LogLevel, sentryTransport} from '#/logger' - -jest.mock('#/env', () => ({ - /* - * Forces debug mode for tests using the default logger. Most tests create - * their own logger instance. - */ - LOG_LEVEL: 'debug', - LOG_DEBUG: '', -})) +import {Logger} from '#/logger' +import {sentryTransport} from '#/logger/transports/sentry' +import {LogLevel} from '#/logger/types' jest.mock('@sentry/react-native', () => ({ addBreadcrumb: jest.fn(), @@ -26,57 +19,27 @@ beforeAll(() => { describe('general functionality', () => { test('default params', () => { const logger = new Logger() - expect(logger.enabled).toBeFalsy() - expect(logger.level).toEqual(LogLevel.Debug) // mocked above - }) - - test('can override default params', () => { - const logger = new Logger({ - enabled: true, - level: LogLevel.Info, - }) - expect(logger.enabled).toBeTruthy() expect(logger.level).toEqual(LogLevel.Info) }) - test('disabled logger does not report', () => { + test('can override default params', () => { const logger = new Logger({ - enabled: false, level: LogLevel.Debug, }) - - const mockTransport = jest.fn() - - logger.addTransport(mockTransport) - logger.debug('message') - - expect(mockTransport).not.toHaveBeenCalled() + expect(logger.level).toEqual(LogLevel.Debug) }) - test('disablement', () => { + test('contextFilter overrides level', () => { const logger = new Logger({ - enabled: true, - level: LogLevel.Debug, + level: LogLevel.Info, + contextFilter: 'test', }) - - logger.disable() - - const mockTransport = jest.fn() - - logger.addTransport(mockTransport) - logger.debug('message') - - expect(mockTransport).not.toHaveBeenCalled() - }) - - test('passing debug contexts automatically enables debug mode', () => { - const logger = new Logger({debug: 'specific'}) expect(logger.level).toEqual(LogLevel.Debug) }) test('supports extra metadata', () => { const timestamp = Date.now() - const logger = new Logger({enabled: true}) + const logger = new Logger({}) const mockTransport = jest.fn() @@ -87,6 +50,7 @@ describe('general functionality', () => { expect(mockTransport).toHaveBeenCalledWith( LogLevel.Warn, + undefined, 'message', extra, timestamp, @@ -95,7 +59,7 @@ describe('general functionality', () => { test('supports nullish/falsy metadata', () => { const timestamp = Date.now() - const logger = new Logger({enabled: true}) + const logger = new Logger({}) const mockTransport = jest.fn() @@ -105,6 +69,7 @@ describe('general functionality', () => { logger.warn('a', null) expect(mockTransport).toHaveBeenCalledWith( LogLevel.Warn, + undefined, 'a', {}, timestamp, @@ -114,6 +79,7 @@ describe('general functionality', () => { logger.warn('b', false) expect(mockTransport).toHaveBeenCalledWith( LogLevel.Warn, + undefined, 'b', {}, timestamp, @@ -123,6 +89,7 @@ describe('general functionality', () => { logger.warn('c', 0) expect(mockTransport).toHaveBeenCalledWith( LogLevel.Warn, + undefined, 'c', {}, timestamp, @@ -130,7 +97,7 @@ describe('general functionality', () => { remove() - logger.addTransport((level, message, metadata) => { + logger.addTransport((level, context, message, metadata) => { expect(typeof metadata).toEqual('object') }) @@ -143,10 +110,17 @@ describe('general functionality', () => { const timestamp = Date.now() const sentryTimestamp = timestamp / 1000 - sentryTransport(LogLevel.Debug, message, {}, timestamp) + sentryTransport( + LogLevel.Debug, + Logger.Context.Default, + message, + {}, + timestamp, + ) expect(Sentry.addBreadcrumb).toHaveBeenCalledWith({ + category: Logger.Context.Default, message, - data: {}, + data: {context: 'logger'}, type: 'default', level: LogLevel.Debug, timestamp: sentryTimestamp, @@ -154,22 +128,31 @@ describe('general functionality', () => { sentryTransport( LogLevel.Info, + Logger.Context.Default, message, {type: 'info', prop: true}, timestamp, ) expect(Sentry.addBreadcrumb).toHaveBeenCalledWith({ + category: Logger.Context.Default, message, - data: {prop: true}, + data: {prop: true, context: 'logger'}, type: 'info', level: LogLevel.Info, timestamp: sentryTimestamp, }) - sentryTransport(LogLevel.Log, message, {}, timestamp) + sentryTransport( + LogLevel.Log, + Logger.Context.Default, + message, + {}, + timestamp, + ) expect(Sentry.addBreadcrumb).toHaveBeenCalledWith({ + category: Logger.Context.Default, message, - data: {}, + data: {context: 'logger'}, type: 'default', level: 'debug', // Sentry bug, log becomes debug timestamp: sentryTimestamp, @@ -177,14 +160,21 @@ describe('general functionality', () => { jest.runAllTimers() expect(Sentry.captureMessage).toHaveBeenCalledWith(message, { level: 'log', - tags: undefined, - extra: {}, + tags: {category: 'logger'}, + extra: {context: 'logger'}, }) - sentryTransport(LogLevel.Warn, message, {}, timestamp) + sentryTransport( + LogLevel.Warn, + Logger.Context.Default, + message, + {}, + timestamp, + ) expect(Sentry.addBreadcrumb).toHaveBeenCalledWith({ + category: Logger.Context.Default, message, - data: {}, + data: {context: 'logger'}, type: 'default', level: 'warning', timestamp: sentryTimestamp, @@ -192,8 +182,8 @@ describe('general functionality', () => { jest.runAllTimers() expect(Sentry.captureMessage).toHaveBeenCalledWith(message, { level: 'warning', - tags: undefined, - extra: {}, + tags: {category: 'logger'}, + extra: {context: 'logger'}, }) const e = new Error('error') @@ -203,6 +193,7 @@ describe('general functionality', () => { sentryTransport( LogLevel.Error, + Logger.Context.Default, e, { tags, @@ -212,9 +203,13 @@ describe('general functionality', () => { ) expect(Sentry.captureException).toHaveBeenCalledWith(e, { - tags, + tags: { + ...tags, + category: 'logger', + }, extra: { prop: true, + context: 'logger', }, }) }) @@ -226,6 +221,7 @@ describe('general functionality', () => { sentryTransport( LogLevel.Debug, + undefined, message, {error: new Error('foo')}, timestamp, @@ -241,7 +237,7 @@ describe('general functionality', () => { test('add/remove transport', () => { const timestamp = Date.now() - const logger = new Logger({enabled: true}) + const logger = new Logger({}) const mockTransport = jest.fn() const remove = logger.addTransport(mockTransport) @@ -256,6 +252,7 @@ describe('general functionality', () => { expect(mockTransport).toHaveBeenNthCalledWith( 1, LogLevel.Warn, + undefined, 'warn', {}, timestamp, @@ -263,22 +260,43 @@ describe('general functionality', () => { }) }) -describe('debug contexts', () => { - const mockTransport = jest.fn() +describe('create', () => { + test('create', () => { + const mockTransport = jest.fn() + const timestamp = Date.now() + const message = nanoid() + const logger = Logger.create(Logger.Context.Default) + + logger.addTransport(mockTransport) + logger.info(message, {}) + + expect(mockTransport).toHaveBeenCalledWith( + LogLevel.Info, + Logger.Context.Default, + message, + {}, + timestamp, + ) + }) +}) +describe('debug contexts', () => { test('specific', () => { + const mockTransport = jest.fn() const timestamp = Date.now() const message = nanoid() const logger = new Logger({ - enabled: true, - debug: 'specific', + // @ts-ignore + context: 'specific', + level: LogLevel.Debug, }) logger.addTransport(mockTransport) - logger.debug(message, {}, 'specific') + logger.debug(message, {}) expect(mockTransport).toHaveBeenCalledWith( LogLevel.Debug, + 'specific', message, {}, timestamp, @@ -286,18 +304,22 @@ describe('debug contexts', () => { }) test('namespaced', () => { + const mockTransport = jest.fn() const timestamp = Date.now() const message = nanoid() const logger = new Logger({ - enabled: true, - debug: 'namespace*', + // @ts-ignore + context: 'namespace:foo', + contextFilter: 'namespace:*', + level: LogLevel.Debug, }) logger.addTransport(mockTransport) - logger.debug(message, {}, 'namespace') + logger.debug(message, {}) expect(mockTransport).toHaveBeenCalledWith( LogLevel.Debug, + 'namespace:foo', message, {}, timestamp, @@ -305,18 +327,21 @@ describe('debug contexts', () => { }) test('ignores inactive', () => { + const mockTransport = jest.fn() const timestamp = Date.now() const message = nanoid() const logger = new Logger({ - enabled: true, - debug: 'namespace:foo:*', + // @ts-ignore + context: 'namespace:bar:baz', + contextFilter: 'namespace:foo:*', }) logger.addTransport(mockTransport) - logger.debug(message, {}, 'namespace:bar:baz') + logger.debug(message, {}) expect(mockTransport).not.toHaveBeenCalledWith( LogLevel.Debug, + 'namespace:bar:baz', message, {}, timestamp, @@ -328,7 +353,6 @@ describe('supports levels', () => { test('debug', () => { const timestamp = Date.now() const logger = new Logger({ - enabled: true, level: LogLevel.Debug, }) const message = nanoid() @@ -339,6 +363,7 @@ describe('supports levels', () => { logger.debug(message) expect(mockTransport).toHaveBeenCalledWith( LogLevel.Debug, + undefined, message, {}, timestamp, @@ -347,6 +372,7 @@ describe('supports levels', () => { logger.info(message) expect(mockTransport).toHaveBeenCalledWith( LogLevel.Info, + undefined, message, {}, timestamp, @@ -355,6 +381,7 @@ describe('supports levels', () => { logger.warn(message) expect(mockTransport).toHaveBeenCalledWith( LogLevel.Warn, + undefined, message, {}, timestamp, @@ -362,13 +389,18 @@ describe('supports levels', () => { const e = new Error(message) logger.error(e) - expect(mockTransport).toHaveBeenCalledWith(LogLevel.Error, e, {}, timestamp) + expect(mockTransport).toHaveBeenCalledWith( + LogLevel.Error, + undefined, + e, + {}, + timestamp, + ) }) test('info', () => { const timestamp = Date.now() const logger = new Logger({ - enabled: true, level: LogLevel.Info, }) const message = nanoid() @@ -382,6 +414,7 @@ describe('supports levels', () => { logger.info(message) expect(mockTransport).toHaveBeenCalledWith( LogLevel.Info, + undefined, message, {}, timestamp, @@ -391,7 +424,6 @@ describe('supports levels', () => { test('warn', () => { const timestamp = Date.now() const logger = new Logger({ - enabled: true, level: LogLevel.Warn, }) const message = nanoid() @@ -408,6 +440,7 @@ describe('supports levels', () => { logger.warn(message) expect(mockTransport).toHaveBeenCalledWith( LogLevel.Warn, + undefined, message, {}, timestamp, @@ -417,7 +450,6 @@ describe('supports levels', () => { test('error', () => { const timestamp = Date.now() const logger = new Logger({ - enabled: true, level: LogLevel.Error, }) const message = nanoid() @@ -436,6 +468,12 @@ describe('supports levels', () => { const e = new Error('original message') logger.error(e) - expect(mockTransport).toHaveBeenCalledWith(LogLevel.Error, e, {}, timestamp) + expect(mockTransport).toHaveBeenCalledWith( + LogLevel.Error, + undefined, + e, + {}, + timestamp, + ) }) }) diff --git a/src/logger/bitdriftTransport.ts b/src/logger/bitdriftTransport.ts deleted file mode 100644 index 159b86300..000000000 --- a/src/logger/bitdriftTransport.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { - debug as bdDebug, - error as bdError, - info as bdInfo, - warn as bdWarn, -} from '../lib/bitdrift' -import {LogLevel, Transport} from './types' - -export function createBitdriftTransport(): Transport { - const logFunctions = { - [LogLevel.Debug]: bdDebug, - [LogLevel.Info]: bdInfo, - [LogLevel.Log]: bdInfo, - [LogLevel.Warn]: bdWarn, - [LogLevel.Error]: bdError, - } as const - - return (level, message) => { - const log = logFunctions[level] - log('' + message) - } -} diff --git a/src/logger/debugContext.ts b/src/logger/debugContext.ts deleted file mode 100644 index 997120786..000000000 --- a/src/logger/debugContext.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * *Do not import this directly.* Instead, use the shortcut reference `logger.DebugContext`. - * - * Add debug contexts here. Although convention typically calls for enums ito - * be capitalized, for parity with the `LOG_DEBUG` env var, please use all - * lowercase. - */ -export const DebugContext = { - // e.g. composer: 'composer' - session: 'session', - notifications: 'notifications', - convo: 'convo', -} as const diff --git a/src/logger/index.ts b/src/logger/index.ts index 102bccef7..70b7ee8be 100644 --- a/src/logger/index.ts +++ b/src/logger/index.ts @@ -1,213 +1,89 @@ -import format from 'date-fns/format' import {nanoid} from 'nanoid/non-secure' -import {isNetworkError} from '#/lib/strings/errors' -import {DebugContext} from '#/logger/debugContext' import {add} from '#/logger/logDump' -import {Sentry} from '#/logger/sentry' -import * as env from '#/env' -import {createBitdriftTransport} from './bitdriftTransport' -import {Metadata} from './types' -import {ConsoleTransportEntry, LogLevel, Transport} from './types' - -export {LogLevel} -export type {ConsoleTransportEntry, Transport} - -const enabledLogLevels: { - [key in LogLevel]: LogLevel[] -} = { - [LogLevel.Debug]: [ - LogLevel.Debug, - LogLevel.Info, - LogLevel.Log, - LogLevel.Warn, - LogLevel.Error, - ], - [LogLevel.Info]: [LogLevel.Info, LogLevel.Log, LogLevel.Warn, LogLevel.Error], - [LogLevel.Log]: [LogLevel.Log, LogLevel.Warn, LogLevel.Error], - [LogLevel.Warn]: [LogLevel.Warn, LogLevel.Error], - [LogLevel.Error]: [LogLevel.Error], -} - -export function prepareMetadata(metadata: Metadata): Metadata { - return Object.keys(metadata).reduce((acc, key) => { - let value = metadata[key] - if (value instanceof Error) { - value = value.toString() +import {bitdriftTransport} from '#/logger/transports/bitdrift' +import {consoleTransport} from '#/logger/transports/console' +import {sentryTransport} from '#/logger/transports/sentry' +import {LogContext, LogLevel, Metadata, Transport} from '#/logger/types' +import {enabledLogLevels} from '#/logger/util' + +const TRANSPORTS: Transport[] = (function configureTransports() { + switch (process.env.NODE_ENV) { + case 'production': { + return [sentryTransport, bitdriftTransport].filter(Boolean) as Transport[] } - return {...acc, [key]: value} - }, {}) -} - -/** - * Used in dev mode to nicely log to the console - */ -export const consoleTransport: Transport = ( - level, - message, - metadata, - timestamp, -) => { - const extra = Object.keys(metadata).length - ? ' ' + JSON.stringify(prepareMetadata(metadata), null, ' ') - : '' - const log = { - [LogLevel.Debug]: console.debug, - [LogLevel.Info]: console.info, - [LogLevel.Log]: console.log, - [LogLevel.Warn]: console.warn, - [LogLevel.Error]: console.error, - }[level] - - if (message instanceof Error) { - console.info( - `${format(timestamp, 'HH:mm:ss')} ${message.toString()}${extra}`, - ) - log(message) - } else { - log(`${format(timestamp, 'HH:mm:ss')} ${message.toString()}${extra}`) - } -} - -export const sentryTransport: Transport = ( - level, - message, - {type, tags, ...metadata}, - timestamp, -) => { - const meta = prepareMetadata(metadata) - - /** - * 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({ - 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 + case 'test': { + return [] } - - /** - * Send all higher levels with `captureMessage`, with appropriate severity - * level - */ - if (level === 'error' || level === 'warn' || level === 'log') { - const messageLevel = ({ - [LogLevel.Log]: 'log', - [LogLevel.Warn]: 'warning', - [LogLevel.Error]: 'error', - }[level] || 'log') as Sentry.Breadcrumb['level'] - // Defer non-critical messages so they're sent in a batch - queueMessageForSentry(message, { - level: messageLevel, - tags, - extra: meta, - }) + default: { + return [consoleTransport] } - } else { - /** - * It's otherwise an Error and should be reported with captureException - */ - Sentry.captureException(message, { - 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]) - } - } -} - -/** - * Main class. Defaults are provided in the constructor so that subclasses are - * technically possible, if we need to go that route in the future. - */ export class Logger { - LogLevel = LogLevel - DebugContext = DebugContext + static Level = LogLevel + static Context = LogContext - enabled: boolean level: LogLevel - transports: Transport[] = [] + context: LogContext | undefined = undefined + contextFilter: string = '' protected debugContextRegexes: RegExp[] = [] + protected transports: Transport[] = [] + + static create(context?: LogContext) { + const logger = new Logger({ + level: process.env.EXPO_PUBLIC_LOG_LEVEL as LogLevel, + context, + contextFilter: process.env.EXPO_PUBLIC_LOG_DEBUG || '', + }) + for (const transport of TRANSPORTS) { + logger.addTransport(transport) + } + return logger + } constructor({ - enabled = process.env.NODE_ENV !== 'test', - level = env.LOG_LEVEL as LogLevel, - debug = env.LOG_DEBUG || '', + level, + context, + contextFilter, }: { - enabled?: boolean level?: LogLevel - debug?: string + context?: LogContext + contextFilter?: string } = {}) { - this.enabled = enabled !== false - this.level = debug ? LogLevel.Debug : level ?? LogLevel.Info // default to info - this.debugContextRegexes = (debug || '').split(',').map(context => { - return new RegExp(context.replace(/[^\w:*]/, '').replace(/\*/g, '.*')) - }) + this.context = context + this.level = level || LogLevel.Info + this.contextFilter = contextFilter || '' + if (this.contextFilter) { + this.level = LogLevel.Debug + } + this.debugContextRegexes = (this.contextFilter || '') + .split(',') + .map(filter => { + return new RegExp(filter.replace(/[^\w:*-]/, '').replace(/\*/g, '.*')) + }) } - debug(message: string, metadata: Metadata = {}, context?: string) { - if (context && !this.debugContextRegexes.find(reg => reg.test(context))) - return - this.transport(LogLevel.Debug, message, metadata) + debug(message: string, metadata: Metadata = {}) { + this.transport({level: LogLevel.Debug, message, metadata}) } info(message: string, metadata: Metadata = {}) { - this.transport(LogLevel.Info, message, metadata) + this.transport({level: LogLevel.Info, message, metadata}) } log(message: string, metadata: Metadata = {}) { - this.transport(LogLevel.Log, message, metadata) + this.transport({level: LogLevel.Log, message, metadata}) } warn(message: string, metadata: Metadata = {}) { - this.transport(LogLevel.Warn, message, metadata) + this.transport({level: LogLevel.Warn, message, metadata}) } error(error: Error | string, metadata: Metadata = {}) { - this.transport(LogLevel.Error, error, metadata) + this.transport({level: LogLevel.Error, message: error, metadata}) } addTransport(transport: Transport) { @@ -217,20 +93,22 @@ export class Logger { } } - disable() { - this.enabled = false - } - - enable() { - this.enabled = true - } - - protected transport( - level: LogLevel, - message: string | Error, - metadata: Metadata = {}, - ) { - if (!this.enabled) return + protected transport({ + level, + message, + metadata = {}, + }: { + level: LogLevel + message: string | Error + metadata: Metadata + }) { + if ( + level === LogLevel.Debug && + !!this.contextFilter && + !!this.context && + !this.debugContextRegexes.find(reg => reg.test(this.context!)) + ) + return const timestamp = Date.now() const meta = metadata || {} @@ -240,6 +118,7 @@ export class Logger { id: nanoid(), timestamp, level, + context: this.context, message, metadata: meta, }) @@ -247,37 +126,20 @@ export class Logger { if (!enabledLogLevels[this.level].includes(level)) return for (const transport of this.transports) { - transport(level, message, meta, timestamp) + transport(level, this.context, message, meta, timestamp) } } } /** - * Logger instance. See `@/logger/README` for docs. + * Default logger instance. See `@/logger/README` for docs. * * Basic usage: * - * `logger.debug(message[, metadata, debugContext])` + * `logger.debug(message[, metadata])` * `logger.info(message[, metadata])` + * `logger.log(message[, metadata])` * `logger.warn(message[, metadata])` * `logger.error(error[, metadata])` - * `logger.disable()` - * `logger.enable()` */ -export const logger = new Logger() - -if (process.env.NODE_ENV !== 'test') { - logger.addTransport(createBitdriftTransport()) -} - -if (process.env.NODE_ENV !== 'test') { - if (__DEV__) { - logger.addTransport(consoleTransport) - /* - * Comment this out to enable Sentry transport in dev - */ - // logger.addTransport(sentryTransport) - } else { - logger.addTransport(sentryTransport) - } -} +export const logger = Logger.create(Logger.Context.Default) diff --git a/src/logger/logDump.ts b/src/logger/logDump.ts index 563b12aa4..12cf899eb 100644 --- a/src/logger/logDump.ts +++ b/src/logger/logDump.ts @@ -1,4 +1,13 @@ -import type {ConsoleTransportEntry} from '#/logger' +import type {LogContext, LogLevel, Metadata} from '#/logger/types' + +export type ConsoleTransportEntry = { + id: string + timestamp: number + level: LogLevel + context: LogContext | undefined + message: string | Error + metadata: Metadata +} let entries: ConsoleTransportEntry[] = [] diff --git a/src/logger/sentry/index.ts b/src/logger/sentry/lib/index.ts index e771560e7..e771560e7 100644 --- a/src/logger/sentry/index.ts +++ b/src/logger/sentry/lib/index.ts diff --git a/src/logger/sentry/index.web.ts b/src/logger/sentry/lib/index.web.ts index e771560e7..e771560e7 100644 --- a/src/logger/sentry/index.web.ts +++ b/src/logger/sentry/lib/index.web.ts diff --git a/src/lib/sentry.ts b/src/logger/sentry/setup/index.ts index b2695694d..b2695694d 100644 --- a/src/lib/sentry.ts +++ b/src/logger/sentry/setup/index.ts diff --git a/src/logger/transports/bitdrift.ts b/src/logger/transports/bitdrift.ts new file mode 100644 index 000000000..6e335f29c --- /dev/null +++ b/src/logger/transports/bitdrift.ts @@ -0,0 +1,30 @@ +import { + debug as bdDebug, + error as bdError, + info as bdInfo, + warn as bdWarn, +} from '#/lib/bitdrift' +import {LogLevel, Transport} from '#/logger/types' +import {prepareMetadata} from '#/logger/util' + +const logFunctions = { + [LogLevel.Debug]: bdDebug, + [LogLevel.Info]: bdInfo, + [LogLevel.Log]: bdInfo, + [LogLevel.Warn]: bdWarn, + [LogLevel.Error]: bdError, +} as const + +export const bitdriftTransport: Transport = ( + level, + context, + message, + metadata, +) => { + const log = logFunctions[level] + log(message.toString(), { + // match Sentry payload + context, + ...prepareMetadata(metadata), + }) +} diff --git a/src/logger/transports/console.ts b/src/logger/transports/console.ts new file mode 100644 index 000000000..6a687c69b --- /dev/null +++ b/src/logger/transports/console.ts @@ -0,0 +1,90 @@ +import format from 'date-fns/format' + +import {LogLevel, Transport} from '#/logger/types' +import {prepareMetadata} from '#/logger/util' +import {isWeb} from '#/platform/detection' + +/** + * Used in dev mode to nicely log to the console + */ +export const consoleTransport: Transport = ( + level, + context, + message, + metadata, + timestamp, +) => { + const hasMetadata = Object.keys(metadata).length + const colorize = withColor( + { + [LogLevel.Debug]: colors.magenta, + [LogLevel.Info]: colors.blue, + [LogLevel.Log]: colors.green, + [LogLevel.Warn]: colors.yellow, + [LogLevel.Error]: colors.red, + }[level], + ) + + let msg = `${colorize(format(timestamp, 'HH:mm:ss'))}` + if (context) { + msg += ` ${colorize(`(${context})`)}` + } + if (message) { + msg += ` ${message.toString()}` + } + + if (isWeb) { + if (hasMetadata) { + console.groupCollapsed(msg) + console.log(metadata) + console.groupEnd() + } else { + console.log(msg) + } + if (message instanceof Error) { + // for stacktrace + console.error(message) + } + } else { + if (hasMetadata) { + msg += ` ${JSON.stringify(prepareMetadata(metadata), null, 2)}` + } + console.log(msg) + if (message instanceof Error) { + // for stacktrace + console.error(message) + } + } +} + +/** + * Color handling copied from Kleur + * + * @see https://github.com/lukeed/kleur/blob/fa3454483899ddab550d08c18c028e6db1aab0e5/colors.mjs#L13 + */ +const colors: { + [key: string]: [number, number] +} = { + default: [0, 0], + blue: [36, 39], + green: [32, 39], + magenta: [35, 39], + red: [31, 39], + yellow: [33, 39], +} + +function withColor([x, y]: [number, number]) { + const rgx = new RegExp(`\\x1b\\[${y}m`, 'g') + const open = `\x1b[${x}m`, + close = `\x1b[${y}m` + + return function (txt: string) { + if (txt == null) return txt + + return ( + open + + (~('' + txt).indexOf(close) ? txt.replace(rgx, close + open) : txt) + + close + ) + } +} 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]) + } + } +} diff --git a/src/logger/types.ts b/src/logger/types.ts index 252e7373b..517893d29 100644 --- a/src/logger/types.ts +++ b/src/logger/types.ts @@ -1,4 +1,15 @@ -import type {Sentry} from '#/logger/sentry' +/** + * DO NOT IMPORT THIS DIRECTLY + * + * Logger contexts, defined here and used via `Logger.Context.*` static prop. + */ +export enum LogContext { + Default = 'logger', + Session = 'session', + Notifications = 'notifications', + ConversationAgent = 'conversation-agent', + DMsAgent = 'dms-agent', +} export enum LogLevel { Debug = 'debug', @@ -10,6 +21,7 @@ export enum LogLevel { export type Transport = ( level: LogLevel, + context: LogContext | undefined, message: string | Error, metadata: Metadata, timestamp: number, @@ -21,6 +33,11 @@ export type Transport = ( */ export type Metadata = { /** + * Reserved for appending `LogContext` to logging payloads + */ + context?: undefined + + /** * Applied as Sentry breadcrumb types. Defaults to `default`. * * @see https://develop.sentry.dev/sdk/event-payloads/breadcrumbs/#breadcrumb-types @@ -43,27 +60,23 @@ export type Metadata = { * @see https://github.com/getsentry/sentry-javascript/blob/903addf9a1a1534a6cb2ba3143654b918a86f6dd/packages/types/src/misc.ts#L65 */ tags?: { - [key: string]: - | number - | string - | boolean - | bigint - | symbol - | null - | undefined + [key: string]: number | string | boolean | null | undefined } /** * Any additional data, passed through to Sentry as `extra` param on * exceptions, or the `data` param on breadcrumbs. */ - [key: string]: unknown -} & Parameters<typeof Sentry.captureException>[1] - -export type ConsoleTransportEntry = { - id: string - timestamp: number - level: LogLevel - message: string | Error - metadata: Metadata + [key: string]: Serializable | Error | unknown } + +export type Serializable = + | string + | number + | boolean + | null + | undefined + | Serializable[] + | { + [key: string]: Serializable + } diff --git a/src/logger/util.ts b/src/logger/util.ts new file mode 100644 index 000000000..f1e12b164 --- /dev/null +++ b/src/logger/util.ts @@ -0,0 +1,29 @@ +import {LogLevel, Metadata, Serializable} from '#/logger/types' + +export const enabledLogLevels: { + [key in LogLevel]: LogLevel[] +} = { + [LogLevel.Debug]: [ + LogLevel.Debug, + LogLevel.Info, + LogLevel.Log, + LogLevel.Warn, + LogLevel.Error, + ], + [LogLevel.Info]: [LogLevel.Info, LogLevel.Log, LogLevel.Warn, LogLevel.Error], + [LogLevel.Log]: [LogLevel.Log, LogLevel.Warn, LogLevel.Error], + [LogLevel.Warn]: [LogLevel.Warn, LogLevel.Error], + [LogLevel.Error]: [LogLevel.Error], +} + +export function prepareMetadata( + metadata: Metadata, +): Record<string, Serializable> { + return Object.keys(metadata).reduce((acc, key) => { + let value = metadata[key] + if (value instanceof Error) { + value = value.toString() + } + return {...acc, [key]: value} + }, {}) +} diff --git a/src/screens/Deactivated.tsx b/src/screens/Deactivated.tsx index 602012f08..c98967364 100644 --- a/src/screens/Deactivated.tsx +++ b/src/screens/Deactivated.tsx @@ -96,7 +96,7 @@ export function Deactivated() { } logger.error(e, { - context: 'Failed to activate account', + message: 'Failed to activate account', }) } finally { setPending(false) diff --git a/src/screens/ModerationInteractionSettings/index.tsx b/src/screens/ModerationInteractionSettings/index.tsx index 99b29d950..ee72ff9da 100644 --- a/src/screens/ModerationInteractionSettings/index.tsx +++ b/src/screens/ModerationInteractionSettings/index.tsx @@ -102,7 +102,7 @@ function Inner({preferences}: {preferences: UsePreferencesQueryResponse}) { Toast.show(_(msg`Settings saved`)) } catch (e: any) { logger.error(`Failed to save post interaction settings`, { - context: 'ModerationInteractionSettingsScreen', + source: 'ModerationInteractionSettingsScreen', safeMessage: e.message, }) setError(_(msg`Failed to save settings. Please try again.`)) diff --git a/src/screens/Settings/components/DeactivateAccountDialog.tsx b/src/screens/Settings/components/DeactivateAccountDialog.tsx index f5059861c..78c685770 100644 --- a/src/screens/Settings/components/DeactivateAccountDialog.tsx +++ b/src/screens/Settings/components/DeactivateAccountDialog.tsx @@ -61,7 +61,7 @@ function DeactivateAccountDialogInner({ } logger.error(e, { - context: 'Failed to deactivate account', + message: 'Failed to deactivate account', }) } finally { setPending(false) diff --git a/src/state/messages/convo/agent.ts b/src/state/messages/convo/agent.ts index eed44c757..73e75f58d 100644 --- a/src/state/messages/convo/agent.ts +++ b/src/state/messages/convo/agent.ts @@ -10,7 +10,7 @@ import EventEmitter from 'eventemitter3' import {nanoid} from 'nanoid/non-secure' import {networkRetry} from '#/lib/async/retry' -import {logger} from '#/logger' +import {Logger} from '#/logger' import {isNative} from '#/platform/detection' import { ACTIVE_POLL_INTERVAL, @@ -34,6 +34,8 @@ import {MessagesEventBus} from '#/state/messages/events/agent' import {MessagesEventBusError} from '#/state/messages/events/types' import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const' +const logger = Logger.create(Logger.Context.ConversationAgent) + export function isConvoItemMessage( item: ConvoItem, ): item is ConvoItem & {type: 'message'} { @@ -125,7 +127,7 @@ export class Convo { getSnapshot(): ConvoState { if (!this.snapshot) this.snapshot = this.generateSnapshot() - // logger.debug('Convo: snapshotted', {}, logger.DebugContext.convo) + // logger.debug('Convo: snapshotted', {}) return this.snapshot } @@ -375,15 +377,11 @@ export class Convo { break } - logger.debug( - `Convo: dispatch '${action.event}'`, - { - id: this.id, - prev: prevStatus, - next: this.status, - }, - logger.DebugContext.convo, - ) + logger.debug(`Convo: dispatch '${action.event}'`, { + id: this.id, + prev: prevStatus, + next: this.status, + }) this.updateLastActiveTimestamp() this.commit() @@ -471,7 +469,7 @@ export class Convo { this.dispatch({event: ConvoDispatchEvent.Ready}) } } catch (e: any) { - logger.error(e, {context: 'Convo: setup failed'}) + logger.error(e, {message: 'Convo: setup failed'}) this.dispatch({ event: ConvoDispatchEvent.Error, @@ -576,7 +574,7 @@ export class Convo { this.sender = sender || this.sender this.recipients = recipients || this.recipients } catch (e: any) { - logger.error(e, {context: `Convo: failed to refresh convo`}) + logger.error(e, {message: `Convo: failed to refresh convo`}) } } @@ -586,7 +584,7 @@ export class Convo { } | undefined async fetchMessageHistory() { - logger.debug('Convo: fetch message history', {}, logger.DebugContext.convo) + logger.debug('Convo: fetch message history', {}) /* * If oldestRev is null, we've fetched all history. @@ -773,7 +771,7 @@ export class Convo { // Ignore empty messages for now since they have no other purpose atm if (!message.text.trim() && !message.embed) return - logger.debug('Convo: send message', {}, logger.DebugContext.convo) + logger.debug('Convo: send message', {}) const tempId = nanoid() @@ -793,7 +791,6 @@ export class Convo { logger.debug( `Convo: processing messages (${this.pendingMessages.size} remaining)`, {}, - logger.DebugContext.convo, ) const pendingMessage = Array.from(this.pendingMessages.values()).shift() @@ -837,7 +834,7 @@ export class Convo { // continue queue processing await this.processPendingMessages() } catch (e: any) { - logger.error(e, {context: `Convo: failed to send message`}) + logger.error(e, {message: `Convo: failed to send message`}) this.handleSendMessageFailure(e) this.isProcessingPendingMessages = false } @@ -883,7 +880,7 @@ export class Convo { } else { this.pendingMessageFailure = 'unrecoverable' logger.error(e, { - context: `Convo handleSendMessageFailure received unknown error`, + message: `Convo handleSendMessageFailure received unknown error`, }) } @@ -902,7 +899,6 @@ export class Convo { logger.debug( `Convo: batch retrying ${this.pendingMessages.size} pending messages`, {}, - logger.DebugContext.convo, ) try { @@ -937,16 +933,15 @@ export class Convo { logger.debug( `Convo: sent ${this.pendingMessages.size} pending messages`, {}, - logger.DebugContext.convo, ) } catch (e: any) { - logger.error(e, {context: `Convo: failed to batch retry messages`}) + logger.error(e, {message: `Convo: failed to batch retry messages`}) this.handleSendMessageFailure(e) } } async deleteMessage(messageId: string) { - logger.debug('Convo: delete message', {}, logger.DebugContext.convo) + logger.debug('Convo: delete message', {}) this.deletedMessages.add(messageId) this.commit() @@ -962,7 +957,7 @@ export class Convo { ) }) } catch (e: any) { - logger.error(e, {context: `Convo: failed to delete message`}) + logger.error(e, {message: `Convo: failed to delete message`}) this.deletedMessages.delete(messageId) this.commit() throw e diff --git a/src/state/messages/events/agent.ts b/src/state/messages/events/agent.ts index 9244a4fa5..9890eee3f 100644 --- a/src/state/messages/events/agent.ts +++ b/src/state/messages/events/agent.ts @@ -3,7 +3,7 @@ import EventEmitter from 'eventemitter3' import {nanoid} from 'nanoid/non-secure' import {networkRetry} from '#/lib/async/retry' -import {logger} from '#/logger' +import {Logger} from '#/logger' import { BACKGROUND_POLL_INTERVAL, DEFAULT_POLL_INTERVAL, @@ -19,6 +19,7 @@ import { import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const' const LOGGER_CONTEXT = 'MessagesEventBus' +const logger = Logger.create(Logger.Context.DMsAgent) export class MessagesEventBus { private id: string @@ -90,17 +91,17 @@ export class MessagesEventBus { } background() { - logger.debug(`${LOGGER_CONTEXT}: background`, {}, logger.DebugContext.convo) + logger.debug(`${LOGGER_CONTEXT}: background`, {}) this.dispatch({event: MessagesEventBusDispatchEvent.Background}) } suspend() { - logger.debug(`${LOGGER_CONTEXT}: suspend`, {}, logger.DebugContext.convo) + logger.debug(`${LOGGER_CONTEXT}: suspend`, {}) this.dispatch({event: MessagesEventBusDispatchEvent.Suspend}) } resume() { - logger.debug(`${LOGGER_CONTEXT}: resume`, {}, logger.DebugContext.convo) + logger.debug(`${LOGGER_CONTEXT}: resume`, {}) this.dispatch({event: MessagesEventBusDispatchEvent.Resume}) } @@ -222,19 +223,15 @@ export class MessagesEventBus { break } - logger.debug( - `${LOGGER_CONTEXT}: dispatch '${action.event}'`, - { - id: this.id, - prev: prevStatus, - next: this.status, - }, - logger.DebugContext.convo, - ) + logger.debug(`${LOGGER_CONTEXT}: dispatch '${action.event}'`, { + id: this.id, + prev: prevStatus, + next: this.status, + }) } private async init() { - logger.debug(`${LOGGER_CONTEXT}: init`, {}, logger.DebugContext.convo) + logger.debug(`${LOGGER_CONTEXT}: init`, {}) try { const response = await networkRetry(2, () => { @@ -258,7 +255,7 @@ export class MessagesEventBus { this.dispatch({event: MessagesEventBusDispatchEvent.Ready}) } catch (e: any) { logger.error(e, { - context: `${LOGGER_CONTEXT}: init failed`, + message: `${LOGGER_CONTEXT}: init failed`, }) this.dispatch({ @@ -327,7 +324,6 @@ export class MessagesEventBus { // this.requestedPollIntervals.values(), // ), // }, - // logger.DebugContext.convo, // ) try { @@ -372,12 +368,12 @@ export class MessagesEventBus { this.emitter.emit('event', {type: 'logs', logs: batch}) } catch (e: any) { logger.error(e, { - context: `${LOGGER_CONTEXT}: process latest events`, + message: `${LOGGER_CONTEXT}: process latest events`, }) } } } catch (e: any) { - logger.error(e, {context: `${LOGGER_CONTEXT}: poll events failed`}) + logger.error(e, {message: `${LOGGER_CONTEXT}: poll events failed`}) this.dispatch({ event: MessagesEventBusDispatchEvent.Error, diff --git a/src/state/session/agent.ts b/src/state/session/agent.ts index ba0c14c1a..531e285ab 100644 --- a/src/state/session/agent.ts +++ b/src/state/session/agent.ts @@ -166,7 +166,7 @@ export async function createAgentAndCreateAccount( }) } catch (e: any) { logger.error(e, { - context: `session: createAgentAndCreateAccount failed to save personal details and feeds`, + message: `session: createAgentAndCreateAccount failed to save personal details and feeds`, }) } } else { @@ -177,7 +177,7 @@ export async function createAgentAndCreateAccount( // snooze first prompt after signup, defer to next prompt snoozeEmailConfirmationPrompt() } catch (e: any) { - logger.error(e, {context: `session: failed snoozeEmailConfirmationPrompt`}) + logger.error(e, {message: `session: failed snoozeEmailConfirmationPrompt`}) } return agent.prepare(gates, moderation, onSessionChange) |