Шаблон полностью управляет структурой и содержимым генерируемого дашборда Grafana.
Find a file
2026-02-27 14:19:32 +03:00
alert_rules_template.json Added multoresource 2026-02-26 15:35:17 +03:00
dashboard_template.json Answers variables 2026-02-27 14:19:32 +03:00
README.md Work version 2026-02-24 12:54:28 +03:00

dashboard_template.json — Руководство по созданию шаблонов

Шаблон полностью управляет структурой и содержимым генерируемого дашборда Grafana. Go-код утилиты является исполнителем инструкций из шаблона — добавление новых панелей не требует изменения кода.


Структура файла

{
  "version": "3.0.0",
  "description": "Описание шаблона",
  "datasource": { ... },
  "dashboard_meta": { ... },
  "dashboard": { ... },
  "layout": { ... },
  "panels": { ... },
  "queries": { ... }
}
Секция Назначение
version Версия шаблона. Изменение вызывает регенерацию дашборда
description Произвольное описание, не используется в генерации
datasource Тип и UID источника данных Grafana
dashboard_meta Метаданные дашборда: UID, теги, версия
dashboard Настройки дашборда Grafana (аннотации, тайм-зона, время по умолчанию)
layout Расположение панелей: какие панели и в каком порядке показывать
panels Определения панелей: внешний вид + логика запросов
queries Переиспользуемые конфигурации запросов

Секция datasource

Задаёт источник данных по умолчанию для всех панелей.

"datasource": {
  "type": "grafana-opensearch-datasource",
  "uid": "af84zsvlp9blsa"
}
Поле Описание
type Тип плагина datasource в Grafana
uid UID источника данных (найти в Grafana → Connections → Data sources)

Секция dashboard_meta

Метаданные создаваемого дашборда. Позволяет управлять UID и тегами без изменения Go-кода.

"dashboard_meta": {
  "uid": "pt-af-requests-auto",
  "tags": ["auto-generated", "waf-monitoring"],
  "version": 0
}
Поле Описание
uid Статичный UID дашборда. Именно по нему Grafana определяет, обновить существующий дашборд или создать новый. Менять с осторожностью — при смене UID будет создан новый дашборд
tags Список тегов. Используются для поиска и фильтрации в Grafana
version Начальная версия. Обычно 0 — Grafana сама увеличивает при каждом обновлении

Секция dashboard

Стандартные настройки дашборда Grafana. Копируются в создаваемый дашборд как есть.

"dashboard": {
  "editable": true,
  "timezone": "browser",
  "time": {"from": "now-6h", "to": "now"},
  "graphTooltip": 0,
  "schemaVersion": 39
}

Полный список доступных полей соответствует Grafana Dashboard JSON Model. Наиболее часто меняемые:

Поле Значения Описание
timezone "browser", "utc", "Europe/Moscow" Часовой пояс
time.from "now-6h", "now-24h", "now-7d" Начало временного диапазона по умолчанию
time.to "now" Конец диапазона
editable true / false Разрешить редактирование дашборда в UI
graphTooltip 0 — отдельный, 1 — общий, 2 — зафиксированный Режим тултипа

Секция layout

Определяет какие панели генерировать для каждого клиента и каждого домена, и где их размещать.

"layout": {
  "use_collapsed_rows": true,
  "tenant_panels": [ ... ],
  "domain_panels": [ ... ]
}
Поле Описание
use_collapsed_rows true — каждый клиент в свёрнутой строке (все панели внутри row). false — панели размещаются последовательно на дашборде
tenant_panels Панели на уровне клиента (агрегируют данные по всем доменам клиента)
domain_panels Панели на уровне домена (отдельно для каждого ресурса клиента)

Структура элемента layout

