hello, world
Первое что пришло мне в голову - это рассказать немного про TICK DSL и дать простенький пример. На момент написания (февраль 2017го) актуальная версия уже 1.2, а начинал я играться с 1.0. Но сути это не меняет, разница между ними в контексте данного повествования неважна. Есть чейнджлог, так, на всякий случай. Здесь я дам тривиальный примерчик с алёртами, коих (примеров, а не алёртов) днём с огнём не.
Вкратце, это такой маленький язык для описания цепочки преобразований ваших (больших) данных, чтобы в итоге получить определённую аналитику. Живёт он в штуковине под названием Kapacitor, которая гордо именует себя data processing framework и вообще ETL jobs launcher. Эта софтина является вишенкой на стеке Telegraf+InfluxDB и, в отличие от первых двух, Kapacitor пока что не умеет работать ни с каким другим хранилищем, кроме InfluxDB. Идея такая - Kapacitor по т.н. подписке (subscription) регулярно ходит в InfluxDB за свежими порциями данных и ранает на этой порции загруженные в него TICK скрипты. Забирать данные он может как батчами так и стримом. Зачем это нужно - коль скоро в InfluxDB лежат (в большинстве адекватных случаев) мониторинговые данные, то рано или поздно вам захочется не выцеливать на графичках пики и бежать тушить пожар, а поступить гораздо спортивнее, например, прикрутить алёртилку. Согласно описанию на главной "Kapacitor makes it easy to create alerts" и ещё пара умных слов про anomaly detection. "Прикольно..", подумал я и пошёл прикручивать.
После беглого знакомства с этим всем, невольно вспоминаешь Pig Latin из мира хадупов. Кто когда-нибудь работал с этой замечательной декларативной тулзой меня поймёт - TICK очень похож. Мы описываем длинную цепочку: data flow, нужные агрегации, что-то считаем, может даже впиливаем свою UDFку, оно там под капотом бурлит, урчит и выдаёт пару гениальных InfluxQL кверей (в случае с пигом это была гениальная мапредьюсная джобка) и вуаля - наши данные попроцессились, мы получили либо аналитику в том же InfluxDB отдельным измерением(measurement), либо алёртом по лбу - возможности есть и для первого и для второго. Цепочка (бусурманск. pipeline) в терминологии TICK есть множество nodes которые собственно процессят данные + edges соединяющие nodes. Это множество является ориентированным ациклическим графом (DAG), что логично - нам важен порядок преобразований и мы не хотим чтобы преобразования происходили по кругу бесконечно. Матчасть учить тут. Сами nodes есть сущности логические, с их помощью мы говорим ЧТО мы хотим сделать с данными. На каждой ноде есть 2 типа методов: property и chaining. Первые меняют стейт ноды, но не двигают нас по цепочке нод дальше. Вторые размножают ноды почкованием - порождают новую ноду и переключают наше внимание на неё. Про edges оф. дока гласит мол "each edge has a type", но толи ребята что-то не договаривают, толи я совсем недалёкий - так я и не понял где эти edges в теле скрипта и как их вообще руками потрогать. Скорее всего под edges понимаются те самые chaining methods и тип edge зависит от самой первой ноды, с которой мы начали процессинг - если это была StreamNode то тип всех edges между порождёнными от неё нодами будет StreamEdge. Если же первая нода была BatchNode, то и тип edges будет BatchEdge. Стоит заметить, что никаких вам тут привычных прелестей компилируемых языков - проверить TICK скрипт заранее можно только на синтаксис. Обдолбались и вызвали chaining method которого нет у данной ноды - получите ошибку в рантайме. Но довольно скучных буков, давайте попробуем уже что-нибудь пощупать!
В качестве простенького примера возьмём алёрт на LA. Данные измерений - стандартные, выдаваемые system инпутом в Telegraf. И плюс прикрученный сверху environment tag, чтобы было проще отделять мух от котлет.
// 'Берём стрим из данных про LA'
// 'qa1 тут как пример айдишника какого-нибудь энва'
var load_data_qa1 = stream // 'Вот нода с которой всё начинается'
// 'для удобства можно её закинуть в переменную, чтобы дальше можно было с ней что-то делать'
|from() // 'вызов chaining метода from() - получаем ноду FromNode'
.measurement('system')
.groupBy('host')
.where(lambda: "environment" == 'qa1')
var load_alert_qa1 = load_data_qa1
|alert()
.stateChangesOnly()
.id('{{ index .Tags "host" }}/Load')
.warn(lambda: "load5" > 5)
.crit(lambda: "load5" > 10)
.message('{{ .Level }} alert: {{ .ID }}')
// 'тут есть возможность заюзать html-like форматирование, чтобы наши письма были не такими страшными'
.details('''
<h1>5 minute load average on {{ index .Tags "host" }} is {{ index .Fields "load5" }}</h1>
<h3>Additional info</h3>
<br>Host: {{ index .Tags "host" }}</br>
<br>Metric: system.load5</br>
<br>Value: {{ index .Fields "load5" }}</br>
<br>Time: {{ .Time }}</br>
<br>--</br>
<br>Our awesome monitoring system</br>
''')
.email()
// 'кладём инфу про алёрт в influxDB, для истории, например'
load_alert_qa1
|influxDBOut()
.database('my_influx_db')
.retentionPolicy('default')
.measurement('load_alerts')
.precision('m')
.tag('environment', 'qa1')