ExpvarMon — консольный мониторинг сервисов на Go

Для Go-программ существует удобнейший стандартный пакадж expvar, позволяющий одной строчкой подключить вывод дебаг информации в JSON-формате. И чтобы максимально быстро и наглядно мониторить текущее состояние, была написана консольная программа Expvarmon, требующая минимум конфигурации для вывода метрик и дебаг-информации для ваших Go-сервисов.Функции:

single- и multi-services режимы мониторинг локальных и удаленных программ произвольное количество сервисов и переменных поддержка значений для памяти, временных интервалов, bool и произвольных чисел/строк sparkline-графики отображение максимальных значений отображение упавших/рестартовавших сервисов авто-ресайз при изменении размеров шрифта или окна 31bb20fddf734c2b9bf6e1959339b0b8.png

ВведениеВ стандартной библиотеке Go есть очень полезный пакадж expvar, который позволяет одной строчкой добавить вывод дебаг информации в json-формате по адресу /debug/vars. По-умолчанию выводятся данные об использовании памяти и работе сборщика мусора (GC), и легко добавляются любые свои метрики/счетчики/переменные. Обычно эти данные собирает отдельный процесс, который кладет в какую-нибудь time-series базу данных, и затем это превращается в удобные и красивые дашборды.Но зачастую, даже во время разработки или отладочных сессий, хочется просто мониторить состояние программы, быть уверенным, что ресурсы используются адекватно нагрузкам, нет утечек памяти или горутин и так далее. Поднимать для этого целую инфраструктуру для мониторинга — часто накладно и неоправданно, а сырой json не сильно презентабелен.

Именно для таких случаев и была за пару выходных написана программа для мониторинга переменных expvar прямо в терминале, которая требует почти нулевую конфигурацию и не использует никаких сторонних баз и ресурсов. Программа использует отличнейший пакадж TermUI от любителя терминалов gizak (посмотрите его домашнюю страничку!).

Установка Установка программы, как и любой другой программы на Go предельно проста: go get github.com/divan/termui Надеюсь, $GOPATH/bin у вас прописан в $PATH.Использование expvar Короткое объяснение import _ «expvar» Длинное объяснение Если вы еще не знакомы с пакетом expvar, то краткое объяснение и инструкция.В функции init () этого пакета написано следующее: func init () { http.HandleFunc (»/debug/vars», expvarHandler) Publish («cmdline», Func (cmdline)) Publish («memstats», Func (memstats)) } Первая строка регистрирует хендлер для обработки URL »/debug/vars» для стандартного http.DefaultServeMux из стандартного пакета net/http. Если вы пока не знаете, как устроен net/http, возможно это будет отдельной статьей, но сейчас вам достаточно знать, что если ваша программа стартанет стандартный http-сервер (скажем, http.ListenAndServe (»:1234», nil)), то у нее автоматически появится обработчик GET-запроса по адресу /debug/vars. И ответ этого запроса будет по умолчанию содержать примерно следующий JSON: $ curl -s http://localhost:1234/debug/vars | json_pp | head { «cmdline» : [ »./expvar.demo», »-bind=:1234», ], «memstats» : { «NumGC» : 5, «Alloc» : 114016, «DebugGC» : false, «HeapObjects» : 519, «HeapSys» : 868352, «StackInuse» : 180224, Это JSON-репрезентация двух переменных, определенных следующими двумя строчками — командная строка и текущие значения runtime.Memstats. Последняя содержит массу подробностей про текущее использование памяти и работу сборщика мусора, большая часть из которых ну уж слишком подробная. Обычно для мониторинга используются значения Alloc, Sys, HeapAlloc — реально используемая память, запрошенная у OS, используемая память в куче соответственно.Поскольку init () вызывает автоматически при импорте пакаджа, в программе достаточно добавить одну строчку:

