2025-06-13 20:28:18 -07:00
#!/usr/bin/env node
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
2025-08-25 22:11:27 +02:00
import path from 'node:path' ;
2025-12-02 21:27:37 -08:00
import * as fs from 'node:fs' ;
2025-08-25 22:11:27 +02:00
import { spawn , execSync } from 'node:child_process' ;
2025-06-13 20:28:18 -07:00
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' ,
2025-06-15 00:47:32 -04:00
'gcp' ,
2025-06-13 20:28:18 -07:00
) ;
registerCleanup (
( ) => [ collectorProcess ] . filter ( ( p ) => p ) , // Function to get processes
( ) => [ collectorLogFd ] . filter ( ( fd ) => fd ) , // Function to get FDs
originalSandboxSetting ,
) ;
2025-06-15 21:12:57 -04:00
const projectId = process . env . OTLP _GOOGLE _CLOUD _PROJECT ;
2025-06-13 20:28:18 -07:00
if ( ! projectId ) {
console . error (
2025-06-15 21:12:57 -04:00
'🛑 Error: OTLP_GOOGLE_CLOUD_PROJECT environment variable is not exported.' ,
2025-06-13 20:28:18 -07:00
) ;
2025-06-15 00:47:32 -04:00
console . log (
' Please set it to your Google Cloud Project ID and try again.' ,
) ;
2025-06-15 21:12:57 -04:00
console . log ( ' `export OTLP_GOOGLE_CLOUD_PROJECT=your-project-id`' ) ;
2025-06-13 20:28:18 -07:00
process . exit ( 1 ) ;
}
2025-06-15 21:12:57 -04:00
console . log ( ` ✅ Using OTLP Google Cloud Project ID: ${ projectId } ` ) ;
2025-06-13 20:28:18 -07:00
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 } ` ) ;
2025-12-02 21:27:37 -08:00
const spawnEnv = { ... process . env } ;
2025-06-13 20:28:18 -07:00
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 ] ,
2025-12-02 21:27:37 -08:00
env : spawnEnv ,
2025-06-13 20:28:18 -07:00
} ) ;
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. ` ) ;
2025-06-14 07:49:21 -07:00
console . log (
'\n🚀 To send telemetry, run the Gemini CLI in a separate terminal window.' ,
) ;
2025-06-13 20:28:18 -07:00
console . log ( ` \n 📄 Collector logs are being written to: ${ OTEL _LOG _FILE } ` ) ;
2025-06-23 01:10:26 -04:00
console . log (
` 📄 Tail collector logs in another terminal: tail -f ${ OTEL _LOG _FILE } ` ,
) ;
2025-06-13 20:28:18 -07:00
console . log ( ` \n 📊 View your telemetry data in Google Cloud Console: ` ) ;
console . log (
2025-06-15 00:47:32 -04:00
` - Logs: https://console.cloud.google.com/logs/query;query=logName%3D%22projects%2F ${ projectId } %2Flogs%2Fgemini_cli%22?project= ${ projectId } ` ,
2025-06-13 20:28:18 -07:00
) ;
console . log (
` - Metrics: https://console.cloud.google.com/monitoring/metrics-explorer?project= ${ projectId } ` ,
) ;
console . log (
2025-06-15 00:47:32 -04:00
` - Traces: https://console.cloud.google.com/traces/list?project= ${ projectId } ` ,
2025-06-13 20:28:18 -07:00
) ;
console . log ( ` \n Press Ctrl+C to exit. ` ) ;
}
main ( ) ;