Skip to content

Commit 6a9d748

Browse files
feat(gemini-cli): add Google Gemini CLI OpenTelemetry example
Send Gemini CLI telemetry — traces, metrics, and logs — to Last9 via OpenTelemetry. Two paths documented: - Direct: GEMINI_TELEMETRY_* env vars set endpoint; standard OTEL_EXPORTER_OTLP_HEADERS provides auth (picked up by underlying OpenTelemetry JS SDK even though gemini-cli does not pass headers explicitly to its OTLP exporters). - Local OTel Collector: useful for batching or fan-out. Traces are opt-in via GEMINI_TELEMETRY_TRACES_ENABLED=true. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d36adf9 commit 6a9d748

5 files changed

Lines changed: 215 additions & 0 deletions

File tree

gemini-cli/.env.example

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Last9 OTLP credentials — get these from:
2+
# https://app.last9.io/integrations?integration=OpenTelemetry
3+
4+
LAST9_OTLP_ENDPOINT=https://otlp.last9.io
5+
LAST9_OTLP_AUTH=Basic <your_credentials>

gemini-cli/.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Environment/secrets
2+
.env
3+
.env.local
4+
.env.*.local
5+
6+
# OS
7+
.DS_Store
8+
Thumbs.db
9+
10+
# Logs
11+
*.log

