[Instrumentation] Signin (#7742)

* first pass at instrumenting login

* round time taken
This commit is contained in:
Samuel Newman
2025-02-14 19:52:53 +00:00
committed by GitHub
parent 11616846ca
commit d793abf8cd
5 changed files with 85 additions and 26 deletions
+16
View File
@@ -51,6 +51,22 @@ export type LogEvents = {
}
'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
+30 -22
View File
@@ -45,6 +45,8 @@ export const LoginForm = ({
onPressRetryConnect,
onPressBack,
onPressForgotPassword,
onAttemptSuccess,
onAttemptFailed,
}: {
error: string
serviceUrl: string
@@ -55,6 +57,8 @@ export const LoginForm = ({
onPressRetryConnect: () => void
onPressBack: () => void
onPressForgotPassword: () => void
onAttemptSuccess: () => void
onAttemptFailed: () => void
}) => {
const t = useTheme()
const [isProcessing, setIsProcessing] = useState<boolean>(false)
@@ -131,6 +135,7 @@ export const LoginForm = ({
},
'LoginForm',
)
onAttemptSuccess()
setShowLoggedOut(false)
setHasCheckedForStarterPack(true)
requestNotificationsPermission('Login')
@@ -142,29 +147,32 @@ export const LoginForm = ({
e instanceof ComAtprotoServerCreateSession.AuthFactorTokenRequiredError
) {
setIsAuthFactorTokenNeeded(true)
} else if (errMsg.includes('Token is invalid')) {
logger.debug('Failed to login due to invalid 2fa token', {
error: errMsg,
})
setError(_(msg`Invalid 2FA confirmation code.`))
} else if (
errMsg.includes('Authentication Required') ||
errMsg.includes('Invalid identifier or password')
) {
logger.debug('Failed to login due to invalid credentials', {
error: errMsg,
})
setError(_(msg`Incorrect username or password`))
} else if (isNetworkError(e)) {
logger.warn('Failed to login due to network error', {error: errMsg})
setError(
_(
msg`Unable to contact your service. Please check your Internet connection.`,
),
)
} else {
logger.warn('Failed to login', {error: errMsg})
setError(cleanError(errMsg))
onAttemptFailed()
if (errMsg.includes('Token is invalid')) {
logger.debug('Failed to login due to invalid 2fa token', {
error: errMsg,
})
setError(_(msg`Invalid 2FA confirmation code.`))
} else if (
errMsg.includes('Authentication Required') ||
errMsg.includes('Invalid identifier or password')
) {
logger.debug('Failed to login due to invalid credentials', {
error: errMsg,
})
setError(_(msg`Incorrect username or password`))
} else if (isNetworkError(e)) {
logger.warn('Failed to login due to network error', {error: errMsg})
setError(
_(
msg`Unable to contact your service. Please check your Internet connection.`,
),
)
} else {
logger.warn('Failed to login', {error: errMsg})
setError(cleanError(errMsg))
}
}
}
}
+4
View File
@@ -4,6 +4,7 @@ import {BskyAgent} from '@atproto/api'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {logEvent} from '#/lib/statsig/statsig'
import {isNetworkError} from '#/lib/strings/errors'
import {cleanError} from '#/lib/strings/errors'
import {checkAndFormatResetCode} from '#/lib/strings/password'
@@ -48,6 +49,7 @@ export const SetNewPasswordForm = ({
msg`You have entered an invalid code. It should look like XXXXX-XXXXX.`,
),
)
logEvent('signin:passwordResetFailure', {})
return
}
@@ -67,9 +69,11 @@ export const SetNewPasswordForm = ({
password,
})
onPasswordSet()
logEvent('signin:passwordResetSuccess', {})
} catch (e: any) {
const errMsg = e.toString()
logger.warn('Failed to set new password', {error: e})
logEvent('signin:passwordResetFailure', {})
setIsProcessing(false)
if (isNetworkError(e)) {
setError(
+30 -3
View File
@@ -1,10 +1,11 @@
import React from 'react'
import React, {useRef} from 'react'
import {KeyboardAvoidingView} from 'react-native'
import {LayoutAnimationConfig} from 'react-native-reanimated'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {DEFAULT_SERVICE} from '#/lib/constants'
import {logEvent} from '#/lib/statsig/statsig'
import {logger} from '#/logger'
import {useServiceQuery} from '#/state/queries/service'
import {SessionAccount, useSession} from '#/state/session'
@@ -28,6 +29,8 @@ enum Forms {
export const Login = ({onPressBack}: {onPressBack: () => void}) => {
const {_} = useLingui()
const failedAttemptCountRef = useRef(0)
const startTimeRef = useRef(Date.now())
const {accounts} = useSession()
const {requestedAccountSwitchTo} = useLoggedOutView()
@@ -79,6 +82,7 @@ export const Login = ({onPressBack}: {onPressBack: () => void}) => {
logger.warn(`Failed to fetch service description for ${serviceUrl}`, {
error: String(serviceError),
})
logEvent('signin:hostingProviderFailedResolution', {})
} else {
setError('')
}
@@ -86,6 +90,27 @@ export const Login = ({onPressBack}: {onPressBack: () => void}) => {
const onPressForgotPassword = () => {
setCurrentForm(Forms.ForgotPassword)
logEvent('signin:forgotPasswordPressed', {})
}
const handlePressBack = () => {
onPressBack()
logEvent('signin:backPressed', {
failedAttemptsCount: failedAttemptCountRef.current,
})
}
const onAttemptSuccess = () => {
logEvent('signin:success', {
isUsingCustomProvider: serviceUrl !== DEFAULT_SERVICE,
timeTakenSeconds: Math.round((Date.now() - startTimeRef.current) / 1000),
failedAttemptsCount: failedAttemptCountRef.current,
})
setCurrentForm(Forms.Login)
}
const onAttemptFailed = () => {
failedAttemptCountRef.current += 1
}
let content = null
@@ -103,9 +128,11 @@ export const Login = ({onPressBack}: {onPressBack: () => void}) => {
serviceDescription={serviceDescription}
initialHandle={initialHandle}
setError={setError}
onAttemptFailed={onAttemptFailed}
onAttemptSuccess={onAttemptSuccess}
setServiceUrl={setServiceUrl}
onPressBack={() =>
accounts.length ? gotoForm(Forms.ChooseAccount) : onPressBack()
accounts.length ? gotoForm(Forms.ChooseAccount) : handlePressBack()
}
onPressForgotPassword={onPressForgotPassword}
onPressRetryConnect={refetchService}
@@ -118,7 +145,7 @@ export const Login = ({onPressBack}: {onPressBack: () => void}) => {
content = (
<ChooseAccountForm
onSelectAccount={onSelectAccount}
onPressBack={onPressBack}
onPressBack={handlePressBack}
/>
)
break
+5 -1
View File
@@ -5,6 +5,7 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {BSKY_SERVICE} from '#/lib/constants'
import {logEvent} from '#/lib/statsig/statsig'
import * as persisted from '#/state/persisted'
import {useSession} from '#/state/session'
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
@@ -39,7 +40,10 @@ export function ServerInputDialog({
setPreviousCustomAddress(result)
}
}
}, [onSelect])
logEvent('signin:hostingProviderPressed', {
hostingProviderDidChange: fixedOption !== BSKY_SERVICE,
})
}, [onSelect, fixedOption])
return (
<Dialog.Outer