1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-31 00:33:33 +00:00

feat: include callee information

This commit is contained in:
Andreas Coroiu
2025-12-08 13:30:59 +01:00
parent 2c617b1090
commit 081eb1047b
9 changed files with 202 additions and 33 deletions

View File

@@ -838,6 +838,7 @@ export default class MainBackground {
this.apiService,
this.stateProvider,
this.configService,
this.logService,
);
this.pinService = new PinService(

View File

@@ -668,6 +668,7 @@ export class ServiceContainer {
this.apiService,
this.stateProvider,
this.configService,
this.logService,
customUserAgent,
);

View File

@@ -1641,6 +1641,7 @@ const safeProviders: SafeProvider[] = [
ApiServiceAbstraction,
StateProvider,
ConfigService,
LogService,
],
}),
safeProvider({

View File

@@ -20,6 +20,7 @@ import { UserId } from "../../../types/guid";
import { UserKey } from "../../../types/key";
import { ConfigService } from "../../abstractions/config/config.service";
import { Environment, EnvironmentService } from "../../abstractions/environment.service";
import { LogService } from "../../abstractions/log.service";
import { PlatformUtilsService } from "../../abstractions/platform-utils.service";
import { SdkClientFactory } from "../../abstractions/sdk/sdk-client-factory";
import { SdkLoadService } from "../../abstractions/sdk/sdk-load.service";
@@ -49,6 +50,7 @@ describe("DefaultSdkService", () => {
let service!: DefaultSdkService;
let accountService!: FakeAccountService;
let fakeStateProvider!: FakeStateProvider;
let logService!: MockProxy<LogService>;
let apiService!: MockProxy<ApiService>;
beforeEach(async () => {
@@ -64,6 +66,7 @@ describe("DefaultSdkService", () => {
const mockUserId = Utils.newGuid() as UserId;
accountService = mockAccountServiceWith(mockUserId);
fakeStateProvider = new FakeStateProvider(accountService);
logService = mock<LogService>();
configService = mock<ConfigService>();
configService.serverConfig$ = new BehaviorSubject(null);
@@ -82,6 +85,7 @@ describe("DefaultSdkService", () => {
apiService,
fakeStateProvider,
configService,
logService,
);
});

View File

@@ -23,11 +23,8 @@ import { KeyService, KdfConfigService, KdfConfig, KdfType } from "@bitwarden/key
import {
PasswordManagerClient,
ClientSettings,
EventDefinition,
FieldValue,
DeviceType as SdkDeviceType,
Span,
SpanDefinition,
TokenProvider,
TracingLevel,
UnsignedSharedKey,
@@ -40,6 +37,7 @@ import { EncString } from "../../../key-management/crypto/models/enc-string";
import { SecurityStateService } from "../../../key-management/security-state/abstractions/security-state.service";
import { OrganizationId, UserId } from "../../../types/guid";
import { Environment, EnvironmentService } from "../../abstractions/environment.service";
import { LogService } from "../../abstractions/log.service";
import { PlatformUtilsService } from "../../abstractions/platform-utils.service";
import { SdkClientFactory } from "../../abstractions/sdk/sdk-client-factory";
import { SdkLoadService } from "../../abstractions/sdk/sdk-load.service";
@@ -55,34 +53,34 @@ import { StateProvider } from "../../state";
import { initializeState } from "./client-managed-state";
const UserClientSpan = SdkLoadService.WithSdk(
() =>
new SpanDefinition("userClient$", "DefaultSdkService", TracingLevel.Info, [
"userId",
"hasOverride",
]),
);
// const UserClientSpan = SdkLoadService.WithSdk(
// () =>
// new SpanDefinition("userClient$", "DefaultSdkService", TracingLevel.Info, [
// "userId",
// "hasOverride",
// ]),
// );
const InitializeClientSpan = SdkLoadService.WithSdk(
// TODO: We can remove userId because it's already in the parent span
() => new SpanDefinition("initializeClient", "DefaultSdkService", TracingLevel.Info, ["userId"]),
);
// const InitializeClientSpan = SdkLoadService.WithSdk(
// // TODO: We can remove userId because it's already in the parent span
// () => new SpanDefinition("initializeClient", "DefaultSdkService", TracingLevel.Info, ["userId"]),
// );
const UserCryptoInitializedEvent = SdkLoadService.WithSdk(
() => new EventDefinition("User crypto initialized", "DefaultSdkService", TracingLevel.Info, []),
);
// const UserCryptoInitializedEvent = SdkLoadService.WithSdk(
// () => new EventDefinition("User crypto initialized", "DefaultSdkService", TracingLevel.Info, []),
// );
const OrgCryptoInitializedEvent = SdkLoadService.WithSdk(
() => new EventDefinition("Org crypto initialized", "DefaultSdkService", TracingLevel.Info, []),
);
// const OrgCryptoInitializedEvent = SdkLoadService.WithSdk(
// () => new EventDefinition("Org crypto initialized", "DefaultSdkService", TracingLevel.Info, []),
// );
const ClientStateInitializedEvent = SdkLoadService.WithSdk(
() => new EventDefinition("Client state initialized", "DefaultSdkService", TracingLevel.Info, []),
);
// const ClientStateInitializedEvent = SdkLoadService.WithSdk(
// () => new EventDefinition("Client state initialized", "DefaultSdkService", TracingLevel.Info, []),
// );
const FeatureFlagsLoadedEvent = SdkLoadService.WithSdk(
() => new EventDefinition("Feature flags loaded", "DefaultSdkService", TracingLevel.Info, []),
);
// const FeatureFlagsLoadedEvent = SdkLoadService.WithSdk(
// () => new EventDefinition("Feature flags loaded", "DefaultSdkService", TracingLevel.Info, []),
// );
// A symbol that represents an overridden client that is explicitly set to undefined,
// blocking the creation of an internal client for that user.
@@ -142,11 +140,14 @@ export class DefaultSdkService implements SdkService {
private apiService: ApiService,
private stateProvider: StateProvider,
private configService: ConfigService,
private logService: LogService,
private userAgent: string | null = null,
) {}
userClient$(userId: UserId): Observable<Rc<PasswordManagerClient>> {
const span = UserClientSpan.requiredValue.enter([new FieldValue("userId", userId)]);
const span = this.logService.span("userClient$", TracingLevel.Info, [
new FieldValue("userId", userId),
]);
return this.sdkClientOverrides.pipe(
takeWhile((clients) => clients[userId] !== UnsetClient, false),
map((clients) => {
@@ -320,7 +321,10 @@ export class DefaultSdkService implements SdkService {
orgKeys: Record<OrganizationId, EncString>,
parent: Span,
) {
using span = (await InitializeClientSpan).enter_with_parent(parent, [
// using span = (await InitializeClientSpan).enter_with_parent(parent, [
// new FieldValue("userId", userId),
// ]);
using span = this.logService.span("initializeClient", TracingLevel.Info, [
new FieldValue("userId", userId),
]);
@@ -340,7 +344,14 @@ export class DefaultSdkService implements SdkService {
},
accountCryptographicState: accountCryptographicState,
});
span.event(await UserCryptoInitializedEvent, `User crypto initialized for user`);
// span.event(await UserCryptoInitializedEvent, `User crypto initialized for user`);
this.logService.event(
span,
"cryptoInitialized",
`User crypto initialized for user`,
TracingLevel.Info,
[],
);
// We initialize the org crypto even if the org_keys are
// null to make sure any existing org keys are cleared.
@@ -349,14 +360,32 @@ export class DefaultSdkService implements SdkService {
Object.entries(orgKeys).map(([k, v]) => [asUuid(k), v.toJSON() as UnsignedSharedKey]),
),
});
span.event(await OrgCryptoInitializedEvent, `Org crypto initialized for user`);
this.logService.event(
span,
"orgCryptoInitialized",
`Org crypto initialized for user`,
TracingLevel.Info,
[],
);
// Initialize the SDK managed database and the client managed repositories.
await initializeState(userId, client.platform().state(), this.stateProvider);
span.event(await ClientStateInitializedEvent, "Client state initialized");
this.logService.event(
span,
"clientStateInitialized",
`Client state initialized`,
TracingLevel.Info,
[],
);
await this.loadFeatureFlags(client);
span.event(await FeatureFlagsLoadedEvent, "Feature flags loaded");
this.logService.event(
span,
"featureFlagsLoaded",
`Feature flags loaded`,
TracingLevel.Info,
[],
);
}
private async loadFeatureFlags(client: PasswordManagerClient) {

View File

@@ -1,14 +1,81 @@
// import { parse } from "stacktrace-parser";
import StackTrace from "stacktrace-js";
import {
EventDefinition,
FieldValue,
Span,
SpanDefinition,
TracingLevel,
} from "@bitwarden/sdk-internal";
import { LogLevel } from "./log-level";
import { LogService } from "./log.service";
export class ConsoleLogService implements LogService {
protected timersMap: Map<string, [number, number]> = new Map();
protected spanDefinitions = new Map<string, SpanDefinition>();
protected eventDefinitions = new Map<string, EventDefinition>();
constructor(
protected isDev: boolean,
protected filter: ((level: LogLevel) => boolean) | null = null,
) {}
/**
* Creates a new span.
* @param name Name of the span to create. Must be unique in the application.
* // TODO: Consider creating the name as we do state KeyDefinitions.
*/
span(name: string, level: TracingLevel, fields: FieldValue[]): Span {
let definition = this.spanDefinitions.get(name);
if (!definition) {
// TODO: A better way to get the caller info would be to use Webpack to modify
// the code calling logService.span(...) at compile time to inject the file, line, and function name.
const stack = StackTrace.getSync();
const callee = stack[1];
definition = new SpanDefinition(
name,
"",
level,
fields.map((f) => f.name),
callee.fileName,
callee.lineNumber,
callee.functionName,
);
this.spanDefinitions.set(name, definition);
}
return definition.enter(fields);
}
event(
span: Span,
name: string,
message: string,
level: TracingLevel,
fields: FieldValue[],
): void {
let definition = this.eventDefinitions.get(name);
if (!definition) {
// TODO: A better way to get the caller info would be to use Webpack to modify
// the code calling logService.event(...) at compile time to inject the file, line, and function name.
const stack = StackTrace.getSync();
const callee = stack[1];
definition = new EventDefinition(
name,
callee.functionName ?? "",
level,
fields.map((f) => f.name),
callee.fileName,
callee.lineNumber,
callee.functionName,
);
this.eventDefinitions.set(name, definition);
}
span.event(definition, message);
}
debug(message?: any, ...optionalParams: any[]) {
if (!this.isDev) {
return;

View File

@@ -1,6 +1,16 @@
import type { FieldValue, Span, TracingLevel } from "@bitwarden/sdk-internal";
import { LogLevel } from "./log-level";
export abstract class LogService {
abstract span(name: string, level: TracingLevel, fields: FieldValue[]): Span;
abstract event(
span: Span,
name: string,
message: string,
level: TracingLevel,
fields: FieldValue[],
): void;
abstract debug(message?: any, ...optionalParams: any[]): void;
abstract info(message?: any, ...optionalParams: any[]): void;
abstract warning(message?: any, ...optionalParams: any[]): void;

55
package-lock.json generated
View File

@@ -67,6 +67,7 @@
"qrious": "4.0.2",
"rxjs": "7.8.1",
"semver": "7.7.3",
"stacktrace-js": "2.0.2",
"tabbable": "6.3.0",
"tldts": "7.0.19",
"ts-node": "10.9.2",
@@ -20910,6 +20911,15 @@
"is-arrayish": "^0.2.1"
}
},
"node_modules/error-stack-parser": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz",
"integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==",
"license": "MIT",
"dependencies": {
"stackframe": "^1.3.4"
}
},
"node_modules/es-abstract": {
"version": "1.24.0",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz",
@@ -37379,6 +37389,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/stack-generator": {
"version": "2.0.10",
"resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz",
"integrity": "sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==",
"license": "MIT",
"dependencies": {
"stackframe": "^1.3.4"
}
},
"node_modules/stack-utils": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
@@ -37400,6 +37419,42 @@
"node": ">=8"
}
},
"node_modules/stackframe": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz",
"integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==",
"license": "MIT"
},
"node_modules/stacktrace-gps": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz",
"integrity": "sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==",
"license": "MIT",
"dependencies": {
"source-map": "0.5.6",
"stackframe": "^1.3.4"
}
},
"node_modules/stacktrace-gps/node_modules/source-map": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
"integrity": "sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/stacktrace-js": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz",
"integrity": "sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==",
"license": "MIT",
"dependencies": {
"error-stack-parser": "^2.0.6",
"stack-generator": "^2.0.5",
"stacktrace-gps": "^3.0.4"
}
},
"node_modules/stat-mode": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz",

View File

@@ -157,8 +157,8 @@
"@angular/platform-browser": "20.3.15",
"@angular/platform-browser-dynamic": "20.3.15",
"@angular/router": "20.3.15",
"@bitwarden/sdk-internal": "0.2.0-main.409",
"@bitwarden/commercial-sdk-internal": "0.2.0-main.409",
"@bitwarden/sdk-internal": "0.2.0-main.409",
"@electron/fuses": "1.8.0",
"@emotion/css": "11.13.5",
"@koa/multer": "4.0.0",
@@ -201,6 +201,7 @@
"qrious": "4.0.2",
"rxjs": "7.8.1",
"semver": "7.7.3",
"stacktrace-js": "2.0.2",
"tabbable": "6.3.0",
"tldts": "7.0.19",
"ts-node": "10.9.2",