oneuptime/Copilot/src/Utils/AgentLogger.ts
Nawaz Dhandala 05b6a1b33b
Some checks are pending
Build / docker-build-accounts (push) Waiting to run
Build / docker-build-isolated-vm (push) Waiting to run
Build / docker-build-home (push) Waiting to run
Build / docker-build-worker (push) Waiting to run
Build / docker-build-workflow (push) Waiting to run
Build / docker-build-api-reference (push) Waiting to run
Build / docker-build-docs (push) Waiting to run
Build / docker-build-otel-collector (push) Waiting to run
Build / docker-build-app (push) Waiting to run
Build / docker-build-copilot (push) Waiting to run
Build / docker-build-e2e (push) Waiting to run
Build / docker-build-admin-dashboard (push) Waiting to run
Build / docker-build-dashboard (push) Waiting to run
Build / docker-build-probe (push) Waiting to run
Compile / compile-accounts (push) Waiting to run
Compile / compile-isolated-vm (push) Waiting to run
Compile / compile-common (push) Waiting to run
Compile / compile-app (push) Waiting to run
Compile / compile-home (push) Waiting to run
Compile / compile-worker (push) Waiting to run
Compile / compile-workflow (push) Waiting to run
Compile / compile-api-reference (push) Waiting to run
Compile / compile-docs-reference (push) Waiting to run
Compile / compile-copilot (push) Waiting to run
Compile / compile-nginx (push) Waiting to run
Compile / compile-infrastructure-agent (push) Waiting to run
Compile / compile-admin-dashboard (push) Waiting to run
Compile / compile-dashboard (push) Waiting to run
Compile / compile-e2e (push) Waiting to run
Compile / compile-probe (push) Waiting to run
Build / docker-build-probe-ingest (push) Waiting to run
Build / docker-build-server-monitor-ingest (push) Waiting to run
Build / docker-build-telemetry (push) Waiting to run
Build / docker-build-incoming-request-ingest (push) Waiting to run
Build / docker-build-status-page (push) Waiting to run
Build / docker-build-test-server (push) Waiting to run
CodeQL / Analyze (push) Waiting to run
Common Jobs / helm-lint (push) Waiting to run
Common Jobs / js-lint (push) Waiting to run
Push Test Images to Docker Hub and GitHub Container Registry / incoming-request-ingest-docker-image-deploy (push) Blocked by required conditions
Compile / compile-probe-ingest (push) Waiting to run
Compile / compile-server-monitor-ingest (push) Waiting to run
Compile / compile-telemetry (push) Waiting to run
Compile / compile-incoming-request-ingest (push) Waiting to run
Compile / compile-status-page (push) Waiting to run
Compile / compile-test-server (push) Waiting to run
Compile / compile-mcp (push) Waiting to run
OpenAPI Spec Generation / generate-openapi-spec (push) Waiting to run
Terraform Provider Generation / generate-terraform-provider (push) Waiting to run
Push Test Images to Docker Hub and GitHub Container Registry / generate-build-number (push) Waiting to run
Push Test Images to Docker Hub and GitHub Container Registry / read-version (push) Waiting to run
Push Test Images to Docker Hub and GitHub Container Registry / publish-mcp-server (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / nginx-docker-image-deploy (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / e2e-docker-image-deploy (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / test-server-docker-image-deploy (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / otel-collector-docker-image-deploy (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / isolated-vm-docker-image-deploy (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / home-docker-image-deploy (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / status-page-docker-image-deploy (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / test-docker-image-deploy (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / probe-ingest-docker-image-deploy (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / server-monitor-ingest-docker-image-deploy (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / telemetry-docker-image-deploy (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / probe-docker-image-deploy (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / dashboard-docker-image-deploy (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / admin-dashboard-docker-image-deploy (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / app-docker-image-deploy (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / api-reference-docker-image-deploy (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / accounts-docker-image-deploy (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / worker-docker-image-deploy (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / copilot-docker-image-deploy (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / workflow-docker-image-deploy (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / docs-docker-image-deploy (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / publish-terraform-provider (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / test-helm-chart (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / test-e2e-test-saas (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / test-e2e-test-self-hosted (push) Blocked by required conditions
Push Test Images to Docker Hub and GitHub Container Registry / infrastructure-agent-deploy (push) Blocked by required conditions
Common Test / test (push) Waiting to run
Incoming Request Ingest Test / test (push) Waiting to run
MCP Server Test / test (push) Waiting to run
ProbeIngest Test / test (push) Waiting to run
Probe Test / test (push) Waiting to run
Telemetry Test / test (push) Waiting to run
Tests / test-app (push) Waiting to run
Tests / test-home (push) Waiting to run
Tests / test-worker (push) Waiting to run
fix: reset log file on each command run to ensure fresh logging
2025-12-09 14:18:50 +00:00

158 lines
4.6 KiB
TypeScript

import fs from "node:fs";
import path from "node:path";
import logger, { LogBody } from "Common/Server/Utils/Logger";
/** Supported log levels for the agent-maintained file logs. */
export type AgentLogLevel = "DEBUG" | "INFO" | "WARN" | "ERROR";
/**
* Extends the shared logger by optionally mirroring output to a persistent file
* for auditing agent activity.
*/
export class AgentLogger {
private static logStream: fs.WriteStream | null = null;
private static logFilePath: string | null = null;
private static exitHandlersRegistered: boolean = false;
private static fileWriteFailed: boolean = false;
/** Enables/disables file logging depending on whether a path is provided. */
public static async configure(options: {
logFilePath?: string | undefined;
}): Promise<void> {
const targetPath: string | undefined = options.logFilePath?.trim()
? path.resolve(options.logFilePath)
: undefined;
if (!targetPath) {
await this.closeStream();
this.logFilePath = null;
logger.debug("File logging disabled");
return;
}
if (this.logFilePath === targetPath && this.logStream) {
return;
}
await this.closeStream();
await fs.promises.mkdir(path.dirname(targetPath), { recursive: true });
// Remove existing debug file to start fresh for each command run
try {
await fs.promises.unlink(targetPath);
} catch {
// File doesn't exist, ignore
}
this.logStream = fs.createWriteStream(targetPath, { flags: "w" });
this.logFilePath = targetPath;
this.fileWriteFailed = false;
this.registerExitHandlers();
this.info(`File logging enabled at ${targetPath}`);
}
/** Writes a debug entry to the console logger and file stream. */
public static debug(message: LogBody, meta?: unknown): void {
logger.debug(message);
this.writeToFile("DEBUG", message, meta);
}
/** Writes an informational entry to the console logger and file stream. */
public static info(message: LogBody, meta?: unknown): void {
logger.info(message);
this.writeToFile("INFO", message, meta);
}
/** Writes a warning entry to the console logger and file stream. */
public static warn(message: LogBody, meta?: unknown): void {
logger.warn(message);
this.writeToFile("WARN", message, meta);
}
/** Writes an error entry to the console logger and file stream. */
public static error(message: LogBody, meta?: unknown): void {
logger.error(message);
this.writeToFile("ERROR", message, meta);
}
/** Closes the file stream if one is currently open. */
private static async closeStream(): Promise<void> {
if (!this.logStream) {
return;
}
await new Promise<void>((resolve: () => void) => {
this.logStream?.end(resolve);
});
this.logStream = null;
logger.debug("File logging stream closed");
}
/**
* Serializes a log entry and safely writes it to the currently configured
* file stream.
*/
private static writeToFile(
level: AgentLogLevel,
message: LogBody,
meta?: unknown,
): void {
if (!this.logStream) {
return;
}
const timestamp: string = new Date().toISOString();
const serializedMessage: string = logger.serializeLogBody(message);
const serializedMeta: string | null = this.serializeMeta(meta);
const line: string = serializedMeta
? `${timestamp} [${level}] ${serializedMessage} ${serializedMeta}`
: `${timestamp} [${level}] ${serializedMessage}`;
try {
this.logStream.write(line + "\n");
} catch (error) {
if (!this.fileWriteFailed) {
this.fileWriteFailed = true;
logger.error(
`Failed to write logs to ${this.logFilePath ?? "<unknown>"}: ${(error as Error).message}`,
);
}
}
}
/** Converts metadata into a string representation for log lines. */
private static serializeMeta(meta?: unknown): string | null {
if (meta === undefined || meta === null) {
return null;
}
if (typeof meta === "string") {
return meta;
}
try {
return JSON.stringify(meta);
} catch (error) {
return `"<unserializable meta: ${(error as Error).message}>"`;
}
}
/** Installs once-only handlers to flush file logs during process exit. */
private static registerExitHandlers(): void {
if (this.exitHandlersRegistered) {
return;
}
const cleanup: () => void = () => {
void this.closeStream();
};
process.once("exit", cleanup);
process.once("SIGINT", cleanup);
process.once("SIGTERM", cleanup);
this.exitHandlersRegistered = true;
}
}
export default AgentLogger;