--- title: Claude Code Telemetry url: https://blog.guigpap.com/en/workflows/claude-code-telemetry/ url_md: https://blog.guigpap.com/en/workflows/claude-code-telemetry.md category: tooling date: '2026-03-28' maturite: production techno: - claude - n8n - odoo - prometheus - github application: - operations - monitoring --- # Claude Code Telemetry > End-to-end Claude Code session tracking with Prometheus metrics and Odoo synchronisation ## 1. What? — Definition and context Every time a Claude Code session starts on this project, a shell hook captures the context (branch, issue, repo) and forwards it to N8N. When the session ends, a second hook collects the Prometheus metrics (active time, cost, tokens, lines of code), reconstructs the branch segments traversed, and syncs everything to the matching Odoo tasks. The result: every Odoo task shows the Claude time spent on it, the API cost, the number of tokens, and the lines changed — automatically, without manual entry. > **Note - Claude Code hook** > > A **hook** is a script that runs automatically at a specific moment. Here, two shell hooks (`session-start` and `session-end`) are configured in `~/.claude/settings.json` to fire at the start and end of every Claude Code session. ### Workflows in the system | Workflow | Nodes | Trigger | Role | |----------|-------|---------|------| | **Session Start** | 4 | Webhook POST | Records the active session | | **Session End** (main) | 30 | Webhook POST | Metrics + Odoo sync | | **Orphan Detection** | ~20 | Schedule 12 h | Recovers crashed sessions | | **Session Categorizer** | 17 | Sunday 11 PM | Categorises unsorted sessions | | **Session Metrics Updater** | 7 | Webhook POST | Enriched update (backfill) | ### End-to-end architecture ```mermaid flowchart TD Start["Claude Code · starts"] HookStart["SessionStart hook"] subgraph SS["Session Start · 4 nodes"] direction TB Dedup1["Dedup"] Register["Register Active Session"] end Work["work · commits · branch switches"] End["Claude Code · ends"] HookEnd["SessionEnd hook · git reflog + git log"] subgraph SE["Session End · 30 nodes"] direction TB Dedup2["Dedup"] PromQ["Prometheus Query"] Split["Split Metrics per segment"] SaveDT["Save Data Table"] LoopIssue["Loop per issue"] Odoo["Find/Create Odoo Task → Update"] Detail["Create Session Detail"] Cleanup["Cleanup Active Session"] end Orphan["Orphan Detection · scheduled"] Start --> HookStart --> Dedup1 --> Register --> Work --> End --> HookEnd --> Dedup2 Dedup2 --> PromQ --> Split --> SaveDT --> LoopIssue --> Odoo --> Detail --> Cleanup Register -.-> Orphan ``` --- ## 2. Why? — Stakes and motivations Without telemetry, there is no way to know how much time and money Claude Code consumes per task. You know the tool was used, but not on what or for how much. For a freelancer billing time, that is a blind spot. ### Problems solved | Problem | Without telemetry | With telemetry | |---------|-------------------|----------------| | **Cost per task** | Read it manually from the API console | USD cost per issue, per model | | **Time spent** | Estimate from memory | Active time from Prometheus + Odoo timesheets | | **Attribution** | "I worked on the project" | Segments per branch, per issue | | **Lost sessions** | Crash = data lost | Orphan Detection recovers metrics | | **Categorisation** | `dev` branches without context | AI categorises into dev/docs/refactor/fix/test | ### Metrics collected | Metric | Source | Granularity | |--------|--------|-------------| | Active time (seconds) | Prometheus OTEL | Per session, per model | | API cost (USD) | Prometheus OTEL | 4 decimals, per model | | Tokens (input/output/cache) | Prometheus OTEL | Per type, per model | | Lines added/removed | git log | Per commit | > **Tip - Instant Prometheus query** > > Metrics are monotonic counters keyed by session_id. A single instant query is enough — the latest value IS the total. No need for a range query. --- ## 3. How? — Technical implementation ### The SessionEnd hook This is the most complex hook. When Claude Code exits, it: 1. Reads the state file written at start (frozen issue_id, timestamp) 2. Rebuilds branch segments through `git reflog --since` 3. Collects commits with `git log --since --all` 4. Enriches each commit with its branch and matching issue 5. Builds a TLDR (changed files, insertions, deletions, dominant type) 6. Sends the enriched payload to N8N The key point: the issue_id is **frozen** when the session starts. Even if the user switches branches mid-session, the system knows which task to attribute the work to. ### Proportional split When a session crosses several branches (for example `feature/176-fix` for 40 minutes then `dev` for 20 minutes), metrics are split proportionally: ```text Total session: 60 minutes Branch feature/176: 40 min → 66% of metrics Branch dev: 20 min → 33% of metrics If total cost = 0.15 USD: Issue #176 → 0.10 USD Branch dev → 0.05 USD (generic task) ``` ### Generic tasks Sessions without an issue (work on `dev`, `main`, or branches without a number) are routed to **generic tasks**: one per (repo + category) combination. The category is derived from the dominant commit type: | Dominant commits | Category | Generic task | |------------------|----------|--------------| | feat, chore | dev | "Dev without issue (features & chores)" | | docs | docs | "Documentation maintenance" | | refactor | refactor | "Refactoring" | | fix | fix | "Fixes (no issue)" | | test | test | "Testing" | ### Odoo fields synchronised Each Odoo task receives 11 enriched fields automatically: | Field | Type | Description | |-------|------|-------------| | `x_claude_time_total` | Float | Cumulative active hours | | `x_claude_cost_total` | Float | Cumulative USD cost | | `x_claude_token_total` | Integer | Cumulative tokens | | `x_claude_sessions` | Integer | Number of sessions | | `x_claude_lines_added` | Integer | Lines added | | `x_claude_lines_removed` | Integer | Lines removed | | `x_claude_cost_by_model` | JSON | Breakdown per model | | `x_claude_tokens_input` | Integer | Input tokens | | `x_claude_tokens_output` | Integer | Output tokens | | `x_claude_tokens_cache` | Integer | Cache tokens | | `x_claude_last_session` | Datetime | Last session | Fields are cumulative: each session adds its metrics on top of those already on the task. ### Orphan Detection Every 12 hours, a workflow checks whether sessions remain stuck in the active sessions table (a sign of a Claude Code crash). Sessions older than 6 hours are considered orphans. The workflow fetches the Prometheus metrics (if still available — 15-day retention), saves them with status `orphaned`, and updates the matching Odoo task. Nothing is lost. > **Caution - Prometheus retention** > > If Prometheus metrics have expired (more than 15 days old), Orphan Detection saves zeros. Commits cannot be recovered either (the hook never ran). It is a safety net, not a guarantee. ### Weekly categorisation Every Sunday at 11 PM, the Session Categorizer uses Claude to analyse uncategorised sessions. The AI receives a batch of sessions with their "topic" (first user message) and can: - Assign a category (dev, docs, refactor, fix, test) - Rename the Odoo task with a more descriptive name - Re-link a session to an issue task if it was misattributed --- ## 4. What if? — Outlook and limits ### Current limits | Limit | Impact | Mitigation | |-------|--------|------------| | **Prometheus 15-day retention** | Metrics lost after 15 days | Saved into a Data Table at session end | | **No metrics per branch** | Prometheus only knows session_id | Proportional split by segment duration | | **Shell hook only** | Depends on bash and git | Scripts tested and shipped through `scripts/claude-hooks/` | ### Evolution scenarios **If a real-time dashboard is needed**: - Prometheus metrics already exist in Grafana - Add a "Claude Code Sessions" dashboard with cost, tokens, and active sessions - Alert if cost crosses a daily threshold **If multi-user support is required**: - Add a `user_id` field in the payload - Split costs per developer - Comparative dashboard inside Odoo **If finer analysis is needed**: - Store full transcripts (only the "topic" is kept today) - Analyse usage patterns: which prompts cost the most - Optimise caches and model choice --- ## Related pages ### Infrastructure - [Monitoring Stack](/en/infrastructure/monitoring-stack/) — Prometheus and Grafana - [AI Stack](/en/infrastructure/ai-stack/) — CLI Ollama ### Workflows - [GitHub-Odoo Sync](/en/workflows/github-odoo-sync/) — Odoo tasks synced from GitHub - [Global Error Handler](/en/workflows/error-handler/) — Error workflow for the telemetry pipeline ### Reference - [Glossary](/en/reference/glossary/) — OTEL, Prometheus, hook ## Metadonnees agent - Cet article est issu du blog GuiGPaP Lab. - Contexte global du blog: https://blog.guigpap.com/llms.txt - Contact auteur: https://odoo.guigpap.com/mon-cv - Licence: CC-BY-SA 4.0