diff options
author | dan <dan.abramov@gmail.com> | 2024-06-21 01:47:56 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-20 15:47:56 -0700 |
commit | 4c48a1f14b317a76e55c008aeeb834ebb8f416d0 (patch) | |
tree | 53ba995f899c9bdc3feb086aeca6dcefa9e64c41 /src/state/session/logging.ts | |
parent | 4bba59790a04d9c708dd3cbecf96fdab7f306d94 (diff) | |
download | voidsky-4c48a1f14b317a76e55c008aeeb834ebb8f416d0.tar.zst |
[Session] Logging (#4476)
* Add session logging (console.log) * Hook it up for real * Send type separately
Diffstat (limited to 'src/state/session/logging.ts')
-rw-r--r-- | src/state/session/logging.ts | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/src/state/session/logging.ts b/src/state/session/logging.ts new file mode 100644 index 000000000..16aa66fe7 --- /dev/null +++ b/src/state/session/logging.ts @@ -0,0 +1,137 @@ +import {AtpSessionData} from '@atproto/api' +import {sha256} from 'js-sha256' +import {Statsig} from 'statsig-react-native-expo' + +import {Schema} from '../persisted' +import {Action, State} from './reducer' +import {SessionAccount} from './types' + +type Reducer = (state: State, action: Action) => State + +type Log = + | { + type: 'reducer:init' + state: State + } + | { + type: 'reducer:call' + action: Action + prevState: State + nextState: State + } + | { + type: 'method:start' + method: + | 'createAccount' + | 'login' + | 'logout' + | 'resumeSession' + | 'removeAccount' + account?: SessionAccount + } + | { + type: 'method:end' + method: + | 'createAccount' + | 'login' + | 'logout' + | 'resumeSession' + | 'removeAccount' + account?: SessionAccount + } + | { + type: 'persisted:broadcast' + data: Schema['session'] + } + | { + type: 'persisted:receive' + data: Schema['session'] + } + | { + type: 'agent:switch' + prevAgent: object + nextAgent: object + } + | { + type: 'agent:patch' + agent: object + prevSession: AtpSessionData | undefined + nextSession: AtpSessionData + } + +export function wrapSessionReducerForLogging(reducer: Reducer): Reducer { + return function loggingWrapper(prevState: State, action: Action): State { + const nextState = reducer(prevState, action) + addSessionDebugLog({type: 'reducer:call', prevState, action, nextState}) + return nextState + } +} + +let nextMessageIndex = 0 +const MAX_SLICE_LENGTH = 1000 + +export function addSessionDebugLog(log: Log) { + try { + if (!Statsig.initializeCalled() || !Statsig.getStableID()) { + // Drop these logs for now. + return + } + if (!Statsig.checkGate('debug_session')) { + return + } + const messageIndex = nextMessageIndex++ + const {type, ...content} = log + let payload = JSON.stringify(content, replacer) + + let nextSliceIndex = 0 + while (payload.length > 0) { + const sliceIndex = nextSliceIndex++ + const slice = payload.slice(0, MAX_SLICE_LENGTH) + payload = payload.slice(MAX_SLICE_LENGTH) + Statsig.logEvent('session:debug', null, { + realmId, + messageIndex: String(messageIndex), + messageType: type, + sliceIndex: String(sliceIndex), + slice, + }) + } + } catch (e) { + console.error(e) + } +} + +let agentIds = new WeakMap<object, string>() +let realmId = Math.random().toString(36).slice(2) +let nextAgentId = 1 + +function getAgentId(agent: object) { + let id = agentIds.get(agent) + if (id === undefined) { + id = realmId + '::' + nextAgentId++ + agentIds.set(agent, id) + } + return id +} + +function replacer(key: string, value: unknown) { + if (typeof value === 'object' && value != null && 'api' in value) { + return getAgentId(value) + } + if ( + key === 'service' || + key === 'email' || + key === 'emailConfirmed' || + key === 'emailAuthFactor' || + key === 'pdsUrl' + ) { + return undefined + } + if ( + typeof value === 'string' && + (key === 'refreshJwt' || key === 'accessJwt') + ) { + return sha256(value) + } + return value +} |