Logger metrics (#7867)

* Adjust datalake abstraction

(cherry picked from commit 8ba6a8d45b1bd5698afbd06d9e858a91789f0ea6)

* Just be really really specific

(cherry picked from commit 920198959659329a7f7f7282a1293aaad198d8e3)

* Add metric method to logger, replace datalake calls with new method

(cherry picked from commit 7a026bbeae75514b64f928d7ff59707c518fd5e5)

* Clarify types

(cherry picked from commit 422b150deb158a70ef37e8a456d91bf26cd0b1bc)
This commit is contained in:
Eric Bailey
2025-02-28 17:13:49 -06:00
committed by GitHub
parent 9e59a2eef2
commit 96f4f6359a
7 changed files with 63 additions and 18 deletions
+3 -2
View File
@@ -32,6 +32,7 @@ import {
import {RouteParams, State} from '#/lib/routes/types' import {RouteParams, State} from '#/lib/routes/types'
import {attachRouteToLogEvents, logEvent} from '#/lib/statsig/statsig' import {attachRouteToLogEvents, logEvent} from '#/lib/statsig/statsig'
import {bskyTitle} from '#/lib/strings/headings' import {bskyTitle} from '#/lib/strings/headings'
import {logger} from '#/logger'
import {isNative, isWeb} from '#/platform/detection' import {isNative, isWeb} from '#/platform/detection'
import {useModalControls} from '#/state/modals' import {useModalControls} from '#/state/modals'
import {useUnreadNotifications} from '#/state/queries/notifications/unread' import {useUnreadNotifications} from '#/state/queries/notifications/unread'
@@ -729,7 +730,7 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
linking={LINKING} linking={LINKING}
theme={theme} theme={theme}
onStateChange={() => { onStateChange={() => {
logEvent('lake:router:navigate', { logger.metric('router:navigate', {
from: prevLoggedRouteName.current, from: prevLoggedRouteName.current,
}) })
prevLoggedRouteName.current = getCurrentRouteName() prevLoggedRouteName.current = getCurrentRouteName()
@@ -738,7 +739,7 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
attachRouteToLogEvents(getCurrentRouteName) attachRouteToLogEvents(getCurrentRouteName)
logModuleInitTime() logModuleInitTime()
onReady() onReady()
logEvent('lake:router:navigate', {}) logger.metric('router:navigate', {})
}}> }}>
{children} {children}
</NavigationContainer> </NavigationContainer>
+27 -8
View File
@@ -5,12 +5,12 @@ import {Statsig, StatsigProvider} from 'statsig-react-native-expo'
import {BUNDLE_DATE, BUNDLE_IDENTIFIER, IS_TESTFLIGHT} from '#/lib/app-info' import {BUNDLE_DATE, BUNDLE_IDENTIFIER, IS_TESTFLIGHT} from '#/lib/app-info'
import {logger} from '#/logger' import {logger} from '#/logger'
import {MetricEvents} from '#/logger/metrics'
import {isWeb} from '#/platform/detection' import {isWeb} from '#/platform/detection'
import * as persisted from '#/state/persisted' import * as persisted from '#/state/persisted'
import {useSession} from '../../state/session' import {useSession} from '../../state/session'
import {timeout} from '../async/timeout' import {timeout} from '../async/timeout'
import {useNonReactiveCallback} from '../hooks/useNonReactiveCallback' import {useNonReactiveCallback} from '../hooks/useNonReactiveCallback'
import {LogEvents} from './events'
import {Gate} from './gates' import {Gate} from './gates'
const SDK_KEY = 'client-SXJakO39w9vIhl3D44u8UupyzFl4oZ2qPIkjwcvuPsV' const SDK_KEY = 'client-SXJakO39w9vIhl3D44u8UupyzFl4oZ2qPIkjwcvuPsV'
@@ -42,7 +42,7 @@ if (isWeb && typeof window !== 'undefined') {
refUrl = decodeURIComponent(params.get('ref_url') ?? '') refUrl = decodeURIComponent(params.get('ref_url') ?? '')
} }
export type {LogEvents} export type {MetricEvents as LogEvents}
function createStatsigOptions(prefetchUsers: StatsigUser[]) { function createStatsigOptions(prefetchUsers: StatsigUser[]) {
return { return {
@@ -91,25 +91,44 @@ export function toClout(n: number | null | undefined): number | undefined {
} }
} }
export function logEvent<E extends keyof LogEvents>( /**
* @deprecated use `logger.metric()` instead
*/
export function logEvent<E extends keyof MetricEvents>(
eventName: E & string, eventName: E & string,
rawMetadata: LogEvents[E] & FlatJSONRecord, rawMetadata: MetricEvents[E] & FlatJSONRecord,
options: {
/**
* Send to our data lake only, not to StatSig
*/
lake?: boolean
} = {lake: false},
) { ) {
try { try {
const fullMetadata = toStringRecord(rawMetadata) const fullMetadata = toStringRecord(rawMetadata)
fullMetadata.routeName = getCurrentRouteName() ?? '(Uninitialized)' fullMetadata.routeName = getCurrentRouteName() ?? '(Uninitialized)'
if (Statsig.initializeCalled()) { if (Statsig.initializeCalled()) {
Statsig.logEvent(eventName, null, fullMetadata) let ev: string = eventName
if (options.lake) {
ev = `lake:${ev}`
}
Statsig.logEvent(ev, null, fullMetadata)
}
/**
* All datalake events should be sent using `logger.metric`, and we don't
* want to double-emit logs to other transports.
*/
if (!options.lake) {
logger.info(eventName, fullMetadata)
} }
logger.info(eventName, fullMetadata)
} catch (e) { } catch (e) {
// A log should never interrupt the calling code, whatever happens. // A log should never interrupt the calling code, whatever happens.
logger.error('Failed to log an event', {message: e}) logger.error('Failed to log an event', {message: e})
} }
} }
function toStringRecord<E extends keyof LogEvents>( function toStringRecord<E extends keyof MetricEvents>(
metadata: LogEvents[E] & FlatJSONRecord, metadata: MetricEvents[E] & FlatJSONRecord,
): Record<string, string> { ): Record<string, string> {
const record: Record<string, string> = {} const record: Record<string, string> = {}
for (let key in metadata) { for (let key in metadata) {
+21
View File
@@ -1,6 +1,8 @@
import {nanoid} from 'nanoid/non-secure' import {nanoid} from 'nanoid/non-secure'
import {logEvent} from '#/lib/statsig/statsig'
import {add} from '#/logger/logDump' import {add} from '#/logger/logDump'
import {MetricEvents} from '#/logger/metrics'
import {bitdriftTransport} from '#/logger/transports/bitdrift' import {bitdriftTransport} from '#/logger/transports/bitdrift'
import {consoleTransport} from '#/logger/transports/console' import {consoleTransport} from '#/logger/transports/console'
import {sentryTransport} from '#/logger/transports/sentry' import {sentryTransport} from '#/logger/transports/sentry'
@@ -89,6 +91,25 @@ export class Logger {
this.transport({level: LogLevel.Error, message: error, metadata}) this.transport({level: LogLevel.Error, message: error, metadata})
} }
metric<E extends keyof MetricEvents>(
event: E & string,
metadata: MetricEvents[E],
options: {
/**
* Optionally also send to StatSig
*/
statsig?: boolean
} = {statsig: false},
) {
logEvent(event, metadata, {
lake: !options.statsig,
})
for (const transport of this.transports) {
transport(LogLevel.Info, LogContext.Metric, event, metadata, Date.now())
}
}
addTransport(transport: Transport) { addTransport(transport: Transport) {
this.transports.push(transport) this.transports.push(transport)
return () => { return () => {
@@ -1,4 +1,4 @@
export type LogEvents = { export type MetricEvents = {
// App events // App events
init: { init: {
initMs: number initMs: number
@@ -30,7 +30,7 @@ export type LogEvents = {
secondsActive: number secondsActive: number
} }
'state:foreground': {} 'state:foreground': {}
'lake:router:navigate': { 'router:navigate': {
from?: string from?: string
} }
'deepLink:referrerReceived': { 'deepLink:referrerReceived': {
+1 -2
View File
@@ -18,8 +18,7 @@ export const bitdriftTransport: Transport = (
) => { ) => {
const log = logFunctions[level] const log = logFunctions[level]
log(message.toString(), { log(message.toString(), {
// match Sentry payload __context__: context,
context,
...prepareMetadata(metadata), ...prepareMetadata(metadata),
}) })
} }
+1 -2
View File
@@ -11,8 +11,7 @@ export const sentryTransport: Transport = (
timestamp, timestamp,
) => { ) => {
const meta = { const meta = {
// match Bitdrift payload __context__: context,
context,
...prepareMetadata(metadata), ...prepareMetadata(metadata),
} }
let _tags = tags || {} let _tags = tags || {}
+8 -2
View File
@@ -9,6 +9,12 @@ export enum LogContext {
Notifications = 'notifications', Notifications = 'notifications',
ConversationAgent = 'conversation-agent', ConversationAgent = 'conversation-agent',
DMsAgent = 'dms-agent', DMsAgent = 'dms-agent',
/**
* METRIC IS FOR INTERNAL USE ONLY, don't create any other loggers using this
* context
*/
Metric = 'metric',
} }
export enum LogLevel { export enum LogLevel {
@@ -33,9 +39,9 @@ export type Transport = (
*/ */
export type Metadata = { export type Metadata = {
/** /**
* Reserved for appending `LogContext` to logging payloads * Reserved for appending `LogContext` in logging payloads
*/ */
context?: undefined __context__?: undefined
/** /**
* Applied as Sentry breadcrumb types. Defaults to `default`. * Applied as Sentry breadcrumb types. Defaults to `default`.