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:
@@ -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 () => {
|
||||
|
||||
@@ -0,0 +1,308 @@
|
||||
export type MetricEvents = {
|
||||
// App events
|
||||
init: {
|
||||
initMs: number
|
||||
}
|
||||
'account:loggedIn': {
|
||||
logContext:
|
||||
| 'LoginForm'
|
||||
| 'SwitchAccount'
|
||||
| 'ChooseAccountForm'
|
||||
| 'Settings'
|
||||
| 'Notification'
|
||||
withPassword: boolean
|
||||
}
|
||||
'account:loggedOut': {
|
||||
logContext:
|
||||
| 'SwitchAccount'
|
||||
| 'Settings'
|
||||
| 'SignupQueued'
|
||||
| 'Deactivated'
|
||||
| 'Takendown'
|
||||
scope: 'current' | 'every'
|
||||
}
|
||||
'notifications:openApp': {}
|
||||
'notifications:request': {
|
||||
context: 'StartOnboarding' | 'AfterOnboarding' | 'Login' | 'Home'
|
||||
status: 'granted' | 'denied' | 'undetermined'
|
||||
}
|
||||
'state:background': {
|
||||
secondsActive: number
|
||||
}
|
||||
'state:foreground': {}
|
||||
'router:navigate': {
|
||||
from?: string
|
||||
}
|
||||
'deepLink:referrerReceived': {
|
||||
to: string
|
||||
referrer: string
|
||||
hostname: string
|
||||
}
|
||||
|
||||
// Screen events
|
||||
'splash:signInPressed': {}
|
||||
'splash:createAccountPressed': {}
|
||||
'signup:nextPressed': {
|
||||
activeStep: number
|
||||
phoneVerificationRequired?: boolean
|
||||
}
|
||||
'signup:backPressed': {
|
||||
activeStep: number
|
||||
}
|
||||
'signup:captchaSuccess': {}
|
||||
'signup:captchaFailure': {}
|
||||
'signin:hostingProviderPressed': {
|
||||
hostingProviderDidChange: boolean
|
||||
}
|
||||
'signin:hostingProviderFailedResolution': {}
|
||||
'signin:success': {
|
||||
failedAttemptsCount: number
|
||||
isUsingCustomProvider: boolean
|
||||
timeTakenSeconds: number
|
||||
}
|
||||
'signin:backPressed': {
|
||||
failedAttemptsCount: number
|
||||
}
|
||||
'signin:forgotPasswordPressed': {}
|
||||
'signin:passwordReset': {}
|
||||
'signin:passwordResetSuccess': {}
|
||||
'signin:passwordResetFailure': {}
|
||||
'onboarding:interests:nextPressed': {
|
||||
selectedInterests: string[]
|
||||
selectedInterestsLength: number
|
||||
}
|
||||
'onboarding:suggestedAccounts:nextPressed': {
|
||||
selectedAccountsLength: number
|
||||
skipped: boolean
|
||||
}
|
||||
'onboarding:followingFeed:nextPressed': {}
|
||||
'onboarding:algoFeeds:nextPressed': {
|
||||
selectedPrimaryFeeds: string[]
|
||||
selectedPrimaryFeedsLength: number
|
||||
selectedSecondaryFeeds: string[]
|
||||
selectedSecondaryFeedsLength: number
|
||||
}
|
||||
'onboarding:topicalFeeds:nextPressed': {
|
||||
selectedFeeds: string[]
|
||||
selectedFeedsLength: number
|
||||
}
|
||||
'onboarding:moderation:nextPressed': {}
|
||||
'onboarding:profile:nextPressed': {}
|
||||
'onboarding:finished:nextPressed': {
|
||||
usedStarterPack: boolean
|
||||
starterPackName?: string
|
||||
starterPackCreator?: string
|
||||
starterPackUri?: string
|
||||
profilesFollowed: number
|
||||
feedsPinned: number
|
||||
}
|
||||
'onboarding:finished:avatarResult': {
|
||||
avatarResult: 'default' | 'created' | 'uploaded'
|
||||
}
|
||||
'home:feedDisplayed': {
|
||||
feedUrl: string
|
||||
feedType: string
|
||||
index: number
|
||||
}
|
||||
'feed:endReached': {
|
||||
feedUrl: string
|
||||
feedType: string
|
||||
itemCount: number
|
||||
}
|
||||
'feed:refresh': {
|
||||
feedUrl: string
|
||||
feedType: string
|
||||
reason: 'pull-to-refresh' | 'soft-reset' | 'load-latest'
|
||||
}
|
||||
'discover:showMore': {
|
||||
feedContext: string
|
||||
}
|
||||
'discover:showLess': {
|
||||
feedContext: string
|
||||
}
|
||||
'discover:clickthrough': {
|
||||
count: number
|
||||
}
|
||||
'discover:engaged': {
|
||||
count: number
|
||||
}
|
||||
'discover:seen': {
|
||||
count: number
|
||||
}
|
||||
|
||||
'composer:gif:open': {}
|
||||
'composer:gif:select': {}
|
||||
|
||||
// Data events
|
||||
'account:create:begin': {}
|
||||
'account:create:success': {}
|
||||
'post:create': {
|
||||
imageCount: number
|
||||
isReply: boolean
|
||||
isPartOfThread: boolean
|
||||
hasLink: boolean
|
||||
hasQuote: boolean
|
||||
langs: string
|
||||
logContext: 'Composer'
|
||||
}
|
||||
'thread:create': {
|
||||
postCount: number
|
||||
isReply: boolean
|
||||
}
|
||||
'post:like': {
|
||||
doesLikerFollowPoster: boolean | undefined
|
||||
doesPosterFollowLiker: boolean | undefined
|
||||
likerClout: number | undefined
|
||||
postClout: number | undefined
|
||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
|
||||
}
|
||||
'post:repost': {
|
||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
|
||||
}
|
||||
'post:unlike': {
|
||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
|
||||
}
|
||||
'post:unrepost': {
|
||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
|
||||
}
|
||||
'post:mute': {}
|
||||
'post:unmute': {}
|
||||
'post:pin': {}
|
||||
'post:unpin': {}
|
||||
'profile:follow': {
|
||||
didBecomeMutual: boolean | undefined
|
||||
followeeClout: number | undefined
|
||||
followerClout: number | undefined
|
||||
logContext:
|
||||
| 'RecommendedFollowsItem'
|
||||
| 'PostThreadItem'
|
||||
| 'ProfileCard'
|
||||
| 'ProfileHeader'
|
||||
| 'ProfileHeaderSuggestedFollows'
|
||||
| 'ProfileMenu'
|
||||
| 'ProfileHoverCard'
|
||||
| 'AvatarButton'
|
||||
| 'StarterPackProfilesList'
|
||||
| 'FeedInterstitial'
|
||||
| 'ProfileHeaderSuggestedFollows'
|
||||
| 'PostOnboardingFindFollows'
|
||||
| 'ImmersiveVideo'
|
||||
}
|
||||
'suggestedUser:follow': {
|
||||
logContext:
|
||||
| 'Explore'
|
||||
| 'InterstitialDiscover'
|
||||
| 'InterstitialProfile'
|
||||
| 'Profile'
|
||||
location: 'Card' | 'Profile'
|
||||
recId?: number
|
||||
position: number
|
||||
}
|
||||
'suggestedUser:press': {
|
||||
logContext: 'Explore' | 'InterstitialDiscover' | 'InterstitialProfile'
|
||||
recId?: number
|
||||
position: number
|
||||
}
|
||||
'suggestedUser:seen': {
|
||||
logContext: 'Explore' | 'InterstitialDiscover' | 'InterstitialProfile'
|
||||
recId?: number
|
||||
position: number
|
||||
}
|
||||
'profile:unfollow': {
|
||||
logContext:
|
||||
| 'RecommendedFollowsItem'
|
||||
| 'PostThreadItem'
|
||||
| 'ProfileCard'
|
||||
| 'ProfileHeader'
|
||||
| 'ProfileHeaderSuggestedFollows'
|
||||
| 'ProfileMenu'
|
||||
| 'ProfileHoverCard'
|
||||
| 'Chat'
|
||||
| 'AvatarButton'
|
||||
| 'StarterPackProfilesList'
|
||||
| 'FeedInterstitial'
|
||||
| 'ProfileHeaderSuggestedFollows'
|
||||
| 'PostOnboardingFindFollows'
|
||||
| 'ImmersiveVideo'
|
||||
}
|
||||
'chat:create': {
|
||||
logContext: 'ProfileHeader' | 'NewChatDialog' | 'SendViaChatDialog'
|
||||
}
|
||||
'chat:open': {
|
||||
logContext:
|
||||
| 'ProfileHeader'
|
||||
| 'NewChatDialog'
|
||||
| 'ChatsList'
|
||||
| 'SendViaChatDialog'
|
||||
}
|
||||
'starterPack:share': {
|
||||
starterPack: string
|
||||
shareType: 'link' | 'qrcode'
|
||||
qrShareType?: 'save' | 'copy' | 'share'
|
||||
}
|
||||
'starterPack:followAll': {
|
||||
logContext: 'StarterPackProfilesList' | 'Onboarding'
|
||||
starterPack: string
|
||||
count: number
|
||||
}
|
||||
'starterPack:delete': {}
|
||||
'starterPack:create': {
|
||||
setName: boolean
|
||||
setDescription: boolean
|
||||
profilesCount: number
|
||||
feedsCount: number
|
||||
}
|
||||
'starterPack:ctaPress': {
|
||||
starterPack: string
|
||||
}
|
||||
'starterPack:opened': {
|
||||
starterPack: string
|
||||
}
|
||||
'link:clicked': {
|
||||
url: string
|
||||
domain: string
|
||||
}
|
||||
|
||||
'feed:interstitial:feedCard:press': {}
|
||||
|
||||
'profile:header:suggestedFollowsCard:press': {}
|
||||
|
||||
'test:all:always': {}
|
||||
'test:all:sometimes': {}
|
||||
'test:all:boosted_by_gate1': {reason: 'base' | 'gate1'}
|
||||
'test:all:boosted_by_gate2': {reason: 'base' | 'gate2'}
|
||||
'test:all:boosted_by_both': {reason: 'base' | 'gate1' | 'gate2'}
|
||||
'test:gate1:always': {}
|
||||
'test:gate1:sometimes': {}
|
||||
'test:gate2:always': {}
|
||||
'test:gate2:sometimes': {}
|
||||
|
||||
'tmd:share': {}
|
||||
'tmd:download': {}
|
||||
'tmd:post': {}
|
||||
|
||||
'trendingTopics:show': {
|
||||
context: 'settings'
|
||||
}
|
||||
'trendingTopics:hide': {
|
||||
context: 'settings' | 'sidebar' | 'interstitial' | 'explore:trending'
|
||||
}
|
||||
'trendingTopic:click': {
|
||||
context: 'sidebar' | 'interstitial' | 'explore'
|
||||
}
|
||||
'recommendedTopic:click': {
|
||||
context: 'explore'
|
||||
}
|
||||
'trendingVideos:show': {
|
||||
context: 'settings'
|
||||
}
|
||||
'trendingVideos:hide': {
|
||||
context: 'settings' | 'interstitial:discover' | 'interstitial:explore'
|
||||
}
|
||||
'videoCard:click': {
|
||||
context: 'interstitial:discover' | 'interstitial:explore' | 'feed'
|
||||
}
|
||||
|
||||
'progressGuide:hide': {}
|
||||
'progressGuide:followDialog:open': {}
|
||||
}
|
||||
@@ -18,8 +18,7 @@ export const bitdriftTransport: Transport = (
|
||||
) => {
|
||||
const log = logFunctions[level]
|
||||
log(message.toString(), {
|
||||
// match Sentry payload
|
||||
context,
|
||||
__context__: context,
|
||||
...prepareMetadata(metadata),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@ export const sentryTransport: Transport = (
|
||||
timestamp,
|
||||
) => {
|
||||
const meta = {
|
||||
// match Bitdrift payload
|
||||
context,
|
||||
__context__: context,
|
||||
...prepareMetadata(metadata),
|
||||
}
|
||||
let _tags = tags || {}
|
||||
|
||||
+8
-2
@@ -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`.
|
||||
|
||||
Reference in New Issue
Block a user