{
  "panel_key": "rps",
  "title_format": "RPS %s",
  "width": 12,
  "x_offset": 0,
  "y_offset": 1,
  "condition": null
}
Поле Обязательно Описание
panel_key Ключ панели из секции panels
width Ширина панели (124, сетка Grafana — 24 колонки)
x_offset Позиция по горизонтали (023)
y_offset Позиция по вертикали внутри collapsed row
title_format Формат заголовка с плейсхолдерами
title_suffix Суффикс к имени домена в заголовке
condition Условие отображения панели

Позиционирование

Grafana использует сетку шириной 24 единицы. Типичные варианты размещения:

Две панели по полэкрана:
  panel_1: width=12, x_offset=0
  panel_2: width=12, x_offset=12

Одна широкая панель:
  panel:   width=24, x_offset=0

Три панели:
  panel_1: width=8, x_offset=0
  panel_2: width=8, x_offset=8
  panel_3: width=8, x_offset=16

Секция panels

Библиотека панелей. Каждая панель описывает внешний вид (Grafana JSON) и логику запросов (query_mode + query_config).

"panels": {
  "row": { ... },
  "rps": { ... },
  "status_codes": { ... },
  "my_new_panel": { ... }
}

Ключ (например "rps") используется в layout как panel_key.

Структура панели

"my_panel": {
  "type": "timeseries",
  "description": "Описание панели",
  "gridPos": {"h": 8, "w": 12, "x": 0, "y": 0},
  "fieldConfig": { ... },
  "options": { ... },
  "query_mode": "multi_target",
  "query_config": { ... }
}

Поля делятся на две категории:

Grafana-поля (передаются в API как есть): type, description, gridPos, fieldConfig, options, transformations, overrides и любые другие стандартные поля Grafana.

Служебные поля шаблона (используются при генерации, не попадают в итоговый JSON): query_mode, query_config.

Совет: проще всего создать нужную панель вручную в Grafana, экспортировать дашборд через Share → Export, скопировать JSON панели и добавить к нему query_mode и query_config.

Специальная панель row

Обязательная панель для создания строк-заголовков клиентов:

"row": {
  "type": "row",
  "collapsed": true,
  "gridPos": {"h": 1, "w": 24, "x": 0, "y": 0},
  "panels": []
}

Не требует query_mode. Заголовок (title) устанавливается автоматически из имени клиента.


Секция queries

Переиспользуемые конфигурации bucket aggregation для OpenSearch/Elasticsearch.

"queries": {
  "bucket_agg": {
    "field": "@timestamp",
    "id": "2",
    "settings": {"interval": "1m", "trimEdges": "1"},
    "type": "date_histogram"
  }
}

bucket_agg используется автоматически во всех построителях targets как агрегация по времени. Изменение interval здесь влияет на разрешение всех графиков.


query_mode — типы запросов

query_mode определяет как будут построены targets панели. Выбирается в зависимости от того, что нужно отобразить.


rps — Запросы в секунду

Один count-запрос + math expression $A / 60 для перевода из запросов/минуту в запросы/секунду. Предназначен для tenant-уровня (агрегация по всем SID клиента).

"query_mode": "rps",
"query_config": {
  "base_query": "SID:({sids})",
  "alias": "{client_title} Total RPS",
  "expression": "$A / 60",
  "expression_ref_id": "RPS"
}
Поле Описание
base_query Lucene-запрос. Плейсхолдер {sids} заменяется на список SID через OR
alias Имя серии на графике. Поддерживает {client_title}
expression Math expression Grafana. $A ссылается на data query
expression_ref_id refId результирующей серии

multi_target — Несколько фильтров

Несколько самостоятельных запросов с разными условиями. Используется для статус-кодов, разбивки по любому признаку.

