Skip to content

Commit b1befee

Browse files
heaventouristgemini-code-assist[bot]jeropmrpmohiburrahmanJefftree
authored
feat(telemetry) Instrument traces with more attributes and make them available to OTEL users (#20237)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Jerop Kipruto <jerop@google.com> Co-authored-by: MD. MOHIBUR RAHMAN <35300157+mrpmohiburrahman@users.noreply.github.com> Co-authored-by: Jeffrey Ying <jeffrey.ying86@live.com> Co-authored-by: Bryan Morgan <bryanmorgan@google.com> Co-authored-by: joshualitt <joshualitt@google.com> Co-authored-by: Dev Randalpura <devrandalpura@google.com> Co-authored-by: Google Admin <github-admin@google.com> Co-authored-by: Ben Knutson <benknutson@google.com>
1 parent 4b7ce1f commit b1befee

21 files changed

Lines changed: 905 additions & 138 deletions

docs/cli/telemetry.md

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,12 @@ Sends telemetry directly to Google Cloud services. No collector needed.
176176
}
177177
```
178178
2. Run Gemini CLI and send prompts.
179-
3. View logs and metrics:
179+
3. View logs, metrics, and traces:
180180
- Open the Google Cloud Console in your browser after sending prompts:
181-
- Logs: https://console.cloud.google.com/logs/
182-
- Metrics: https://console.cloud.google.com/monitoring/metrics-explorer
183-
- Traces: https://console.cloud.google.com/traces/list
181+
- Logs (Logs Explorer): https://console.cloud.google.com/logs/
182+
- Metrics (Metrics Explorer):
183+
https://console.cloud.google.com/monitoring/metrics-explorer
184+
- Traces (Trace Explorer): https://console.cloud.google.com/traces/list
184185
185186
### Collector-based export (advanced)
186187
@@ -208,11 +209,12 @@ forward data to Google Cloud.
208209
- Save collector logs to `~/.gemini/tmp/<projectHash>/otel/collector-gcp.log`
209210
- Stop collector on exit (e.g. `Ctrl+C`)
210211
3. Run Gemini CLI and send prompts.
211-
4. View logs and metrics:
212+
4. View logs, metrics, and traces:
212213
- Open the Google Cloud Console in your browser after sending prompts:
213-
- Logs: https://console.cloud.google.com/logs/
214-
- Metrics: https://console.cloud.google.com/monitoring/metrics-explorer
215-
- Traces: https://console.cloud.google.com/traces/list
214+
- Logs (Logs Explorer): https://console.cloud.google.com/logs/
215+
- Metrics (Metrics Explorer):
216+
https://console.cloud.google.com/monitoring/metrics-explorer
217+
- Traces (Trace Explorer): https://console.cloud.google.com/traces/list
216218
- Open `~/.gemini/tmp/<projectHash>/otel/collector-gcp.log` to view local
217219
collector logs.
218220
@@ -270,10 +272,10 @@ For local development and debugging, you can capture telemetry data locally:
270272
3. View traces at http://localhost:16686 and logs/metrics in the collector log
271273
file.
272274
273-
## Logs and metrics
275+
## Logs, metrics, and traces
274276
275-
The following section describes the structure of logs and metrics generated for
276-
Gemini CLI.
277+
The following section describes the structure of logs, metrics, and traces
278+
generated for Gemini CLI.
277279
278280
The `session.id`, `installation.id`, `active_approval_mode`, and `user.email`
279281
(available only when authenticated with a Google account) are included as common
@@ -824,6 +826,32 @@ Optional performance monitoring for startup, CPU/memory, and phase timing.
824826
- `current_value` (number)
825827
- `baseline_value` (number)
826828
829+
### Traces
830+
831+
Traces offer a granular, "under-the-hood" view of every agent and backend
832+
operation. By providing a high-fidelity execution map, they enable precise
833+
debugging of complex tool interactions and deep performance optimization. Each
834+
trace captures rich, consistent metadata via custom span attributes:
835+
836+
- `gen_ai.operation.name` (string): The high-level operation kind (e.g.
837+
"tool_call", "llm_call").
838+
- `gen_ai.agent.name` (string): The service agent identifier ("gemini-cli").
839+
- `gen_ai.agent.description` (string): The service agent description.
840+
- `gen_ai.input.messages` (string): Input messages or metadata specific to the
841+
operation.
842+
- `gen_ai.output.messages` (string): Output messages or metadata generated from
843+
the operation.
844+
- `gen_ai.request.model` (string): The request model name.
845+
- `gen_ai.response.model` (string): The response model name.
846+
- `gen_ai.system_instructions` (json string): The system instructions.
847+
- `gen_ai.prompt.name` (string): The prompt name.
848+
- `gen_ai.tool.name` (string): The executed tool's name.
849+
- `gen_ai.tool.call_id` (string): The generated specific ID of the tool call.
850+
- `gen_ai.tool.description` (string): The executed tool's description.
851+
- `gen_ai.tool.definitions` (json string): The executed tool's description.
852+
- `gen_ai.conversation.id` (string): The current CLI session ID.
853+
- Additional user-defined Custom Attributes passed via the span's configuration.
854+
827855
#### GenAI semantic convention
828856
829857
The following metrics comply with [OpenTelemetry GenAI semantic conventions] for

docs/local-development.md

Lines changed: 49 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,21 @@
11
# Local development guide
22

33
This guide provides instructions for setting up and using local development
4-
features, such as development tracing.
4+
features, such as tracing.
55

6-
## Development tracing
6+
## Tracing
77

8-
Development traces (dev traces) are OpenTelemetry (OTel) traces that help you
9-
debug your code by instrumenting interesting events like model calls, tool
10-
scheduler, tool calls, etc.
8+
Traces are OpenTelemetry (OTel) records that help you debug your code by
9+
instrumenting key events like model calls, tool scheduler operations, and tool
10+
calls.
1111

12-
Dev traces are verbose and are specifically meant for understanding agent
13-
behavior and debugging issues. They are disabled by default.
12+
Traces provide deep visibility into agent behavior and are invaluable for
13+
debugging complex issues. They are captured automatically when telemetry is
14+
enabled.
1415

15-
To enable dev traces, set the `GEMINI_DEV_TRACING=true` environment variable
16-
when running Gemini CLI.
16+
### Viewing traces
1717

18-
### Viewing dev traces
19-
20-
You can view dev traces using either Jaeger or the Genkit Developer UI.
18+
You can view traces using either Jaeger or the Genkit Developer UI.
2119

2220
#### Using Genkit
2321

@@ -37,13 +35,12 @@ Genkit provides a web-based UI for viewing traces and other telemetry data.
3735
Genkit Developer UI: http://localhost:4000
3836
```
3937

40-
2. **Run Gemini CLI with dev tracing:**
38+
2. **Run Gemini CLI:**
4139

42-
In a separate terminal, run your Gemini CLI command with the
43-
`GEMINI_DEV_TRACING` environment variable:
40+
In a separate terminal, run your Gemini CLI command:
4441

4542
```bash
46-
GEMINI_DEV_TRACING=true gemini
43+
gemini
4744
```
4845

4946
3. **View the traces:**
@@ -53,7 +50,7 @@ Genkit provides a web-based UI for viewing traces and other telemetry data.
5350

5451
#### Using Jaeger
5552

56-
You can view dev traces in the Jaeger UI. To get started, follow these steps:
53+
You can view traces in the Jaeger UI. To get started, follow these steps:
5754

5855
1. **Start the telemetry collector:**
5956

@@ -67,13 +64,12 @@ You can view dev traces in the Jaeger UI. To get started, follow these steps:
6764
This command also configures your workspace for local telemetry and provides
6865
a link to the Jaeger UI (usually `http://localhost:16686`).
6966

70-
2. **Run Gemini CLI with dev tracing:**
67+
2. **Run Gemini CLI:**
7168

72-
In a separate terminal, run your Gemini CLI command with the
73-
`GEMINI_DEV_TRACING` environment variable:
69+
In a separate terminal, run your Gemini CLI command:
7470

7571
```bash
76-
GEMINI_DEV_TRACING=true gemini
72+
gemini
7773
```
7874

7975
3. **View the traces:**
@@ -84,10 +80,10 @@ You can view dev traces in the Jaeger UI. To get started, follow these steps:
8480
For more detailed information on telemetry, see the
8581
[telemetry documentation](./cli/telemetry.md).
8682

87-
### Instrumenting code with dev traces
83+
### Instrumenting code with traces
8884

89-
You can add dev traces to your own code for more detailed instrumentation. This
90-
is useful for debugging and understanding the flow of execution.
85+
You can add traces to your own code for more detailed instrumentation. This is
86+
useful for debugging and understanding the flow of execution.
9187

9288
Use the `runInDevTraceSpan` function to wrap any section of code in a trace
9389
span.
@@ -96,29 +92,39 @@ Here is a basic example:
9692

9793
```typescript
9894
import { runInDevTraceSpan } from '@google/gemini-cli-core';
99-
100-
await runInDevTraceSpan({ name: 'my-custom-span' }, async ({ metadata }) => {
101-
// The `metadata` object allows you to record the input and output of the
102-
// operation as well as other attributes.
103-
metadata.input = { key: 'value' };
104-
// Set custom attributes.
105-
metadata.attributes['gen_ai.request.model'] = 'gemini-4.0-mega';
106-
107-
// Your code to be traced goes here
108-
try {
109-
const output = await somethingRisky();
110-
metadata.output = output;
111-
return output;
112-
} catch (e) {
113-
metadata.error = e;
114-
throw e;
115-
}
116-
});
95+
import { GeminiCliOperation } from '@google/gemini-cli-core/lib/telemetry/constants.js';
96+
97+
await runInDevTraceSpan(
98+
{
99+
operation: GeminiCliOperation.ToolCall,
100+
attributes: {
101+
[GEN_AI_AGENT_NAME]: 'gemini-cli',
102+
},
103+
},
104+
async ({ metadata }) => {
105+
// The `metadata` object allows you to record the input and output of the
106+
// operation as well as other attributes.
107+
metadata.input = { key: 'value' };
108+
// Set custom attributes.
109+
metadata.attributes['custom.attribute'] = 'custom.value';
110+
111+
// Your code to be traced goes here
112+
try {
113+
const output = await somethingRisky();
114+
metadata.output = output;
115+
return output;
116+
} catch (e) {
117+
metadata.error = e;
118+
throw e;
119+
}
120+
},
121+
);
117122
```
118123
119124
In this example:
120125
121-
- `name`: The name of the span, which will be displayed in the trace.
126+
- `operation`: The operation type of the span, represented by the
127+
`GeminiCliOperation` enum.
122128
- `metadata.input`: (Optional) An object containing the input data for the
123129
traced operation.
124130
- `metadata.output`: (Optional) An object containing the output data from the

integration-tests/acp-telemetry.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ describe('ACP telemetry', () => {
7272
GEMINI_TELEMETRY_ENABLED: 'true',
7373
GEMINI_TELEMETRY_TARGET: 'local',
7474
GEMINI_TELEMETRY_OUTFILE: telemetryPath,
75-
// GEMINI_DEV_TRACING not set: fake responses aren't instrumented for spans
7675
},
7776
},
7877
);

packages/cli/src/ui/hooks/useGeminiStream.test.tsx

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import type {
2525
Config,
2626
EditorType,
2727
AnyToolInvocation,
28+
SpanMetadata,
2829
} from '@google/gemini-cli-core';
2930
import {
3031
CoreToolCallStatus,
@@ -39,6 +40,7 @@ import {
3940
coreEvents,
4041
CoreEvent,
4142
MCPDiscoveryState,
43+
GeminiCliOperation,
4244
getPlanModeExitMessage,
4345
} from '@google/gemini-cli-core';
4446
import type { Part, PartListUnion } from '@google/genai';
@@ -101,6 +103,19 @@ const MockValidationRequiredError = vi.hoisted(
101103
},
102104
);
103105

