
Типичный (наш) стек журналов на базе Loki состоит из трех компонентов:
Чтобы разобраться, все действия буду проводить графаной в облаке, а значит нужно:
InstallationToken name и получить код установки Alloy (как установить из консоли).По факту запуска видим:
Alloy is now running!
To check the status of Alloy, run: sudo systemctl status alloy.service
To restart Alloy, run: sudo systemctl restart alloy.service
The config file is located at: /etc/alloy/config.alloyА дефолтный файл /etc/alloy/config.alloy будет с таким содержимым:
prometheus.exporter.self "integrations_alloy" { }
discovery.relabel "integrations_alloy" {
targets = prometheus.exporter.self.integrations_alloy.targets
rule {
target_label = "instance"
replacement = constants.hostname
}
rule {
target_label = "alloy_hostname"
replacement = constants.hostname
}
rule {
target_label = "job"
replacement = "integrations/alloy-check"
}
}
#
prometheus.scrape "integrations_alloy" {
targets = discovery.relabel.integrations_alloy.output
forward_to = [prometheus.relabel.integrations_alloy.receiver]
scrape_interval = "60s"
}
prometheus.relabel "integrations_alloy" {
forward_to = [prometheus.remote_write.metrics_service.receiver]
rule {
source_labels = ["__name__"]
regex = "(prometheus_target_sync_length_seconds_sum|prometheus_target_scrapes_.*|prometheus_target_interval.*|prometheus_sd_discovered_targets|alloy_build.*|prometheus_remote_write_wal_samples_appended_total|process_start_time_seconds)"
action = "keep"
}
}
prometheus.remote_write "metrics_service" {
endpoint {
url = "https://prometheus-prod-24-prod-eu-west-2.grafana.net/api/prom/push"
basic_auth {
username = "XXX"
password = "XXX="
}
}
}
loki.write "grafana_cloud_loki" {
endpoint {
url = "https://logs-prod-012.grafana.net/loki/api/v1/push"
basic_auth {
username = "XXX"
password = "XXX="
}
}
}Проверим работу Alloy, для этого откроем в браузере http://localhost:12345 (красным показал как данные двигаются на самом деле):

Как видно, prometheus.scrape это главный компонент, который собирает в себя, а потом отправляет в место назначения:
А в графане на странице https://xxx.grafana.net/a/grafana-collector-app/alloy/monitoring мы видим уже собранные данные:

Когда изучаешь новый интрумент, тебе хочется начать с чего-то простого, и я предолженным шагам в https://xxx.grafana.net/ решил настроить Alloy для отслеживания работы самого докера конечно вместе с логами, и вот как это должно выглядеть по факту:


