From 73cc59410295378861e7e24d9bb015deb30e4a3a Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Wed, 19 Nov 2025 10:49:56 +0100 Subject: [PATCH] wip: first version of tracing --- .../default-master-password-unlock.service.ts | 55 ++++++++++++++++++- .../abstractions/sdk/sdk-load.service.ts | 13 ++++- .../services/sdk/default-sdk.service.ts | 31 +++++++++++ 3 files changed, 96 insertions(+), 3 deletions(-) diff --git a/libs/common/src/key-management/master-password/services/default-master-password-unlock.service.ts b/libs/common/src/key-management/master-password/services/default-master-password-unlock.service.ts index 89a87403e49..e183aae59e7 100644 --- a/libs/common/src/key-management/master-password/services/default-master-password-unlock.service.ts +++ b/libs/common/src/key-management/master-password/services/default-master-password-unlock.service.ts @@ -3,15 +3,61 @@ import { firstValueFrom } from "rxjs"; // eslint-disable-next-line no-restricted-imports import { KeyService } from "@bitwarden/key-management"; import { LogService } from "@bitwarden/logging"; -import { isCryptoError } from "@bitwarden/sdk-internal"; +import { + EventDefinition, + isCryptoError, + SpanDefinition, + TracingLevel, +} from "@bitwarden/sdk-internal"; import { UserId } from "@bitwarden/user-core"; +import { SdkLoadService } from "../../../platform/abstractions/sdk/sdk-load.service"; import { HashPurpose } from "../../../platform/enums"; import { UserKey } from "../../../types/key"; import { MasterPasswordUnlockService } from "../abstractions/master-password-unlock.service"; import { InternalMasterPasswordServiceAbstraction } from "../abstractions/master-password.service.abstraction"; import { MasterPasswordUnlockData } from "../types/master-password.types"; +const UnlockWithMasterPasswordSpan = SdkLoadService.WithSdk( + () => + new SpanDefinition( + "unlockWithMasterPassword", + "DefaultMasterPasswordUnlockService", + TracingLevel.Info, + [], + ), +); + +const InputValidatedEvent = SdkLoadService.WithSdk( + () => + new EventDefinition( + "inputValidated", + "DefaultMasterPasswordUnlockService", + TracingLevel.Debug, + [], + ), +); + +const UserKeyUnwrappedEvent = SdkLoadService.WithSdk( + () => + new EventDefinition( + "userKeyUnwrapped", + "DefaultMasterPasswordUnlockService", + TracingLevel.Debug, + [], + ), +); + +const LegacyStateSetEvent = SdkLoadService.WithSdk( + () => + new EventDefinition( + "legacyStateSet", + "DefaultMasterPasswordUnlockService", + TracingLevel.Debug, + [], + ), +); + export class DefaultMasterPasswordUnlockService implements MasterPasswordUnlockService { constructor( private readonly masterPasswordService: InternalMasterPasswordServiceAbstraction, @@ -20,7 +66,12 @@ export class DefaultMasterPasswordUnlockService implements MasterPasswordUnlockS ) {} async unlockWithMasterPassword(masterPassword: string, userId: UserId): Promise { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + using _span = (await UnlockWithMasterPasswordSpan).enter(); + console.log("DefaultMasterPasswordUnlockService: unlockWithMasterPassword called"); + this.validateInput(masterPassword, userId); + (await InputValidatedEvent).record(); const masterPasswordUnlockData = await firstValueFrom( this.masterPasswordService.masterPasswordUnlockData$(userId), @@ -34,8 +85,10 @@ export class DefaultMasterPasswordUnlockService implements MasterPasswordUnlockS masterPassword, masterPasswordUnlockData, ); + (await UserKeyUnwrappedEvent).record(); await this.setLegacyState(masterPassword, masterPasswordUnlockData, userId); + (await LegacyStateSetEvent).record(); return userKey; } diff --git a/libs/common/src/platform/abstractions/sdk/sdk-load.service.ts b/libs/common/src/platform/abstractions/sdk/sdk-load.service.ts index fb443d61777..447479dfbb5 100644 --- a/libs/common/src/platform/abstractions/sdk/sdk-load.service.ts +++ b/libs/common/src/platform/abstractions/sdk/sdk-load.service.ts @@ -1,4 +1,4 @@ -import { init_sdk } from "@bitwarden/sdk-internal"; +import { init_sdk, LogLevel } from "@bitwarden/sdk-internal"; // eslint-disable-next-line @typescript-eslint/no-unused-vars -- used in docs import type { SdkService } from "./sdk.service"; @@ -33,6 +33,15 @@ export abstract class SdkLoadService { SdkLoadService.markAsFailed = (error: unknown) => reject(new SdkLoadFailedError(error)); }); + /** + * Helper to run a function after the SDK is ready. + * @param fn The function to run after the SDK is ready. + * @returns The result of the function. + */ + static readonly WithSdk = (fn: () => T | Promise): Promise => { + return SdkLoadService.Ready.then(() => fn()); + }; + /** * Load WASM and initalize SDK-JS integrations such as logging. * This method should be called once at the start of the application. @@ -41,7 +50,7 @@ export abstract class SdkLoadService { async loadAndInit(): Promise { try { await this.load(); - init_sdk(); + init_sdk(LogLevel.Debug); SdkLoadService.markAsReady(); } catch (error) { SdkLoadService.markAsFailed(error); diff --git a/libs/common/src/platform/services/sdk/default-sdk.service.ts b/libs/common/src/platform/services/sdk/default-sdk.service.ts index 5084f5f5f18..d65cde7b3cb 100644 --- a/libs/common/src/platform/services/sdk/default-sdk.service.ts +++ b/libs/common/src/platform/services/sdk/default-sdk.service.ts @@ -23,7 +23,11 @@ import { KeyService, KdfConfigService, KdfConfig, KdfType } from "@bitwarden/key import { PasswordManagerClient, ClientSettings, + EventDefinition, + DeviceType as SdkDeviceType, + SpanDefinition, TokenProvider, + TracingLevel, UnsignedSharedKey, WrappedAccountCryptographicState, } from "@bitwarden/sdk-internal"; @@ -49,6 +53,26 @@ import { StateProvider } from "../../state"; import { initializeState } from "./client-managed-state"; +const InitializeClientSpan = SdkLoadService.WithSdk( + () => new SpanDefinition("initializeUserClient", "DefaultSdkService", TracingLevel.Info, []), +); + +const UserCryptoInitializedEvent = SdkLoadService.WithSdk( + () => new EventDefinition("userCryptoInitialized", "DefaultSdkService", TracingLevel.Debug, []), +); + +const OrgCryptoInitializedEvent = SdkLoadService.WithSdk( + () => new EventDefinition("orgCryptoInitialized", "DefaultSdkService", TracingLevel.Debug, []), +); + +const ClientStateInitializedEvent = SdkLoadService.WithSdk( + () => new EventDefinition("clientStateInitialized", "DefaultSdkService", TracingLevel.Debug, []), +); + +const FeatureFlagsLoadedEvent = SdkLoadService.WithSdk( + () => new EventDefinition("featureFlagsLoaded", "DefaultSdkService", TracingLevel.Debug, []), +); + // A symbol that represents an overridden client that is explicitly set to undefined, // blocking the creation of an internal client for that user. const UnsetClient = Symbol("UnsetClient"); @@ -279,6 +303,9 @@ export class DefaultSdkService implements SdkService { accountCryptographicState: WrappedAccountCryptographicState, orgKeys: Record, ) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + using _span = (await InitializeClientSpan).enter(); + await client.crypto().initialize_user_crypto({ userId: asUuid(userId), email: account.email, @@ -295,6 +322,7 @@ export class DefaultSdkService implements SdkService { }, accountCryptographicState: accountCryptographicState, }); + (await UserCryptoInitializedEvent).record(); // We initialize the org crypto even if the org_keys are // null to make sure any existing org keys are cleared. @@ -303,11 +331,14 @@ export class DefaultSdkService implements SdkService { Object.entries(orgKeys).map(([k, v]) => [asUuid(k), v.toJSON() as UnsignedSharedKey]), ), }); + (await OrgCryptoInitializedEvent).record(); // Initialize the SDK managed database and the client managed repositories. await initializeState(userId, client.platform().state(), this.stateProvider); + (await ClientStateInitializedEvent).record(); await this.loadFeatureFlags(client); + (await FeatureFlagsLoadedEvent).record(); } private async loadFeatureFlags(client: PasswordManagerClient) {