Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,6 @@ cython_debug/
# Sphinx
_build/
_autosummary/

# Ephemeral Databases
*.db
1 change: 1 addition & 0 deletions samples/adk-sql-agent/.python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.13
53 changes: 53 additions & 0 deletions samples/adk-sql-agent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# OpenTelemetry ADK instrumentation example

<!-- TODO: link to devsite doc once it is published -->

This sample is an ADK agent instrumented with OpenTelemetry to send traces and logs with GenAI
prompts and responses, and metrics to Google Cloud Observability.

The Agent is a SQL expert that has full access to an ephemeral SQLite database. The database is
initially empty.

## APIs and Permissions

Enable the relevant Cloud Observability APIs if they aren't already enabled.
```sh
gcloud services enable aiplatform.googleapis.com telemetry.googleapis.com logging.googleapis.com monitoring.googleapis.com cloudtrace.googleapis.com
```

This sample writes to Cloud Logging, Cloud Monitoring, and Cloud Trace. Grant yourself the
following roles to run the example:
- `roles/logging.logWriter` – see https://cloud.google.com/logging/docs/access-control#permissions_and_roles
- `roles/monitoring.metricWriter` – see https://cloud.google.com/monitoring/access-control#predefined_roles
- `roles/telemetry.writer` – see https://cloud.google.com/trace/docs/iam#telemetry-roles

## Running the example

The sample can easily be run in Cloud Shell. You can also use
[Application Default Credentials][ADC] locally. Clone and set environment variables:
```sh
git clone https://github.com/GoogleCloudPlatform/opentelemetry-operations-python.git
cd opentelemetry-operations-python/samples/adk-sql-agent
```

Configure the environment and run the sample:
```sh
python -m venv venv/
source venv/bin/activate
pip install -r requirements.txt
env $(cat opentelemetry.env | xargs) python main.py
```

Alternatively if you have [`uv`](https://docs.astral.sh/uv/) installed:

```sh
uv run --env-file opentelemetry.env main.py
```

## Viewing the results

To view the generated traces with [Generative AI
events](https://cloud.google.com/trace/docs/finding-traces#view_generative_ai_events) in the
GCP console, use the [Trace Explorer](https://cloud.google.com/trace/docs/finding-traces). Filter for spans with OpenTelemetry Service: `adk-sql-agent`.

[ADC]: https://cloud.google.com/docs/authentication/application-default-credentials
132 changes: 132 additions & 0 deletions samples/adk-sql-agent/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import logging
import google.auth
import google.auth.transport.requests
import grpc
from google.auth.transport.grpc import AuthMetadataPlugin
from opentelemetry import _events as events
from opentelemetry import _logs as logs
from opentelemetry import metrics, trace
from opentelemetry.exporter.cloud_logging import CloudLoggingExporter
from opentelemetry.exporter.cloud_monitoring import CloudMonitoringMetricsExporter
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
OTLPSpanExporter,
)
from opentelemetry.instrumentation.sqlite3 import SQLite3Instrumentor
from opentelemetry.instrumentation.google_genai import GoogleGenAiSdkInstrumentor
from opentelemetry.instrumentation.vertexai import VertexAIInstrumentor
from opentelemetry.sdk._events import EventLoggerProvider
from opentelemetry.sdk._logs import LoggerProvider
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
import os

import uvicorn
from fastapi import FastAPI
from google.adk.cli.fast_api import get_fast_api_app

# Get the directory where main.py is located
AGENT_DIR = os.path.dirname(os.path.abspath(__file__))
# Example session DB URL (e.g., SQLite)
SESSION_DB_URL = "sqlite:///./sessions.db"
# Example allowed origins for CORS
ALLOWED_ORIGINS = ["http://localhost", "http://localhost:8080", "*"]
# Set web=True if you intend to serve a web interface, False otherwise
SERVE_WEB_INTERFACE = True

# [START opentelemetry_adk_otel_setup]
def setup_opentelemetry() -> None:
credentials, project_id = google.auth.default()
resource = Resource.create(
attributes={
SERVICE_NAME: "adk-sql-agent",
# The project to send spans to
"gcp.project_id": project_id,
}
)

# Set up OTLP auth
request = google.auth.transport.requests.Request()
auth_metadata_plugin = AuthMetadataPlugin(credentials=credentials, request=request)
channel_creds = grpc.composite_channel_credentials(
grpc.ssl_channel_credentials(),
grpc.metadata_call_credentials(auth_metadata_plugin),
)

# Set up OpenTelemetry Python SDK
tracer_provider = TracerProvider(resource=resource)
tracer_provider.add_span_processor(
BatchSpanProcessor(
OTLPSpanExporter(
credentials=channel_creds,
endpoint="https://telemetry.googleapis.com:443/v1/traces",
)
)
)
trace.set_tracer_provider(tracer_provider)

logger_provider = LoggerProvider(resource=resource)
logger_provider.add_log_record_processor(
BatchLogRecordProcessor(CloudLoggingExporter())
)
logs.set_logger_provider(logger_provider)

event_logger_provider = EventLoggerProvider(logger_provider)
events.set_event_logger_provider(event_logger_provider)

reader = PeriodicExportingMetricReader(CloudMonitoringMetricsExporter())
meter_provider = MeterProvider(metric_readers=[reader], resource=resource)
metrics.set_meter_provider(meter_provider)

# Load instrumentors
SQLite3Instrumentor().instrument()
VertexAIInstrumentor().instrument()
GoogleGenAiSdkInstrumentor().instrument()


# [END opentelemetry_adk_otel_setup]


def main() -> None:
# Make sure to set:
# OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true
# OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true
# in order to full prompts and responses and logs messages.
# For this sample, these can be set by loading the `main.env` file.
setup_opentelemetry()

# Call the function to get the FastAPI app instance.
# Ensure that the agent director name is the name of directory containing agent subdirectories,
# where each subdirectory represents a single agent with __init__.py and agent.py files.
# For this example this would be the current directory containing main.py.
# Note: Calling this method attempts to set the global tracer provider, which has already been
# set by the setup_opentelemetry() function.
app = get_fast_api_app(
agents_dir=AGENT_DIR,
session_service_uri=SESSION_DB_URL,
allow_origins=ALLOWED_ORIGINS,
web=SERVE_WEB_INTERFACE,
)

uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))


if __name__ == "__main__":
main()
2 changes: 2 additions & 0 deletions samples/adk-sql-agent/opentelemetry.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true
31 changes: 31 additions & 0 deletions samples/adk-sql-agent/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

[project]
name = "adk-sql-agent"
version = "0.1.0"
description = "An ADK agent that can run queries on an ephemeral SQLite database"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"google-adk>=1.6.1",
"opentelemetry-exporter-gcp-logging>=1.9.0a0",
"opentelemetry-exporter-gcp-monitoring>=1.9.0a0",
"opentelemetry-exporter-otlp-proto-grpc>=1.33.0",
"opentelemetry-instrumentation-google-genai>=0.2b0",
"opentelemetry-instrumentation-httpx>=0.54b0",
"opentelemetry-instrumentation-requests>=0.54b0",
"opentelemetry-instrumentation-sqlite3>=0.54b0",
"opentelemetry-instrumentation-vertexai>=2.0b0",
]
Loading