"query_mode": "multi_target",
"query_config": {
  "base_query": "SID:{sid}",
  "metric": "count",
  "targets": [
    {"alias": "2**", "query_suffix": "AND response_status_code:[200 TO 299]", "ref_id": "A"},
    {"alias": "3**", "query_suffix": "AND response_status_code:[300 TO 399]", "ref_id": "B"},
    {"alias": "4**", "query_suffix": "AND response_status_code:[400 TO 499]", "ref_id": "C"},
    {"alias": "5**", "query_suffix": "AND response_status_code:[500 TO 599]", "ref_id": "D"}
  ]
}
Поле Описание
base_query Базовый Lucene-запрос. {sid} заменяется на SID ресурса
metric Тип агрегации: "count"
targets[].alias Имя серии
targets[].query_suffix Дополнение к base_query через AND
targets[].ref_id Уникальный идентификатор запроса (A, B, C...)

range_percent — Распределение по диапазонам в %

Скрытый total-запрос + запросы по диапазонам + math expressions ($X / $Total) * 100. Используется для распределения времени ответа, размеров ответов и т.п.

"query_mode": "range_percent",
"query_config": {
  "base_query": "SID:{sid}",
  "field_filter": "upstream_response_time_digital:[0 TO *]",
  "total_ref_id": "F",
  "range_field": "upstream_response_time_digital",
  "ranges": [
    {"label": "0-0.3s",  "from": "0",   "to": "0.3", "ref_id": "A"},
    {"label": "0.3-1s",  "from": "0.3", "to": "1",   "ref_id": "B"},
    {"label": "1-3s",    "from": "1",   "to": "3",   "ref_id": "C"},
    {"label": "3-10s",   "from": "3",   "to": "10",  "ref_id": "D"},
    {"label": "10s+",    "from": "10",  "to": "*",   "ref_id": "E"}
  ]
}
Поле Описание
field_filter Фильтр для total-запроса (исключает записи без нужного поля)
total_ref_id refId total-запроса, на который делится каждый диапазон
range_field Поле OpenSearch по которому строятся диапазоны
ranges[].label Подпись на графике
ranges[].from Начало диапазона (Lucene range syntax)
ranges[].to Конец диапазона. "*" означает неограниченно
ranges[].ref_id refId data-запроса

Диапазоны можно добавлять/убирать не меняя Go-код.


multi_sum_expression — Несколько метрик суммирования

Несколько sum-запросов, каждый с math expression. Используется для отображения нескольких потоков трафика на одном графике.

"query_mode": "multi_sum_expression",
"query_config": {
  "base_query": "SID:{sid}",
  "targets": [
    {
      "alias": "Response Traffic",
      "field": "upstream_response_length_digital",
      "ref_id": "A",
      "expression": "$A / 60",
      "expression_ref_id": "ResponseTraffic"
    },
    {
      "alias": "Request Traffic",
      "field": "upstream_bytes_sent_digital",
      "ref_id": "B",
      "expression": "$B / 60",
      "expression_ref_id": "RequestTraffic"
    }
  ]
}
Поле Описание
targets[].alias Имя серии исходного запроса
targets[].field Поле OpenSearch для sum-агрегации
targets[].ref_id refId data-запроса
targets[].expression Math expression. $A ссылается на ref_id
targets[].expression_ref_id refId результирующей серии (отображается в легенде)

sum_expression — Одна метрика суммирования

Упрощённый вариант multi_sum_expression для одной метрики.

"query_mode": "sum_expression",
"query_config": {
  "base_query": "SID:{sid}",
  "field": "upstream_response_length_digital",
  "ref_id": "A",
  "expression": "$A / 60",
  "expression_ref_id": "Traffic"
}

bucket_logs — Произвольные bucket aggregations

Полный контроль над агрегациями OpenSearch. Используется для barchart, таблиц, любых нестандартных агрегаций.

"query_mode": "bucket_logs",
"query_config": {
  "base_query": "log_type:access AND SID:{sid}",
  "bucket_aggs": [
    {
      "field": "response_status_code",
      "id": "3",
      "settings": {"min_doc_count": "1", "order": "desc", "orderBy": "_count", "size": "10"},
      "type": "terms"
    },
    {
      "field": "@timestamp",
      "id": "2",
      "settings": {"interval": "1m", "trimEdges": "1"},
      "type": "date_histogram"
    }
  ]
}
Поле Описание
base_query Lucene-запрос. {sid} заменяется на SID ресурса
bucket_aggs Массив bucket aggregations в формате OpenSearch Grafana плагина. Порядок важен — внешняя агрегация первая

Плейсхолдеры в заголовках

Заголовки панелей задаются через title_format или title_suffix в секции layout.

title_format

Полный контроль над заголовком. Поддерживает плейсхолдеры:

Плейсхолдер Значение
{client} Название клиента
{domain} Доменное имя ресурса
{sid} SID ресурса
%s Название клиента (fmt.Sprintf, для обратной совместимости)

Примеры:

"title_format": "RPS {client}"
"title_format": "RPS %s"
"title_format": "{client} / {domain}"

title_suffix

Суффикс, который добавляется к имени домена: "<domain> <suffix>".

Тоже поддерживает плейсхолдеры:

"title_suffix": "{sid} Status Codes"
// Результат: "example.com sid123 Status Codes"

"title_suffix": "Traffic"
// Результат: "example.com Traffic"

Приоритет

Если в layout задан title_format — используется он. Если только title_suffix — используется суффикс. Если не задано ничего — используется title из определения панели в секции panels.


Условия отображения панелей

Поле condition в элементе layout позволяет показывать панель только при выполнении условия.

{
  "panel_key": "rps",
  "width": 12,
  "x_offset": 0,
  "condition": {
    "type": "min_domains",
    "value": 2
  }
}

Доступные типы условий

min_domains — минимальное количество доменов

Показывает панель только если у клиента не менее N доменов.

"condition": {"type": "min_domains", "value": 3}

max_domains — максимальное количество доменов

Показывает панель только если у клиента не более N доменов.

"condition": {"type": "max_domains", "value": 1}

client_name_matches — фильтр по имени клиента

Показывает панель только для клиентов, в имени которых содержится указанная строка.

"condition": {"type": "client_name_matches", "value": "Enterprise"}

Если condition не задан или равен null — панель отображается всегда.


Как добавить новую панель

Шаг 1. Определить панель в секции panels

"panels": {
  "blocked_requests": {
    "type": "timeseries",
    "description": "Заблокированные запросы WAF",
    "gridPos": {"h": 8, "w": 12, "x": 0, "y": 0},
    "fieldConfig": {
      "defaults": {
        "color": {"mode": "palette-classic"},
        "custom": {
          "drawStyle": "line",
          "fillOpacity": 10,
          "lineWidth": 1
        }
      }
    },
    "options": {
      "legend": {"displayMode": "list", "placement": "bottom", "showLegend": true}
    },
    "query_mode": "multi_target",
    "query_config": {
      "base_query": "SID:{sid}",
      "metric": "count",
      "targets": [
        {"alias": "Blocked", "query_suffix": "AND action:block", "ref_id": "A"},
        {"alias": "Passed",  "query_suffix": "AND action:pass",  "ref_id": "B"}
      ]
    }
  }
}

Шаг 2. Добавить в layout

"domain_panels": [
  {
    "panel_key": "blocked_requests",
    "title_suffix": "{sid} WAF Actions",
    "width": 12,
    "x_offset": 0,
    "y_offset": 25
  }
]

Этого достаточно. Пересборка бинарника не нужна.

Если нужен принципиально новый тип запроса

Если существующие query_mode не покрывают нужный случай — добавьте в panels.go:

// 1. Функция-строитель targets
func buildMyModeTargets(cfg map[string]interface{}, sidQuery, ...) []interface{} {
    // логика построения targets
}

// 2. Регистрация в реестре (одна строка)
var queryModeRegistry = map[string]targetBuilder{
    // ...существующие...
    "my_mode": buildMyModeTargets,
}

После этого "query_mode": "my_mode" становится доступен в шаблоне.