Compare commits

...

26 Commits

Author SHA1 Message Date
Eric Bailey 1617eb5d66 Colocate bitdrift in logger, use logger for transport (#7859)
* Colocate bitdrift in logger, use logger for transport

* Fix mocks, format

* Ok I guess mocks dir doesn't work
2025-02-28 12:09:54 -06:00
Eric Bailey f65866c641 Update tests 2025-02-27 10:45:55 -06:00
Eric Bailey ba07c0deb1 Reserve context prop on metadata and include in transports 2025-02-27 10:33:06 -06:00
Eric Bailey 5d4b6d6ff1 Correct transports 2025-02-26 22:10:00 -06:00
Eric Bailey 4b343e513e Clean up metadata before passing to Bitdrift 2025-02-26 21:19:11 -06:00
Eric Bailey b215579c1a Fix up debugging 2025-02-26 20:41:48 -06:00
Eric Bailey ce3d6685ac Clean up context, use it, pass metadata through to Bitdrift 2025-02-26 20:23:29 -06:00
Eric Bailey f7e9ddf787 Revert Sentry changes 2025-02-26 20:12:36 -06:00
Eric Bailey 71395ede31 No positional args 2025-02-26 20:09:22 -06:00
Eric Bailey 71a890efe0 Clean up tests 2025-02-26 20:09:22 -06:00
Eric Bailey 59bbba3ec8 Update tests 2025-02-26 20:09:22 -06:00
Eric Bailey 6c6e217fa9 Format 2025-02-26 20:09:22 -06:00
Eric Bailey f3312fdd11 Docs 2025-02-26 20:09:22 -06:00
Eric Bailey 66280fd731 Clean up types 2025-02-26 20:09:22 -06:00
Eric Bailey df50f7d6a3 Enable Sentry on web only 2025-02-26 20:09:21 -06:00
Eric Bailey 1d859325c7 Ensure bitdrift is enabled in dev 2025-02-26 20:09:21 -06:00
Eric Bailey 3d8804ddaf Pass through context 2025-02-26 20:09:21 -06:00
Eric Bailey 6ee0d3ca30 Reduce reuse 2025-02-26 20:09:21 -06:00
Eric Bailey ea6d2bec7a Move colorizing out of console transport body 2025-02-26 20:09:21 -06:00
Eric Bailey dad50ce731 Remove manual prefix 2025-02-26 20:09:21 -06:00
Eric Bailey 46f56278c3 Remove temp default context 2025-02-26 20:09:21 -06:00
Eric Bailey 7ee247c472 Re-org, add colors and grouping to console logging 2025-02-26 20:09:21 -06:00
Eric Bailey 11109c3854 Migrate usages of debug context 2025-02-26 20:09:21 -06:00
Eric Bailey 9f7a4166aa Fix imports, simplify transports config 2025-02-26 20:09:21 -06:00
Eric Bailey 5df4ded61e Refactor context and filtering 2025-02-26 20:09:21 -06:00
Eric Bailey 4f3dbd2102 Remove enablement 2025-02-26 20:09:21 -06:00
33 changed files with 580 additions and 519 deletions
+1 -1
View File
@@ -119,5 +119,5 @@ jest.mock('statsig-react-native-expo', () => ({
},
}))
jest.mock('../src/lib/bitdrift', () => ({}))
jest.mock('../src/logger/bitdrift/lib', () => ({}))
jest.mock('../src/lib/statsig/statsig', () => ({}))
+2 -2
View File
@@ -1,6 +1,6 @@
import 'react-native-url-polyfill/auto'
import '#/lib/sentry' // must be near top
import '#/lib/bitdrift' // must be near top
import '#/logger/sentry/setup'
import '#/logger/bitdrift/setup'
import '#/view/icons'
import React, {useEffect, useState} from 'react'
+1 -1
View File
@@ -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'
+1 -1
View File
@@ -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')
}
},
@@ -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(
+7 -14
View File
@@ -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()
+7 -9
View File
@@ -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})
}
+1 -6
View File
@@ -4,7 +4,6 @@ import {AppState, AppStateStatus} from 'react-native'
import {Statsig, StatsigProvider} from 'statsig-react-native-expo'
import {BUNDLE_DATE, BUNDLE_IDENTIFIER, IS_TESTFLIGHT} from '#/lib/app-info'
import * as bitdrift from '#/lib/bitdrift'
import {logger} from '#/logger'
import {isWeb} from '#/platform/detection'
import * as persisted from '#/state/persisted'
@@ -102,11 +101,7 @@ export function logEvent<E extends keyof LogEvents>(
if (Statsig.initializeCalled()) {
Statsig.logEvent(eventName, null, fullMetadata)
}
// Intentionally bypass the logger abstraction to log rich objects.
console.groupCollapsed(eventName)
console.log(fullMetadata)
console.groupEnd()
bitdrift.info(eventName, fullMetadata)
logger.info(eventName, fullMetadata)
} catch (e) {
// A log should never interrupt the calling code, whatever happens.
logger.error('Failed to log an event', {message: e})
+24 -78
View File
@@ -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])
```
// or, optionally create new instance with custom context
// const logger = Logger.create(Logger.Context.Notifications)
#### Modes
// for dev-only logs
logger.debug(message, {})
The "modes" referred to here are inferred from `process.env.NODE_ENV`,
which matches how React Native sets the `__DEV__` global.
// for production breadcrumbs
logger.info(message, {})
#### Log Levels
// seldom used, prefer `info`
logger.log(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 non-error issues to look into, seldom used, prefer `error`
logger.warn(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`.
// for known errors without an exception, use a string
logger.error(`known error`, {})
## Usage
```typescript
import { logger } from '#/logger';
```
### `logger.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`
#### Log Levels
Warnings will be sent to Sentry as a separate Issue with level `warning`, as
well as as breadcrumbs, with a severity level of `warning`
Log level defaults to `info`. You can set this via the `EXPO_PUBLIC_LOG_LEVEL`
env var in `.env.local`.
### `logger.log`
#### Filtering debugs by context
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`.
For example, a debug log like this:
```typescript
logger.debug(message, {}, logger.DebugContext.composer);
```
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.
+5 -2
View File
@@ -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(),
+123 -85
View File
@@ -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
expect(logger.level).toEqual(LogLevel.Info)
})
test('can override default params', () => {
const logger = new Logger({
enabled: true,
level: LogLevel.Debug,
})
expect(logger.level).toEqual(LogLevel.Debug)
})
test('contextFilter overrides level', () => {
const logger = new Logger({
level: LogLevel.Info,
contextFilter: 'test',
})
expect(logger.enabled).toBeTruthy()
expect(logger.level).toEqual(LogLevel.Info)
})
test('disabled logger does not report', () => {
const logger = new Logger({
enabled: false,
level: LogLevel.Debug,
})
const mockTransport = jest.fn()
logger.addTransport(mockTransport)
logger.debug('message')
expect(mockTransport).not.toHaveBeenCalled()
})
test('disablement', () => {
const logger = new Logger({
enabled: true,
level: LogLevel.Debug,
})
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)
expect(Sentry.addBreadcrumb).toHaveBeenCalledWith({
sentryTransport(
LogLevel.Debug,
Logger.Context.Default,
message,
data: {},
{},
timestamp,
)
expect(Sentry.addBreadcrumb).toHaveBeenCalledWith({
category: Logger.Context.Default,
message,
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)
expect(Sentry.addBreadcrumb).toHaveBeenCalledWith({
sentryTransport(
LogLevel.Log,
Logger.Context.Default,
message,
data: {},
{},
timestamp,
)
expect(Sentry.addBreadcrumb).toHaveBeenCalledWith({
category: Logger.Context.Default,
message,
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)
expect(Sentry.addBreadcrumb).toHaveBeenCalledWith({
sentryTransport(
LogLevel.Warn,
Logger.Context.Default,
message,
data: {},
{},
timestamp,
)
expect(Sentry.addBreadcrumb).toHaveBeenCalledWith({
category: Logger.Context.Default,
message,
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,
)
})
})
+1
View File
@@ -0,0 +1 @@
export {debug, error, info, warn} from '@bitdrift/react-native'
@@ -1,9 +1,7 @@
import {init, SessionStrategy} from '@bitdrift/react-native'
import {Statsig} from 'statsig-react-native-expo'
import {initPromise} from './statsig/statsig'
export {debug, error, info, warn} from '@bitdrift/react-native'
import {initPromise} from '#/lib/statsig/statsig'
const BITDRIFT_API_KEY = process.env.BITDRIFT_API_KEY
-22
View File
@@ -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)
}
}
-13
View File
@@ -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
+81 -216
View File
@@ -1,213 +1,92 @@
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'
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'
import {isNative} from '#/platform/detection'
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()
const TRANSPORTS: Transport[] = (function configureTransports() {
switch (process.env.NODE_ENV) {
case 'production': {
return [sentryTransport, isNative && 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,
})
}
} 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])
default: {
return [consoleTransport]
}
}
}
})()
/**
* 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[] = []
constructor({
enabled = process.env.NODE_ENV !== 'test',
level = env.LOG_LEVEL as LogLevel,
debug = env.LOG_DEBUG || '',
}: {
enabled?: boolean
level?: LogLevel
debug?: 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, '.*'))
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
}
debug(message: string, metadata: Metadata = {}, context?: string) {
if (context && !this.debugContextRegexes.find(reg => reg.test(context)))
return
this.transport(LogLevel.Debug, message, metadata)
constructor({
level,
context,
contextFilter,
}: {
level?: LogLevel
context?: LogContext
contextFilter?: string
} = {}) {
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 = {}) {
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 +96,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 +121,7 @@ export class Logger {
id: nanoid(),
timestamp,
level,
context: this.context,
message,
metadata: meta,
})
@@ -247,37 +129,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)
+10 -1
View File
@@ -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[] = []
+25
View File
@@ -0,0 +1,25 @@
import {debug, error, info, warn} from '#/logger/bitdrift/lib'
import {LogLevel, Transport} from '#/logger/types'
import {prepareMetadata} from '#/logger/util'
const logFunctions = {
[LogLevel.Debug]: debug,
[LogLevel.Info]: info,
[LogLevel.Log]: info,
[LogLevel.Warn]: warn,
[LogLevel.Error]: error,
} as const
export const bitdriftTransport: Transport = (
level,
context,
message,
metadata,
) => {
const log = logFunctions[level]
log(message.toString(), {
// match Sentry payload
context,
...prepareMetadata(metadata),
})
}
+90
View File
@@ -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
)
}
}
+102
View File
@@ -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])
}
}
}
+31 -18
View File
@@ -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,
@@ -20,6 +32,11 @@ export type Transport = (
* `captureException` parameter, `CaptureContext`.
*/
export type Metadata = {
/**
* Reserved for appending `LogContext` to logging payloads
*/
context?: undefined
/**
* Applied as Sentry breadcrumb types. Defaults to `default`.
*
@@ -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
}
+29
View File
@@ -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}
}, {})
}
+1 -1
View File
@@ -96,7 +96,7 @@ export function Deactivated() {
}
logger.error(e, {
context: 'Failed to activate account',
message: 'Failed to activate account',
})
} finally {
setPending(false)
@@ -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.`))
@@ -61,7 +61,7 @@ function DeactivateAccountDialogInner({
}
logger.error(e, {
context: 'Failed to deactivate account',
message: 'Failed to deactivate account',
})
} finally {
setPending(false)
+18 -23
View File
@@ -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
+14 -18
View File
@@ -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,
+2 -2
View File
@@ -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)