1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-11 05:53:42 +00:00

partial work on ecs refactor; not sure it is worth keeping

This commit is contained in:
✨ Audrey ✨
2025-03-10 17:34:19 -04:00
parent e7752ee4e6
commit 1a86c10f87
4 changed files with 188 additions and 25 deletions

View File

@@ -1,22 +1,35 @@
import { Primitive } from "type-fest";
/** Elastic Common Schema log format - core fields.
*/
export interface EcsFormat {
"@timestamp": Date,
"@timestamp": number;
/** custom key/value pairs */
labels?: Record<string, string>,
labels?: Record<string, Primitive>;
/** system message related to the event */
message?: string,
message?: string;
/** keywords tagging the event */
tags?: Array<string>,
tags?: Array<string>;
/** describe the event; it is recommended that all events have these. */
event: {
kind?: "alert" | "enrichment" | "event" | "metric" | "state",
category?: "api" | "authentication" | "iam" | "process" | "session" ,
type?: "access" | "admin" | "allowed" | "creation" | "deletion" | "denied" | "end" | "error" | "info" | "start" | "user",
outcome?: "failure" | "success" | "unknown",
}
};
kind?: "alert" | "enrichment" | "event" | "metric" | "state";
category?: "api" | "authentication" | "iam" | "process" | "session";
type?:
| "access"
| "admin"
| "allowed"
| "creation"
| "deletion"
| "denied"
| "end"
| "error"
| "info"
| "start"
| "user";
outcome?: "failure" | "success" | "unknown";
};
}

View File

@@ -4,26 +4,27 @@ import { EcsFormat } from "./core";
/** extends core event logs with additional information */
export type EventFormat = EcsFormat & {
event: Partial<ProcessEvent> & Partial<ApplicationEvent> & {
/** event severity as a number */
severity?: LogLevelType,
},
}
action?: string;
event: Partial<ProcessEvent> &
Partial<ApplicationEvent> & {
/** event severity as a number */
severity?: LogLevelType;
};
};
export type ProcessEvent = {
start: Date,
duration: number,
end: Date,
start: Date;
duration: number;
end: Date;
};
export type ApplicationEvent = {
/** source of the event; this is usually a client type or service name */
provider: string,
/** source of the event; this is usually a client type or service name */
provider: string;
/** reason why the event occurred, according to the source */
reason: string,
/** reason why the event occurred, according to the source */
reason: string;
/** reference URL for the event */
reference: string,
/** reference URL for the event */
reference: string;
};

View File

@@ -0,0 +1,23 @@
import { EcsFormat } from "./core";
export type ServiceFormat = EcsFormat & {
/** documents the program providing the log */
service: {
/** Which kind of client is it? */
name: "android" | "cli" | "desktop" | "extension" | "ios" | "web";
/** identifies the service as a type of client device */
type: "client";
/** Information about the instance of the service providing the log */
node: {
/** a unique identifier(s) for this client installation */
name: string;
};
/** The environment to which the client was connected */
environment: "production" | "testing" | "development" | "local";
/** the unique identifier(s) for this client installation */
version: "2025.3.1-innovation-sprint";
};
};

View File

@@ -0,0 +1,126 @@
import { Subject } from "rxjs";
import { Primitive } from "type-fest";
import { LogService } from "../../platform/abstractions/log.service";
import { LogLevelType } from "../../platform/enums";
import { EcsFormat, ErrorFormat, EventFormat, LogFormat, UserFormat } from "./ecs-format";
import { ServiceFormat } from "./ecs-format/service";
import { SemanticLogger } from "./semantic-logger.abstraction";
type ClientInfo = ServiceFormat & { log: { logger: string } };
export class EcsLogger implements SemanticLogger {
constructor(
private logger: LogService,
private clientInfo: ClientInfo,
private now = () => Date.now(),
) {
this.pipe = new Subject();
}
private pipe: Subject<EcsFormat>;
log$() {
return this.pipe.asObservable();
}
event(info: EventFormat) {}
debug(labels: Record<string, Primitive> | string, message?: string): void {
this.log(labels, LogLevelType.Debug, message);
}
info(labels: Record<string, Primitive> | string, message?: string): void {
this.log(labels, LogLevelType.Info, message);
}
warn(labels: Record<string, Primitive> | string, message?: string): void {
this.log(labels, LogLevelType.Warning, message);
}
caught(error: Error, labels?: Record<string, Primitive>) {
const log: LogFormat & ErrorFormat & Partial<UserFormat> = {
...this.clientInfo,
error: {
message: error.message,
stack_trace: error.stack,
type: error.name,
},
labels,
event: {
kind: "event",
category: "process",
type: "error",
...this.clientInfo.event,
},
log: {
level: stringifyLevel(LogLevelType.Error),
...this.clientInfo.log,
},
"@timestamp": this.now(),
};
this.write(log);
}
error(labels: Record<string, Primitive> | string, message?: string): void {
this.log(labels, LogLevelType.Error, message);
}
panic(labels: Record<string, Primitive> | string, message?: string): never {
const panicMessage = this.log(labels, LogLevelType.Error, message) ?? "a fatal error occurred";
throw new Error(panicMessage);
}
private log(
content: Record<string, Primitive> | string,
level: LogLevelType,
maybeMessage?: string,
) {
const labels = typeof content === "string" ? { content } : content;
const message =
maybeMessage ?? (typeof content === "string" ? content : "message not provided");
const log: LogFormat & EventFormat = {
...this.clientInfo,
message,
labels,
event: {
kind: "event",
category: "process",
type: level === LogLevelType.Error ? "error" : "info",
severity: level,
...this.clientInfo.event,
},
log: {
level: stringifyLevel(level),
...this.clientInfo.log,
},
"@timestamp": this.now(),
};
this.write(log);
return log.message;
}
private write(event: EcsFormat) {
this.pipe.next(event);
}
}
function stringifyLevel(level: LogLevelType) {
switch (level) {
case LogLevelType.Debug:
return "debug";
case LogLevelType.Info:
return "info";
case LogLevelType.Warning:
return "warn";
case LogLevelType.Error:
return "error";
default:
return "info";
}
}