Amplifier Logging Hook Module
May 31, 2026 · View on GitHub
Provides visibility into agent execution through lifecycle event logging.
Overview
This hook module integrates with Amplifier's hook system to log lifecycle events from the session and all loaded modules.
Standard Events (always logged):
- Session start/end
- Tool invocations and results
- Sub-agent spawning and completion
- Errors and warnings
Module Events (auto-discovered):
- Custom lifecycle events from loaded modules
- Module-specific operations (e.g.,
skills:discovered,skills:loaded) - Automatically detected via the
observability.eventscapability
Features
- Zero code changes required - pure configuration
- Auto-discovery of module events - modules declare observable events, logging "just works"
- Standard Python logging - no external dependencies
- Configurable levels - DEBUG, INFO, WARNING, ERROR
- Flexible output - console, file, or both
- Clean formatting - timestamp, level, module, message
Custom Event Logging
Auto-Discovery (Recommended)
Modules can declare their observable lifecycle events using the observability.events capability. hooks-logging automatically discovers and logs these events.
Module declares events (amplifier-module-skills/__init__.py):
async def mount(coordinator, config):
# Declare observable events for this module
# Get existing list, extend, then re-register (aggregation pattern)
obs_events = coordinator.get_capability("observability.events") or []
obs_events.extend([
"skills:discovered", # When skills are found
"skills:loaded", # When skill loaded successfully
"skill:executed" # When skill runs
])
coordinator.register_capability("observability.events", obs_events)
# ... rest of mount logic
hooks-logging auto-discovers (zero configuration needed):
- Queries
coordinator.get_capability("observability.events")at mount time - Registers handlers for all discovered events
- Module events logged alongside standard events
Result: Custom module events appear in logs automatically, no loader configuration needed.
Manual Configuration
For precise control, explicitly list events in configuration:
hooks:
- module: hooks-logging
config:
auto_discover: false # Disable auto-discovery
additional_events:
- "skills:discovered"
- "skills:loaded"
- "custom:my:event"
Event Exclusion Filter
The exclude_events key suppresses high-frequency events from events.jsonl
without unregistering them from the hook system. The handler returns
continue immediately — no record is built or written.
Patterns follow fnmatch.fnmatch semantics: * matches any sequence of
characters, ? matches exactly one character, and [seq] matches any
character in seq. Matching is case-sensitive.
Default: ["llm:stream_*delta"]
events.jsonl is a session audit log only — it is not used for session
resume. The default "llm:stream_*delta" glob is the convention pattern
from the provider streaming contract's "Event dispositions" section. It
matches the transient streaming delta category (e.g. llm:stream_block_delta,
and any future *_delta variants) — events that are emitted once per
token-ish fragment (hundreds to thousands per turn) and flood the log with no
audit value. The structural events llm:stream_block_start,
llm:stream_block_end, and llm:stream_aborted are intentionally spared.
Convention note: This default is intentionally identical to the context-intelligence hook's default. The two hooks stay aligned via the provider streaming contract, not by shared code — never extract a shared constant; update both hooks by updating the contract.
hooks:
- module: hooks-logging
config:
# Default: drop transient streaming deltas (convention pattern)
exclude_events:
- "llm:stream_*delta"
# Drop all four streaming-related events
# exclude_events:
# - "llm:stream_*"
# Disable the filter entirely (write every event — useful for debugging)
# exclude_events: []
| Setting | Effect |
|---|---|
| (absent) | Uses default: ["llm:stream_*delta"] (convention glob) |
["llm:stream_*delta"] | Drop transient delta events; keep structural events (default) |
["llm:stream_*"] | Drop all four streaming events |
[] | Disable filter — write every event |
Hybrid Approach
Combine auto-discovery with manual additions:
hooks:
- module: hooks-logging
config:
auto_discover: true # Default: discovers module events
additional_events:
- "custom:debug:event" # Add events not declared by modules
Module Author Guide
When creating modules that emit lifecycle events:
-
Declare your observable events in
mount()using the aggregation pattern:# Get existing list, extend with your events, re-register obs_events = coordinator.get_capability("observability.events") or [] obs_events.extend([ "mymodule:started", "mymodule:completed" ]) coordinator.register_capability("observability.events", obs_events) -
Emit events at appropriate lifecycle points:
await coordinator.hooks.emit("mymodule:started", {"config": config}) # ... module logic await coordinator.hooks.emit("mymodule:completed", {"status": "success"}) -
Document your events in module README for power users who disable auto-discovery
Benefits:
- ✅ Logging "just works" for users
- ✅ No fragile coupling between module and logging config
- ✅ Events discoverable by other observability hooks
- ✅ Power users can still manually configure if needed
Prerequisites
- Python 3.11+
- UV - Fast Python package manager
Installing UV
# macOS/Linux/WSL
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
Installation
uv pip install -e ./amplifier-module-hooks-logging
Configuration
Add to your Amplifier configuration file (e.g., test-full-features.toml):
[hooks]
enabled = ["logging"]
[hooks.logging]
level = "INFO" # DEBUG, INFO, WARNING, ERROR, CRITICAL
output = "console" # console, file, or both
file = "amplifier.log" # Required if output includes "file"
Log Levels
INFO (Recommended)
Shows key events without overwhelming detail:
- Session lifecycle
- Tool invocations (name only)
- Sub-agent activity
- Errors and warnings
DEBUG
Shows all details INCLUDING detailed LLM request/response logging:
Standard Events:
- Tool arguments and results
- Full message content
- All lifecycle events
LLM Debug Events (requires providers.*.config.debug: true):
llm:request:debug- Detailed request summary (message count, model, parameters)llm:response:debug- Detailed response summary (content preview, usage, timings)llm:request:raw- Complete raw request params sent to vendor API (requiresraw_debug: true)llm:response:raw- Complete raw response object from vendor API (requiresraw_debug: true)
Configuration Example (Standard Debug):
providers:
- module: provider-anthropic
config:
debug: true # Enable standard DEBUG event emission
timeout: 300.0
api_key: ${ANTHROPIC_API_KEY}
hooks:
- module: hooks-logging
config:
level: "DEBUG" # Capture DEBUG events in logs
Configuration Example (Ultra-Verbose Raw Debug):
providers:
- module: provider-anthropic
config:
debug: true # Enable standard DEBUG events
raw_debug: true # Enable RAW DEBUG events (complete vendor API I/O)
timeout: 300.0
api_key: ${ANTHROPIC_API_KEY}
hooks:
- module: hooks-logging
config:
level: "DEBUG" # Capture all DEBUG events including raw
Debug Levels:
debug: false(default) - INFO events only, no debug detailsdebug: true- Standard debug events with summaries and previewsdebug: true, raw_debug: true- Ultra-verbose with complete raw API I/O
Note: DEBUG level can generate significant log volume. RAW DEBUG generates extreme log volume with complete LLM request/response payloads. Use raw_debug only for deep debugging of provider integration issues.
Log Location: Session logs are written to ~/.amplifier/projects/<project>/sessions/<session_id>/events.jsonl
Project Directory Detection: The <project> slug is determined by:
- The
session.working_dircoordinator capability (if registered) - Falls back to
Path.cwd()for backward compatibility
This enables correct project identification in server/web deployments where the process cwd differs from the user's project directory.
WARNING
Shows only warnings and errors:
- Tool failures
- Performance issues
- Configuration problems
ERROR
Shows only errors:
- Critical failures
- Unhandled exceptions
Usage
Once configured, logging happens automatically. No code changes needed.
# Start Amplifier with logging enabled
amplifier run --config test-full-features.toml --mode chat
Example Output
2025-10-06 12:00:00 [INFO] amplifier_module_hooks_logging: === Session Started ===
2025-10-06 12:00:01 [INFO] amplifier_module_hooks_logging: Tool invoked: grep
2025-10-06 12:00:02 [INFO] amplifier_module_hooks_logging: Tool completed: grep ✓
2025-10-06 12:00:05 [INFO] amplifier_module_hooks_logging: Sub-agent spawning: architect
2025-10-06 12:00:10 [INFO] amplifier_module_hooks_logging: Sub-agent completed: architect
2025-10-06 12:00:11 [INFO] amplifier_module_hooks_logging: === Session Ended ===
Philosophy Alignment
This module follows Amplifier's kernel philosophy:
- Mechanism, not policy: Provides logging mechanism; modules declare what to log (policy)
- Auto-discovery via capabilities: Uses coordinator capability system for module self-declaration
- Ruthless simplicity: Reuses existing hook and capability mechanisms, no new abstractions
- Modular design: Modules own their observability contract; logging discovers it
- Zero loader coupling: Modules declare events; logging auto-discovers; loaders configure nothing
- Backward compatible: Auto-discovery default; power users can disable and configure manually
Development
Adding Dependencies
# Add runtime dependency
uv add pydantic
# Add development dependency
uv add --dev pytest
# Update dependencies
uv lock --upgrade
Running Tests
uv run pytest
Running Tests
uv run pytest tests/
Contributing
Note
This project is not currently accepting external contributions, but we're actively working toward opening this up. We value community input and look forward to collaborating in the future. For now, feel free to fork and experiment!
Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit Contributor License Agreements.
When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.
Trademarks
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow Microsoft's Trademark & Brand Guidelines. Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies.