← Блог

OTel для Claude Code — рентген твоих скиллов

Сережа Рис · 30 April 2026

claude-codeobservabilitygrafanaвайбкодингскиллы

Grafana — это дашборд, который показывает, какие из моих скиллов в Claude Code реально вызываются, а какие висят мёртвым грузом. Нативного счётчика на скилл не существует — тикет #35319 «Skill invocation tracking» висит открытым с 17 марта. Решается через стандартный OpenTelemetry-стек, который агент собирает локально в Docker за один вечер.

Зачем мне вообще видеть скиллы

У меня в ~/.claude/skills/ лежит около 40 своих скиллов. Сверху — плагины (superpowers, codex, frontend-design и ещё несколько). На круг получается ~80 установленных. Подозрение давно простое: половина — мёртвый груз. Кто-то писался под разовую задачу и больше не звался ни разу. Кто-то дублирует другой скилл. Кто-то вообще не подхватывается, потому что триггер сформулирован криво.

Команда /stats в Claude Code показывает токены и streak. Не скиллы. Я хотел увидеть простую картину: какие скиллы за неделю стрельнули хотя бы раз, какие — ни разу, и кто в топе.

Про сам факт, что скиллы синкаются между четырьмя машинами через приватный репо cc-skills, я уже писал. Это решило задачу «одинаковая среда везде». Но не решило задачу «понять, что из этой среды я реально использую». Пока коллекция растёт быстрее, чем я её ревьюю — без счётчика я слепой.

Что такое Grafana, Prometheus, Loki и OpenTelemetry — для вайбкодера

Если ты не из observability-мира, четыре названия в стеке выглядят как магия. Расшифровка:

Картинка целиком:

        ┌─────────────────────┐
        │   Claude Code CLI   │
        │  ~/.claude/skills/  │
        └──────────┬──────────┘
                   │ OTLP/gRPC
                   │ event: skill_activated
                   │ attr:  skill.name
                   ▼
        ┌─────────────────────┐
        │   OTel Collector    │  :4317 gRPC
        │   (роутер сигналов) │  :4318 HTTP
        └────┬───────────┬────┘
             │           │
     metrics │           │ logs
             ▼           ▼
      ┌──────────┐  ┌──────────┐
      │Prometheus│  │   Loki   │
      │  :9090   │  │  :3100   │
      └─────┬────┘  └────┬─────┘
            │            │
            └─────┬──────┘
                  ▼
          ┌───────────────┐
          │    Grafana    │  :13737
          │  (5 дашбордов)│
          └───────────────┘

Claude Code как клиент шлёт OTLP-события в коллектор, тот делит метрики и логи между Prometheus и Loki, а Grafana поверх рисует.

Setup: один промпт, четыре контейнера

Готовый шаблон для этого кейса нашёлся — публичный репо ColeMurray/claude-code-otel с пятью преднастроенными дашбордами, docker-compose.yml и Makefile. Ничего своего ради эксперимента писать не пришлось — клонируем и поднимаем.

Был выбор Docker engine на macOS: Docker Desktop или Colima. Выбрал Colima — без GUI, без лицензионных ограничений, спокойнее по памяти на M2 Pro. Написал Claude Code на Opus 4.7:

Поставь Colima вместо Docker Desktop — мне нужен Docker engine, но без GUI-лицензии и тяжёлой VM. Запусти Colima на 2 CPU и 4 GB. Дальше склонируй ColeMurray/claude-code-otel в ~/Documents/GitHub/ и подними make up. Проверь что 4 контейнера живые. Если порт 3000 занят (у меня там Next.js dev-сервер крутится) — переедь Grafana на свободный высокий порт.

Дальше — уже не я. Агент поставил Colima через brew, поднял VM, склонировал репо, прочитал Makefile и попытался запустить стек. Тут начались грабли.

Грабли

Грабля 1: docker compose v1 vs v2

make up упал с unknown shorthand flag: 'd' in -d. Агент разобрался: Makefile написан под docker compose v2 (плагин), а brew по умолчанию ставит docker-compose v1 (отдельным бинарём через дефис). Флаги в них слегка разные. Решение — донастроить compose-плагин: создать папку для CLI-плагинов и слинковать туда v2. Это я не делал руками, агент сам выполнил mkdir и ln, перезапустил make up, контейнеры поднялись.

