For a copy-paste starting point, see config.example.toml.
For ready-to-use hooks, see example-hooks.md.
The dispatcher loads the first config it finds, checked in this order:
- the path in
$COPILOT_OTEL_CONFIG $COPILOT_HOME/copilot-otel-logging.toml~/.copilot/copilot-otel-logging.toml
If none exists, the plugin does nothing.
| Key | Default | Description |
|---|---|---|
endpoint |
(required) | Base OTLP/HTTP URL. Logs POST to {endpoint}/v1/logs; session spans to {endpoint}/v1/traces. |
service_name |
github-copilot-cli |
Value of resource.service.name. |
timeout_sec |
5 |
Timeout applied to each command and to each OTLP export. |
session_trace |
true |
Emit one span per session and link its logs to it. |
session_span_name |
copilot.session |
Name of the per-session span. |
[otel.headers] |
— | Extra HTTP headers. Values support ${ENV} / $ENV expansion at runtime. |
Each event is a TOML array of tables under hooks.<Event>. Each entry is one
command that runs in order; its stdout becomes part of the OTLP record.
[[hooks.UserPromptSubmit]]
name = "github-user" # -> attribute copilot.github-user
run = "gh api user -q .login"| Field | Required | Description |
|---|---|---|
name |
yes | Identifier used in the OTLP attribute/body key (copilot.<name>). |
run |
yes* | Shell command for macOS/Linux (bash/sh). |
run_powershell |
no | PowerShell command; preferred on Windows (see below). |
as |
no | Where the output goes: attribute (default), body, or span. |
* At least one of run / run_powershell must be present for the entry to do
anything on the current platform.
Commands run via the shell and can read these environment variables, populated from the event payload:
| Env var | Source | Available on |
|---|---|---|
COPILOT_HOOK_EVENT |
event name | all events |
COPILOT_HOOK_SESSION_ID |
session id | all events |
COPILOT_HOOK_CWD |
working directory | all events |
COPILOT_HOOK_TIMESTAMP |
event timestamp | all events |
COPILOT_HOOK_PROMPT |
submitted prompt | UserPromptSubmit |
COPILOT_HOOK_TOOL_NAME |
tool name | PreToolUse, PostToolUse |
COPILOT_HOOK_TOOL_RESULT |
tool result text | PostToolUse |
COPILOT_HOOK_STOP_REASON |
why the turn stopped | Stop |
COPILOT_HOOK_PAYLOAD |
full raw payload JSON | all events |
Anything not listed is reachable by parsing COPILOT_HOOK_PAYLOAD.
Each command's output is routed by its as value:
attribute(default) → acopilot.<name>attribute on the log record (a failed command also getscopilot.<name>.error). Best for small, queryable facts you filter/group/aggregate on — user, model, branch, repo, cost.body→ an entry in the record's bodyarrayValue(name,output, anderroronly on failure). Best for free-form or large/high-cardinality text — the prompt, tool results — that you don't want in your attribute indexes.span→ added to the session span's attributes (SessionEndonly). The value also appears on the SessionEnd log record.
[[hooks.UserPromptSubmit]]
name = "model"
run = "..." # -> attribute copilot.model (default)
[[hooks.UserPromptSubmit]]
name = "prompt"
run = "..."
as = "body" # -> body array elementEach command uses run on macOS/Linux (executed with bash/sh). To support
Windows, add an optional run_powershell; on Windows it is preferred over run
and executed with PowerShell (pwsh if available, else powershell). Requires
Python (python / python3) on PATH.
[[hooks.UserPromptSubmit]]
name = "prompt"
run = "printf '%s' \"$COPILOT_HOOK_PROMPT\""
run_powershell = "Write-Output $env:COPILOT_HOOK_PROMPT"