Мне интересно следить, какие статьи в моём блоге наиболее популярны, и сколько людей заглядывает в блог каждый день. Этот блог прошел через несколько этапов, позволяющих оценить эти показатели. Сначала это была Google Аналитика, но делиться всей этой информацией с Google не очень комфортно. Поэтому я перешёл на Matomo, развернутый на сервере CHATON: Libréon.
Проблема с Matomo в том, что он определяется как трекер большинством блокировщиков рекламы — независимо от того, используете ли вы его для сбора данных об аудитории или для полноценного трекинга. А на техническом блоге почти все посетители используют блокировщики рекламы, из-за чего статистика получается искажённой. Кроме того, как мне кажется, Matomo работает не слишком быстро, а данные обновляются с задержкой — иногда приходится ждать до следующего дня.
Я уже успел поработать со стеком Grafana + OpenTelemetry в проекте Baywatch, и мне захотелось применить эту же связку для замены Matomo. К тому же у меня уже был настроен сервер со всеми необходимыми компонентами.
Другая проблема – если сайт статический, размещенный на Github Pages или других “Pages”, у вас нет доступа к логам веб-сервера, поэтому для их сбора необходимо использовать JS-клиент.
Первый шаг – это JavaScript-клиент, который будет «стучаться» в Open Telemetry collector с информацией о посещённой страницей. У OpenTelemetry есть SDK, с помощью которого можно передавать метрики и трассировки.
Передавать сигнал «ping» в виде метрики невозможно, т.к. это не сочетается с принципами работы OpenTelemetry и Prometheus. Дело в том, что передается только значение
Зато хорошо работает отправка интервалов (spans). При каждом посещении Otela отправляет интервал с атрибутами посещенных страниц (заголовок, ссылка и т.д.) в OpenTelemetry collector.
Проект Otela доступен github: github.com/Marthym/otela
Проект Otela вырос из одного из примеров в SDK OpenTelemetry и распространяется с открытым исходным кодом. Файл
В контексте OpenTelemetry интервал представляет собой отдельную операцию или событие при трассировке. Это основная единица наблюдения. Каждый раз, когда пользователь посещает страницу на вашем сайте с Otela, создаётся интервал, который фиксирует это «действие»: посещённый URL, заголовок страницы, использованный браузер, источник (реферер) и другие данные. Этот интервал затем отправляется в OpenTelemetry Collector. Он содержит настраиваемые атрибуты, которые можно извлекать и трансформировать для генерации метрик, используемых в Prometheus. Короче говоря, интервал — это информационно обогащённый и структурированный лог, идеально подходящий для отслеживания взаимодействий пользователей на статическом сайте.
Чтобы установить Otela, достаточно добавить следующий JavaScript-код в конец HTML-страницы:
Также нужно настроить OpenTelemetry Collector для приёма интервалов, а также указать источник JS-скрипта. В примерах используется артефакт с GitHub, но на практике лучше встраивать
Для сбора интервалов, отправленных Otela, необходимо настроить OpenTelemetry Collector таким образом, чтобы он принимал входящие интервалы, преобразовывал их в метрики и записывал в Prometheus.
На стороне приёмника важно правильно настроить CORS. В зависимости от ваших задач, постарайтесь максимально ограничить доступ. В приведённом примере CORS настроены довольно свободно, но если вы отслеживаете только один сайт — лучше ужесточить конфигурацию.
Сначала фильтруются входящие данные — остаются только интервалы, поступающие с отслеживаемых сайтов. Затем обрабатываются атрибуты интервалов в зависимости от того, что именно нужно сохранить. В нашем случае мы не сохраняем полный referrer, а оставляем только домен.
Остаётся только преобразовать интервалы в метрики — то есть просто посчитать интервалы, чтобы получить количество посещений. В OpenTelemetry для этого есть специальные коннекторы. Они позволяют связать конвейер интервалов напрямую с конвейером метрик.
Таким образом, мы подключаем коннектор одновременно к выходу трассировок и ко входу метрик. Помимо подсчёта интервалов, коннектор «span-to-metrics» позволяет рассчитывать процентильные значения.
Чтобы отличать пользователей друг от друга, нужно получать их IP-адреса. Однако сделать это средствами JavaScript невозможно — для этого потребуются дополнительные запросы и, скорее всего, придётся передавать данные стороннему сервису.
Здесь на помощь приходит фронтенд на базе NginX, который должен стоять перед OpenTelemetry Collector. Он знает IP-адрес клиента, отправляющего интервал, и может передать эту информацию в OpenTelemetry через заголовки запроса.
А с помощью модуля GeoIP для NginX можно дополнительно определить географические координаты посетителей и отображать их на карте в Grafana.
Со стороны OpenTelemetry конфигурация выглядит следующим образом:
Важно указать параметр
Наконец после небольшой настройки в Grafana мы получаем дашборд, который выглядит следующим образом:
Да, видно, что этот блог посещают лишь несколько любопытных технарей 😄 — хотя, справедливости ради, это был снимок трафика за выходные. Обычно трафик увеличивается в будние дни.
Вы можете найти JSON дашборда в этом gist.
С помощью счётчика интервалов и настроенных атрибутов дашборд показывает:
Все конфигурационные файлы доступны в этом gist.
В итоге, эта конфигурация идеально подходит под мои нужды и является отличной заменой Matomo. К тому же, благодаря Otela, все данные хранятся и контролируются в Франции. Ничто не покидает мои серверы.
Однако решение не без недостатков. Prometheus не предназначен для хранения такого типа данных. Для логов лучше подойдёт что-то вроде Loki. На данный момент Otela — это всего лишь концепт, и я ещё не нашёл наилучший способ перенаправить данные в Loki.
В продолжение темы рекомендуем вам ознакомиться с книгой «Изучаем OpenTelemetry: современный мониторинг систем».
TL;DR
Хотите отслеживать трафик на статическом блоге без Matomo и Google Analytics?
Рассказываю, как с помощью OpenTelemetry, Prometheus, Grafana и небольшого самописного скрипта на JS (Otela) можно элегантно и эффективно следить за посещаемостью — при этом полностью сохранив контроль над своими данными.
Проблема
Проблема с Matomo в том, что он определяется как трекер большинством блокировщиков рекламы — независимо от того, используете ли вы его для сбора данных об аудитории или для полноценного трекинга. А на техническом блоге почти все посетители используют блокировщики рекламы, из-за чего статистика получается искажённой. Кроме того, как мне кажется, Matomo работает не слишком быстро, а данные обновляются с задержкой — иногда приходится ждать до следующего дня.
Я уже успел поработать со стеком Grafana + OpenTelemetry в проекте Baywatch, и мне захотелось применить эту же связку для замены Matomo. К тому же у меня уже был настроен сервер со всеми необходимыми компонентами.
Другая проблема – если сайт статический, размещенный на Github Pages или других “Pages”, у вас нет доступа к логам веб-сервера, поэтому для их сбора необходимо использовать JS-клиент.

