Seeing What Happened

View Source

After: You can see what your agent did via logs and telemetry.

When your agent runs, Jido emits telemetry events and structured logs automatically. This guide shows you how to observe agent behavior during development.

Quick Start: Enable Debug Logging

Add this to config/dev.exs:

config :logger, level: :debug

Now when you run cmd/2 or start an AgentServer, you'll see structured log output.

The fastest way to see what's happening during development is instance-level debug mode:

MyApp.Jido.debug(:on)       # developer-friendly verbosity for this instance
MyApp.Jido.debug(:verbose)  # maximum detail — trace-level, full args
MyApp.Jido.debug(:off)      # back to configured defaults

This enables debug logging and event recording for all agents in the instance without changing the global logger level. You still need config :logger, level: :debug in your config/dev.exs so the Logger doesn't filter out debug messages before Jido sees them.

What Gets Logged

When you execute a command:

{agent, directives} = MyAgent.cmd(agent, {SomeAction, %{value: 42}})

You'll see output like:

[debug] [Agent] Command started agent_id="agent_abc123" agent_module=MyAgent action="{SomeAction, %{value: 42}}"
[debug] [Agent] Command completed agent_id="agent_abc123" duration_μs=1234 directive_count=2

AgentServer operations log signal processing:

[debug] [AgentServer] Signal processing started agent_id="agent_abc123" signal_type="jido.agent.cmd"
[debug] [AgentServer] Signal processing completed agent_id="agent_abc123" duration_μs=5678 directive_count=1

Telemetry Events

Jido emits these events automatically:

EventWhen
[:jido, :agent, :cmd, :start]cmd/2 begins
[:jido, :agent, :cmd, :stop]cmd/2 completes
[:jido, :agent, :cmd, :exception]cmd/2 raises
[:jido, :agent_server, :signal, :start]Signal processing begins
[:jido, :agent_server, :signal, :stop]Signal processing completes
[:jido, :agent_server, :directive, :start]Directive execution begins
[:jido, :agent_server, :directive, :stop]Directive execution completes
[:jido, :agent, :strategy, :cmd, :start]Strategy executes command
[:jido, :agent, :strategy, :cmd, :stop]Strategy command completes

All events include metadata:

  • :agent_id — the agent's unique identifier
  • :agent_module — the agent module name
  • :jido_instance — the Jido instance that owns this agent (or nil in non-instance contexts)
  • :duration — execution time (nanoseconds, on :stop events)
  • :directive_count — number of directives produced

Attach a Handler

Attach your own telemetry handler to collect metrics or log in your preferred format:

defmodule MyApp.JidoMetrics do
  require Logger

  def setup do
    :telemetry.attach_many(
      "my-jido-handler",
      [
        [:jido, :agent, :cmd, :stop],
        [:jido, :agent, :cmd, :exception],
        [:jido, :agent_server, :signal, :stop]
      ],
      &__MODULE__.handle_event/4,
      nil
    )
  end

  def handle_event([:jido, :agent, :cmd, :stop], measurements, metadata, _config) do
    duration_ms = System.convert_time_unit(measurements.duration, :native, :millisecond)

    Logger.info("Agent command completed",
      agent_id: metadata.agent_id,
      jido_instance: metadata.jido_instance,
      duration_ms: duration_ms,
      directives: metadata.directive_count
    )
  end

  def handle_event([:jido, :agent, :cmd, :exception], _measurements, metadata, _config) do
    Logger.error("Agent command failed",
      agent_id: metadata.agent_id,
      error: inspect(metadata.error)
    )
  end

  def handle_event([:jido, :agent_server, :signal, :stop], measurements, metadata, _config) do
    duration_ms = System.convert_time_unit(measurements.duration, :native, :millisecond)

    Logger.info("Signal processed",
      agent_id: metadata.agent_id,
      signal_type: metadata.signal_type,
      duration_ms: duration_ms
    )
  end
end

Call MyApp.JidoMetrics.setup() in your application startup.

Correlation IDs

Jido automatically correlates signals across a processing chain. When trace context is active, telemetry metadata includes:

  • :jido_trace_id — shared across the entire call chain
  • :jido_span_id — unique to the current operation
  • :jido_parent_span_id — the parent operation that triggered this one
  • :jido_causation_id — the signal ID that caused this signal

Use these to trace a request through multiple agents or action chains:

def handle_event(event, measurements, metadata, _config) do
  if trace_id = metadata[:jido_trace_id] do
    Logger.metadata(trace_id: trace_id)
  end

  # Your logging/metrics code
end

Local Introspection (Debug Mode)

For quick debugging without setting up telemetry handlers, debug mode records recent events in an in-memory ring buffer (500 events by default, configurable via debug_max_events).

Instance-Level Debug (Primary Workflow)

Enable debug for an entire instance — all agents in that instance start recording events immediately:

MyApp.Jido.debug(:on)

# ... run some operations ...

{:ok, events} = MyApp.Jido.recent(pid, 10)

Check the current debug state with:

MyApp.Jido.debug_status()

Per-Agent Debug

For surgical debugging of a single agent, per-agent opt-in still works:

{:ok, pid} = MyApp.Jido.start_agent(MyAgent, debug: true)

Boot-Time Config

Enable debug at startup via application config:

# config/dev.exs
config :my_app, MyApp.Jido, debug: true

See Runtime - Debug Mode for details.

Next Steps

This guide covers development observability. For a step-by-step debugging workflow, see Debugging. For production monitoring with custom metrics, OpenTelemetry integration, and performance dashboards, see Observability.

Key modules: