about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEric Bailey <git@esb.lol>2025-02-28 17:13:49 -0600
committerGitHub <noreply@github.com>2025-02-28 17:13:49 -0600
commit96f4f6359add6a4f2a37df8f17cf3f2f59f0a2a6 (patch)
treeafabc0fe9628a66f1db69f9e07b824db725aa6e1
parent9e59a2eef2f82511a9a9a056dbdb0c62fe2f61c6 (diff)
downloadvoidsky-96f4f6359add6a4f2a37df8f17cf3f2f59f0a2a6.tar.zst
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)
-rw-r--r--src/Navigation.tsx5
-rw-r--r--src/lib/statsig/statsig.tsx35
-rw-r--r--src/logger/index.ts21
-rw-r--r--src/logger/metrics.ts (renamed from src/lib/statsig/events.ts)4
-rw-r--r--src/logger/transports/bitdrift.ts3
-rw-r--r--src/logger/transports/sentry.ts3
-rw-r--r--src/logger/types.ts10
7 files changed, 63 insertions, 18 deletions
diff --git a/src/Navigation.tsx b/src/Navigation.tsx
index cf09406a6..baf99f110 100644
--- a/src/Navigation.tsx
+++ b/src/Navigation.tsx
@@ -32,6 +32,7 @@ import {
 import {RouteParams, State} from '#/lib/routes/types'
 import {attachRouteToLogEvents, logEvent} from '#/lib/statsig/statsig'
 import {bskyTitle} from '#/lib/strings/headings'
+import {logger} from '#/logger'
 import {isNative, isWeb} from '#/platform/detection'
 import {useModalControls} from '#/state/modals'
 import {useUnreadNotifications} from '#/state/queries/notifications/unread'
@@ -729,7 +730,7 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
       linking={LINKING}
       theme={theme}
       onStateChange={() => {
-        logEvent('lake:router:navigate', {
+        logger.metric('router:navigate', {
           from: prevLoggedRouteName.current,
         })
         prevLoggedRouteName.current = getCurrentRouteName()
@@ -738,7 +739,7 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
         attachRouteToLogEvents(getCurrentRouteName)
         logModuleInitTime()
         onReady()
-        logEvent('lake:router:navigate', {})
+        logger.metric('router:navigate', {})
       }}>
       {children}
     </NavigationContainer>
diff --git a/src/lib/statsig/statsig.tsx b/src/lib/statsig/statsig.tsx
index a64c710ea..7f04da126 100644
--- a/src/lib/statsig/statsig.tsx
+++ b/src/lib/statsig/statsig.tsx
@@ -5,12 +5,12 @@ import {Statsig, StatsigProvider} from 'statsig-react-native-expo'
 
 import {BUNDLE_DATE, BUNDLE_IDENTIFIER, IS_TESTFLIGHT} from '#/lib/app-info'
 import {logger} from '#/logger'
+import {MetricEvents} from '#/logger/metrics'
 import {isWeb} from '#/platform/detection'
 import * as persisted from '#/state/persisted'
 import {useSession} from '../../state/session'
 import {timeout} from '../async/timeout'
 import {useNonReactiveCallback} from '../hooks/useNonReactiveCallback'
-import {LogEvents} from './events'
 import {Gate} from './gates'
 
 const SDK_KEY = 'client-SXJakO39w9vIhl3D44u8UupyzFl4oZ2qPIkjwcvuPsV'
@@ -42,7 +42,7 @@ if (isWeb && typeof window !== 'undefined') {
   refUrl = decodeURIComponent(params.get('ref_url') ?? '')
 }
 
-export type {LogEvents}
+export type {MetricEvents as LogEvents}
 
 function createStatsigOptions(prefetchUsers: StatsigUser[]) {
   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,
-  rawMetadata: LogEvents[E] & FlatJSONRecord,
+  rawMetadata: MetricEvents[E] & FlatJSONRecord,
+  options: {
+    /**
+     * Send to our data lake only, not to StatSig
+     */
+    lake?: boolean
+  } = {lake: false},
 ) {
   try {
     const fullMetadata = toStringRecord(rawMetadata)
     fullMetadata.routeName = getCurrentRouteName() ?? '(Uninitialized)'
     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) {
     // A log should never interrupt the calling code, whatever happens.
     logger.error('Failed to log an event', {message: e})
   }
 }
 
-function toStringRecord<E extends keyof LogEvents>(
-  metadata: LogEvents[E] & FlatJSONRecord,
+function toStringRecord<E extends keyof MetricEvents>(
+  metadata: MetricEvents[E] & FlatJSONRecord,
 ): Record<string, string> {
   const record: Record<string, string> = {}
   for (let key in metadata) {
diff --git a/src/logger/index.ts b/src/logger/index.ts
index 410d29bb3..0a50a9d21 100644
--- a/src/logger/index.ts
+++ b/src/logger/index.ts
@@ -1,6 +1,8 @@
 import {nanoid} from 'nanoid/non-secure'
 
+import {logEvent} from '#/lib/statsig/statsig'
 import {add} from '#/logger/logDump'
+import {MetricEvents} from '#/logger/metrics'
 import {bitdriftTransport} from '#/logger/transports/bitdrift'
 import {consoleTransport} from '#/logger/transports/console'
 import {sentryTransport} from '#/logger/transports/sentry'
@@ -89,6 +91,25 @@ export class Logger {
     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) {
     this.transports.push(transport)
     return () => {
diff --git a/src/lib/statsig/events.ts b/src/logger/metrics.ts
index 519e3997e..e3bd93314 100644
--- a/src/lib/statsig/events.ts
+++ b/src/logger/metrics.ts
@@ -1,4 +1,4 @@
-export type LogEvents = {
+export type MetricEvents = {
   // App events
   init: {
     initMs: number
@@ -30,7 +30,7 @@ export type LogEvents = {
     secondsActive: number
   }
   'state:foreground': {}
-  'lake:router:navigate': {
+  'router:navigate': {
     from?: string
   }
   'deepLink:referrerReceived': {
diff --git a/src/logger/transports/bitdrift.ts b/src/logger/transports/bitdrift.ts
index cf125c6e2..a407f9485 100644
--- a/src/logger/transports/bitdrift.ts
+++ b/src/logger/transports/bitdrift.ts
@@ -18,8 +18,7 @@ export const bitdriftTransport: Transport = (
 ) => {
   const log = logFunctions[level]
   log(message.toString(), {
-    // match Sentry payload
-    context,
+    __context__: context,
     ...prepareMetadata(metadata),
   })
 }
diff --git a/src/logger/transports/sentry.ts b/src/logger/transports/sentry.ts
index 890918d67..33dd78ec2 100644
--- a/src/logger/transports/sentry.ts
+++ b/src/logger/transports/sentry.ts
@@ -11,8 +11,7 @@ export const sentryTransport: Transport = (
   timestamp,
 ) => {
   const meta = {
-    // match Bitdrift payload
-    context,
+    __context__: context,
     ...prepareMetadata(metadata),
   }
   let _tags = tags || {}
diff --git a/src/logger/types.ts b/src/logger/types.ts
index 517893d29..9110a8c6f 100644
--- a/src/logger/types.ts
+++ b/src/logger/types.ts
@@ -9,6 +9,12 @@ export enum LogContext {
   Notifications = 'notifications',
   ConversationAgent = 'conversation-agent',
   DMsAgent = 'dms-agent',
+
+  /**
+   * METRIC IS FOR INTERNAL USE ONLY, don't create any other loggers using this
+   * context
+   */
+  Metric = 'metric',
 }
 
 export enum LogLevel {
@@ -33,9 +39,9 @@ export type Transport = (
  */
 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`.