import _ «expvar» expvarmon Данная же программа предельно проста — она с указанным интервалом вычитывает этот JSON для указанного сервиса или сервисов, и отображает его в удобном для мониторинга виде, при этом показывает sparkline-графики для числовых значений. Все что ей нужно для запуска, это порт или «хост: порт» сервиса (-ов) которые вы хотите мониторить. К примеру: expvarmon -ports=»80» expvarmon -ports=»23000–23010,80» expvarmon -ports=»80, remoteapp.corp.local:80–82» Можно указывать как одну, так и 30+ портов/сервисов — на сколько у вас хватит размеров терминала.Программа может также мониторить саму себя: expvarmon -self Интервал по умолчанию — 5 секунд, но можно указать меньше или больше. Имейте ввиду, что слишком короткий интервал не рекомендован, так как даже обновление memstats влияет на сборщик мусора и увеличивает паузы. Если ваша аппликация бежит под хорошой нагрузкой, сильно короткий интервал (100ms) может повлиять на продуктивность. expvarmon -self -i 5m expvarmon -self -i 30s По умолчанию мониторятся следующие переменные: mem: memstats.Alloc mem: memstats.Sys mem: memstats.HeapAlloc mem: memstats.HeapInuse memstats.EnableGC memstats.NumGC duration: memstats.PauseTotalNs Значения переменных берутся в том виде, в каком они есть в JSON, с записью через точку. Модификаторы «mem:», «duration:», «str:» — опциональны, и влияют на форматирование/отображение. Если указывать переменные без модификатора, то они будут отображаться как есть. Модификатор «mem:» будет конвертировать значения в «KB, MB, etc» представление, а «duration:» — конвертировать int64 значение в time.Duration («ns, ms, s, m, etc»). Модификатор «str:» просто говорит, что значение не цифровое, и sparkline-график для этой переменной рисовать не нужно.К примеру:

expvarmon -ports=»80» -vars=«mem: memstats.Alloc, duration: Response.Mean, Goroutines, str: Uptime» Опять же, можно указать как одну переменную, так и пару десятков, насколько у вас хватит размера терминала.В программе есть два режима — для одного сервиса и для нескольких сервисов. В первом случае графики отображаются для всех переменных, во втором — только для первых двух, в том порядке, в котором они указаны. Для графиков отображаются максимальные значения, которые наблюдались за сессию мониторинга.

Дополнительно Expvarmon отображает иконками сервисы, которые падали и которые лежат в данный момент. К сожалению, если интервал опроса больше, чем время падения/рестарта сервиса, то падение сервиса программа не словит.Также важно понимать, что данные никуда не записываются и не сохраняются. На графиках показываются последние значения — и масштаб графиков зависит от интервала и размера терминала. Ни зума, ни истории, ни поиска по времени тут нет. Это решение для более простой задачи — мгновенного мониторинга текущих значений.

Благодаря возможностям TermUI, программа динамически меняет размеры всех виджетов при изменении размера шрифта или окна терминала.

Дополнительные переменные Лично мне в стандартном перечне переменных expvar не хватает двух вещей — количества запущенных горутин и аптайм сервиса. Вот демо-враппер, который экспортит три дополнительные переменные. Просто подключаете его в свою программу, одним импортом. package myexpvars

import ( «expvar» «math/rand» «runtime» «time» )

var ( startTime = time.Now ().UTC () )

// goroutines is an expvar.Func compliant wrapper for runtime.NumGoroutine function. func goroutines () interface{} { return runtime.NumGoroutine () }

// uptime is an expvar.Func compliant wrapper for uptime info. func uptime () interface{} { uptime:= time.Since (startTime) return int64(uptime) }

// responseTime fake var. func responseTime () interface{} { resp:= time.Duration (rand.Intn (1000)) * time.Millisecond return int64(resp) }

func init () { expvar.Publish («Goroutines», expvar.Func (goroutines)) expvar.Publish («Uptime», expvar.Func (uptime)) expvar.Publish («MeanResponse», expvar.Func (responseTime)) } Что делать, если используется сторонний http-роутер, вместо стандартного Многие веб-сервисы на Go пишутся с использованием дополнительных веб-фреймворков или более продвинутых http-роутеров. expvar из коробки с ними работать не будет. Для него вам нужно будет таки стартануть стандартный http.ListenAndServer () на другом порту. А это даже лучше, так как открывать наружу /debug/vars крайне не рекомендуется, если речь идет о публичных веб-сервисах.Если же вы используете стандартный net/http, но хотите, чтобы expvar был на другом порту, проще всего сделать так. «Основной» ServeMux запустить следующим образом:

mux:= http.NewServerMux () server:= &http.Server{Addr:»:80», Handler: mux} server.ListenAndServe () , а /debug/vars повесить на стандартный http.ListenAndServe (»:1234», nil) Скриншоты 050acca2ca19472eabd30283683a5276.png232a2cd13fed4f9c82ee6572765933a5.pngfcb1c262373846ceb562259b6c0822bb.pngc4f12a4437754ed89512b92e23c3bc29.png

Ссылки Github: github.com/divan/expvarmonExpvar docs: golang.org/pkg/expvarruntime.Memstats: golang.org/pkg/runtime/#MemStats

© Habrahabr.ru