gemini-cli/README.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Gemini CLI — OpenTelemetry to Last9
2+
3+
Send Google's [Gemini CLI](https://github.com/google-gemini/gemini-cli) telemetry — traces, metrics, and logs — to Last9 via OpenTelemetry.
4+
5+
Gemini CLI emits all three OpenTelemetry signal types:
6+
- **Traces** — spans for tool calls, API requests, agent runs (opt-in via `GEMINI_TELEMETRY_TRACES_ENABLED=true`)
7+
- **Metrics** — session counts, token usage, latency, file ops, agent durations
8+
- **Logs** — structured events for prompts, API requests/responses, slash commands, file operations
9+
10+
## Prerequisites
11+
12+
- A Last9 account ([app.last9.io](https://app.last9.io))
13+
- OTLP credentials from **Integrations → OpenTelemetry** in the Last9 dashboard
14+
- Gemini CLI installed (`npm install -g @google/gemini-cli`)
15+
16+
## Option 1 — Direct Export to Last9 (no Collector)
17+
18+
Gemini CLI's OTLP exporters take `url` only, but the underlying OpenTelemetry JS SDK reads standard `OTEL_EXPORTER_OTLP_HEADERS` from env for authentication — so direct export works.
19+
20+
```bash
21+
# Gemini CLI telemetry switches
22+
export GEMINI_TELEMETRY_ENABLED=true
23+
export GEMINI_TELEMETRY_TARGET=local
24+
export GEMINI_TELEMETRY_OTLP_ENDPOINT="https://<your-last9-otlp-endpoint>"
25+
export GEMINI_TELEMETRY_OTLP_PROTOCOL=http
26+
export GEMINI_TELEMETRY_TRACES_ENABLED=true
27+
28+
# Standard OTel env — picked up by SDK for auth headers
29+
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic <your-last9-auth-token>"
30+
```
31+
32+
Reload and run:
33+
34+
```bash
35+
source ~/.zshrc
36+
gemini -p "explain this repo"
37+
```
38+
39+
## Option 2 — Local OpenTelemetry Collector
40+
41+
Useful for batching, retries, or fan-out to multiple backends.
42+
43+
1. Copy `.env.example` to `.env` and fill in your Last9 credentials:
44+
45+
```bash
46+
cp .env.example .env
47+
```
48+
49+
2. Start the collector:
50+
51+
```bash
52+
docker compose up -d
53+
```
54+
55+
3. Point Gemini CLI at the local collector:
56+
57+
```bash
58+
export GEMINI_TELEMETRY_ENABLED=true
59+
export GEMINI_TELEMETRY_TARGET=local
60+
export GEMINI_TELEMETRY_OTLP_ENDPOINT=http://localhost:4317
61+
export GEMINI_TELEMETRY_OTLP_PROTOCOL=grpc
62+
export GEMINI_TELEMETRY_TRACES_ENABLED=true
63+
unset OTEL_EXPORTER_OTLP_HEADERS # collector handles auth
64+
```
65+
66+
4. Run Gemini CLI:
67+
68+
```bash
69+
gemini
70+
```
71+
72+
## Configuration Reference
73+
74+
### Gemini CLI-specific
75+
76+
| Variable | Default | Purpose |
77+
|---|---|---|
78+
| `GEMINI_TELEMETRY_ENABLED` | `false` | Master toggle |
79+
| `GEMINI_TELEMETRY_TARGET` | `local` | `local` (OTLP) or `gcp` (Google Cloud) |
80+
| `GEMINI_TELEMETRY_OTLP_ENDPOINT` | `http://localhost:4317` | OTLP endpoint (base URL — `/v1/traces` etc. appended) |
81+
| `GEMINI_TELEMETRY_OTLP_PROTOCOL` | `grpc` | `grpc` or `http` |
82+
| `GEMINI_TELEMETRY_TRACES_ENABLED` | `false` | Opt-in span export |
83+
| `GEMINI_TELEMETRY_LOG_PROMPTS` | `true` | Include prompt text in logs |
84+
| `GEMINI_TELEMETRY_OUTFILE` || Write to local file instead of OTLP |
85+
86+
### Standard OTel env (used for auth headers)
87+
88+
| Variable | Purpose |
89+
|---|---|
90+
| `OTEL_EXPORTER_OTLP_HEADERS` | `Authorization=Basic <token>` |
91+
| `OTEL_EXPORTER_OTLP_{TRACES,METRICS,LOGS}_HEADERS` | Per-signal override |
92+
93+
### `.env` variables (Collector path)
94+
95+
| Variable | Purpose |
96+
|---|---|
97+
| `LAST9_OTLP_ENDPOINT` | Last9 OTLP endpoint (e.g. `https://otlp.last9.io`) |
98+
| `LAST9_OTLP_AUTH` | Last9 Basic auth header (`Basic <base64>`) |
99+
100+
## Verification
101+
102+
After running a Gemini CLI session for a minute:
103+
104+
1. **Traces** — filter by `service.name = gemini-cli` in Last9 Traces Explorer. Span name: `llm_call`.
105+
2. **Metrics** — search for `gemini_cli_session_count_total`, `gemini_cli_api_request_count_total`, `gemini_cli_token_usage_total`
106+
3. **Logs** — filter by `service.name = gemini-cli` in Last9 Logs Explorer
107+
108+
<details>
109+
<summary>Notable metrics</summary>
110+
111+
| Metric | Type |
112+
|---|---|
113+
| `gemini_cli.session.count` | counter |
114+
| `gemini_cli.tool.call.count` | counter |
115+
| `gemini_cli.tool.call.latency` | histogram |
116+
| `gemini_cli.api.request.count` | counter |
117+
| `gemini_cli.api.request.latency` | histogram |
118+
| `gemini_cli.token.usage` | counter |
119+
| `gemini_cli.file.operation.count` | counter |
120+
| `gemini_cli.lines.changed` | counter |
121+
| `gemini_cli.agent.run.count` | counter |
122+
| `gemini_cli.agent.duration` | histogram |
123+
| `gemini_cli.startup.duration` | histogram |
124+
| `gemini_cli.memory.usage` | gauge |
125+
| `gemini_cli.cpu.usage` | gauge |
126+
| `gen_ai.client.token.usage` | counter (GenAI semconv) |
127+
| `gen_ai.client.operation.duration` | histogram (GenAI semconv) |
128+
129+
</details>
130+
131+
## Troubleshooting
132+
133+
- **No data in Last9** — confirm `echo $GEMINI_TELEMETRY_ENABLED` returns `true` in the shell that ran `gemini`. Restart shell after editing `~/.zshrc`.
134+
- **Authentication errors** — verify `OTEL_EXPORTER_OTLP_HEADERS` is `Authorization=Basic <token>` (key=value format, not HTTP colon syntax). Trailing whitespace breaks it.
135+
- **Traces missing** — set `GEMINI_TELEMETRY_TRACES_ENABLED=true`. Span export is opt-in.
136+
- **gRPC connection refused** — make sure `-p 4317:4317` is exposed (collector path) and you're using `grpc` protocol with `http://localhost:4317` (not `https`).

gemini-cli/docker-compose.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
services:
2+
otel-collector:
3+
image: otel/opentelemetry-collector-contrib:0.144.0
4+
container_name: gemini-cli-otel-collector
5+
command: ["--config=/etc/otel/config.yaml"]
6+
volumes:
7+
- ./otel-collector-config.yaml:/etc/otel/config.yaml:ro
8+
env_file: .env
9+
environment:
10+
- DEPLOYMENT_ENV=${DEPLOYMENT_ENV:-development}
11+
ports:
12+
- "4317:4317" # gRPC receiver
13+
- "4318:4318" # HTTP receiver
14+
- "13133:13133" # Health check
15+
healthcheck:
16+
test: ["CMD", "wget", "--spider", "-q", "http://localhost:13133"]
17+
interval: 10s
18+
timeout: 5s
19+
retries: 3
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
receivers:
2+
# Receive logs, traces, and metrics from Gemini CLI via gRPC and HTTP
3+
otlp:
4+
protocols:
5+
grpc:
6+
endpoint: 0.0.0.0:4317
7+
http:
8+
endpoint: 0.0.0.0:4318
9+
10+
processors:
11+
batch:
12+
timeout: 5s
13+
send_batch_size: 1000
14+
15+
# Add organization-level resource attributes
16+
resource:
17+
attributes:
18+
- key: deployment.environment
19+
value: "${env:DEPLOYMENT_ENV}"
20+
action: upsert
21+
22+
exporters:
23+
otlp/last9:
24+
endpoint: "${env:LAST9_OTLP_ENDPOINT}"
25+
headers:
26+
"Authorization": "${env:LAST9_OTLP_AUTH}"
27+
28+
debug:
29+
verbosity: basic
30+
31+
service:
32+
pipelines:
33+
traces:
34+
receivers: [otlp]
35+
processors: [batch, resource]
36+
exporters: [otlp/last9, debug]
37+
metrics:
38+
receivers: [otlp]
39+
processors: [batch, resource]
40+
exporters: [otlp/last9, debug]
41+
logs:
42+
receivers: [otlp]
43+
processors: [batch, resource]
44+
exporters: [otlp/last9, debug]

0 commit comments

Comments
 (0)