Using logging_plugin in Agent Engine to Log Custom Fields #4202
|
I am building an Agent Engine application using ADK and deploying it via Agent Engine. A simplified version of the setup looks like this: class AgentEngineApp(AdkApp):
def set_up(self) -> None:
"""Initialize the agent engine app with logging and telemetry."""
vertexai.init()
setup_telemetry()
super().set_up()
logging.basicConfig(level=logging.INFO)
logging_client = google_cloud_logging.Client()
self.logger = logging_client.logger(__name__)
if gemini_location:
os.environ["GOOGLE_CLOUD_LOCATION"] = gemini_location
def register_feedback(self, feedback: dict[str, Any]) -> None:
"""Collect and log feedback."""
feedback_obj = Feedback.model_validate(feedback)
self.logger.log_struct(feedback_obj.model_dump(), severity="INFO")
def register_operations(self) -> dict[str, list[str]]:
"""Registers the operations of the Agent."""
operations = super().register_operations()
operations[""] = operations.get("", []) + ["register_feedback"]
return operations
gemini_location = os.environ.get("GOOGLE_CLOUD_LOCATION")
logs_bucket_name = os.environ.get("LOGS_BUCKET_NAME")
agent_engine = AgentEngineApp(
app=adk_app,
artifact_service_builder=lambda: GcsArtifactService(bucket_name=logs_bucket_name)
if logs_bucket_name
else InMemoryArtifactService(),
)QuestionI want to understand how to properly use the Specifically:
GoalAny guidance, examples, or best practices for doing this with |
Replies: 2 comments 7 replies
|
Response from ADK Answering Agent (experimental, answer may be inaccurate) TLDR: To log custom fields like Hello! As an AI agent, I can provide some guidance on how to best approach logging custom fields with ADK in Agent Engine. The standard For your use case, the recommended pattern is to leverage ADK's session 1. Store Per-Request Data in Session StateYour application should add the contextual information (like 2. Create a Custom Logging PluginYou can create your own plugin to inspect the Here is an example of a simple plugin that logs custom fields before a tool is executed: import logging
from google.adk.plugins import BasePlugin, ToolContext
class ContextualLoggingPlugin(BasePlugin):
"""A plugin to log contextual data from the session state."""
def __init__(self, logger):
self.logger = logger
def before_tool_callback(self, tool_context: ToolContext) -> None:
"""Called before a tool is executed."""
custom_fields = {
"user_email": tool_context.state.get("user_email"),
"country": tool_context.state.get("country"),
"tool_name": tool_context.tool_name,
"tool_parameters": tool_context.tool_parameters,
}
# Remove fields that are not set
custom_fields = {k: v for k, v in custom_fields.items() if v is not None}
if custom_fields:
self.logger.info("Executing tool with context: %s", custom_fields)
# In your main application setup:
# logger = ... (your configured logger)
# contextual_logger_plugin = ContextualLoggingPlugin(logger)
#
# agent_engine = AgentEngineApp(
# app=adk_app,
# plugins=[contextual_logger_plugin],
# ...
# )In this example, the 3. For Production: Use
|
|
ttps://google.github.io/adk-docs/observability/bigquery-agent-analytics/ BigQuery Agent Analytics Plugin: the ADK offers a plugin to log detailed agent analytics to BigQuery. This allows you to run complex queries on your agent's behavior and performance, get out of box observability for both token usage, tool call/LLM call error rate and the latency for both tool call and LLM call immediately. The plugin just need one line code change to add plugin into your APP. With this plugin, you can monitor which tool/subagent would cost your token most in the whole agent life-cycle |
Thanks @boyangsvl — agreed on all three points, and they reframe #6222.
On Alternative #1: you're right that it's not just suboptimal, it's unsound. Since a
before_model_callbackcan return a newLlmRequest(not only mutate it in place), stashing the reference inbefore_model_callbackand serializing it later can capture the wrong object. That also reinforces why the fix has to read whatever object is actually passed togenerate_content_async, rather than a reference captured earlier.On the hook being read-only: makes sense. I'll change the proposed signature from
-> Optional[LlmResponse]to a pure observer (-> None, no ability to short-circuit or mutate). That makes myon_model_request…