Скажу честно: к этой статье меня подтолкнула причина донельзя прозаическая. Сижу недавно на брифинге, и прилетает жалоба — сервер отчётов работает из рук вон плохо. Сервер при этом не мой, в орбиту обслуживания он не входил, да и сам я последнее время вожусь с публикой помоложе и помоднее, Superset и FineBI. А тут пришлось тряхнуть стариной и вспомнить, как оно всё устроено у SSRS. Вспомнил. Заодно собрал то, чего мне самому когда-то не хватало, — и решил, что пора всё это выложить в одну статью.
Отчётный сервер — штука незаметная ровно до того момента, пока кто-нибудь из бизнеса не напишет: «а почему мне со вчерашнего дня не приходит утренняя рассылка». И вот ты сидишь, открываешь портал, видишь, что подписка вроде есть, вроде активна, а письма не уходят. Лезешь в логи. А логов-то нормальных и нет — есть таблица где-то внутри базы, в которую никто никогда не заглядывал.
Знакомо? Если вы держите SSRS или его старшего брата Power BI Report Server, наверняка знакомо.
Я полез искать готовый дашборд. По уму — чтобы взять, накатить, и видеть сразу: какие рассылки падают, какие отчёты открываются по минуте, что вообще творится на сервере ночью, когда пользователей нет. Знаете, что я нашёл? Десяток одинаковых сборок «SQL Server overview» с CPU, памятью и числом сессий. Безликие счётчики, которые к отчётному серверу имеют отношение постольку-поскольку. Ни одного, который бы лез внутрь ReportServer и показывал то, ради чего этот сервер вообще существует — отчёты и их доставку.
Пришлось собирать самому. Заняло это, прямо скажем, прилично времени. Но получилось то, чем не стыдно пользоваться каждый день — и вот об этом расскажу подробно, с запросами, граблями и парой неочевидных вещей, на которые я убил несколько часов.
Коротко
-
Готового вменяемого дашборда под SSRS/PBIRS в сети нет — только обобщённые «обзоры SQL Server», которые не показывают ни сбоев подписок, ни тяжёлых отчётов, ни фоновой активности pbix.
-
Всё ценное лежит внутри базы
ReportServer: таблицыExecutionLog,Subscriptions,SubscriptionHistory,Catalog. Microsoft их официально не документирует, так что приходится разбираться по факту. -
Power BI отчёты (
.pbix) ведут себя в логе совсем не так, как обычные пагинированные (.rdl) — и если этого не знать, метрики врут. -
Мониторинг строится на двух источниках: прямые SQL-запросы к
ReportServer(бизнес-метрики) иwindows_exporterчерез VictoriaMetrics (инфраструктура нод). -
По дороге всплыла классика — расхождение времени между Grafana и MSSQL на величину часового пояса. Лечится тонким слоем view поверх базы.
-
PromQL-запросы к VictoriaMetrics один в один работают и в Prometheus — стек взаимозаменяем.
А всю сборку целиком — оба дашборда в JSON, скрипт создания view и рабочий конфиг экспортёра — я выложил в исходной версии этого разбора у себя в блоге. Накатывается за полчаса, дальше только свои источники подставить. Ссылку на пакет дам в конце, так что дочитавшего ждёт готовый комплект.
Почему вообще понадобился отдельный дашборд
Reporting Services живёт с нами очень давно. И за эти годы у него так и не появилось внятной системы наблюдения. Парадокс: система зрелая, стоит в каждом втором энтерпрайзе, генерит критичную для бизнеса отчётность — а смотреть на её здоровье нечем.
С приходом Power BI Report Server стало только хуже. Теперь на одном сервере соседствуют два мира: классические пагинированные отчёты .rdl и интерактивные Power BI отчёты .pbix. Ведут они себя по-разному, нагружают сервер по-разному, и пишут в журнал тоже по-разному. А мониторинг, если он вообще есть, обычно покрывает в лучшем случае «жив ли сервис».
Мне же нужно было видеть конкретные вещи. Не абстрактную загрузку CPU, а ответы на рабочие вопросы:
-
Какие отчёты упали при открытии и с какой ошибкой — чтобы отдать трейс разработчику отчёта, а не разводить руками.
-
Какие подписки и рассылки не доставлены — и кому они принадлежат, чтобы было с кого спросить.
-
Какие отчёты тяжёлые — где сервер реально потеет, выгребая данные из источника.
-
Чем сервер занят ночью, когда живых пользователей нет, а нагрузка почему-то есть.
Ни один готовый дашборд из тех, что я видел, на эти вопросы не отвечает. Потому что ответы лежат внутри самой базы ReportServer, а не в системных счётчиках Windows. Туда и пришлось копать.
Что внутри базы ReportServer
Сразу важная оговорка: Microsoft не документирует схему ReportServer. Официально. Это внутренняя база Reporting Services, и её структуру могут менять между версиями без предупреждения. Никакого справочника полей, как для системных представлений SQL Server, по ней не существует и не появится.
Поэтому всё, что ниже — результат раскопок по факту, на живой базе, со сверкой через sys.columns и неофициальные источники вроде mssqltips. Проверяйте на своей версии, поля могут отличаться.
Ключевые таблицы, ради которых всё затевалось:
ExecutionLogStorage — журнал выполнения. Каждый запуск отчёта, каждое открытие, каждая отправка подписки оставляет здесь строку. Поверх неё есть представления ExecutionLog, ExecutionLog2, ExecutionLog3 — они декодируют числовые коды в человекочитаемый вид. Я работал через ExecutionLog3. Главные поля:
-
ItemPath— путь к отчёту. -
UserName— кто запускал. -
RequestType— тип запроса:Interactive(пользователь открыл),Subscription(рассылка),Refresh Cache(фоновое обновление данных). -
Format— формат вывода:RPL(просмотр на экране),PDF,EXCELOPENXML,MHTMLи так далее. -
ItemAction— действие:Render,ConceptualSchema,DataRefresh,SaveToCatalogи прочие. -
TimeStart,TimeEnd— начало и конец. -
TimeDataRetrieval,TimeProcessing,TimeRendering— три фазы выполнения в миллисекундах. Вот это золото для поиска тяжёлых отчётов. -
Status— результат.rsSuccessили код ошибки. -
ByteCount,RowCount— объём результата и число строк.
Subscriptions — сами подписки. Кто владелец, какое расписание, какое описание, когда последний запуск.
SubscriptionHistory — история отработки подписок. Вот тут ключевой момент: ошибки доставки рассылок живут именно здесь, а не в ExecutionLog. Об этом дальше отдельно.
Catalog — все объекты сервера: отчёты, папки, источники данных. Тип объекта в поле Type: 2 — пагинированный отчёт (.rdl), 13 — Power BI отчёт (.pbix), 1 — папка, и так далее.
Users — пользователи. Сюда джойнятся все эти OwnerID, CreatedByID, ModifiedByID, чтобы вместо GUID показать живое имя.
Самое коварное: pbix — это не rdl
Вот на этом месте я потратил больше всего времени, и вот это нигде толком не описано.
Когда вы открываете обычный пагинированный отчёт .rdl, сервер честно его рендерит и записывает в лог реальные фазы: столько-то на выборку данных, столько-то на обработку, столько-то на рендеринг. Красиво, измеримо, понятно.
С Power BI отчётом всё иначе. Рендеринг .pbix происходит в браузере у пользователя, а не на сервере. Поэтому фазы времени в логе по нему — нули. Сервер только отдаёт модель данных и обслуживает запросы визуалов, а рисует всё клиент.
И вот что это означает на практике. Одно открытие .pbix-отчёта порождает в логе не одну строку, а целую пачку. Один заход пользователя — это:
-
одна строка
ConceptualSchema(собственно факт открытия отчёта), -
и следом десятки строк
QueryData— по запросу на каждый визуал на странице.
Если вы наивно посчитаете «вызовы отчёта» через COUNT(*), у вас один любопытный аналитик, потыкавший в дашборд, превратится в сотню «вызовов». Я это увидел на реальных данных: 70 строк в логе за пять минут схлопывались в 8 настоящих взаимодействий. А на большем срезе — 354 сырых строки на 19 реальных открытий.
Как считать правильно? Открытие .pbix-отчёта — это ровно одна строка с ItemAction = 'ConceptualSchema'. Не QueryData, не ASModelStream — только ConceptualSchema. Вот надёжный маркер:
SELECT COUNT(*) AS pbix_opensFROM ReportServer.dbo.ExecutionLog3WHERE ItemAction = 'ConceptualSchema' AND TimeStart >= @from AND TimeStart < @to;
Одна запись — одно открытие. Проверено на живых данных: совпадает с реальным числом заходов, без шума визуалов.
И раз уж зашла речь про фазы — для панели «тяжёлые отчёты», которая считает среднее время по TimeDataRetrieval + TimeProcessing + TimeRendering, нужно .pbix исключать вообще. Иначе их нули занижают средние, и реально медленные пагинированные отчёты тонут в этой массе. Фильтр простой: Format NOT IN ('PBIX', 'DataModel') AND Format IS NOT NULL.
Подписки: ошибки доставки лежат в отдельной таблице
Ещё одна неочевидная вещь. Казалось бы, упала рассылка — смотри в ExecutionLog. Так вот, там их и не будет.
ExecutionLog со строкой RequestType = 'Subscription' фиксирует, что отчёт под рассылку сгенерировался. А вот доставлен ли он — ушло ли письмо, записался ли файл на сетевую шару — это уже в SubscriptionHistory. И это разные события. Отчёт может прекрасно отрендериться (в ExecutionLog будет rsSuccess), а письмо не уйдёт, потому что почтовый сервер прилёг. В ExecutionLog тишина, а в SubscriptionHistory — ошибка.
Поэтому «упавшие подписки» надо считать из SubscriptionHistory, где Status > 0. Вот так выглядит сводка по проблемным рассылкам — с владельцем, описанием и числом ошибок за период:
SELECT s.SubscriptionID, c.Path AS ReportPath, u.UserName AS Owner, s.Description, s.LastStatus, s.LastRunTime, she.ErrorsInPeriodFROM ReportServer.dbo.Subscriptions sJOIN ReportServer.dbo.[Catalog] c ON c.ItemID = s.Report_OIDLEFT JOIN ReportServer.dbo.Users u ON u.UserID = s.OwnerIDINNER JOIN ( SELECT sh.SubscriptionID, SUM(CASE WHEN sh.Status > 0 THEN 1 ELSE 0 END) AS ErrorsInPeriod FROM ReportServer.dbo.SubscriptionHistory sh GROUP BY sh.SubscriptionID) she ON she.SubscriptionID = s.SubscriptionIDWHERE she.ErrorsInPeriod > 0ORDER BY s.LastRunTime DESC;
Маленькая деталь, на которой легко обжечься: джойн к Users обязательно LEFT. Если у подписки удалили владельца (уволился человек, вычистили учётку из AD), а сама подписка осталась — при INNER JOIN она просто пропадёт из выборки. А это как раз самые интересные подписки, осиротевшие. Их надо видеть, а не прятать.
И ещё. В таблице Subscriptions живут не только рассылки. Запланированное обновление данных .pbix (тот самый scheduled refresh) реализовано через тот же механизм подписок и сидит здесь же, с EventType = 'DataModelRefresh'. Если вы считаете «настоящие» рассылки — эти записи надо отфильтровать: EventType <> 'DataModelRefresh'. Иначе refresh-задачи раздувают вам статистику подписок.
Отдельно стоит сказать про трейс для разработчиков. Сводная таблица показывает, что подписка падала и сколько раз — но разработчику отчёта этого мало, ему нужен текст ошибки. Поэтому я сделал клик по строке проваливающимся во второй дашборд — «История подписки». Технически это data link на ячейке: при клике Grafana передаёт SubscriptionID и путь отчёта через URL в переменные дочернего дашборда, и тот показывает всю историю доставки именно этой подписки с расшифрованными сообщениями об ошибках.
Сообщения, кстати, лежат в поле Details записи истории — это JSON, внутри массив Errors с полем Message. Достаются они через OPENJSON с JSON_VALUE, кириллица декодируется, несколько сообщений склеиваются в одну ячейку. Получается, что инженер кликнул по упавшей рассылке — и сразу видит «не удалось подключиться к SMTP» или «источник данных недоступен», готовый текст для передачи дальше. Этот дочерний дашборд тоже входит в сборку.
Чем сервер занят, когда никого нет
Любимая моя панель. Открываешь утром графики, а ночью, в три часа, когда ни одного живого пользователя, — всплеск нагрузки. Откуда?
Это фоновое обновление моделей .pbix. Сервер по расписанию подтягивает свежие данные в импортированные модели, чтобы утром пользователь видел актуальные цифры. В логе это RequestType = 'Refresh Cache' под системной учёткой, и цикл из трёх стадий: DataRefresh (тянет данные из источника), ASModelStream (загоняет модель в движок) и SaveToCatalog (пишет обновлённую модель обратно в базу).
Вот на SaveToCatalog есть ByteCount — размер записанной модели. По нему можно построить график «объём записи моделей при обновлении» и наглядно видеть, когда и насколько сервер сам себя нагружает:
SELECT $__timeGroup(TimeStartUtc, '10m', 0) AS time, SUM(CAST(ByteCount AS BIGINT)) AS [Объём записи моделей]FROM ReportServer.dbo.ExecutionLog3_utcWHERE RequestType = 'Refresh Cache' AND Status = 'rsSuccess' AND ItemAction = 'SaveToCatalog' AND $__timeFilter(TimeStartUtc)GROUP BY $__timeGroup(TimeStartUtc, '10m', 0)ORDER BY 1;
Важно понимать, что именно показывают эти байты. Это размер уже сжатой модели, которую сервер записал обратно в каталог — в саму базу ReportServer. То есть вес финального артефакта, а не объём сырых данных, вычитанных из источника (он обычно в разы больше — VertiPaq хорошо жмёт). Практический вывод простой: тяжёлые .pbix реально раздувают вам базу и бэкапы. У меня попадались модели под полтора гигабайта на одно обновление. Полезно знать в лицо такие отчёты.
Кстати, заметьте имя view в запросе — ExecutionLog3_utc. Не ExecutionLog3. Вот об этом — отдельная и поучительная история.
История про три часа, которые сломали половину дашборда
Когда основные панели заработали, я наткнулся на странность. Беру пик активности на графике — вижу аномальные 172 события в одну точку. Иду в SSMS проверить — а там по этому времени ноль. Пусто. Запрос тот же, данные те же, цифры разные.
Это классика связки Grafana плюс SQL, и грабли эти ловят многих.
Дело в том, что Grafana ожидает время в колонке всегда в UTC. А Reporting Services пишет TimeStart в локальном времени сервера. У меня это московское, плюс три часа от UTC. И когда Grafana формирует фильтр по времени, она шлёт в запрос границы в UTC, а сравнивает их с колонкой, которая в локальном времени. Сдвиг ровно на три часа. «Пик в 21:10» на графике физически выбирал данные за 18:10 — то есть ночной пик рассылок, где этих событий и правда под две сотни.
Самое противное: настройкой таймзоны дашборда это не лечится. Потому что источников два. VictoriaMetrics отдаёт время в UTC, как и положено. А MSSQL — в локальном. Любая глобальная настройка зоны починит один источник и сломает второй. Поставишь UTC — поедут графики windows_exporter. Поставишь местное — поедут SQL-панели.
Правильное решение — привести оба источника к UTC. VictoriaMetrics уже в UTC, значит надо подтянуть MSSQL. И вот тут красивый ход: не трогать ни системные таблицы (это запрещено и небезопасно), ни каждый запрос по отдельности, а сделать тонкий слой view, который добавляет к данным колонку с пересчитанным временем.
CREATE OR ALTER VIEW dbo.ExecutionLog3_utc ASSELECT *, DATEADD(HOUR, DATEDIFF(HOUR, GETDATE(), GETUTCDATE()), TimeStart) AS TimeStartUtcFROM dbo.ExecutionLog3;
DATEDIFF(HOUR, GETDATE(), GETUTCDATE()) вычисляет смещение сервера от UTC прямо на лету, без хардкода тройки. Сервер сам себя спрашивает «на сколько я отличаюсь от UTC» и сдвигает. Перевели на зимнее время, переехали в другой регион — формула продолжит работать.
Таких view получилось три: для ExecutionLog3, для SubscriptionHistory (там колонка StartTime) и для Subscriptions (там LastRunTime). Дальше все панели переключаются на эти view и на UTC-колонки — и в фильтрах времени, и в выводимых датах. После этого оба источника живут в одной зоне, таймзона дашборда ставится в браузерную, и всё сходится: и графики из VictoriaMetrics, и таблицы из SQL показывают одно и то же время.
Нюанс, который стоит запомнить намертво: в SQL-панелях UTC-колонку надо использовать везде — и в фильтрах, и в выводе. Если оставить в SELECT обычную локальную колонку, Grafana при отображении прибавит к ней те же три часа, и вы получите время из будущего. Я на этом споткнулся — в таблице подписок «последний запуск» показывал время, которого ещё не наступило.
На чём всё стоит
Архитектура простая и разделённая по природе данных.
Бизнес-метрики — прямыми SQL-запросами к базе ReportServer. Всё, что про отчёты, подписки, ошибки, тяжесть — это туда. Grafana с MSSQL-датасорсом, запросы вы видели выше.
Инфраструктура нод — через windows_exporter, который отдаёт метрики в VictoriaMetrics, а Grafana их оттуда читает. Это CPU, память, диски серверов, где крутится сам отчётный сервис.
Отдельно отмечу: PromQL-запросы, которые я приведу для VictoriaMetrics, работают один в один в Prometheus. Это совместимый язык запросов, так что если у вас Prometheus вместо VictoriaMetrics — просто берите и используйте, менять ничего не надо.
windows_exporter: включаем сбор по процессам
По умолчанию windows_exporter не собирает метрики в разрезе процессов. А нам это нужно — чтобы видеть отдельно потребление самого сервиса отчётов и встроенного движка Analysis Services, который обслуживает .pbix-модели.
Включается коллектор process в конфиге экспортёра:
collectors: enabled: "[defaults],process"collector: process: include: .+ exclude: ''
После правки — перезапуск службы, и через минуту в хранилище появляются метрики windows_process_* по всем процессам ноды.
Маленькая ремарка из практики, которая сэкономит вам полчаса. Если на одной ноде сбор есть, а на другой нет — первым делом проверьте, не читает ли служба конфиг из другого файла. У меня на одном сервере экспортёр запускался с config.yml, а правил я по привычке config.yaml. Расширения разные, файл живой — другой. Правки уходили в пустоту, метрики не появлялись. Минут сорок я искал проблему не там. Проверяется одной строкой:
(Get-CimInstance win32_service | Where-Object Name -eq 'windows_exporter').PathName
В выводе будет видно, какой файл реально читает служба.
Метрики из VictoriaMetrics
Самое полезное — в разрезе процессов отчётного сервера.
Память встроенного Analysis Services. Движок msmdsrv держит в памяти модели данных .pbix. Именно он раздувается при обновлении моделей и при активной работе пользователей с Power BI отчётами. По сути — сколько оперативки съели Power BI модели:
windows_process_working_set_bytes{process="msmdsrv", instance=~"$rs_instance"}
Загрузка процессора этим же движком. Тут есть тонкость с интерпретацией. Метрика нормируется на одно ядро, поэтому значения легко переваливают за сто процентов — триста означает работу на трёх ядрах одновременно. Это нормально: обновление моделей идёт в несколько потоков.
sum by (instance) (rate(windows_process_cpu_time_total{process="msmdsrv", instance=~"$rs_instance"}[$__rate_interval])) * 100
Задержка дисков нод. Для отчётного сервера диск часто оказывается узким местом — временные файлы при рендеринге тяжёлых отчётов, кэш. Средняя задержка операции считается как отношение накопленного времени к числу операций, отдельно по чтению и записи или общая:
rate(windows_logical_disk_read_write_latency_seconds_total{instance=~"$rs_instance", volume=~"[A-Z]:"}[$__rate_interval])/clamp_min( rate(windows_logical_disk_reads_total{instance=~"$rs_instance", volume=~"[A-Z]:"}[$__rate_interval]) + rate(windows_logical_disk_writes_total{instance=~"$rs_instance", volume=~"[A-Z]:"}[$__rate_interval]), 1)
Тут пара рабочих хитростей. Фильтр volume=~"[A-Z]:" оставляет только тома с буквами и отсекает служебные разделы вроде HarddiskVolume1, которые только шумят. А clamp_min(..., 1) страхует от деления на ноль, когда операций в интервале не было.
Здоровые значения для SSD — единицы миллисекунд. Десятки и сотни — диск перегружен и тормозит рендеринг. На виртуалках с общим хранилищем чуть выше нормы из-за слоя виртуализации, это ожидаемо.
Руководство по установке
Импорт несложный, но есть несколько шагов, которые надо выполнить именно в этом порядке, иначе панели приедут пустыми. Разберём по пунктам.
Шаг 1. Создать три view в базе ReportServer
Панели читают время не из системных представлений напрямую, а из трёх view с пересчитанной в UTC колонкой — ExecutionLog3_utc, SubscriptionHistory_utc, Subscriptions_utc. Без них SQL-панели выдадут ошибку «invalid object name». Поэтому это самый первый шаг.
Готовый скрипт — файл create_view_report_server.sql из сборки. Выполняется один раз в базе ReportServer:
USE [ReportServer]GOCREATE OR ALTER VIEW dbo.ExecutionLog3_utc ASSELECT *, DATEADD(HOUR, DATEDIFF(HOUR, GETDATE(), GETUTCDATE()), TimeStart) AS TimeStartUtcFROM dbo.ExecutionLog3;GOCREATE OR ALTER VIEW dbo.SubscriptionHistory_utc ASSELECT *, DATEADD(HOUR, DATEDIFF(HOUR, GETDATE(), GETUTCDATE()), StartTime) AS StartTimeUtcFROM dbo.SubscriptionHistory;GOCREATE OR ALTER VIEW dbo.Subscriptions_utc ASSELECT *, DATEADD(HOUR, DATEDIFF(HOUR, GETDATE(), GETUTCDATE()), LastRunTime) AS LastRunTimeUtcFROM dbo.Subscriptions;GO
Эти view ничего не меняют в системных таблицах — они только добавляют сверху колонку с временем, пересчитанным в UTC. DATEDIFF(HOUR, GETDATE(), GETUTCDATE()) вычисляет смещение сервера от UTC на лету, без хардкода. Безопасно для последующих обновлений Reporting Services.
Шаг 2. Включить коллектор process в windows_exporter
По умолчанию windows_exporter не собирает метрики в разрезе процессов, а они нужны — чтобы видеть отдельно сам сервис отчётов и движок Analysis Services (msmdsrv), который обслуживает .pbix-модели.
В конфиге экспортёра (файл config.yml из сборки, целиком) коллектор process должен быть в списке включённых, плюс блок с фильтром процессов:
collectors: enabled: "[defaults],license,process,scheduled_task,textfile,time,vmware"collector: process: include: .+ exclude: ''
После правки — перезапуск службы: Restart-Service windows_exporter. Через минуту в хранилище появятся метрики windows_process_*.
Две вещи, на которых легко потерять время. Первое: убедитесь, что служба читает именно тот файл, который вы правите — проверяется через (Get-CimInstance win32_service | Where-Object Name -eq 'windows_exporter').PathName. Расширения .yml и .yaml в одной папке — разные файлы, и правка не в тот уходит впустую. Второе: если служба не стартует после правки — чаще всего порт 9182 занят зависшим процессом экспортёра, ищется через netstat -ano | findstr ":9182".
Шаг 3. Импортировать оба дашборда
В Grafana: Dashboards → New → Import → Upload JSON. Сначала основной дашборд, затем дашборд истории подписки. При импорте Grafana запросит источники данных:
-
MSSQL — подключение к базе
ReportServer. Все панели подписок, ошибок и тяжёлых отчётов читают отсюда. Если у вас именованный инстанс или база называется иначе — поправьте имя в запросах (вездеReportServer.dbo.*). -
VictoriaMetrics или Prometheus — метрики
windows_exporter. Отсюда процессор, память, диски нод. Запросы совместимы с обоими хранилищами.
Дашборд истории подписки просит только MSSQL. Привяжите оба дашборда к одному и тому же MSSQL-датасорсу — иначе провал по клику из таблицы упавших подписок работать не будет.
Шаг 4. Настроить переменную с нодами
Самый важный шаг, и тут два сценария.
Несколько нод (scale-out PBIRS). Дашборд тянет список нод динамически — переменная rs_instance спрашивает у метрик, какие хосты есть:
label_values(windows_os_hostname{hostname=~"$host_filter"},instance)
$host_filter — вторая переменная, куда вписывается регулярка под имена ваших серверов. По умолчанию стоит пример srv-rs-0[12].*. Поменяйте на свой паттерн — и список инстансов подтянется сам. Новый сервер с windows_exporter попадёт в список автоматически, без правки дашборда.
Частая ошибка: если паттерн в host_filter не совпадает с реальными именами хостов, список будет пустым и инфраструктурные панели окажутся без данных. Симптом «узлы не подтягиваются» — почти всегда это. Проверьте в Explore, что windows_os_hostname{hostname=~"ваш-паттерн"} вообще что-то возвращает. И учтите: в части версий экспортёра имя хоста лежит в windows_cs_hostname, а не в windows_os_hostname — если первая пустая, пробуйте вторую.
Один инстанс SSRS. Динамический список не нужен. Откройте переменную host_filter и впишите туда точное имя своего сервера (или маску под него) — единственная нода подтянется. Лишнюю плашку сверху уберите: в настройках переменной host_filter поставьте Show on dashboard → Nothing. Вписали имя один раз — и забыли.
Шаг 5. Проверить вспомогательные переменные
Ещё две переменные работают из коробки, трогать обычно не нужно:
-
report— выпадающий список всех отчётов (тянется изCatalog), фильтрует панели по конкретному отчёту.__ALL__показывает всё. -
fail_class— скрытый переключатель классов сбоев (.rdl / открытия .pbix / refresh) для фильтрации.
После этих пяти шагов дашборд оживёт полностью: SQL-панели увидят view, инфраструктурные — метрики нод, провал в историю подписки заработает.
Где забрать сборку
Весь комплект для развёртывания я выложил в исходной версии этого разбора у себя в блоге — там он лежит файлами, которые можно сразу скачать:
-
Основной дашборд — 21 панель: счётчики сбоев, таблицы подписок и отчётов с трейсом, производительность, фоновая нагрузка, здоровье нод.
-
Дашборд истории подписки — открывается по клику из таблицы упавших подписок, показывает расшифрованные ошибки доставки.
-
Скрипт создания view — три UTC-view, выполнить в базе
ReportServer(Шаг 1). -
Конфиг windows_exporter — рабочий конфиг с включённым коллектором
process(Шаг 2).
Забрать всё одним пакетом — в оригинале статьи на моем сайте в блоге. В дашбордах нет ни реальных хостов, ни строк подключения — datasource подставите свои при импорте.
Что в итоге получилось
Дашборд, который отвечает на рабочие вопросы вместо показа безликих графиков.
Сверху — счётчики сбоев: сколько подписок упало, сколько отчётов не открылось, сколько подписок осиротело без владельца. Дальше — таблицы с трейсом: какой отчёт, какая ошибка, кто запускал, кто последним менял. Этот трейс удобно передать разработчику отчёта — вместо «у меня не работает» он получает конкретный код ошибки и время.
Тут напрашивается следующий шаг, до которого у меня пока не дошли руки. Раз в таблице уже есть ModifiedByID — кто последним правил отчёт, — можно навесить на эти панели алертинг Grafana и при сбое автоматически дёргать того, кто за отчёт отвечает. Если отчётами занимается команда аналитиков, это закрывает разрыв «сломалось у пользователя — автор узнал через неделю»: автор получает уведомление сразу, в момент падения, с готовым трейсом. Пока это в планах, но архитектурно всё для этого на дашборде уже лежит.
Ниже — производительность: время по фазам для пагинированных отчётов, топ самых тяжёлых, разбивка нагрузки. Отдельно — фоновая активность .pbix: открытия по ConceptualSchema, обновления моделей, объём записи в каталог. И в самом низу — здоровье нод: процессор и память сервиса, задержки дисков, паспорт серверов с характеристиками.
Времени ушло прилично, чего уж там. Причём бо́льшую его часть съели не запросы, а вот эти неочевидные грабли: .pbix врёт про фазы, ошибки доставки лежат в другой таблице, время разъезжается на часовой пояс. Зато теперь, когда приходит «а почему мне не пришла рассылка», ответ находится за пятнадцать секунд. Раньше на это уходило полдня раскопок в логах.
Если соберётесь повторять — главное, держите в голове, что .pbix живёт по своим правилам, не по правилам .rdl. Это сэкономит вам тот самый день, который потратил я.
А чем вы смотрите за своими отчётными серверами — или они у вас живут как чёрный ящик, пока не прилетит «рассылка не пришла»? Любопытно, кто как решает.
ссылка на оригинал статьи https://habr.com/ru/articles/1047918/