Грабля 2: порт 3000 уже занят

Grafana по дефолту слушает 3000. У меня на 3000 живёт Next.js dev-сервер другого проекта — он стартует с launchd при логине. curl localhost:3000 возвращал HTML с заголовком Next-Router-State-Tree, а не Grafana login page. Заметил, попросил агента переехать. Агент поправил docker-compose.yml: проброс "3000:3000" сменил на "13737:3000", перезапустил Grafana через docker compose up -d grafana --force-recreate. Логин page открылся.

Маленькая деталь, но без неё запуск ломается без понятной ошибки в логах — просто «сайт открывается, но не тот».

Грабля 3: главная — без OTEL_LOG_TOOL_DETAILS всё бесполезно

Это та грабля, ради которой стоит писать пост. Без отдельной env-переменной все user-defined и plugin-скиллы в логах сливаются в один общий placeholder custom_skill. Дашборд показывает «47 вызовов custom_skill» — и я не знаю, что внутри: daily-tasks, gh-issues, meeting-copilot или какой-нибудь скилл из superpowers. Имя скилла теряется на уровне инструментирования, не на уровне Loki-запроса.

Эту деталь я выловил из research’а заранее, до запуска. Иначе пришлось бы неделю смотреть в красивый, но бесполезный график. После того как разобрался, попросил агента дописать env-блок:

Дополни env-секцию в ~/.claude/settings.json. Кроме базовых OTel переменных (CLAUDE_CODE_ENABLE_TELEMETRY, exporter настройки) добавь обязательно OTEL_LOG_TOOL_DETAILS=1 — иначе все мои user-defined и plugin skills сольются в placeholder custom_skill в Loki, и я не увижу реальных имён. Я бы это пропустил.

Бонусом — проверка пайплайна до того, как я рестартану claude-процесс. Если стек битый, лучше узнать это сразу, а не через сутки тишины.

До того как я рестартану claude, проверь что pipeline живой. Отправь curl’ом фейковый OTLP-payload на collector с метрикой test_skill_invocation_total{skill_name="manager"}. Подожди 15 секунд (scrape interval), запроси Prometheus и убедись что метрика появилась. Если pipe работает — значит проблема не в стеке, а только в том что claude пока не отправляет.

Тестовая метрика появилась в Prometheus за 15 секунд. Рестарт claude, первый запрос — и в дашборде поползли реальные имена скиллов. Тот же паттерн «настроил один раз и забыл», как в посте про хуки Claude Code и GitHub-issues — раз поднял, дальше работает само.

Что смотрю в дашборде

ColeMurray-репо приходит с пятью преднастроенными дашбордами: Cost & Usage, Tool Performance, User Activity, Performance & Errors, Event Logs. Из коробки они показывают токены, частоту инструментов, ошибки. Под скиллы я хочу одну простую штуку — top и zero-usage. Запрос в Loki через LogQL короткий:

sum by (skill_name) (count_over_time({event_name="skill_activated"} [7d]))

Что я ищу:

Privacy-нюанс: у Claude Code есть отдельная переменная OTEL_LOG_USER_PROMPTS=1, которая логирует мои промпты целиком в Loki. Для локального self-host у меня на ноутбуке это допустимо — данные никуда не утекают. Для shared infra я бы не включал. У меня выключено: для трекинга скиллов имя скилла важнее текста промпта.

Outro

Backfill невозможен — данные пошли с момента рестарта. Через две недели, около 13 мая, сделаю первый проход: топ-10 оставлю и шлифую, zero-usage за 14 дней удаляю или сливаю в более общий скилл.

Это не observability ради observability. Коллекция скиллов растёт быстрее, чем я её ревьюю, — без счётчика «полезный но редкий» неотличим от «забытый и сломанный». Та же логика, по которой я писал скилл для аудита продуктовых данных: аудит — повторяемая практика, и для своих инструментов нужна та же дисциплина, что и для бизнеса.

Подписаться на обновления — @sereja_tech