Otela — JS-клиент
Первый шаг – это JavaScript-клиент, который будет «стучаться» в Open Telemetry collector с информацией о посещённой страницей. У OpenTelemetry есть SDK, с помощью которого можно передавать метрики и трассировки.
Передавать сигнал «ping» в виде метрики невозможно, т.к. это не сочетается с принципами работы OpenTelemetry и Prometheus. Дело в том, что передается только значение
1
, и счетчик сбрасывается на каждом визите. Зато хорошо работает отправка интервалов (spans). При каждом посещении Otela отправляет интервал с атрибутами посещенных страниц (заголовок, ссылка и т.д.) в OpenTelemetry collector.
Проект Otela доступен github: github.com/Marthym/otela
Проект Otela вырос из одного из примеров в SDK OpenTelemetry и распространяется с открытым исходным кодом. Файл
otela.js
собирается с помощью WebPack, что позволяет максимально сократить его размер — его легко подключить к любой странице.Что такое интервал?
В контексте OpenTelemetry интервал представляет собой отдельную операцию или событие при трассировке. Это основная единица наблюдения. Каждый раз, когда пользователь посещает страницу на вашем сайте с Otela, создаётся интервал, который фиксирует это «действие»: посещённый URL, заголовок страницы, использованный браузер, источник (реферер) и другие данные. Этот интервал затем отправляется в OpenTelemetry Collector. Он содержит настраиваемые атрибуты, которые можно извлекать и трансформировать для генерации метрик, используемых в Prometheus. Короче говоря, интервал — это информационно обогащённый и структурированный лог, идеально подходящий для отслеживания взаимодействий пользователей на статическом сайте.
Установка
Чтобы установить Otela, достаточно добавить следующий JavaScript-код в конец HTML-страницы:
<!-- Otela -->
<script>
var _ota=window._ota=window._ota||{};_ota.t="your.opentelemetry.server";
(function(){
var t=document,e=t.createElement("script"),a=t.getElementsByTagName("script")[0];
e.async=!0;e.src="https://github.com/Marthym/otela/releases/download/1.0.0/otela.js";a.parentNode.insertBefore(e,a)
})();
</script>
<!-- Конец кода Otela -->
Также нужно настроить OpenTelemetry Collector для приёма интервалов, а также указать источник JS-скрипта. В примерах используется артефакт с GitHub, но на практике лучше встраивать
otela.js
прямо в исходный код сайта — это снижает риск блокировки скрипта по URL и улучшает загрузку.Конфигурация OpenTelemetry
Для сбора интервалов, отправленных Otela, необходимо настроить OpenTelemetry Collector таким образом, чтобы он принимал входящие интервалы, преобразовывал их в метрики и записывал в Prometheus.
Receiver (приёмник)
receivers:
otlp/otela:
protocols:
http:
endpoint: ':4318'
cors:
allowed_origins:
- http://*
- https://*
max_age: 7200
На стороне приёмника важно правильно настроить CORS. В зависимости от ваших задач, постарайтесь максимально ограничить доступ. В приведённом примере CORS настроены довольно свободно, но если вы отслеживаете только один сайт — лучше ужесточить конфигурацию.
Processors (процессоры)
processors:
batch:
filter/otela:
spans:
include:
match_type: strict
services:
- "blog.ght1pc9kc.fr"
- "swr.ght1pc9kc.fr"
attributes/otela:
actions:
- key: referrer
pattern: ^https?:\/\/(?P<dummy>[^@\n]+@)?(?P<referrer>[^:\/\n?]+)
action: extract
- key: dummy
action: delete
- key: referrer
value: direct
action: insert
Сначала фильтруются входящие данные — остаются только интервалы, поступающие с отслеживаемых сайтов. Затем обрабатываются атрибуты интервалов в зависимости от того, что именно нужно сохранить. В нашем случае мы не сохраняем полный referrer, а оставляем только домен.
Connector (коннектор)
Остаётся только преобразовать интервалы в метрики — то есть просто посчитать интервалы, чтобы получить количество посещений. В OpenTelemetry для этого есть специальные коннекторы. Они позволяют связать конвейер интервалов напрямую с конвейером метрик.
connectors:
spanmetrics/otela:
namespace: otela
histogram:
explicit:
buckets: [6ms, 10ms, 100ms, 250ms, 500ms]
dimensions:
- name: navigator
- name: os
- name: platform
- name: referrer
- name: title
service:
pipelines:
traces:
receivers: [otlp/otela]
processors: [filter/otela, attributes/otela]
exporters: [spanmetrics/otela]
metrics:
receivers: [spanmetrics/otela]
processors: [batch]
exporters: [prometheus]
Таким образом, мы подключаем коннектор одновременно к выходу трассировок и ко входу метрик. Помимо подсчёта интервалов, коннектор «span-to-metrics» позволяет рассчитывать процентильные значения.
Получение IP-адресов
Чтобы отличать пользователей друг от друга, нужно получать их IP-адреса. Однако сделать это средствами JavaScript невозможно — для этого потребуются дополнительные запросы и, скорее всего, придётся передавать данные стороннему сервису.
Здесь на помощь приходит фронтенд на базе NginX, который должен стоять перед OpenTelemetry Collector. Он знает IP-адрес клиента, отправляющего интервал, и может передать эту информацию в OpenTelemetry через заголовки запроса.
location / {
try_files $uri $uri/ @proxy;
}
location @proxy {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port 443;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-GeoIP-Latitude $geoip_latitude;
proxy_set_header X-GeoIP-Longitude $geoip_longitude;
proxy_pass $upstream;
}
А с помощью модуля GeoIP для NginX можно дополнительно определить географические координаты посетителей и отображать их на карте в Grafana.
Со стороны OpenTelemetry конфигурация выглядит следующим образом:
receivers:
otlp/otela:
protocols:
http:
endpoint: ':4318'
include_metadata: true
cors:
allowed_origins:
- http://*
- https://*
max_age: 7200
connectors:
spanmetrics/otela:
namespace: otela
histogram:
explicit:
buckets: [6ms, 10ms, 100ms, 250ms, 500ms]
dimensions:
- name: ip
- name: navigator
- name: os
- name: platform
- name: referrer
- name: title
- name: latitude
- name: longitude
attributes/otela:
actions:
- key: referrer
pattern: ^https?:\/\/(?P<dummy>[^@\n]+@)?(?P<referrer>[^:\/\n?]+)
action: extract
- key: dummy
action: delete
- key: referrer
value: direct
action: insert
- key: ip
from_context: X-Real-IP
action: upsert
- key: latitude
from_context: X-GeoIP-Latitude
action: upsert
- key: longitude
from_context: X-GeoIP-Longitude
action: upsert
Важно указать параметр
include_metadata: true
, который позволяет извлекать заголовки NginX в процессорах OpenTelemetry. Затем достаточно извлечь эти значения и добавить их в атрибуты интервалов перед отправкой в Prometheus.🔥Важно учесть🔥: сбор IP-адресов и геоданных значительно увеличивает кардинальность атрибутов метрик, что влияет на требования к объёму хранимых данных в Prometheus. Поэтому следует осторожно использовать эту конфигурацию и установить ограничения на объём хранения.
--storage.tsdb.retention.time=90d --storage.tsdb.retention.size=4GB
В этом примере срок хранения данных ограничен 90 днями, а объём хранилища — 4 ГБ.
Помещаем результат Grafana
Наконец после небольшой настройки в Grafana мы получаем дашборд, который выглядит следующим образом:

Да, видно, что этот блог посещают лишь несколько любопытных технарей 😄 — хотя, справедливости ради, это был снимок трафика за выходные. Обычно трафик увеличивается в будние дни.
Вы можете найти JSON дашборда в этом gist.
С помощью счётчика интервалов и настроенных атрибутов дашборд показывает:
- Общее количество посещений
- Количество уникальных посетителей
- Источники трафика
- Какие страницы были посещены и как часто
- А ещё — благодаря геолокации, точки на карте 🗺️
Заключение
Все конфигурационные файлы доступны в этом gist.
В итоге, эта конфигурация идеально подходит под мои нужды и является отличной заменой Matomo. К тому же, благодаря Otela, все данные хранятся и контролируются в Франции. Ничто не покидает мои серверы.
Однако решение не без недостатков. Prometheus не предназначен для хранения такого типа данных. Для логов лучше подойдёт что-то вроде Loki. На данный момент Otela — это всего лишь концепт, и я ещё не нашёл наилучший способ перенаправить данные в Loki.
В продолжение темы рекомендуем вам ознакомиться с книгой «Изучаем OpenTelemetry: современный мониторинг систем».