diff options
Diffstat (limited to 'src/logger')
-rw-r--r-- | src/logger/index.ts | 21 | ||||
-rw-r--r-- | src/logger/metrics.ts | 308 | ||||
-rw-r--r-- | src/logger/transports/bitdrift.ts | 3 | ||||
-rw-r--r-- | src/logger/transports/sentry.ts | 3 | ||||
-rw-r--r-- | src/logger/types.ts | 10 |
5 files changed, 339 insertions, 6 deletions
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/logger/metrics.ts b/src/logger/metrics.ts new file mode 100644 index 000000000..e3bd93314 --- /dev/null +++ b/src/logger/metrics.ts @@ -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': {} +} 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`. |