Для этого нужно разрешить Alloy вычивать данные из docker-a и кажется достаточно просто настроить конфигурацию, чтобы данные пошли в loki, а из loki забирать данные Графаной (инструкция есть в https://xxx.grafana.net/ - Add new connection - Docker).
Однако, я столкнулся с рядом проблем (ошибок со стороны Alloy), а именно:
Поэтому, на дев-машине я пока оставляю рабочим этот вариант (буду тестировать), а на проде буду использовать более понятный вариант сбора логов (более контролируемый и понятный, но в который все равно нужно въехать, о нем ниже).
Прежде чем начать, хочу дать совет для подключения используйте инструкции в https://xxx.grafana.net/ - Add new connection, а документацию на сайте используйте уже потом (для необходимой кастомизации), но не наоборот. Итак, что у нас будет:
1. docker (nginx) пишет логи в свои файлы (docker сам будет ротировать лог-файлы)
2. alloy читает эти лог-файлы и отправляет данные в базу данных loki, вот детали:
loki.source.file читет файл nginx.log с помощью компонента local.file_matchloki.source.file читает из loki.source.file и отправляет в loki.writeloki.write отправяет данные по адресу endpoint.urlМы не будем использовать UDP: вот почему не стоит отправлять логи по UDP из докера в Alloy loki.source.gelf который слушает UDP порт, так что следующий вариант не будем использовать:
docker run --log-driver syslog --log-opt syslog-address=udp://1.2.3.4:1111 alpine echo hello worldЧтобы все это заработало, нужно следовать инструкции на странице https://xxx.grafana.net/connections/add-new-connection/nginx?page=alloy вкладка Configuration Details (такая же инструкция в документации) а именно:
1. Объявить формат логов в файле /etc/nginx/nginx.conf в секции http (чтобы nginx писал логи в json-формате):
user nginx;
worker_processes auto;
error_log /dev/fd/2 error;
...
http {
...
log_format json_analytics escape=json '{'
'"msec": "$msec", ' # request unixtime in seconds with a milliseconds resolution
'"connection": "$connection", ' # connection serial number
'"connection_requests": "$connection_requests", ' # number of requests made in connection
'"pid": "$pid", ' # process pid
'"request_id": "$request_id", ' # the unique request id
'"request_length": "$request_length", ' # request length (including headers and body)
'"remote_addr": "$remote_addr", ' # client IP
'"remote_user": "$remote_user", ' # client HTTP username
'"remote_port": "$remote_port", ' # client port
'"time_local": "$time_local", '
'"time_iso8601": "$time_iso8601", ' # local time in the ISO 8601 standard format
'"request": "$request", ' # full path no arguments if the request
'"request_uri": "$request_uri", ' # full path and arguments if the request
'"args": "$args", ' # args
'"status": "$status", ' # response status code
'"body_bytes_sent": "$body_bytes_sent", ' # the number of body bytes exclude headers sent to a client
'"bytes_sent": "$bytes_sent", ' # the number of bytes sent to a client
'"http_referer": "$http_referer", ' # HTTP referer
'"http_user_agent": "$http_user_agent", ' # user agent
'"http_x_forwarded_for": "$http_x_forwarded_for", ' # http_x_forwarded_for
'"http_host": "$http_host", ' # the request Host: header
'"server_name": "$server_name", ' # the name of the vhost serving the request
'"request_time": "$request_time", ' # request processing time in seconds with msec resolution
'"upstream": "$upstream_addr", ' # upstream backend server for proxied requests
'"upstream_connect_time": "$upstream_connect_time", ' # upstream handshake time incl. TLS
'"upstream_header_time": "$upstream_header_time", ' # time spent receiving upstream headers
'"upstream_response_time": "$upstream_response_time", ' # time spent receiving upstream body
'"upstream_response_length": "$upstream_response_length", ' # upstream response length
'"upstream_cache_status": "$upstream_cache_status", ' # cache HIT/MISS where applicable
'"ssl_protocol": "$ssl_protocol", ' # TLS protocol
'"ssl_cipher": "$ssl_cipher", ' # TLS cipher
'"scheme": "$scheme", ' # http or https
'"request_method": "$request_method", ' # request method
'"server_protocol": "$server_protocol", ' # request protocol, like HTTP/1.1 or HTTP/2.0
'"pipe": "$pipe", ' # "p" if request was pipelined, "." otherwise
'"gzip_ratio": "$gzip_ratio"'
'}';
access_log /dev/fd/1 json_analytics;
server {
server_name app-frontend;
listen 8000 default_server;
root /app;
location / {
try_files $uri 404;
}
}
}2. Настроить слежку за всеми логами, которые пишет докер:
local.file_match "tmp" {
path_targets = [{"__path__" = "/var/lib/docker/containers/**/*-json.log"}]
}3. Даем Alloy права доступа на чтение лог-файлов докера.
4. Вот только nginx тоже пишет в json, поэтому получился json в json, пример:
{"log":"{\"msec\": \"1713252610.587\", \"connection\": \"1\", \"connection_requests\": \"2\", \"pid\": \"30\", \"request_id\": \"d993e679d01b173b2a19716c585b4278\", \"request_length\": \"662\", \"remote_addr\": \"172.16.54.1\", \"remote_user\": \"\", \"remote_port\": \"47238\", \"time_local\": \"16/Apr/2024:07:30:10 +0000\", \"time_iso8601\": \"2024-04-16T07:30:10+00:00\", \"request\": \"GET /favicon.ico HTTP/1.1\", \"request_uri\": \"/favicon.ico\", \"args\": \"\", \"status\": \"200\", \"body_bytes_sent\": \"15406\", \"bytes_sent\": \"15650\", \"http_referer\": \"http://localhost:8000/robots.txt\", \"http_user_agent\": \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36\", \"http_x_forwarded_for\": \"\", \"http_host\": \"localhost:8000\", \"server_name\": \"app-frontend\", \"request_time\": \"0.000\", \"upstream\": \"\", \"upstream_connect_time\": \"\", \"upstream_header_time\": \"\", \"upstream_response_time\": \"\", \"upstream_response_length\": \"\", \"upstream_cache_status\": \"\", \"ssl_protocol\": \"\", \"ssl_cipher\": \"\", \"scheme\": \"http\", \"request_method\": \"GET\", \"server_protocol\": \"HTTP/1.1\", \"pipe\": \".\", \"gzip_ratio\": \"\"}\n","stream":"stdout","time":"2024-04-16T07:30:10.588288009Z"}Чтобы внутренний json превратить в нормальный, воспользуемся loki.process инструкцией stage.docker (или stage.cri) и в файл /etc/alloy/config.alloy добавим:
loki.process "our_dockers" {
forward_to = [loki.write.grafana_cloud_loki.receiver]
stage.docker {}
}И не забываем, что кто-то должен переместить данные из local.file_match "tmp" в loki.process "our_dockers":
loki.source.file "files" {
targets = local.file_match.tmp.targets
forward_to = [loki.process.our_dockers.receiver]
}5. Перезапускаем Alloy: sudo systemctl restart alloy.service и смотрим статус, если конфиг в порядке, то увидим:
$ sudo systemctl status alloy.service
● alloy.service - Vendor-agnostic OpenTelemetry Collector distribution with programmable pipelines
Loaded: loaded (/lib/systemd/system/alloy.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2024-04-16 15:09:10 MSK; 47s ago
Docs: https://grafana.com/docs/alloy
Main PID: 1818021 (alloy)
Tasks: 19 (limit: 38083)
Memory: 44.2M
CPU: 675ms
CGroup: /system.slice/alloy.service
└─1818021 /usr/bin/alloy run --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy
апр 16 15:09:10 server alloy[1818021]: ts=2024-04-16T12:09:10.650496356Z level=info msg="finished ...И получаем:

6. Заходим на https://xxx.grafana.net/connections/add-new-connection/nginx?page=alloy и видим, что подключение состоялось:

7. Заходим на страницу интеграции https://xxx.grafana.net/dashboards/f/integration---nginx/integration-nginx и видим, что все хорошо:

8. Нажимаем "Loki NGINX Service Mesh - JSON version" (там где оранжевая плашка) и видим такую страницу:

Дальше можно нажать на значок троеточия и нажать Explore, в результате увидим наши логи:

Внимание: стрелочкой показал на логи, которые были созданы еще до того, как я настроил nginx писать логи в json.
Если что-то пошло не так, то заходим в https://xxx.grafana.net/connections/datasources и выбираем grafanacloud-logs и внизу страницы нажимаем кнопку Test и получаем информацию с ссылками:
Data source successfully connected.
Next, you can start to visualize data by "building a dashboard" or by querying data in "the Explore view"На этой же странице справа вверху видим кнопку "Explore data", благодаря которой можно посмотреть на таблицу с данными.
Да, Alloy можно запустить в docker-контейнере:
docker run -d \
--name grafana-alloy \
--restart=unless-stopped \
-v <DATA_FILE_PATH>:/var/lib/alloy/data \
-v <CONFIG_FILE_PATH>:/etc/alloy/config.alloy \
-v /var/lib/docker/containers:/var/lib/docker/containers \
-p 12345:12345 \
grafana/alloy:latest \
run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/var/lib/alloy/data /etc/alloy/config.alloyА дальше можно посмотреть видосики: 1
Когда я только тестировал настройки, то совершал ошибки (конечно никаких логов в Grafana я не видел), поэтому начал дебажить, для этого в файл /etc/alloy/config.alloy добавил инструкцию логирования (по-умолчанию level = "info"):
logging {
level = "debug"
format = "logfmt"
}Перезапускаем sudo systemctl restart alloy.service и проверяем что работает sudo systemctl status alloy.service
Видим, что успешный статус + последние лог-записи, которые сделал Alloy. Но, последних логов недостаточно, поэтому начинаю следить за логами (в моем случае ошибка была вначале):
journalctl -u alloy.service -fВ отдельном окне перезапускайте Alloy и смотрите записи на предмет error, у меня было так:
...
...level=error msg="failed to start reporter" err="context canceled"
...
...level=info msg="loki.source.file component shutting down, stopping readers and positions file" component_path=/ component_id=loki.source.file.files
...level=error msg="error running exporter" component_path=/ component_id=prometheus.exporter.self.integrations_alloy err="context canceled"сноска: последние лог-записи можно посмотреть так: journalctl -u alloy.service -n 300 --no-pager | grep loki
Еще я тестировал более простые настройки, для этого создал файл /var/www/jb/app/alloy.json в который сохранил несколько json-логов ngnix, вот какие настройки я указал:
local.file_match "tmp" {
path_targets = [{"__path__" = "/var/www/jb/app/alloy.json"}]
}
loki.source.file "files" {
targets = local.file_match.tmp.targets
forward_to = [loki.write.grafana_cloud_loki.receiver]
}Дальше я перешел на страницу просмотра логов и у меня ничего не отображалось, в блоке Label filters:
Select label и выбрал filenameSelect value и выбрал мой файл /var/www/jb/app/alloy.jsonи логи отобразились:

Оказывается проблема была в том, что у Alloy не хватало прав на чтение файлов созданных из под рута (докер создает файлы из под рута и гугл говорит, что иначе докер не умеет).
Переходим в левом меню в Explore и дальше по шагам:

Взять любые значения и применить к ним фильтр json:
{stream=~".+", job=~".*", instance=~".*", cluster=""} | jsonэквивалент:
{stream=~".+"} | jsonВ распарсенном json поискать значения парочки ключей:
{stream=~".+"} | json | http_host=`site.ru` | status > 299Вот так я ищу ошибку в monolog:
{stream=~".+"} | json | message=`error message`Найти точно по левой части и что-угодно справа:
request_uri=~"^/foo(.*)"Найти по части совпадения строки:
{stream=~".+"}|~ "(.*)Find and save image problems(.*)"Тоже самое, но сначала распарсить json:
{stream=~".+"} | json | message=~`(.*)Uncaught PHP Exception Doctrine(.*)`OR условие:
... | json | scheme=`https` | status=~"301|302" [$__auto])))Отрицание:
http_referer !~`(http|https)://yapro.ru(.*)`Сформировать строку из разных значений:
| line_format "➡️ {{.request_method}} {{.request_uri}} with HTTP status: {{.status}} "Из документации:
=: exactly equal, example:=~: regex matches, example: {name =~ "mysql.+"}|=: Log line contains string, example: {job="mysql"} |= "error" или |= "error"|~: Log line contains a match to the regular expression, examples:{name="a"} |~ "t-ops.*io:2003"{name="a"} |~ `error=\w+`!=: not equal, example: {instance=~"kafka-[23]"} != "kafka.server:type=ReplicaManager"!~: regex does not match, example: {name !~ "mysql.+"} или {name !~ `mysql-\d+`}Еще немного:|= "string" — ищет вхождения строки (регистрозависимо) во всей текстовой линии лога Log Queries | Grafana Loki.
{job="mysql"} |= "error" != "timeout"{job="mysql"} |= "error" | json | line_format "{{.err}}"{job="mysql"} | json | line_format "{{.message}}" |= "error"Применить функцию подсчета записей, чтобы построить график:
sum(count_over_time({stream=~".+", job=~".*", instance=~".*", cluster=""} | json | http_host=`yapro.ru` | status > 299 [$__auto]))ГЛАВНОЕ: при этом чуть ниже настройки выбрать: Type: Instant
Сгруппировать по статусу ответа:
sum by (status) (rate({stream=~".+"} | json | scheme=`https` [$__auto]))тоже самое (пока не понимаю чем rate от count_over_time отличается:
sum by (status) (count_over_time({stream=~".+"} | json | scheme=`https` [$__interval]))но в мануале написано (а еще тут подробно разжовывается):
rate(unwrapped-range): вычисляет посекундную скорость всех значений в указанном интервале
sum_over_time(unwrapped-range): сумма всех значений в указанном интервале
avg_over_time(unwrapped-range): среднее значение всех точек в указанном интервале
max_over_time(unwrapped-range): максимальное значение всех точек в указанном интервале
min_over_time(unwrapped-range): минимальное значение всех точек в указанном интервале
stdvar_over_time(unwrapped-range): стандартная дисперсия значений в указанном интервале.
stddev_over_time(unwrapped-range): стандартное отклонение генеральной совокупности значений в указанном интервале.
quantile_over_time(scalar,unwrapped-range): φ-квантиль (0 ≤ φ ≤ 1) значений в указанном интервале (примеры)
Среднее значение по полю (берем значение из поля request_time за промежуток времени и делим на количество записей в этот промежуток):
avg_over_time({stream=~".+"} | json | scheme=`https` | unwrap request_time | __error__="" [$__auto]) by (cluster)Больше примеров тут. Для внимательных - мы добавили | __error__="" чтобы исключать ошибочные логи (насколько я понимаю, не имеющие поля request_time
Надо бы разобраться в этих:
count_over_timeРегулярки:
sum by (host) (count_over_time({host=~"A.*"} |= "filter" [3h]))
/
sum by (host) (sum_over_time({host=~"A.*", units="sub-item"} |= "data" | regexp `val:(?P<myval>\d+),` | unwrap myval [3h]))Рассчитайте p99 задержки nginx-ingress по пути:
quantile_over_time(0.99,
{cluster="ops-tools1",container="ingress-nginx"}
| json
| __error__ = ""
| unwrap request_time [1m]) by (path)Следующий запрос показывает, как можно переформатировать строку журнала, чтобы ее было легче читать на экране.
{cluster="ops-tools1", name="querier", namespace="loki-dev"}
|= "metrics.go" != "loki-canary"
| logfmt
| query != ""
| label_format query="{{ Replace .query \"\\n\" \"\" -1 }}"
| line_format "{{ .ts}}\t{{.duration}}\ttraceID = {{.traceID}}\t{{ printf \"%-100.100s\" .query }} "Форматирование меток используется для очистки запроса, в то время как формат строки сокращает объем информации и создает табличный вывод.
Мои эксперименты:
sum by (row_count) (sum_over_time({stream=~".+"} | json | scheme=`https` | http_host=`yapro.ru` | request_uri=~"/articles(.*)" | http_referer =~ `(.+)(yandex\.ru)(.*)` | http_user_agent!~"(.*)(bot)(.*)"| line_format "row_count=1"| logfmt | unwrap row_count|__error__="" [$__auto])) + INSTANCEКоличество переходов с Google (Type=Instance):
sum(count_over_time({stream=~".+"} | json | scheme = `https` | http_host = `yapro.ru` | http_referer =~ `(.+)(google\.com)(.*)` [$__auto]))Все те же переходы, только с группировкой по remote_addr - полезно, когда выводишь таблицу:
sum by (remote_addr) (count_over_time({stream=~".+"} | json | scheme = `https` | http_host = `yapro.ru` | http_referer =~ `(.+)(google\.com)(.*)` [$__auto]))в итоге таблица выглядит так:

А если нужно просто число уникальных пользователей, то количество IP перешедших с Google:
count(sum by (remote_addr) (count_over_time({stream=~".+"} | json | scheme = `https` | http_host = `yapro.ru` | http_referer =~ `(.+)(google\.com)(.*)` [$__auto])))А если нужно сложить весь правый столбец и показать одно число:
sum(sum by (remote_addr) (count_over_time({stream=~".+"} | json | scheme = `https` | http_host = `yapro.ru` | http_referer =~ `(.+)(google\.com)(.*)` [$__auto])))но если вдуматься, то зачем группировать, если можно просто сложить:
sum(count_over_time({stream=~".+"} | json | scheme = `https` | http_host = `yapro.ru` | http_referer =~ `(.+)(google\.com)(.*)` [$__auto]))Можно посмотреть как он отправляет запросы, если направить конфиг на свой сайт в котором сохранить запрос в виде curl.
Поэтому просто в файл /etc/alloy/config.alloy добавляем:
local.file_match "logs_integrations_integrations_nginx" {
path_targets = [{
__path__ = "<path to json nginx access log>",
}]
}
loki.source.file "logs_integrations_integrations_nginx" {
targets = local.file_match.logs_integrations_integrations_nginx.targets
forward_to = [loki.write.grafana_cloud_loki.receiver]
}используемые компоненты:
На странице интергации в облаке предлагали сконфигурировать так:
local.file_match "logs_integrations_integrations_nginx" {
path_targets = [{
__address__ = "localhost",
__path__ = "<path to json nginx access log>",
host = "<http_hostname>",
instance = bestbuilder.server,
}]
}на странице конфигурации Alloy написан такой пример:
local.file "my_file" {
filename = "/tmp/my-file.txt"
}
prometheus.remote_write "default" {
endpoint {
url = "http://localhost:9009/api/prom/push"
}
}Надо не забыть, что например когда контейнер еще только запускается, то пишет не в json, поэтому в файле *-json.log:
{"log":"[16-Apr-2024 07:28:43] NOTICE: fpm is running, pid 1\n","stream":"stderr","time":"2024-04-16T07:28:43.729502671Z"}
{"log":"[16-Apr-2024 07:28:43] NOTICE: ready to handle connections\n","stream":"stderr","time":"2024-04-16T07:28:43.729537216Z"}
{"log":"[16-Apr-2024 07:29:24] NOTICE: Finishing ...\n","stream":"stderr","time":"2024-04-16T07:29:24.041604778Z"}
{"log":"[16-Apr-2024 07:29:24] NOTICE: exiting, bye-bye!\n","stream":"stderr","time":"2024-04-16T07:29:24.041645623Z"}
{"log":"[16-Apr-2024 07:30:00] NOTICE: fpm is running, pid 1\n","stream":"stderr","time":"2024-04-16T07:30:00.221043605Z"}
{"log":"[16-Apr-2024 07:30:00] NOTICE: ready to handle connections\n","stream":"stderr","time":"2024-04-16T07:30:00.221060411Z"}Из коробки в графане есть возможность фильтровать по нужным полям:

Однако этих фильтров недостаточно + из-за того, что я запускаю Alloy в докере, у меня они ущербны:
Поэтому, погуглив оказалось, называются эти фильтры - labels, которые можно переназначать и добавлять.
Когда мы находимся на странице https://grafana/explore мы можем кликнуть на кнопку Query Inspector и в появившемся внизу блоке кликнуть на JSON, в котором увидим приблизительно следующее:
[
{
"schema": {
"refId": "A",
"meta": {
"typeVersion": [
0,
0
],
"custom": {
"frameType": "LabeledTimeValues",
"lokiQueryStatKey": "Summary: total bytes processed"
},
"stats": [
{
"displayName": "Summary: bytes processed per second",
"unit": "Bps",
"value": 58798060
},
{
"displayName": "Summary: lines processed per second",
"value": 528453
},
{
"displayName": "Summary: total bytes processed",
"unit": "decbytes",
"value": 297410
},
{
"displayName": "Summary: total lines processed",
"value": 2673
},
{
"displayName": "Summary: exec time",
"unit": "s",
"value": 0.005058
},
{
"displayName": "Ingester: total reached",
"value": 1
},
{
"displayName": "Ingester: total chunks matched",
"value": 6
},
{
"displayName": "Ingester: total batches",
"value": 2
},
{
"displayName": "Ingester: total lines sent",
"value": 2
},
{
"displayName": "Ingester: head chunk bytes",
"unit": "decbytes",
"value": 0
},
{
"displayName": "Ingester: head chunk lines",
"value": 0
},
{
"displayName": "Ingester: decompressed bytes",
"unit": "decbytes",
"value": 0
},
{
"displayName": "Ingester: decompressed lines",
"value": 0
},
{
"displayName": "Ingester: compressed bytes",
"unit": "decbytes",
"value": 0
},
{
"displayName": "Ingester: total duplicates",
"value": 0
}
],
"executedQueryString": "Expr: {hostname=\"dc01-misc-pdocker01.yapro.ru\", service_name=~`(.*)`}",
"preferredVisualisationType": "logs",
"limit": 1000,
"searchWords": []
},
"fields": [
{
"name": "labels",
"type": "other",
"typeInfo": {
"frame": "json.RawMessage"
},
"config": {}
},
{
"name": "Time",
"type": "time",
"typeInfo": {
"frame": "time.Time"
},
"config": {}
},
{
"name": "Line",
"type": "string",
"typeInfo": {
"frame": "string"
},
"config": {}
},
{
"name": "tsNs",
"type": "string",
"typeInfo": {
"frame": "string"
},
"config": {}
},
{
"name": "labelTypes",
"type": "other",
"typeInfo": {
"frame": "json.RawMessage"
},
"config": {
"custom": {
"hidden": true
}
}
},
{
"name": "id",
"type": "string",
"typeInfo": {
"frame": "string"
},
"config": {}
}
]
},
"data": {
"values": [
[
{
"detected_level": "unknown",
"hostname": "dc01-misc-pdocker01.yapro.ru",
"platform": "docker",
"service_name": "scb-be-1.2.0"
},
{
"detected_level": "unknown",
"hostname": "dc01-misc-pdocker01.yapro.ru",
"platform": "docker",
"service_name": "scb-be-1.2.0"
}
],
[
1768813758604,
1768813758604
],
[
"{\"remote_user\":\"\",\"time_local\":\"19/Jan/2026:12:09:18 +0300\",\"request_uri\":\"/api/scb/asrDocuments\",\"request_uri_wo_query_string\":\"/api/scb/asrDocuments\",\"request_method\":\"POST\",\"status\":\"500\",\"status_group\":\"5xx\",\"request_time\":\"0.032\",\"upstream_response_time\":\"0.031\",\"body_bytes_sent\":\"5\",\"http_referer\":\"https://scb.ttk-svyaz.ru/documents\",\"http_user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36\",\"remote_addr\":\"192.168.201.37\",\"x_forwarded_for\":\"192.168.12.26\"}",
"{\"message\":\"Call to protected method App\\\\Controllers\\\\Documents\\\\DocumentsController::asrDocuments() from scope App\\\\Init\",\"context\":[{\"__type\":\"object\",\"__class\":\"Error\",\"message\":\"Call to protected method App\\\\Controllers\\\\Documents\\\\DocumentsController::asrDocuments() from scope App\\\\Init\",\"string\":\"\",\"code\":0,\"file\":\"/var/www/html/app/Init.php\",\"line\":45,\"trace\":[{\"file\":\"/var/www/html/public/index.php\",\"line\":5,\"function\":\"run\",\"class\":\"App\\\\Init\",\"type\":\"->\"}],\"previous\":null}],\"level\":400,\"level_name\":\"ERROR\",\"channel\":\"app\",\"datetime\":{\"__type\":\"object\",\"__class\":\"Monolog\\\\JsonSerializableDateTimeImmutable\",\"useMicroseconds\":true,\"date\":\"2026-01-19 12:09:18.603671\",\"timezone_type\":3,\"timezone\":\"Europe/Moscow\"},\"extra\":[]}"
],
[
"1768813758604808444",
"1768813758604019307"
],
[
{
"detected_level": "S",
"hostname": "I",
"platform": "I",
"service_name": "I"
},
{
"detected_level": "S",
"hostname": "I",
"platform": "I",
"service_name": "I"
}
],
[
"1768813758604808444_7b3fc3b0",
"1768813758604019307_25750484"
]
],
"nanos": [
null,
[
808444,
19307
],
null,
null,
null,
null
]
}
}
]Тут четко видно, что сами логи идут отдельно от информации о логах (два разных массива), например:
{
"detected_level": "unknown",
"hostname": "dc01-misc-pdocker01.yapro.ru",
"platform": "docker",
"service_name": "scb-be-1.2.0"
}На этом пока все, удачки.
p.s. self hosted решение графаны у себя на сервере настраивается так, но пока оставлю это на потом.
Полезное: 1
Комментарии
Комментариев пока нет.