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

This commit is contained in:
Eric Bailey
2026-01-21 14:36:25 -06:00
parent 2d25f1caee
commit eeb3e90b1a
3 changed files with 53 additions and 40 deletions
+3
View File
@@ -131,6 +131,8 @@ export function AnalyticsContext({
combinedMetadata.base.sessionId = sessionId
combinedMetadata.geolocation = geolocation
const context: AnalyticsBaseContextType = {
...parentContext,
// TODO trim down metadata
logger: createLogger(Logger.Context.Default, combinedMetadata),
metadata: combinedMetadata,
metric: (event, payload, extraMetadata) => {
@@ -196,6 +198,7 @@ export function useAnalyticsBase() {
export function useAnalytics() {
const ctx = useContext(Context)
if (!('feature' in ctx) || !('Features' in ctx)) {
console.log(ctx)
throw new Error(
'useAnalytics must be used within an AnalyticsFeaturesContext',
)
+11
View File
@@ -1,5 +1,6 @@
import {onAppStateChange} from '#/lib/appState'
import {isNetworkError} from '#/lib/strings/errors'
import {Logger} from '#/logger'
import {Sentry} from '#/logger/sentry/lib'
import * as env from '#/env'
@@ -13,6 +14,7 @@ type Event<M extends Record<string, any>> = {
}
const TRACKING_ENDPOINT = env.METRICS_API_HOST + '/t'
const logger = Logger.create(Logger.Context.Metric, {})
export class MetricsClient<M extends Record<string, any>> {
private started: boolean = false
@@ -49,6 +51,11 @@ export class MetricsClient<M extends Record<string, any>> {
metadata,
})
logger.debug(`event: ${event as string}`, {
payload,
metadata,
})
if (this.queue.length > 100) {
this.flush()
}
@@ -62,6 +69,10 @@ export class MetricsClient<M extends Record<string, any>> {
}
private async sendBatch(events: Event<M>[], isRetry: boolean = false) {
logger.debug(`sendBatch: ${events.length}`, {
isRetry,
})
try {
const body = JSON.stringify(events)
if (env.IS_WEB && 'navigator' in globalThis && navigator.sendBeacon) {
+39 -40
View File
@@ -11,7 +11,6 @@ import {type AppBskyFeedDefs} from '@atproto/api'
import throttle from 'lodash.throttle'
import {PROD_FEEDS, STAGING_FEEDS} from '#/lib/constants'
import {Logger} from '#/logger'
import {
type FeedSourceFeedInfo,
type FeedSourceInfo,
@@ -22,6 +21,7 @@ import {
type FeedPostSliceItem,
} from '#/state/queries/post-feed'
import {getItemsForFeedback} from '#/view/com/posts/PostFeed'
import {useAnalytics} from '#/analytics'
import {useAgent} from './session'
export const FEEDBACK_FEEDS = [...PROD_FEEDS, ...STAGING_FEEDS]
@@ -42,8 +42,6 @@ export const THIRD_PARTY_ALLOWED_INTERACTIONS = new Set<
'app.bsky.feed.defs#interactionSeen',
])
const logger = Logger.create(Logger.Context.FeedFeedback)
export type StateContext = {
enabled: boolean
onItemSeen: (item: any) => void
@@ -65,6 +63,8 @@ export function useFeedFeedback(
feedSourceInfo: FeedSourceInfo | undefined,
hasSession: boolean,
) {
const ax = useAnalytics()
const logger = ax.logger.useContext(ax.logger.Context.FeedFeedback)
const agent = useAgent()
const feed =
@@ -85,22 +85,55 @@ export function useFeedFeedback(
WeakSet<FeedPostSliceItem | AppBskyFeedDefs.Interaction>
>(new WeakSet())
const flushEvents = useCallback(
(stats: AggregatedStats | null, feedDescriptor: string) => {
if (stats === null) {
return
}
if (stats.clickthroughCount > 0) {
ax.metric('feed:clickthrough', {
count: stats.clickthroughCount,
feed: feedDescriptor,
})
stats.clickthroughCount = 0
}
if (stats.engagedCount > 0) {
ax.metric('feed:engaged', {
count: stats.engagedCount,
feed: feedDescriptor,
})
stats.engagedCount = 0
}
if (stats.seenCount > 0) {
ax.metric('feed:seen', {
count: stats.seenCount,
feed: feedDescriptor,
})
stats.seenCount = 0
}
},
[ax],
)
const aggregatedStats = useRef<AggregatedStats | null>(null)
const throttledFlushAggregatedStats = useMemo(
() =>
throttle(
() =>
flushToStatsig(
flushEvents(
aggregatedStats.current,
feed?.feedDescriptor ?? 'unknown',
),
45e3,
5e3,
{
leading: true, // The outer call is already throttled somewhat.
trailing: true,
},
),
[feed?.feedDescriptor],
[feed?.feedDescriptor, flushEvents],
)
const sendToFeedNoDelay = useCallback(() => {
@@ -299,37 +332,3 @@ function sendOrAggregateInteractionsForStats(
}
}
}
function flushToStatsig(stats: AggregatedStats | null, feedDescriptor: string) {
if (stats === null) {
return
}
if (stats.clickthroughCount > 0) {
logger.metric('feed:clickthrough', {
count: stats.clickthroughCount,
feed: feedDescriptor,
})
stats.clickthroughCount = 0
}
if (stats.engagedCount > 0) {
logger.metric('feed:engaged', {
count: stats.engagedCount,
feed: feedDescriptor,
})
stats.engagedCount = 0
}
if (stats.seenCount > 0) {
logger.metric(
'feed:seen',
{
count: stats.seenCount,
feed: feedDescriptor,
},
{statsig: false},
)
stats.seenCount = 0
}
}