@shichen335/openclaw-logfire
v1.0.0
Published
Pydantic Logfire observability plugin for OpenClaw — OTEL GenAI semantic conventions, distributed tracing, token metrics, trace links
Downloads
174
Maintainers
Readme
@shichen335/openclaw-logfire
openclaw-logfire sends OpenClaw agent activity to Pydantic Logfire as OTLP traces and metrics.
It captures the real execution shape of an OpenClaw run:
- one root
invoke_agentspan per agent invocation - staged
chat <model>spans reconstructed fromllm_inputandllm_output - one
execute_tool <tool>span per tool call - token usage metrics and cumulative session usage
- optional
traceparentinjection for HTTP commands such ascurl,wget,http, andhttpie
The plugin is designed around the latest OpenClaw hook flow and OTEL GenAI semantic conventions, while staying practical about privacy controls and debugging.
Requirements
- OpenClaw
>= 2026.2.1 - Node.js
>= 20 - A Logfire
write token - Network access to Logfire OTLP endpoints:
https://logfire-us.pydantic.devhttps://logfire-eu.pydantic.dev
Install
For most users, install it as an OpenClaw plugin:
openclaw plugins install @shichen335/openclaw-logfireThen enable it in openclaw.json:
{
"plugins": {
"entries": {
"openclaw-logfire": {
"enabled": true,
"config": {}
}
}
}
}The plugin id must be openclaw-logfire.
Get A Logfire Write Token
This plugin uses a Logfire write token. The environment variable name is LOGFIRE_TOKEN.
- Open Logfire Login and sign up or sign in.
- If this is your first time in Logfire, finish the onboarding flow.
- If you want a dedicated project for OpenClaw, open
Organization > Projectsand create one. - Open the target project.
- Go to
Settings > Write tokens. - Click
New write token. - Copy the token immediately. Logfire does not show the full token again later.
- Export it in your shell:
export LOGFIRE_TOKEN="<your-write-token>"Useful official links:
Quick Start
Minimal configuration:
{
"plugins": {
"entries": {
"openclaw-logfire": {
"enabled": true,
"config": {}
}
}
}
}Then restart OpenClaw. The plugin reads LOGFIRE_TOKEN at runtime and starts exporting spans.
If LOGFIRE_TOKEN is missing, the plugin disables itself and logs an error instead of starting half-configured.
Recommended Configuration
This example is based on a real OpenClaw setup and works well when you want rich debugging and full message capture. Replace placeholders before use.
{
"plugins": {
"entries": {
"openclaw-logfire": {
"enabled": true,
"config": {
// Prefer LOGFIRE_TOKEN via environment variable.
// You can set "token" here, but env var is safer.
"projectUrl": "https://logfire.pydantic.dev/<org>/<project>",
// Map non-standard provider ids to OTel-friendly names.
"providerNameMap": {
"customprovider": "openai"
},
// Full payload capture for debugging.
"captureMessageContent": true,
"captureHistoryMessages": true,
"historyMessagesMaxLength": 100000,
"toolInputMaxLength": 100000,
"toolOutputMaxLength": 16384,
// Privacy and persistence switches.
"redactSecrets": false,
"saveHookLogs": false
}
}
}
}
}Notes:
projectUrlenables clickable trace links whenenableTraceLinksis on.providerNameMapis useful when your OpenClaw provider id is not a standard OTel GenAI provider name.captureMessageContent: truealso increases what can be captured from tool inputs and outputs. Review privacy expectations before enabling it.captureToolDefinitionsis accepted by the schema, but it is currently reserved and does not change runtime behavior in1.0.0.
What The Plugin Captures
Span tree
A typical trace looks like this:
invoke_agent main
|- chat gpt-5.4
|- running 2 tools
| |- execute_tool Read
| `- execute_tool Shell
`- chat gpt-5.4Root span
The root invoke_agent <agent> span stores:
- conversation id and channel metadata when available
- cumulative input, output, cache read, and cache write token counts
- tool count and overall duration
- reconstructed
pydantic_ai.all_messages final_resultwhen a final assistant result can be extracted
Chat spans
The plugin does not keep a single long-lived chat span open through the whole turn.
Instead, it reconstructs one or more chat <model> spans from the final conversation shape emitted by llm_output.
That means:
- tool-call boundaries appear as separate chat phases
- final assistant content is more accurate when OpenClaw emits
llm_outputafterlastAssistantis fully assembled - if
lastAssistantis incomplete, the plugin falls back toassistantTexts
Tool spans
Each tool call becomes execute_tool <tool>.
Depending on configuration, the span may include:
gen_ai.tool.call.argumentsgen_ai.tool.call.result- output size and timing metadata
Tool calls from the same runId also produce a synthetic group span such as running 1 tool or running 3 tools.
Metrics
When enableMetrics is true, the plugin exports:
gen_ai.client.token.usagegen_ai.client.operation.duration
Configuration Reference
All config lives under plugins.entries.openclaw-logfire.config.
Environment variable fallbacks
| Variable | Used for | Notes |
|---|---|---|
| LOGFIRE_TOKEN | token | Required at runtime unless token is set directly |
| LOGFIRE_PROJECT_URL | projectUrl | Optional |
| LOGFIRE_ENVIRONMENT | environment | Defaults to development |
| LOGFIRE_PROVIDER_NAME | providerName | Optional |
Active runtime options
These options are parsed by the config resolver and currently affect runtime behavior.
| Key | Type | Default | Description |
|---|---|---:|---|
| token | string | "" | Logfire write token. Prefer LOGFIRE_TOKEN instead of committing it into config. |
| projectUrl | string | "" | Project URL used to build clickable trace links. |
| region | "us" \| "eu" | "us" | Selects the OTLP base endpoint. |
| environment | string | "development" | Deployment environment resource attribute. |
| serviceName | string | "openclaw-agent" | OTEL service.name. |
| providerName | string | "" | Default provider name when OpenClaw metadata does not provide one. |
| providerNameMap | Record<string, string> | {} | Maps OpenClaw provider ids to OTEL provider names. |
| captureToolInput | boolean | true | Captures tool arguments. |
| captureToolOutput | boolean | false | Captures tool results. |
| toolInputMaxLength | integer | 2048 | Truncation limit for tool input capture. |
| toolOutputMaxLength | integer | 512 | Truncation limit for tool output capture and chat output capture. |
| captureMessageContent | boolean | false | Captures chat input, chat output, and system instructions. Privacy-sensitive. |
| captureHistoryMessages | boolean | false | Helps reconstruct multi-turn conversation history on the root span. |
| historyMessagesMaxLength | integer | 16384 | Truncation limit for serialized history messages. |
| redactSecrets | boolean | true | Redacts common API keys, bearer tokens, JWTs, passwords, and similar secrets before capture. |
| distributedTracing.enabled | boolean | false | Enables command-level trace propagation. |
| distributedTracing.injectIntoCommands | boolean | true | Injects traceparent into matching HTTP commands. |
| distributedTracing.urlPatterns | string[] | ["*"] | URL glob patterns that are allowed for injection. |
| enableMetrics | boolean | true | Enables OTLP metric export. |
| metricsIntervalMs | integer | 60000 | Metric export interval in milliseconds. |
| enableTraceLinks | boolean | true | Logs clickable project trace links when projectUrl is configured. |
| saveHookLogs | boolean | false | Persists raw hook payloads to ~/.openclaw/logs/ for debugging. |
| resourceAttributes | Record<string, string> | {} | Additional OTEL resource attributes. |
| spanProcessorType | "batch" \| "simple" | "batch" | Use simple when debugging exporter behavior. |
| batchConfig.maxQueueSize | integer | 2048 | Batch span processor queue size. |
| batchConfig.maxExportBatchSize | integer | 512 | Maximum spans per export batch. |
| batchConfig.scheduledDelayMs | integer | 5000 | Delay between batch exports. |
Accepted but currently reserved options
These keys are accepted by the schema and config resolver, but they are not fully wired into runtime behavior in 1.0.0.
| Key | Current status |
|---|---|
| captureStackTraces | Reserved. Current runtime does not branch on this flag. |
| captureToolDefinitions | Reserved. Accepted by schema, but not currently attached to spans. |
| distributedTracing.extractFromWebhooks | Reserved. Current implementation focuses on outbound command injection. |
| logLevel | Reserved. Accepted by config, but plugin logging does not currently change behavior from it. |
| useGenAiCompatibilityScope | Legacy compatibility field kept in config code, not surfaced in plugin schema. |
Privacy And Safety Notes
captureMessageContent: trueis the highest-impact privacy switch.captureToolOutput: truecan capture large or sensitive tool results.redactSecrets: truehelps, but it is best-effort rather than a formal DLP guarantee.saveHookLogs: truewrites raw payloads to local disk. Turn it off after debugging.- Prefer
LOGFIRE_TOKENin the shell environment instead of committing tokens intoopenclaw.json.
Distributed Tracing
When enabled, the plugin injects W3C traceparent into matching command parameters before the tool runs.
{
"plugins": {
"entries": {
"openclaw-logfire": {
"enabled": true,
"config": {
"distributedTracing": {
"enabled": true,
"injectIntoCommands": true,
"urlPatterns": [
"https://api.example.com/*",
"http://localhost:8000/*"
]
}
}
}
}
}
}Current scope:
- works for outbound command injection only
- targets HTTP-like commands such as
curl,wget,http, andhttpie - respects
distributedTracing.urlPatterns
Troubleshooting
No traces appear
Check these first:
LOGFIRE_TOKENis set in the environment seen by OpenClaw.- The plugin entry key is exactly
openclaw-logfire. - OpenClaw is at least
2026.2.1. - OpenClaw was restarted after config changes.
- Your machine can reach the selected Logfire region endpoint.
Agent spans exist, but chat spans are incomplete
Make sure OpenClaw emits llm_output after the final lastAssistant object is fully assembled. The plugin reconstructs chat phases from the completed assistant payload.
Tool spans do not show failures clearly
Current OpenClaw hook payloads do not always expose tool-level error details at tool_result_persist, so failures may be reflected on the root agent span rather than the individual tool span.
I need raw hook payloads
Temporarily set:
{
"plugins": {
"entries": {
"openclaw-logfire": {
"enabled": true,
"config": {
"saveHookLogs": true
}
}
}
}
}This writes hook payloads to ~/.openclaw/logs/.
Local Development
git clone https://github.com/chenbaiyujason/openclaw-logfire
cd openclaw-logfire
npm install
npm run build
npm run typecheck
npm testTo load the local checkout in OpenClaw, either symlink it into your extensions directory or add the repo path to plugins.load.paths.
ln -s "$(pwd)" ~/.openclaw/extensions/openclaw-logfireOr:
{
"plugins": {
"load": {
"paths": [
"/absolute/path/to/openclaw-logfire"
]
}
}
}Then export LOGFIRE_TOKEN, restart OpenClaw, and verify with:
openclaw plugins listLicense
MIT