106+
const mockRunInDevTraceSpan = vi.hoisted(() =>
107+
vi.fn(async (opts, fn) => {
108+
const metadata: SpanMetadata = {
109+
name: opts.operation,
110+
attributes: opts.attributes || {},
111+
};
112+
return await fn({
113+
metadata,
114+
endSpan: vi.fn(),
115+
});
116+
}),
117+
);
118+
104119
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
105120
const actualCoreModule = (await importOriginal()) as any;
106121
return {
@@ -113,6 +128,7 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
113128
tokenLimit: vi.fn().mockReturnValue(100), // Mock tokenLimit
114129
recordToolCallInteractions: vi.fn().mockResolvedValue(undefined),
115130
getCodeAssistServer: vi.fn().mockReturnValue(undefined),
131+
runInDevTraceSpan: mockRunInDevTraceSpan,
116132
};
117133
});
118134

@@ -794,6 +810,23 @@ describe('useGeminiStream', () => {
794810
item.text.includes('Got it. Focusing on tests only.'),
795811
),
796812
).toBe(true);
813+
814+
expect(mockRunInDevTraceSpan).toHaveBeenCalledWith(
815+
expect.objectContaining({
816+
operation: GeminiCliOperation.SystemPrompt,
817+
}),
818+
expect.any(Function),
819+
);
820+
821+
const spanArgs = mockRunInDevTraceSpan.mock.calls[0];
822+
const fn = spanArgs[1];
823+
const metadata = { attributes: {} };
824+
await act(async () => {
825+
await fn({ metadata, endSpan: vi.fn() });
826+
});
827+
expect(metadata).toMatchObject({
828+
input: sentParts,
829+
});
797830
});
798831

799832
it('should handle all tool calls being cancelled', async () => {
@@ -2452,6 +2485,11 @@ describe('useGeminiStream', () => {
24522485
// This is the core fix validation: Rationale comes before tools are even scheduled (awaited)
24532486
expect(rationaleIndex).toBeLessThan(scheduleIndex);
24542487
expect(rationaleIndex).toBeLessThan(toolGroupIndex);
2488+
2489+
// Ensure all state updates from recursive submitQuery are settled
2490+
await waitFor(() => {
2491+
expect(result.current.streamingState).toBe(StreamingState.Idle);
2492+
});
24552493
});
24562494

24572495
it('should process @include commands, adding user turn after processing to prevent race conditions', async () => {
@@ -3554,4 +3592,31 @@ describe('useGeminiStream', () => {
35543592
expect(result.current.pendingHistoryItems.length).toEqual(0);
35553593
});
35563594
});
3595+
3596+
it('should trace UserPrompt telemetry on submitQuery', async () => {
3597+
const { result } = renderTestHook();
3598+
3599+
mockSendMessageStream.mockReturnValue(
3600+
(async function* () {
3601+
yield { type: ServerGeminiEventType.Content, value: 'Response' };
3602+
})(),
3603+
);
3604+
3605+
await act(async () => {
3606+
await result.current.submitQuery('telemetry test query');
3607+
});
3608+
3609+
const userPromptCall = mockRunInDevTraceSpan.mock.calls.find(
3610+
(call) =>
3611+
call[0].operation === GeminiCliOperation.UserPrompt ||
3612+
call[0].operation === 'UserPrompt',
3613+
);
3614+
expect(userPromptCall).toBeDefined();
3615+
3616+
const spanMetadata = {} as SpanMetadata;
3617+
await act(async () => {
3618+
await userPromptCall![1]({ metadata: spanMetadata, endSpan: vi.fn() });
3619+
});
3620+
expect(spanMetadata.input).toBe('telemetry test query');
3621+
});
35573622
});

packages/cli/src/ui/hooks/useGeminiStream.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
CoreToolCallStatus,
3737
buildUserSteeringHintPrompt,
3838
generateSteeringAckMessage,
39+
GeminiCliOperation,
3940
getPlanModeExitMessage,
4041
} from '@google/gemini-cli-core';
4142
import type {
@@ -1262,7 +1263,11 @@ export const useGeminiStream = (
12621263
prompt_id?: string,
12631264
) =>
12641265
runInDevTraceSpan(
1265-
{ name: 'submitQuery' },
1266+
{
1267+
operation: options?.isContinuation
1268+
? GeminiCliOperation.SystemPrompt
1269+
: GeminiCliOperation.UserPrompt,
1270+
},
12661271
async ({ metadata: spanMetadata }) => {
12671272
spanMetadata.input = query;
12681273

0 commit comments

Comments
 (0)