Files
bsky-social-app/src/logger/index.tsx
T
Eric Bailey 25a1ed1209 [APP-1782] Analytics migration (#9734)
* WIP

* Clean up growthbook code, integrate into init and sessions

* Move everything out of React

* Add metrics client

* Move to separate file

* Shared metadata cache

* Ensure we update metadata when session ID changes

* Ensure userMetadata is cleared when logging out

* WIP revamp

* Integrate feature gates into analytics context

* Clean up old code

* Fix useMeta util

* Some comments and cleanup

* Add logger to base analytics context

* Refactor current route handling

* Rip out LogEvent from navigation

* Update tracking endpoint

* Migrate toClout

* Clear out statsig client

* Add todo, reset logger readme

* Ope fix statsig noop

* Refactor logging in feed-feedback, add debug logging to metrics client

* Remove LogEvents alias for Metrics

* Prefer root package export

* Remove Metrics alias from logger

* [APP-1782] Migrate to new analytics APIs (#9735)

* Migrate logEvent to useAnalytics

* Migrate logger.metric to useAnalytics

* Migrate tricky spot, fix types

* Migrate remaining tricky spot

* Missed one

* Remove metric() from logger

* Migrate useGate to useAnalytics

* Remove all other StatSig mentions

* Update event payload

* Update logger tests

* Mock expo method

* Fix session ID bug

* Add session ID test

* Add test for metrics client

* Clarify intent

* Clean up core analytics file

* Clean up the call once utils

* Fix TODO

* Fix TODO

* Fix TODO

* Fix TODO

* Fix TODO

* Remove debug code

* Fix navigation context

* OK nav context is not working, todo

* Checkpoint: works but feels hacky

* Fix navigation context issue

* Improve feature API

* Improve metric logging

* Update logger tests
2026-01-22 15:33:45 -06:00

159 lines
3.8 KiB
TypeScript

import {nanoid} from 'nanoid/non-secure'
import {add} from '#/logger/logDump'
import {consoleTransport} from '#/logger/transports/console'
import {sentryTransport} from '#/logger/transports/sentry'
import {
LogContext,
LogLevel,
type Metadata,
type Transport,
} from '#/logger/types'
import {enabledLogLevels} from '#/logger/util'
import {ENV} from '#/env'
const TRANSPORTS: Transport[] = (function configureTransports() {
switch (ENV) {
case 'production': {
return [sentryTransport].filter(Boolean)
}
case 'test': {
return []
}
default: {
return [consoleTransport]
}
}
})()
export class Logger {
static Level = LogLevel
static Context = LogContext
level: LogLevel
context: LogContext | undefined = undefined
contextFilter: string = ''
ambientMetadata: Record<string, unknown> = {}
protected debugContextRegexes: RegExp[] = []
protected transports: Transport[] = []
static create(context?: LogContext, metadata: Record<string, unknown> = {}) {
const logger = new Logger({
level: process.env.EXPO_PUBLIC_LOG_LEVEL as LogLevel,
context,
contextFilter: process.env.EXPO_PUBLIC_LOG_DEBUG || '',
metadata,
})
for (const transport of TRANSPORTS) {
logger.addTransport(transport)
}
return logger
}
constructor({
level,
context,
contextFilter,
metadata: ambientMetadata = {},
}: {
level?: LogLevel
context?: LogContext
contextFilter?: string
metadata?: Record<string, unknown>
} = {}) {
this.context = context
this.level = level || LogLevel.Info
this.contextFilter = contextFilter || ''
this.ambientMetadata = ambientMetadata
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({level: LogLevel.Info, message, metadata})
}
log(message: string, metadata: Metadata = {}) {
this.transport({level: LogLevel.Log, message, metadata})
}
warn(message: string, metadata: Metadata = {}) {
this.transport({level: LogLevel.Warn, message, metadata})
}
error(error: Error | string, metadata: Metadata = {}) {
this.transport({level: LogLevel.Error, message: error, metadata})
}
addTransport(transport: Transport) {
this.transports.push(transport)
return () => {
this.transports.splice(this.transports.indexOf(transport), 1)
}
}
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 = {
__metadata__: this.ambientMetadata,
...metadata,
}
// send every log to syslog
add({
id: nanoid(),
timestamp,
level,
context: this.context,
message,
metadata: meta,
})
if (!enabledLogLevels[this.level].includes(level)) return
for (const transport of this.transports) {
transport(level, this.context, message, meta, timestamp)
}
}
}
/**
* Default logger instance. See `@/logger/README` for docs.
*
* Basic usage:
*
* `logger.debug(message[, metadata])`
* `logger.info(message[, metadata])`
* `logger.log(message[, metadata])`
* `logger.warn(message[, metadata])`
* `logger.error(error[, metadata])`
*/
export const logger = Logger.create(Logger.Context.Default)