189 lines
5.5 KiB
JavaScript
Executable File
189 lines
5.5 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
/**
|
|
* @license
|
|
* Copyright 2025 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import path from 'path';
|
|
import fs from 'fs';
|
|
import { spawn, execSync } from 'child_process';
|
|
import {
|
|
OTEL_DIR,
|
|
BIN_DIR,
|
|
fileExists,
|
|
waitForPort,
|
|
ensureBinary,
|
|
manageTelemetrySettings,
|
|
registerCleanup,
|
|
} from './telemetry_utils.js';
|
|
|
|
const OTEL_CONFIG_FILE = path.join(OTEL_DIR, 'collector-gcp.yaml');
|
|
const OTEL_LOG_FILE = path.join(OTEL_DIR, 'collector-gcp.log');
|
|
|
|
const getOtelConfigContent = (projectId) => `
|
|
receivers:
|
|
otlp:
|
|
protocols:
|
|
grpc:
|
|
endpoint: "localhost:4317"
|
|
processors:
|
|
batch:
|
|
timeout: 1s
|
|
exporters:
|
|
googlecloud:
|
|
project: "${projectId}"
|
|
metric:
|
|
prefix: "custom.googleapis.com/gemini_cli"
|
|
log:
|
|
default_log_name: "gemini_cli"
|
|
debug:
|
|
verbosity: detailed
|
|
service:
|
|
telemetry:
|
|
logs:
|
|
level: "debug"
|
|
metrics:
|
|
level: "none"
|
|
pipelines:
|
|
traces:
|
|
receivers: [otlp]
|
|
processors: [batch]
|
|
exporters: [googlecloud]
|
|
metrics:
|
|
receivers: [otlp]
|
|
processors: [batch]
|
|
exporters: [googlecloud, debug]
|
|
logs:
|
|
receivers: [otlp]
|
|
processors: [batch]
|
|
exporters: [googlecloud, debug]
|
|
`;
|
|
|
|
async function main() {
|
|
console.log('✨ Starting Local Telemetry Exporter for Google Cloud ✨');
|
|
|
|
let collectorProcess;
|
|
let collectorLogFd;
|
|
|
|
const originalSandboxSetting = manageTelemetrySettings(
|
|
true,
|
|
'http://localhost:4317',
|
|
'gcp',
|
|
);
|
|
registerCleanup(
|
|
() => [collectorProcess].filter((p) => p), // Function to get processes
|
|
() => [collectorLogFd].filter((fd) => fd), // Function to get FDs
|
|
originalSandboxSetting,
|
|
);
|
|
|
|
const projectId = process.env.OTLP_GOOGLE_CLOUD_PROJECT;
|
|
if (!projectId) {
|
|
console.error(
|
|
'🛑 Error: OTLP_GOOGLE_CLOUD_PROJECT environment variable is not exported.',
|
|
);
|
|
console.log(
|
|
' Please set it to your Google Cloud Project ID and try again.',
|
|
);
|
|
console.log(' `export OTLP_GOOGLE_CLOUD_PROJECT=your-project-id`');
|
|
process.exit(1);
|
|
}
|
|
console.log(`✅ Using OTLP Google Cloud Project ID: ${projectId}`);
|
|
|
|
console.log('\n🔑 Please ensure you are authenticated with Google Cloud:');
|
|
console.log(
|
|
' - Run `gcloud auth application-default login` OR ensure `GOOGLE_APPLICATION_CREDENTIALS` environment variable points to a valid service account key.',
|
|
);
|
|
console.log(
|
|
' - The account needs "Cloud Trace Agent", "Monitoring Metric Writer", and "Logs Writer" roles.',
|
|
);
|
|
|
|
if (!fileExists(BIN_DIR)) fs.mkdirSync(BIN_DIR, { recursive: true });
|
|
|
|
const otelcolPath = await ensureBinary(
|
|
'otelcol-contrib',
|
|
'open-telemetry/opentelemetry-collector-releases',
|
|
(version, platform, arch, ext) =>
|
|
`otelcol-contrib_${version}_${platform}_${arch}.${ext}`,
|
|
'otelcol-contrib',
|
|
false, // isJaeger = false
|
|
).catch((e) => {
|
|
console.error(`🛑 Error getting otelcol-contrib: ${e.message}`);
|
|
return null;
|
|
});
|
|
if (!otelcolPath) process.exit(1);
|
|
|
|
console.log('🧹 Cleaning up old processes and logs...');
|
|
try {
|
|
execSync('pkill -f "otelcol-contrib"');
|
|
console.log('✅ Stopped existing otelcol-contrib process.');
|
|
} catch (_e) {
|
|
/* no-op */
|
|
}
|
|
try {
|
|
fs.unlinkSync(OTEL_LOG_FILE);
|
|
console.log('✅ Deleted old GCP collector log.');
|
|
} catch (e) {
|
|
if (e.code !== 'ENOENT') console.error(e);
|
|
}
|
|
|
|
if (!fileExists(OTEL_DIR)) fs.mkdirSync(OTEL_DIR, { recursive: true });
|
|
fs.writeFileSync(OTEL_CONFIG_FILE, getOtelConfigContent(projectId));
|
|
console.log(`📄 Wrote OTEL collector config to ${OTEL_CONFIG_FILE}`);
|
|
|
|
console.log(`🚀 Starting OTEL collector for GCP... Logs: ${OTEL_LOG_FILE}`);
|
|
collectorLogFd = fs.openSync(OTEL_LOG_FILE, 'a');
|
|
collectorProcess = spawn(otelcolPath, ['--config', OTEL_CONFIG_FILE], {
|
|
stdio: ['ignore', collectorLogFd, collectorLogFd],
|
|
env: { ...process.env },
|
|
});
|
|
|
|
console.log(
|
|
`⏳ Waiting for OTEL collector to start (PID: ${collectorProcess.pid})...`,
|
|
);
|
|
|
|
try {
|
|
await waitForPort(4317);
|
|
console.log(`✅ OTEL collector started successfully on port 4317.`);
|
|
} catch (err) {
|
|
console.error(`🛑 Error: OTEL collector failed to start on port 4317.`);
|
|
console.error(err.message);
|
|
if (collectorProcess && collectorProcess.pid) {
|
|
process.kill(collectorProcess.pid, 'SIGKILL');
|
|
}
|
|
if (fileExists(OTEL_LOG_FILE)) {
|
|
console.error('📄 OTEL Collector Log Output:');
|
|
console.error(fs.readFileSync(OTEL_LOG_FILE, 'utf-8'));
|
|
}
|
|
process.exit(1);
|
|
}
|
|
|
|
collectorProcess.on('error', (err) => {
|
|
console.error(`${collectorProcess.spawnargs[0]} process error:`, err);
|
|
process.exit(1);
|
|
});
|
|
|
|
console.log(`\n✨ Local OTEL collector for GCP is running.`);
|
|
console.log(
|
|
'\n🚀 To send telemetry, run the Gemini CLI in a separate terminal window.',
|
|
);
|
|
console.log(`\n📄 Collector logs are being written to: ${OTEL_LOG_FILE}`);
|
|
console.log(
|
|
`📄 Tail collector logs in another terminal: tail -f ${OTEL_LOG_FILE}`,
|
|
);
|
|
console.log(`\n📊 View your telemetry data in Google Cloud Console:`);
|
|
console.log(
|
|
` - Logs: https://console.cloud.google.com/logs/query;query=logName%3D%22projects%2F${projectId}%2Flogs%2Fgemini_cli%22?project=${projectId}`,
|
|
);
|
|
console.log(
|
|
` - Metrics: https://console.cloud.google.com/monitoring/metrics-explorer?project=${projectId}`,
|
|
);
|
|
console.log(
|
|
` - Traces: https://console.cloud.google.com/traces/list?project=${projectId}`,
|
|
);
|
|
console.log(`\nPress Ctrl+C to exit.`);
|
|
}
|
|
|
|
main();
|