Skip to content

feat: add otel structured logging#552

Open
pawel-maciejczek wants to merge 2 commits intogoogle:mainfrom
pawel-maciejczek:feature/telemetry-logging
Open

feat: add otel structured logging#552
pawel-maciejczek wants to merge 2 commits intogoogle:mainfrom
pawel-maciejczek:feature/telemetry-logging

Conversation

@pawel-maciejczek
Copy link
Contributor

@pawel-maciejczek pawel-maciejczek commented Feb 10, 2026

This PR introduces structured logging for GenAI requests and responses.

Logging is based on OTel GenAI semconv (linked from the code), but aims for consistency with ADK Python implementation. The deviations between the current implementation and semconv are documented in the code.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @pawel-maciejczek, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces comprehensive OpenTelemetry structured logging into the application. The primary goal is to enhance observability by capturing detailed information about LLM interactions, including system messages, user inputs, and inference outcomes. This integration provides a standardized way to emit logs, which can then be processed and exported to various backend systems, such as Google Cloud, for analysis and monitoring. The changes involve adding new logging utilities, updating core LLM interaction logic to use these utilities, and extending the telemetry setup to configure and manage OpenTelemetry log providers and processors.

Highlights

  • OpenTelemetry Structured Logging Integration: Integrated OpenTelemetry structured logging capabilities across the application, enabling detailed log capture for various events.
  • New Logging Functions: Introduced dedicated functions (LogSystemMessage, LogUserMessage, LogInferenceResult) within the telemetry package to standardize the logging of LLM interactions.
  • LLM Interaction Logging: Modified the core LLM call flow to utilize the new telemetry logging functions, capturing system messages, user prompts, and inference results.
  • Telemetry Configuration Extension: Extended the telemetry configuration to support OpenTelemetry log processors and logger providers, allowing for flexible log export mechanisms, including to Google Cloud.
  • Dependency Updates: Updated go.mod and go.sum to include new OpenTelemetry logging libraries and to bump versions of existing OpenTelemetry, gRPC, Protobuf, and other related modules.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • examples/tools/loadartifacts/main.go
    • Imported the new telemetry package.
    • Initialized OpenTelemetry providers using telemetry.New and SetGlobalOtelProviders in the main function.
  • go.mod
    • Added new OpenTelemetry logging dependencies (go.opentelemetry.io/contrib/bridges/otelslog, go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp, go.opentelemetry.io/otel/log, go.opentelemetry.io/otel/sdk/log).
    • Updated various OpenTelemetry core and contrib module versions (e.g., go.opentelemetry.io/otel, go.opentelemetry.io/otel/metric, go.opentelemetry.io/otel/sdk, go.opentelemetry.io/otel/sdk/metric, go.opentelemetry.io/otel/trace).
    • Updated golang.org/x/sync from v0.18.0 to v0.19.0.
    • Updated google.golang.org/grpc from v1.77.0 to v1.78.0.
    • Updated google.golang.org/protobuf from v1.36.10 to v1.36.11.
    • Updated github.com/grpc-ecosystem/grpc-gateway/v2 from v2.27.3 to v2.27.7.
    • Updated golang.org/x/crypto from v0.45.0 to v0.47.0.
    • Updated golang.org/x/net from v0.47.0 to v0.49.0.
    • Updated golang.org/x/text from v0.31.0 to v0.33.0.
    • Updated golang.org/x/oauth2 from v0.32.0 to v0.34.0.
    • Updated google.golang.org/genproto/googleapis/api and google.golang.org/genproto/googleapis/rpc versions.
  • go.sum
    • Updated checksums to reflect changes in go.mod, including new and updated dependencies.
  • internal/llminternal/base_flow.go
    • Added calls to telemetry.LogSystemMessage, telemetry.LogUserMessage, and telemetry.LogInferenceResult within the LLM call loop to capture interaction details.
  • internal/telemetry/telemetry.go
    • Imported strings, go.opentelemetry.io/contrib/bridges/otelslog, go.opentelemetry.io/otel/log, go.opentelemetry.io/otel/log/global, and google.golang.org/adk/internal/version.
    • Initialized slogger using otelslog.NewLogger and logger using global.GetLoggerProvider() for structured logging.
    • Implemented LogSystemMessage to log system instructions from LLM requests.
    • Implemented LogUserMessage to log user content from LLM requests.
    • Implemented LogInferenceResult to log the content of LLM responses.
  • telemetry/config.go
    • Added sdklog import.
    • Extended the config struct with logProcessors (for additional log processors) and loggerProvider (to override the default logger provider) fields.
    • Added WithLogProcessors option to register custom log processors.
    • Added WithLoggerProvider option to override the default logger provider with a preconfigured instance.
  • telemetry/setup_otel.go
    • Added errors and otlploghttp imports.
    • Modified the configure function to include configureLoggerProcessors for setting up log exporters.
    • Updated the newInternal function to initialize and return an sdklog.LoggerProvider alongside the TracerProvider.
    • Adjusted resolveResource to explicitly handle resource.ErrSchemaURLConflict during resource merging.
    • Renamed configureExporters to configureSpanProcessors to clarify its purpose for spans.
    • Implemented configureLoggerProcessors to set up OTLP HTTP log exporters based on environment variables and a GCP-specific log processor if otelToCloud is enabled.
    • Added initLoggerProvider function to create and configure the OpenTelemetry LoggerProvider using configured processors and resources.
    • Implemented newGcpLogProcessor to create an OTLP HTTP log exporter specifically configured for Google Cloud Telemetry, including authentication and endpoint settings.
  • telemetry/telemetry.go
    • Added LoggerProvider field of type *sdklog.LoggerProvider to the Providers struct.
    • Updated the Shutdown method to gracefully shut down the LoggerProvider if it is configured.
    • Modified the SetGlobalOtelProviders method to set the global OpenTelemetry LoggerProvider if available.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces OpenTelemetry structured logging, which is a great addition for observability. The changes are extensive, touching configuration, setup, and internal logic to emit logs. I've identified a few critical issues that need to be addressed: a resource leak due to an incorrect shutdown sequence, a potential panic from a nil pointer dereference, a syntax error that will prevent compilation, and redundant logging within a loop. Addressing these will significantly improve the robustness and correctness of the new logging functionality.

