mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-01 07:24:38 -07:00
138 lines
3.8 KiB
TypeScript
138 lines
3.8 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2025 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import { type JWTInput } from 'google-auth-library';
|
|
import { TraceExporter } from '@google-cloud/opentelemetry-cloud-trace-exporter';
|
|
import { MetricExporter } from '@google-cloud/opentelemetry-cloud-monitoring-exporter';
|
|
import { Logging, type Log } from '@google-cloud/logging';
|
|
import {
|
|
hrTimeToMilliseconds,
|
|
ExportResultCode,
|
|
type ExportResult,
|
|
} from '@opentelemetry/core';
|
|
import type {
|
|
ReadableLogRecord,
|
|
LogRecordExporter,
|
|
} from '@opentelemetry/sdk-logs';
|
|
|
|
/**
|
|
* Google Cloud Trace exporter that extends the official trace exporter
|
|
*/
|
|
export class GcpTraceExporter extends TraceExporter {
|
|
constructor(projectId?: string, credentials?: JWTInput) {
|
|
super({
|
|
projectId,
|
|
credentials,
|
|
resourceFilter: /^gcp\./,
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Google Cloud Monitoring exporter that extends the official metrics exporter
|
|
*/
|
|
export class GcpMetricExporter extends MetricExporter {
|
|
constructor(projectId?: string, credentials?: JWTInput) {
|
|
super({
|
|
projectId,
|
|
credentials,
|
|
prefix: 'custom.googleapis.com/gemini_cli',
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Google Cloud Logging exporter that uses the Cloud Logging client
|
|
*/
|
|
export class GcpLogExporter implements LogRecordExporter {
|
|
private logging: Logging;
|
|
private log: Log;
|
|
private pendingWrites: Array<Promise<void>> = [];
|
|
|
|
constructor(projectId?: string, credentials?: JWTInput) {
|
|
this.logging = new Logging({ projectId, credentials });
|
|
this.log = this.logging.log('gemini_cli');
|
|
}
|
|
|
|
export(
|
|
logs: ReadableLogRecord[],
|
|
resultCallback: (result: ExportResult) => void,
|
|
): void {
|
|
try {
|
|
const entries = logs.map((log) => {
|
|
const entry = this.log.entry(
|
|
{
|
|
severity: this.mapSeverityToCloudLogging(log.severityNumber),
|
|
timestamp: new Date(hrTimeToMilliseconds(log.hrTime)),
|
|
resource: {
|
|
type: 'global',
|
|
labels: {
|
|
project_id: this.logging.projectId,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
...log.attributes,
|
|
...log.resource?.attributes,
|
|
message: log.body,
|
|
},
|
|
);
|
|
return entry;
|
|
});
|
|
|
|
const writePromise = this.log
|
|
.write(entries)
|
|
.then(() => {
|
|
resultCallback({ code: ExportResultCode.SUCCESS });
|
|
})
|
|
.catch((error: Error) => {
|
|
resultCallback({
|
|
code: ExportResultCode.FAILED,
|
|
error,
|
|
});
|
|
})
|
|
.finally(() => {
|
|
const index = this.pendingWrites.indexOf(writePromise);
|
|
if (index > -1) {
|
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
this.pendingWrites.splice(index, 1);
|
|
}
|
|
});
|
|
this.pendingWrites.push(writePromise);
|
|
} catch (error) {
|
|
resultCallback({
|
|
code: ExportResultCode.FAILED,
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
error: error as Error,
|
|
});
|
|
}
|
|
}
|
|
|
|
async forceFlush(): Promise<void> {
|
|
if (this.pendingWrites.length > 0) {
|
|
await Promise.all(this.pendingWrites);
|
|
}
|
|
}
|
|
|
|
async shutdown(): Promise<void> {
|
|
await this.forceFlush();
|
|
this.pendingWrites = [];
|
|
}
|
|
|
|
private mapSeverityToCloudLogging(severityNumber?: number): string {
|
|
if (!severityNumber) return 'DEFAULT';
|
|
|
|
// Map OpenTelemetry severity numbers to Cloud Logging severity levels
|
|
// https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitynumber
|
|
if (severityNumber >= 21) return 'CRITICAL';
|
|
if (severityNumber >= 17) return 'ERROR';
|
|
if (severityNumber >= 13) return 'WARNING';
|
|
if (severityNumber >= 9) return 'INFO';
|
|
if (severityNumber >= 5) return 'DEBUG';
|
|
return 'DEFAULT';
|
|
}
|
|
}
|