Для Go-программ существует удобнейший стандартный пакадж expvar, позволяющий одной строчкой подключить вывод дебаг информации в JSON-формате. И чтобы максимально быстро и наглядно мониторить текущее состояние, была написана консольная программа Expvarmon, требующая минимум конфигурации для вывода метрик и дебаг-информации для ваших Go-сервисов.
Функции:
- single- и multi-services режимы
- мониторинг локальных и удаленных программ
- произвольное количество сервисов и переменных
- поддержка значений для памяти, временных интервалов, bool и произвольных чисел/строк
- sparkline-графики
- отображение максимальных значений
- отображение упавших/рестартовавших сервисов
- авто-ресайз при изменении размеров шрифта или окна
Введение
В стандартной библиотеке 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)
Скриншоты
Ссылки
Github: github.com/divan/expvarmon
Expvar docs: golang.org/pkg/expvar
runtime.Memstats: golang.org/pkg/runtime/#MemStats
ссылка на оригинал статьи http://habrahabr.ru/post/257593/
Добавить комментарий