[Statsig] Sample noisy events (#4288)

* Sample state:background and state:foreground

* Sample feed events

* Add DEV protection against forgetting to add events to the list
This commit is contained in:
dan 2024-05-30 16:32:59 +01:00 committed by GitHub
parent 9431201026
commit d6275e98c2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 41 additions and 21 deletions

View file

@ -20,10 +20,10 @@ export type LogEvents = {
context: 'StartOnboarding' | 'AfterOnboarding' | 'Login' context: 'StartOnboarding' | 'AfterOnboarding' | 'Login'
status: 'granted' | 'denied' | 'undetermined' status: 'granted' | 'denied' | 'undetermined'
} }
'state:background': { 'state:background:sampled': {
secondsActive: number secondsActive: number
} }
'state:foreground': {} 'state:foreground:sampled': {}
'router:navigate:sampled': {} 'router:navigate:sampled': {}
// Screen events // Screen events
@ -57,18 +57,18 @@ export type LogEvents = {
'onboarding:finished:avatarResult': { 'onboarding:finished:avatarResult': {
avatarResult: 'default' | 'created' | 'uploaded' avatarResult: 'default' | 'created' | 'uploaded'
} }
'home:feedDisplayed': { 'home:feedDisplayed:sampled': {
feedUrl: string feedUrl: string
feedType: string feedType: string
index: number index: number
reason: 'focus' | 'tabbar-click' | 'pager-swipe' | 'desktop-sidebar-click' reason: 'focus' | 'tabbar-click' | 'pager-swipe' | 'desktop-sidebar-click'
} }
'feed:endReached': { 'feed:endReached:sampled': {
feedUrl: string feedUrl: string
feedType: string feedType: string
itemCount: number itemCount: number
} }
'feed:refresh': { 'feed:refresh:sampled': {
feedUrl: string feedUrl: string
feedType: string feedType: string
reason: 'pull-to-refresh' | 'soft-reset' | 'load-latest' reason: 'pull-to-refresh' | 'soft-reset' | 'load-latest'

View file

@ -87,7 +87,14 @@ export function toClout(n: number | null | undefined): number | undefined {
} }
} }
const DOWNSAMPLED_EVENTS = new Set(['router:navigate:sampled']) const DOWNSAMPLED_EVENTS: Set<keyof LogEvents> = new Set([
'router:navigate:sampled',
'state:background:sampled',
'state:foreground:sampled',
'home:feedDisplayed:sampled',
'feed:endReached:sampled',
'feed:refresh:sampled',
])
const isDownsampledSession = Math.random() < 0.9 // 90% likely const isDownsampledSession = Math.random() < 0.9 // 90% likely
export function logEvent<E extends keyof LogEvents>( export function logEvent<E extends keyof LogEvents>(
@ -98,6 +105,13 @@ export function logEvent<E extends keyof LogEvents>(
if (isDownsampledSession && DOWNSAMPLED_EVENTS.has(eventName)) { if (isDownsampledSession && DOWNSAMPLED_EVENTS.has(eventName)) {
return return
} }
if (process.env.NODE_ENV === 'development') {
if (eventName.endsWith(':sampled')) {
logger.error(
'Did you forget to add ' + eventName + ' to DOWNSAMPLED_EVENTS?',
)
}
}
const fullMetadata = { const fullMetadata = {
...rawMetadata, ...rawMetadata,
} as Record<string, string> // Statsig typings are unnecessarily strict here. } as Record<string, string> // Statsig typings are unnecessarily strict here.
@ -199,14 +213,14 @@ AppState.addEventListener('change', (state: AppStateStatus) => {
lastState = state lastState = state
if (state === 'active') { if (state === 'active') {
lastActive = performance.now() lastActive = performance.now()
logEvent('state:foreground', {}) logEvent('state:foreground:sampled', {})
} else { } else {
let secondsActive = 0 let secondsActive = 0
if (lastActive != null) { if (lastActive != null) {
secondsActive = Math.round((performance.now() - lastActive) / 1e3) secondsActive = Math.round((performance.now() - lastActive) / 1e3)
} }
lastActive = null lastActive = null
logEvent('state:background', { logEvent('state:background:sampled', {
secondsActive, secondsActive,
}) })
} }

View file

@ -76,7 +76,7 @@ export function FeedPage({
scrollToTop() scrollToTop()
truncateAndInvalidate(queryClient, FEED_RQKEY(feed)) truncateAndInvalidate(queryClient, FEED_RQKEY(feed))
setHasNew(false) setHasNew(false)
logEvent('feed:refresh', { logEvent('feed:refresh:sampled', {
feedType: feed.split('|')[0], feedType: feed.split('|')[0],
feedUrl: feed, feedUrl: feed,
reason: 'soft-reset', reason: 'soft-reset',
@ -102,7 +102,7 @@ export function FeedPage({
scrollToTop() scrollToTop()
truncateAndInvalidate(queryClient, FEED_RQKEY(feed)) truncateAndInvalidate(queryClient, FEED_RQKEY(feed))
setHasNew(false) setHasNew(false)
logEvent('feed:refresh', { logEvent('feed:refresh:sampled', {
feedType: feed.split('|')[0], feedType: feed.split('|')[0],
feedUrl: feed, feedUrl: feed,
reason: 'load-latest', reason: 'load-latest',

View file

@ -15,7 +15,7 @@ const AnimatedPagerView = Animated.createAnimatedComponent(PagerView)
export interface PagerRef { export interface PagerRef {
setPage: ( setPage: (
index: number, index: number,
reason: LogEvents['home:feedDisplayed']['reason'], reason: LogEvents['home:feedDisplayed:sampled']['reason'],
) => void ) => void
} }
@ -32,7 +32,7 @@ interface Props {
onPageSelected?: (index: number) => void onPageSelected?: (index: number) => void
onPageSelecting?: ( onPageSelecting?: (
index: number, index: number,
reason: LogEvents['home:feedDisplayed']['reason'], reason: LogEvents['home:feedDisplayed:sampled']['reason'],
) => void ) => void
onPageScrollStateChanged?: ( onPageScrollStateChanged?: (
scrollState: 'idle' | 'dragging' | 'settling', scrollState: 'idle' | 'dragging' | 'settling',
@ -61,7 +61,7 @@ export const Pager = forwardRef<PagerRef, React.PropsWithChildren<Props>>(
React.useImperativeHandle(ref, () => ({ React.useImperativeHandle(ref, () => ({
setPage: ( setPage: (
index: number, index: number,
reason: LogEvents['home:feedDisplayed']['reason'], reason: LogEvents['home:feedDisplayed:sampled']['reason'],
) => { ) => {
pagerView.current?.setPage(index) pagerView.current?.setPage(index)
onPageSelecting?.(index, reason) onPageSelecting?.(index, reason)

View file

@ -18,7 +18,7 @@ interface Props {
onPageSelected?: (index: number) => void onPageSelected?: (index: number) => void
onPageSelecting?: ( onPageSelecting?: (
index: number, index: number,
reason: LogEvents['home:feedDisplayed']['reason'], reason: LogEvents['home:feedDisplayed:sampled']['reason'],
) => void ) => void
} }
export const Pager = React.forwardRef(function PagerImpl( export const Pager = React.forwardRef(function PagerImpl(
@ -38,14 +38,17 @@ export const Pager = React.forwardRef(function PagerImpl(
React.useImperativeHandle(ref, () => ({ React.useImperativeHandle(ref, () => ({
setPage: ( setPage: (
index: number, index: number,
reason: LogEvents['home:feedDisplayed']['reason'], reason: LogEvents['home:feedDisplayed:sampled']['reason'],
) => { ) => {
onTabBarSelect(index, reason) onTabBarSelect(index, reason)
}, },
})) }))
const onTabBarSelect = React.useCallback( const onTabBarSelect = React.useCallback(
(index: number, reason: LogEvents['home:feedDisplayed']['reason']) => { (
index: number,
reason: LogEvents['home:feedDisplayed:sampled']['reason'],
) => {
const scrollY = window.scrollY const scrollY = window.scrollY
// We want to determine if the tabbar is already "sticking" at the top (in which // We want to determine if the tabbar is already "sticking" at the top (in which
// case we should preserve and restore scroll), or if it is somewhere below in the // case we should preserve and restore scroll), or if it is somewhere below in the

View file

@ -226,7 +226,7 @@ let Feed = ({
const onRefresh = React.useCallback(async () => { const onRefresh = React.useCallback(async () => {
track('Feed:onRefresh') track('Feed:onRefresh')
logEvent('feed:refresh', { logEvent('feed:refresh:sampled', {
feedType: feedType, feedType: feedType,
feedUrl: feed, feedUrl: feed,
reason: 'pull-to-refresh', reason: 'pull-to-refresh',
@ -244,7 +244,7 @@ let Feed = ({
const onEndReached = React.useCallback(async () => { const onEndReached = React.useCallback(async () => {
if (isFetching || !hasNextPage || isError) return if (isFetching || !hasNextPage || isError) return
logEvent('feed:endReached', { logEvent('feed:endReached:sampled', {
feedType: feedType, feedType: feedType,
feedUrl: feed, feedUrl: feed,
itemCount: feedItems.length, itemCount: feedItems.length,

View file

@ -99,7 +99,7 @@ function HomeScreenReady({
useFocusEffect( useFocusEffect(
useNonReactiveCallback(() => { useNonReactiveCallback(() => {
if (selectedFeed) { if (selectedFeed) {
logEvent('home:feedDisplayed', { logEvent('home:feedDisplayed:sampled', {
index: selectedIndex, index: selectedIndex,
feedType: selectedFeed.split('|')[0], feedType: selectedFeed.split('|')[0],
feedUrl: selectedFeed, feedUrl: selectedFeed,
@ -140,9 +140,12 @@ function HomeScreenReady({
) )
const onPageSelecting = React.useCallback( const onPageSelecting = React.useCallback(
(index: number, reason: LogEvents['home:feedDisplayed']['reason']) => { (
index: number,
reason: LogEvents['home:feedDisplayed:sampled']['reason'],
) => {
const feed = allFeeds[index] const feed = allFeeds[index]
logEvent('home:feedDisplayed', { logEvent('home:feedDisplayed:sampled', {
index, index,
feedType: feed.split('|')[0], feedType: feed.split('|')[0],
feedUrl: feed, feedUrl: feed,