Skip to content

Commit 932c036

Browse files
committed
Add ADK instrumentation sample
1 parent cb7e94a commit 932c036

File tree

11 files changed

+3251
-0
lines changed

11 files changed

+3251
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,6 @@ cython_debug/
146146
# Sphinx
147147
_build/
148148
_autosummary/
149+
150+
# Ephemeral Databases
151+
*.db
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.13

samples/adk-sql-agent/README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# OpenTelemetry ADK instrumentation example
2+
3+
<!-- TODO: link to devsite doc once it is published -->
4+
5+
This sample is an ADK agent instrumented with OpenTelemetry to send traces and logs with GenAI
6+
prompts and responses, and metrics to Google Cloud Observability.
7+
8+
The Agent is a SQL expert that has full access to an ephemeral SQLite database. The database is
9+
initially empty.
10+
11+
## APIs and Permissions
12+
13+
Enable the relevant Cloud Observability APIs if they aren't already enabled.
14+
```sh
15+
gcloud services enable aiplatform.googleapis.com telemetry.googleapis.com logging.googleapis.com monitoring.googleapis.com cloudtrace.googleapis.com
16+
```
17+
18+
This sample writes to Cloud Logging, Cloud Monitoring, and Cloud Trace. Grant yourself the
19+
following roles to run the example:
20+
- `roles/logging.logWriter` – see https://cloud.google.com/logging/docs/access-control#permissions_and_roles
21+
- `roles/monitoring.metricWriter` – see https://cloud.google.com/monitoring/access-control#predefined_roles
22+
- `roles/telemetry.writer` – see https://cloud.google.com/trace/docs/iam#telemetry-roles
23+
24+
## Running the example
25+
26+
The sample can easily be run in Cloud Shell. You can also use
27+
[Application Default Credentials][ADC] locally. Clone and set environment variables:
28+
```sh
29+
git clone https://github.com/GoogleCloudPlatform/opentelemetry-operations-python.git
30+
cd opentelemetry-operations-python/samples/adk-sql-agent
31+
```
32+
33+
Create a virtual environment and install the dependencies:
34+
```sh
35+
python -m venv venv/
36+
source venv/bin/activate
37+
pip install -r requirements.txt
38+
```
39+
40+
Configure the environment and run the sample:
41+
42+
```sh
43+
env $(cat main.env | xargs) python main.py
44+
```
45+
46+
Alternatively if you have [`uv`](https://docs.astral.sh/uv/) installed:
47+
48+
```sh
49+
uv run --env-file main.env main.py
50+
```
51+
52+
## Viewing the results
53+
54+
To view the generated traces with [Generative AI
55+
events](https://cloud.google.com/trace/docs/finding-traces#view_generative_ai_events) in the
56+
GCP console, use the [Trace Explorer](https://cloud.google.com/trace/docs/finding-traces). Filter for spans with OpenTelemetry Service: `adk-sql-agent`.
57+
58+
[ADC]: https://cloud.google.com/docs/authentication/application-default-credentials

samples/adk-sql-agent/main.env

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
LOGFIRE_SEND_TO_LOGFIRE=false
2+
OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true
3+
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true
4+
5+
GOOGLE_GENAI_USE_VERTEXAI=TRUE
6+
GOOGLE_CLOUD_LOCATION=us-central1

samples/adk-sql-agent/main.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import logging
16+
import google.auth
17+
import google.auth.transport.requests
18+
import grpc
19+
from google.auth.transport.grpc import AuthMetadataPlugin
20+
from opentelemetry import _events as events
21+
from opentelemetry import _logs as logs
22+
from opentelemetry import metrics, trace
23+
from opentelemetry.exporter.cloud_logging import CloudLoggingExporter
24+
from opentelemetry.exporter.cloud_monitoring import CloudMonitoringMetricsExporter
25+
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
26+
OTLPSpanExporter,
27+
)
28+
from opentelemetry.instrumentation.sqlite3 import SQLite3Instrumentor
29+
from opentelemetry.instrumentation.google_genai import GoogleGenAiSdkInstrumentor
30+
from opentelemetry.instrumentation.vertexai import VertexAIInstrumentor
31+
from opentelemetry.sdk._events import EventLoggerProvider
32+
from opentelemetry.sdk._logs import LoggerProvider
33+
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
34+
from opentelemetry.sdk.metrics import MeterProvider
35+
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
36+
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
37+
from opentelemetry.sdk.trace import TracerProvider
38+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
39+
import os
40+
41+
import uvicorn
42+
from fastapi import FastAPI
43+
from google.adk.cli.fast_api import get_fast_api_app
44+
45+
# Get the directory where main.py is located
46+
AGENT_DIR = os.path.dirname(os.path.abspath(__file__))
47+
# Example session DB URL (e.g., SQLite)
48+
SESSION_DB_URL = "sqlite:///./sessions.db"
49+
# Example allowed origins for CORS
50+
ALLOWED_ORIGINS = ["http://localhost", "http://localhost:8080", "*"]
51+
# Set web=True if you intend to serve a web interface, False otherwise
52+
SERVE_WEB_INTERFACE = True
53+
54+
logging.getLogger("opentelemetry").setLevel(logging.DEBUG)
55+
56+
57+
# [START opentelemetry_adk_otel_setup]
58+
def setup_opentelemetry() -> None:
59+
credentials, project_id = google.auth.default()
60+
resource = Resource.create(
61+
attributes={
62+
SERVICE_NAME: "adk-sql-agent",
63+
# The project to send spans to
64+
"gcp.project_id": project_id,
65+
}
66+
)
67+
68+
# Set up OTLP auth
69+
request = google.auth.transport.requests.Request()
70+
auth_metadata_plugin = AuthMetadataPlugin(credentials=credentials, request=request)
71+
channel_creds = grpc.composite_channel_credentials(
72+
grpc.ssl_channel_credentials(),
73+
grpc.metadata_call_credentials(auth_metadata_plugin),
74+
)
75+
76+
# Set up OpenTelemetry Python SDK
77+
tracer_provider = TracerProvider(resource=resource)
78+
tracer_provider.add_span_processor(
79+
BatchSpanProcessor(
80+
OTLPSpanExporter(
81+
credentials=channel_creds,
82+
endpoint="https://telemetry.googleapis.com:443/v1/traces",
83+
)
84+
)
85+
)
86+
trace.set_tracer_provider(tracer_provider)
87+
88+
logger_provider = LoggerProvider(resource=resource)
89+
logger_provider.add_log_record_processor(
90+
BatchLogRecordProcessor(CloudLoggingExporter())
91+
)
92+
logs.set_logger_provider(logger_provider)
93+
94+
event_logger_provider = EventLoggerProvider(logger_provider)
95+
events.set_event_logger_provider(event_logger_provider)
96+
97+
reader = PeriodicExportingMetricReader(CloudMonitoringMetricsExporter())
98+
meter_provider = MeterProvider(metric_readers=[reader], resource=resource)
99+
metrics.set_meter_provider(meter_provider)
100+
101+
# Load instrumentors
102+
SQLite3Instrumentor().instrument()
103+
VertexAIInstrumentor().instrument()
104+
GoogleGenAiSdkInstrumentor().instrument()
105+
106+
107+
# [END opentelemetry_adk_otel_setup]
108+
109+
110+
def main() -> None:
111+
# Make sure to set:
112+
# OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true
113+
# OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true
114+
# in order to full prompts and responses and logs messages.
115+
# For this sample, these can be set by loading the `main.env` file.
116+
setup_opentelemetry()
117+
118+
# Call the function to get the FastAPI app instance.
119+
# Ensure that the agent director name is the name of directory containing agent subdirectories,
120+
# where each subdirectory represents a single agent with __init__.py and agent.py files.
121+
# For this example this would be the current directory containing main.py.
122+
# Note: Calling this method attempts to set the global tracer provider, which has already been
123+
# set by the setup_opentelemetry() function.
124+
app: FastAPI = get_fast_api_app(
125+
agents_dir=AGENT_DIR,
126+
session_service_uri=SESSION_DB_URL,
127+
allow_origins=ALLOWED_ORIGINS,
128+
web=SERVE_WEB_INTERFACE,
129+
)
130+
131+
uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))
132+
133+
134+
if __name__ == "__main__":
135+
main()
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
[project]
16+
name = "adk-sql-agent"
17+
version = "0.1.0"
18+
description = "An ADK agent that can run queries on an ephemeral SQLite database"
19+
readme = "README.md"
20+
requires-python = ">=3.12"
21+
dependencies = [
22+
"google-adk>=1.6.1",
23+
"opentelemetry-exporter-gcp-logging>=1.9.0a0",
24+
"opentelemetry-exporter-gcp-monitoring>=1.9.0a0",
25+
"opentelemetry-exporter-otlp-proto-grpc>=1.33.0",
26+
"opentelemetry-instrumentation-google-genai>=0.2b0",
27+
"opentelemetry-instrumentation-httpx>=0.54b0",
28+
"opentelemetry-instrumentation-requests>=0.54b0",
29+
"opentelemetry-instrumentation-sqlite3>=0.54b0",
30+
"opentelemetry-instrumentation-vertexai>=2.0b0",
31+
"rich>=14.0.0",
32+
]

0 commit comments

Comments
 (0)