<aside> 📝 参考資料
<aside> 💡 計装するのは トレース + ログ
</aside>
export GOOGLE_API_KEY=AI...
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "google-genai",
# ]
# ///
from google import genai
googleai_client = genai.Client()
response = googleai_client.models.generate_content(
model="gemini-2.5-flash",
contents="OpenTelemetry について短い詩を書いてください。",
)
print(response.text)
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "google-genai",
# "opentelemetry-instrumentation-google-genai",
# "opentelemetry-sdk",
# ]
# ///
import os
from google import genai
from opentelemetry._events import set_event_logger_provider
from opentelemetry._logs import get_logger_provider, set_logger_provider
from opentelemetry.instrumentation.google_genai import GoogleGenAiSdkInstrumentor
from opentelemetry.sdk._events import EventLoggerProvider
from opentelemetry.sdk._logs import LoggerProvider
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, ConsoleLogExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter
from opentelemetry.trace import get_tracer_provider, set_tracer_provider
set_tracer_provider(TracerProvider())
get_tracer_provider().add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))
set_logger_provider(LoggerProvider())
get_logger_provider().add_log_record_processor(
BatchLogRecordProcessor(ConsoleLogExporter())
)
set_event_logger_provider(EventLoggerProvider())
os.environ["OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT"] = "true"
GoogleGenAiSdkInstrumentor().instrument()
googleai_client = genai.Client()
response = googleai_client.models.generate_content(
model="gemini-2.5-flash",
contents="OpenTelemetry について短い詩を書いてください。",
)
print(response.text)
{
"body": {
"content": "OpenTelemetry \\u306b\\u3064\\u3044\\u3066\\u77ed\\u3044\\u8a69\\u3092\\u66f8\\u3044\\u3066\\u304f\\u3060\\u3055\\u3044\\u3002"
},
"severity_number": null,
"severity_text": null,
"attributes": {
"gen_ai.system": "gemini"
},
"dropped_attributes": 0,
"timestamp": null,
"observed_timestamp": "2025-11-07T16:10:11.001160Z",
"trace_id": "0xe5826adf517c38de8599104dd601f736",
"span_id": "0x7500e671096be906",
"trace_flags": 1,
"resource": {
"attributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.38.0",
"service.name": "unknown_service"
},
"schema_url": ""
},
"event_name": "gen_ai.user.message"
}
{
"body": {
"index": 0,
"content": {
"parts": [
{
"function_call": null,
"code_execution_result": null,
"executable_code": null,
"file_data": null,
"function_response": null,
"inline_data": null,
"text": "OpenTelemetry\\u3001\\u958b\\u304b\\u308c\\u305f\\u6249\\u3001\\n\\u898b\\u3048\\u306c\\u6d41\\u308c\\u3092\\u3001\\u5149\\u3067\\u7167\\u3089\\u3059\\u3002\\n\\u5206\\u6563\\u3057\\u305f\\u4e16\\u754c\\u3001\\u5171\\u901a\\u306e\\u8a00\\u8449\\u3002\\n\\n\\u30c8\\u30ec\\u30fc\\u30b9\\u306f\\u7b4b\\u9053\\u3001\\u8fbf\\u308b\\u8ecc\\u8de1\\u3001\\n\\u30e1\\u30c8\\u30ea\\u30af\\u30b9\\u306f\\u9f13\\u52d5\\u3001\\u30b7\\u30b9\\u30c6\\u30e0\\u306e\\u606f\\u5439\\u3002\\n\\u30ed\\u30b0\\u306f\\u8a9e\\u308b\\u3001\\u771f\\u5b9f\\u306e\\u97ff\\u304d\\u3002\\n\\n\\u96c6\\u3081\\u3089\\u308c\\u3057\\u30c7\\u30fc\\u30bf\\u3001\\u7d21\\u304c\\u308c\\u308b\\u77e5\\u6075\\u3001\\n\\u95c7\\u306b\\u6f5c\\u3080\\u8b0e\\u3092\\u3001\\u660e\\u308b\\u307f\\u306b\\u51fa\\u3059\\u3002\\n\\u30b7\\u30b9\\u30c6\\u30e0\\u306e\\u672a\\u6765\\u3092\\u3001\\u78ba\\u304b\\u306b\\u5c0e\\u304f\\u3002",
"thought": null,
"thought_signature": null,
"video_metadata": null
}
],
"role": "model"
},
"finish_reason": "STOP"
},
"severity_number": null,
"severity_text": null,
"attributes": {
"gen_ai.system": "gemini"
},
"dropped_attributes": 0,
"timestamp": null,
"observed_timestamp": "2025-11-07T16:10:21.172870Z",
"trace_id": "0xe5826adf517c38de8599104dd601f736",
"span_id": "0x7500e671096be906",
"trace_flags": 1,
"resource": {
"attributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.38.0",
"service.name": "unknown_service"
},
"schema_url": ""
},
"event_name": "gen_ai.choice"
}
{
"name": "generate_content gemini-2.5-flash",
"context": {
"trace_id": "0xe5826adf517c38de8599104dd601f736",
"span_id": "0x7500e671096be906",
"trace_state": "[]"
},
"kind": "SpanKind.INTERNAL",
"parent_id": null,
"start_time": "2025-11-07T16:10:11.001064Z",
"end_time": "2025-11-07T16:10:21.173106Z",
"status": {
"status_code": "UNSET"
},
"attributes": {
"code.function.name": "google.genai.Models.generate_content",
"gen_ai.request.model": "gemini-2.5-flash",
"gen_ai.operation.name": "generate_content",
"gen_ai.usage.input_tokens": 10,
"gen_ai.usage.output_tokens": 99,
"gen_ai.response.finish_reasons": [
"stop"
],
"gen_ai.system": "gemini"
},
"events": [],
"links": [],
"resource": {
"attributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.38.0",
"service.name": "unknown_service"
},
"schema_url": ""
}
}
(※「発展」は一緒に手を動かす時間的余裕がない可能性が高いです)
拙ブログ Googleが発表した genai-processors の Research Agent Example を触る
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "genai-processors",
# "opentelemetry-instrumentation-google-genai",
# "opentelemetry-sdk",
# ]
# ///
import asyncio
import os
import time
from datetime import datetime
from genai_processors import content_api, processor, streams
from genai_processors.examples import research
from opentelemetry._events import set_event_logger_provider
from opentelemetry._logs import get_logger_provider, set_logger_provider
from opentelemetry.instrumentation.google_genai import GoogleGenAiSdkInstrumentor
from opentelemetry.sdk._events import EventLoggerProvider
from opentelemetry.sdk._logs import LoggerProvider
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, ConsoleLogExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter
from opentelemetry.trace import get_tracer_provider, set_tracer_provider
ProcessorPart = processor.ProcessorPart
def render_part(part: ProcessorPart) -> None:
if part.substream_name == "status":
print(f"--- \\n *Status*: {part.text}")
else:
try:
print(part.text)
except Exception:
print(f" {part.text} ")
GOOGLE_API_KEY = os.environ["GOOGLE_API_KEY"]
USER_PROMPT = "Research the best things about owning dalmatians!"
async def main():
input_stream = streams.stream_content([ProcessorPart(USER_PROMPT)])
output_parts = content_api.ProcessorContent()
async for content_part in research.ResearchAgent(api_key=GOOGLE_API_KEY)(
input_stream
):
if content_part.substream_name == "status":
render_part(content_part)
output_parts += content_part
render_part(
ProcessorPart(f"""# Final synthesized research
{content_api.as_text(output_parts, substream_name="")}""")
)
if __name__ == "__main__":
os.environ["OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT"] = "true"
tracing_file = f"trace-{datetime.now().strftime('%Y%m%d-%H%M%S')}.jsonl"
with open(tracing_file, "w") as out:
set_tracer_provider(TracerProvider())
get_tracer_provider().add_span_processor(
BatchSpanProcessor(ConsoleSpanExporter(out=out))
)
set_logger_provider(LoggerProvider())
get_logger_provider().add_log_record_processor(
BatchLogRecordProcessor(ConsoleLogExporter(out=out))
)
set_event_logger_provider(EventLoggerProvider())
GoogleGenAiSdkInstrumentor().instrument()
asyncio.run(main())
time.sleep(5)
<aside> 📝 類例 Google先生の新作、Geminiでテキストから任意の情報を抽出する LangExtract をお試し
</aside>
拙ブログ OpenTelemetry で google-genai のテレメトリを、ローカル環境に docker run で立てた exporter に送る
docker run -p 4317:4317 -p 4318:4318 --rm -v $(pwd)/collector-config.yaml:/etc/otelcol/config.yaml otel/opentelemetry-collector:0.139.0
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
exporters:
debug:
verbosity: detailed
service:
pipelines:
traces:
receivers: [otlp]
exporters: [debug]
metrics:
receivers: [otlp]
exporters: [debug]
logs:
receivers: [otlp]
exporters: [debug]
generate.pyを戻す