@pawel-maciejczek pawel-maciejczek force-pushed the feature/telemetry-logging branch 6 times, most recently from b33ec10 to 237e5a2 Compare February 12, 2026 12:02
@pawel-maciejczek pawel-maciejczek force-pushed the feature/telemetry-logging branch from de517dd to 7bcb69d Compare February 12, 2026 14:57
@pawel-maciejczek pawel-maciejczek marked this pull request as ready for review February 12, 2026 14:57
useStream := runconfig.FromContext(ctx).StreamingMode == runconfig.StreamingModeSSE

// Log request before calling the model.
telemetry.LogRequest(ctx, req)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Is it possible to hide this and telemetry.LogResponse in the generateContent function?

In python we take up a pretty small code footprint when it comes to instrumentation in the business logic:
https://github.com/google/adk-python/blob/ede925b5025972cffcfaf178b2f81679fabbe90f/src/google/adk/flows/llm_flows/base_llm_flow.py#L1166-L1170

// guessedGenAISystem is the AI system we are using.
var guessedGenAISystem = guessAISystem()

var logger = global.GetLoggerProvider().Logger(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: rename to otelLogger.

It's a bit confusing, because most languages have some logger already, and it usually signals stdout logging

Copy link
Collaborator

@dpasiukevich dpasiukevich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, let's resolve the small nits and we can merge this.

Thanks!


// Message content is not logged by default. Set the following env variable to enable logging of prompt/response content.
// OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true
var elideMessageContent = !isEnvVarTrue("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pawel-maciejczek @RKest folks do you know if this is mentioned in the public docs anywhere for ADK? This envvar looks useful, but right now it's hidden in the internal code and users won't know about it unless they'd parse the code.

return val == "true" || val == "1"
}

func guessAISystem() string {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you use this func

func GetGoogleLLMVariant(llm model.LLM) genai.Backend {

VertexAI or GeminiAPI not only can be configured by this env var but also by genai.Client.Backend field.

// guessedGenAISystem is the AI system we are using.
var guessedGenAISystem = guessAISystem()

var logger = global.GetLoggerProvider().Logger(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to double check -- logger provider instantiation will be done in a separate PR?

// TODO(#479) init logger provider

@verdverm
Copy link

verdverm commented Feb 13, 2026

general question on this, will it enable me to add labels to various groupings in ADK such that I have some good filtering and pivot variables for grafana?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants