Введение
Собственно, речь пойдет о неплохом open source инструменте под названием grafana и как создать свой dashboard без особых усилий. Итак, вот что нам понадобится:
1. Сам grafana, скачать можно тут https://grafana.com/grafana/download есть для любых операционных систем, я покажу пример установки на Ubuntu 16.04
2. Плагин, который будет получать данные из 1С https://grafana.com/plugins/grafana-simple-json-datasource/installation
3. Установленный веб сервер (apache, IIS, неважно) главное, чтобы была возможность публикации http сервиса
4. Ну и, конечно же, сам http сервис, откуда мы будем получать данные для построения графиков
Здесь нет ничего сложного, всего несколько команд и сервис по созданию dashboar'ов у вас в кармане.
1. в файл /etc/apt/sources.list добавляем строчку
deb https://packagecloud.io/grafana/stable/debian/ jessie main
2. Добавляем ключ пакета. Выполняем команду
curl https://packagecloud.io/gpg.key | sudo apt-key add -
3. Обновляем данные о репозиториях
sudo apt-get update
4. Ну и собственно сама установка
sudo apt-get install grafana
Все, установка завершена. Теперь можно приступить к настройке
1. Открываем файл /etc/grafana/grafana.ini
2. Зададим логин и пароль администратора, по умолчанию admin admin
Находим секцию security убираем точки с запятой пред параметрами и заменяем их на то что нужно
[security]
# default admin user, created on startup
;admin_user = admin
# default admin password, can be changed before first start of grafana, or in profile settings
;admin_password = admin
3. Если вы хотите, чтобы доступ на просмотр dashboard'а имели все, тогда предлагаю открыть гостевой доступ. Находим секцию auth.anonymous и выставляем параметры следующим образом
[auth.anonymous]
# enable anonymous access
enabled = true
# specify organization name that should be used for unauthenticated users
# Наименование организации в GRAFANA
org_name = Main Org.
# specify role for unauthenticated users
org_role = Viewer
Отлично, первоначальную настройку произвели, теперь установим плагин, который нам понадобится для передачи данных. Для этого вводим команду
sudo grafana-cli plugins install grafana-simple-json-datasource
Все, теперь мы готовы запустить наш сервис, вводим команду
sudo service grafana-server start
По умолчанию сервер запускается на 3000 порту зайдем и проверим, что все работает, заходим в браузер и в адресной строке, пишем http://адрес_сервера:3000, должна открыться вот такая страница:
Или если вы не стали открывать гостевой доступ, то форма входа
HTTP Сервис.
Теперь настало время создать наш http сервис, обращаясь к которому grafana будет получать данные. Чтобы все работало корректно, 1С должна понимать несколько запросов и отправлять ответы в формате JSON:
PUT
Запрос на адрес "/" должен просто вернуть ответ с кодом 200 - служит для проверки соединения
POST
Запрос на адрес "/search
" - метод должен вернуть массив метрик, которые можно использовать
Запрос на адрес "/
query
" - основной метод, должен вернуть данные по запросу
Запрос на адрес "/
annotations
" - метод возвращает описание определенной точки и отображает ее на графике
Запрос /search
Тут все достаточно просто, если идет обращение по этому адресу, то мы должны вернуть массив с метриками, которые возможно запросить из базы
МассивДоступныхПолей = Новый Массив;
МассивДоступныхПолей.Добавить("Продажи");
МассивДоступныхПолей.Добавить("ПродажиМолока");
МассивДоступныхПолей.Добавить("ПродажиХлеба");
МассивДоступныхПолей.Добавить("ПродажиМасла");
МассивДоступныхПолей.Добавить("ВыполнениеПлана");
МассивДоступныхПолей.Добавить("ПродажиПоГруппам");
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON,МассивДоступныхПолей);
СтрокаДжисон = ЗаписьJSON.Закрыть();
Ответ.УстановитьТелоИзСтроки(СтрокаДжисон);
Запрос /query
Сам запрос от grafana выглядит следующим образом
{
"panelId": 1,
"range": {
"from": "2016-10-31T06:33:44.866Z",
"to": "2016-10-31T12:33:44.866Z",
"raw": {
"from": "now-6h",
"to": "now"
}
},
"rangeRaw": {
"from": "now-6h",
"to": "now"
},
"interval": "30s",
"intervalMs": 30000,
"targets": [
{ "target": "upper_50", "refId": "A", "type": "timeserie" },
{ "target": "upper_75", "refId": "B", "type": "timeserie" }
],
"format": "json",
"maxDataPoints": 550
}
Здесь нас интересует:
Данные range - from и to, обозначающие период, за который запрашиваются данные и массив targets содержащий название метрик (target), по которым нужны данные, а также формат(type), в котором нужно вернуть эти данные.
В ответ на запрос, если тип запроса timeserie
, мы должны вернуть массив структур с данными по каждой метрике, данные метрики это массив координат [Значение, Время на графике]:
Данные = Запрос.ПолучитьТелоКакСтроку();
ЧтениеJSON = Новый ЧтениеJSON();
ЧтениеJSON.УстановитьСтроку(Данные);
//Получаем структуру данных запроса
ЗапросГрафана = ПрочитатьJSON(ЧтениеJSON);
ГенераторСЧ = Новый ГенераторСлучайныхЧисел(ПреобразуемДатуДляГрафан(ТекущаяДата()));
НачалоПериода = ПолучитьДатуГрафана(ЗапросГрафана.range.from);
ОкончаниеПериода = ПолучитьДатуГрафана(ЗапросГрафана.range.to);
МассивДанных = Новый Массив;
Для Каждого Метрика Из ЗапросГрафана.targets Цикл
СтруктураДанных = Новый Структура;
СтруктураДанных.Вставить("target",Метрика.target);
МассивТочек = Новый Массив;
ВыборкаДат = ПолучитьТаблицуДат(НачалоПериода,ОкончаниеПериода,"ЧАС");
Пока ВыборкаДат.Следующий() Цикл
ТекущаяДатаДляГрафа = ПреобразуемДатуДляГрафан(ВыборкаДат.ДатаПериода);
ДанныеТочки = Новый Массив;
ДанныеТочки.Добавить(ГенераторСЧ.СлучайноеЧисло(0,99999));
ДанныеТочки.Добавить(ТекущаяДатаДляГрафа);
МассивТочек.Добавить(ДанныеТочки);
КонецЦикла;
СтруктураДанных.Вставить("datapoints",МассивТочек);
МассивДанных.Добавить(СтруктураДанных);
КонецЦикла;
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON,МассивДанных);
СтрокаДжисон = ЗаписьJSON.Закрыть();
Ответ.УстановитьТелоИзСтроки(СтрокаДжисон);
Если же тип запроса table, то возвращается массив структур с описанием колонок и строк таблицы.
Данные = Запрос.ПолучитьТелоКакСтроку();
ЧтениеJSON = Новый ЧтениеJSON();
ЧтениеJSON.УстановитьСтроку(Данные);
//Получаем структуру данных запроса
ЗапросГрафана = ПрочитатьJSON(ЧтениеJSON);
ГенераторСЧ = Новый ГенераторСлучайныхЧисел(ПреобразуемДатуДляГрафан(ТекущаяДата()));
НачалоПериода = ПолучитьДатуГрафана(ЗапросГрафана.range.from);
ОкончаниеПериода = ПолучитьДатуГрафана(ЗапросГрафана.range.to);
МассивДанных = Новый Массив;
//Формируем тестовые данные для таблицы продаж по группам
СтруктураТаблицы = Новый Структура;
//Формируем колонки таблицы, колонки - это массив,
//содержащий структуру из Заголовка(text) и типа(type)
Колонки = Новый Массив;
СтруктураКолонки = Новый Структура;
СтруктураКолонки.Вставить("text","Товарная группа");
СтруктураКолонки.Вставить("type","string");
Колонки.Добавить(СтруктураКолонки);
СтруктураКолонки = Новый Структура;
СтруктураКолонки.Вставить("text","Дата");
СтруктураКолонки.Вставить("type","time");
Колонки.Добавить(СтруктураКолонки);
СтруктураКолонки = Новый Структура;
СтруктураКолонки.Вставить("text","Сумма");
СтруктураКолонки.Вставить("type","float");
Колонки.Добавить(СтруктураКолонки);
СтруктураТаблицы.Вставить("columns",Колонки);
//Формируем строки таблицы. Строки - это массив,
//содержащий массив значений строки
ТекущаяДатаДляГрафа = ПреобразуемДатуДляГрафан(ОкончаниеПериода);
СтрокиТаблицы = Новый Массив;
//Сформируем данные строки
СтрокаТаблицы = Новый Массив;
СтрокаТаблицы.Добавить("Молоко");
СтрокаТаблицы.Добавить(ТекущаяДатаДляГрафа);
СтрокаТаблицы.Добавить(ГенераторСЧ.СлучайноеЧисло(0,99999));
СтрокиТаблицы.Добавить(СтрокаТаблицы);
СтрокаТаблицы = Новый Массив;
СтрокаТаблицы.Добавить("Хлеб");
СтрокаТаблицы.Добавить(ТекущаяДатаДляГрафа);
СтрокаТаблицы.Добавить(ГенераторСЧ.СлучайноеЧисло(0,99999));
СтрокиТаблицы.Добавить(СтрокаТаблицы);
СтрокаТаблицы = Новый Массив;
СтрокаТаблицы.Добавить("Масло");
СтрокаТаблицы.Добавить(ТекущаяДатаДляГрафа);
СтрокаТаблицы.Добавить(ГенераторСЧ.СлучайноеЧисло(0,99999));
СтрокиТаблицы.Добавить(СтрокаТаблицы);
СтруктураТаблицы.Вставить("rows", СтрокиТаблицы);
СтруктураТаблицы.Вставить("type", "table");
МассивДанных.Добавить(СтруктураТаблицы)
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON,МассивДанных);
СтрокаДжисон = ЗаписьJSON.Закрыть();
Ответ.УстановитьТелоИзСтроки(СтрокаДжисон);
Запрос /annotations
Если честно, я не нашел применения данным, которые передаются по этому запросу, поэтому я просто опишу, что должно передаваться, а вы сами для себя определите, надо вам это или нет. Вообще, как описывает документация, annotations это точки, которые каким-то образом должны были повлиять на данные, допустим, начало маркетинговой акции. Сам запрос выглядит следующим образом:
{
"range": {
"from": "2016-04-15T13:44:39.070Z",
"to": "2016-04-15T14:44:39.070Z"
},
"rangeRaw": {
"from": "now-1h",
"to": "now"
},
"annotation": {
"name": "deploy",
"datasource": "Simple JSON Datasource",
"iconColor": "rgba(255, 96, 96, 1)",
"enable": true,
"query": "#deploy"
}
}
В ответ на этот запрос мы должны отправить массив точек, которые хотели бы выделить на графике.
Данные = Запрос.ПолучитьТелоКакСтроку();
ЧтениеJSON = Новый ЧтениеJSON();
ЧтениеJSON.УстановитьСтроку(Данные);
//Получаем структуру данных запроса
ЗапросГрафана = ПрочитатьJSON(ЧтениеJSON);
НачалоПериода = ПолучитьДатуГрафана(ЗапросГрафана.range.from);
ОкончаниеПериода = ПолучитьДатуГрафана(ЗапросГрафана.range.to);
МассивДанных = Новый Массив;
СтруктураАнотаций = Новый Структура;
//Передаем структуру анотации из запроса
СтруктураАнотаций.Вставить("annotation",ЗапросГрафана.annotation);
ТекущаяДата_Юникс = ПреобразуемДатуДляГрафан(ОкончаниеПериода-3600);
СтруктураАнотаций.Вставить("time",ТекущаяДата_Юникс);
СтруктураАнотаций.Вставить("title","Запустили маркетинг");
СтруктураАнотаций.Вставить("text","Очень крутой маркетинг");
СтруктураАнотаций.Вставить("tags","ТЭГ");
МассивДанных.Добавить(СтруктураАнотаций);
ФорматОбмена = Новый ЗаписьJSON;
ФорматОбмена.УстановитьСтроку();
ЗаписатьJSON(ФорматОбмена,МассивДанных);
СтрокаДжисон = ФорматОбмена.Закрыть();
Ответ.УстановитьТелоИзСтроки(СтрокаДжисон);
Наш http сервис готов, публикуем его на веб сервере и переходим к созданию самого dashboard'а.
Создание dashboard
Заходим на адрес сервера grafana http://адрес_сервера:3000. И входим в систему под правами администратора:
Переходим в раздел data source
Создаем коннектор к базе, от куда будем брать данные
Задаем имя ресурса пусть будет 1С Grafana, тип выбираем SimpleJson, ставим галочку default (что бы нам не выбирать, из какого ресурса брать данные), указываем адрес опубликованного http сервиса, указываем настройки подключения, если нужна авторизация указываем пользователя и пароль для подключения к 1С, и нажимаем кнопку add.
После того, как коннектор добавлен, проверяем его доступность, нажимаем кнопку "save&test", если все работает, увидим сообщение "data source is working"
Теперь приступаем к созданию самого dashboard'а.
Откроется окно конструктора. Первое, что нас здесь интересует, это интервал, за который отображаются данные. Отображение текущего интервала находится в правом верхнем углу, а при нажатии на него открывается окно настроек. Для теста я буду использовать интервал с начала месяца по текущий момент.
Теперь можно выбрать панель и попробовать вывести данные. Добавим панель graph и настроим ее
Нажмем на заголовок панели и редактировать
Снизу появиться окно с настройками
Выберем метрики, которые бы хотели отображать в выпадающем списке select metrik, это как раз тот самый массив, который мы передаем при обработке запроса "search", я выбрал 2 метрики
И вот что получилось
Если что-то не работает в настройках панели есть query inspector, который отображает, какой запрос был отправлен и что было получено, очень удобно для отладки.
Теперь покажу как в панели использовать период отличный от основного. Допустим, что мы хотим смотреть продажи в час за текущий день. Добавим еще одну панель graph в строке панелей слева есть значок если на него навести появиться меню управления строкой.
Перейдем в настройки панели, на вкладку time range, нас интересует показатель Override relative time, который указывает, что панель будет показывать данные за последний час, день, месяц, год. Допустим мы хотим смотреть данные за текущий день, тогда вписываем туда конструкцию вида now/d, что будет означать показывать данные с начала дня, у такой панели в верхнем правом углу появиться надпись, которая будет указывать, что для нее изменен период выборки.
И теперь выберем метрику, которую будем показывать. И вот что у нас должно было получиться.
Добавим еще одну строку
И панель SingleStat, перейдем в ее настройки, метрику я выберу выполнение плана, перейдем на вкладку options, поставим галочку Gauge - show, отобразиться полукруг. И тут есть интересный параметр Stat, в этом параметре можно указать, какое значение отображать, максимальное, минимальное, среднее, текущее и т.д., для выполнения плана я отправляю всего одну точку, поэтому здесь не важно, что я выберу, но если будет несколько точек, то можно очень эффективно это использовать.
Добавим еще одну строку и панель "table" в метрике укажем ПродажиПоГруппам, а тип table.
И вот окончательный вариант, который мы получили после всех наших действий.
Ну вот, собственно, такой dashboard мы смогли получить, без глобальной разработки с достаточно обширным функционалом, который расширяется за счет плагинов и имеет достаточно гибкие настройки. Статья была написана с использованием тестовой конфигурации, которую я выложил. Я постарался максимально прокомментировать там все, что я делаю. Если появятся какие-то вопросы, постараюсь ответить.