Часто используется корпоративными организациями и командами, использующими распределенные системы.Go
язык для написания своих приложений, чтобы воспользоваться преимуществамиGo
Такие языки, как каналы иgoroutine
параллельные функции, такие как . Если вы отвечаете за R&D или O&MGo
приложения, хорошо продуманная стратегия ведения журнала может помочь вам понять поведение пользователя, обнаружить ошибки и контролировать производительность приложения.
В этой статье будет рассказано о некоторых элементах управленияGo
Инструменты и методы ведения журнала. Сначала мы рассмотрим, какой пакет ведения журнала использовать для различных требований ведения журнала. Затем в нем представлены методы, позволяющие сделать журналы более удобными для поиска и надежными, уменьшить использование ресурсов журнала и стандартизировать сообщения журнала.
Выбор пакета журнала
Go
Библиотека протоколирования стандартной библиотеки очень проста и предоставляет толькоprint
,panic
иfatal
Эти три функции не поддерживают более точные уровни журналов, сегментацию файлов журналов и распределение журналов, поэтому было создано множество сторонних библиотек журналов.logrus
,zap
,glog
Ждать. Давайте сначала рассмотрим характеристики этих библиотек журналов, а затем выберем подходящую библиотеку журналов в соответствии с реальным приложением.
стандартная библиотека журналов
Go
Встроенная библиотека журналов для (log
) С рекордером по умолчанию (logger
), регистратор записывает стандартную ошибку и автоматически добавляет временные метки к записям без необходимости настройки. Вы можете использовать его для регистрации локальных разработок и фрагментов экспериментального кода. На этом этапе получение быстрой обратной связи от вашего кода может быть более важным, чем создание богатых структурированных журналов.
logrus
logrus
— это пакет ведения журналов, разработанный для структурированного ведения журналов, который идеально подходит дляJSON
Форматирование журнала.JSON
Формат позволяет машинам легко анализироватьGo
бревно. Более того, посколькуJSON
являются четко определенными стандартами, поэтому контекст можно легко добавить, включив новые поля, а синтаксический анализатор может извлекать их автоматически.
использоватьlogrus
, вы можете использовать функциюWithFields
Определите стандартные поля для добавления в журнал JSON, как показано ниже. Затем регистратор можно вызывать на разных уровнях журнала, например.Info()
,Warn()
иError()
.logrus
Библиотека автоматически запустится сJSON
Формат, записанный в журнал и вставьте стандартные поля и все поля вы мгновенное определение.
package main
import (
log "github.com/sirupsen/logrus"
)
func main() {
log.SetFormatter(&log.JSONFormatter{})
standardFields := log.Fields{
"hostname": "staging-1",
"appname": "foo-app",
"session": "1ce3f6v",
}
requestLogger := log.withFields(standardFields)
requestLogger.WithFields(log.Fields{"string": "foo", "int": 1, "float": 1.1}).Info("My first ssl event from Golang")
}
Сгенерированный журнал будет вJSON
Объект включает в себя сообщение, уровень журнала, метку времени, стандартные поля и поля, которые записываются на лету, вызывая регистратор:
{"appname":"foo-app","float":1.1,"hostname":"staging-1","int":1,"level":"info","msg":"My first ssl event from Golang","session":"1ce3f6v","string":"foo","time":"2019-03-06T13:37:12-05:00"}
glog
glog
Позволяет включать или отключать определенные уровни ведения журнала, что полезно для проверки томов журнала при переключении между средой разработки и рабочей средой. Он позволяет использовать флаги в командной строке (например, -v для подробных сведений), чтобы установить уровень ведения журнала при выполнении вашего кода. Тогда ты можешьif
используется в заявленииV()
Функция записывает только на определенном уровне журналаGo
бревно. ФункцииInfo()
,Warning()
,Error()
иFatal()
Укажите уровни журнала индивидуально0
прибыть3
if err != nil && glog.V(2){
glog.Error(err)
}
###Выбор библиотеки журналов
Приведенный выше анализ, стандартная библиотекаlog
Подходит только для быстрой проверки и отладки фрагментов кода, не относящихся к проекту.logrus
В структуре логов лучше всего, поможет анализ логов.glog
Дисковое пространство, занимаемое журналом, может быть уменьшено. Однако по сравнению с проблемой, заключающейся в том, что сгенерированные журналы занимают много места, журналы, подходящие для анализа, приносят большую ценность прикладным продуктам, поэтомуlogrus
Используйте еще немного. Многие проекты с открытым исходным кодом, такие какDocker
,Prometheus
ждать егоlogrus
для записи своих журналов.
Введение в использование logrus
logrus
СейчасGithub
Библиотека журналов с наибольшим количеством звезд на данный момент (2020.03)star
Количество 14000+,fork
Количество 1600+.logrus
Мощный, производительный и очень гибкий, с возможностью настройки плагинов. Многие проекты с открытым исходным кодом, такие какDocker
,Prometheus
ждать егоlogrus
для записи своих журналов.
-
logrus
полностью совместимыйGo
Модуль журнала стандартной библиотеки с шестью уровнями журнала:debug
,info
,warn
,error
,fatal
иpanic
,ЭтоGo
Расширенный набор API модуля ведения журнала из стандартной библиотеки. Если в вашем проекте используется модуль ведения журнала из стандартной библиотеки, вы можете перейти на модуль ведения журнала из стандартной библиотеки с наименьшими затратами.logrus
начальство. -
Масштабировать
Hook
Механизм: Позволяет пользователям проходитьhook
способ распространения журнала в любое место, например, в локальную файловую систему, стандартный вывод,logstash
,elasticsearch
илиmq
Ждать. -
logrus
Есть два встроенных формата журнала,JSONFormatter
иTextFormatter
Вы также можете реализовать свои собственные ручные интерфейсы Fixatter, чтобы определить свой собственный формат журнала. -
Field
механизм:logrus
предлагается пройтиField
Механизм мелкозернистого структурированного ведения журнала вместо ведения журнала с помощью подробных сообщений. -
Entry
:logrus.WithFields
автоматически вернет*Entry
,Entry
Автоматически добавит время создания записи в запись журналаtime
поле.
Основное использование
logrus
иGo
Модуль ведения журнала стандартной библиотеки полностью совместим,logrus
Выход, формат или уровень журнала могут быть определены простой конфигурацией.
package main
import (
"os"
log "github.com/sirupsen/logrus"
)
func init() {
// 设置日志格式为json格式
log.SetFormatter(&log.JSONFormatter{})
// 设置将日志输出到指定文件(默认的输出为stderr,标准错误)
// 日志消息输出可以是任意的io.writer类型
logFile := ...
file, _ := os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
log.SetOutput(file)
// 设置只记录日志级别为warn及其以上的日志
log.SetLevel(log.WarnLevel)
}
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
log.WithFields(log.Fields{
"omg": true,
"number": 122,
}).Warn("The group's number increased tremendously!")
log.WithFields(log.Fields{
"omg": true,
"number": 100,
}).Fatal("The ice breaks!")
}
Пользовательский регистратор
Если вы хотите писать в несколько мест в приложенииlog
, вы можете создать несколько регистраторовLogger
пример.
package main
import (
"github.com/sirupsen/logrus"
"os"
)
// logrus提供了New()函数来创建一个logrus的实例.
// 项目中,可以创建任意数量的logrus实例.
var log = logrus.New()
func main() {
// 为当前logrus实例设置消息的输出,同样地,
// 可以设置logrus实例的输出到任意io.writer
log.Out = os.Stdout
// 为当前logrus实例设置消息输出格式为json格式.
// 同样地,也可以单独为某个logrus实例设置日志级别和hook,这里不详细叙述.
log.Formatter = &logrus.JSONFormatter{}
log.WithFields(logrus.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
}
Fields
logrus
Не рекомендуется использовать подробные сообщения для регистрации текущей информации, рекомендуется использоватьFields
Для записи уточненной и структурированной информации, например, следующий способ записи журналов:
log.Fatalf("Failed to send event %s to topic %s with key %d", event, topic, key)
существуетlogrus
не рекомендуется в Китае,logrus
Приветствуются следующие альтернативы:
log.WithFields(log.Fields{
"event": event,
"topic": topic,
"key": key,
}).Fatal("Failed to send event")
WithFields
Пользователей можно настроить так, чтобы они входили в систему так, как они рекомендуют. ноWithFields
Тем не менее, это необязательно, потому что в некоторых сценах действительно нужно записать простое сообщение.
Default Fields
Часто в приложении или его части полезно всегда иметь несколько фиксированных полей записи. Например, при работе с пользователямиHTTP
По запросу все журналы в контексте будут иметьrequest_id
иuser_ip。
Чтобы избежать регистрации каждый раз, используйте:
log.WithFields(log.Fields{“request_id”: request_id, “user_ip”: user_ip})
мы можем создатьlogrus.Entry
экземпляр, установите значение по умолчанию для этого экземпляраFields
,Пучокlogrus.Entry
экземпляр установлен на регистраторLogger
, эти поля по умолчанию будут присоединяться каждый раз при регистрации.
requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})
requestLogger.Info("something happened on that request") # will log request_id and user_ip
requestLogger.Warn("something not great happened")
Интерфейс крючка
logrus
Самая захватывающая особенность - это его расширяемость.HOOK
механизм. Инициализируя какlogrus
Добавить кhook
,logrus
Могут быть реализованы различные расширения.
logrus
изhook
Интерфейс определяется следующим образом, принцип заключается в том, чтобы перехватывать модификацию каждый раз, когда лог пишетсяlogrus.Entry
.
// logrus在记录Levels()返回的日志级别的消息时会触发HOOK,
// 按照Fire方法定义的内容修改logrus.Entry.
type Hook interface {
Levels() []Level
Fire(*Entry) error
}
простой обычайhook
следующее,DefaultFieldHook
Определение будет включать поля по умолчанию в сообщения журнала на всех уровнях.appName=”myAppName”
.
type DefaultFieldHook struct {
}
func (hook *DefaultFieldHook) Fire(entry *log.Entry) error {
entry.Data["appName"] = "MyAppName"
return nil
}
func (hook *DefaultFieldHook) Levels() []log.Level {
return log.AllLevels
}
hook
Использование также очень просто, вызов до инициализацииlog.AddHook(hook)
добавить соответствующийhook
Вот и все.Hook
Более распространено использование для отправки предупреждений о сообщениях журнала с указанными уровнями ошибок в почтовые группы или системы мониторинга ошибок (такие какsentry
), который действует как активное уведомление об ошибке.
logrus
Официально только встроенныйsyslog
изhook
. ноGithub
Есть много стороннихhook
доступный. Как то, что я только что сказалsentry
Связанныйhook
.
sentry-hook
Sentry
Это система мониторинга ошибок, вы можете использовать сервис производителя или построить его на своем сервере.Sentry
. узор сGitLab
Во многом он также предоставляет установочный пакет одним щелчком мыши. Зарегистрируйтесь в приложенииSentry
затем назначитDSN
для подключенияSentry
Служить.
import (
"github.com/sirupsen/logrus"
"github.com/evalphobia/logrus_sentry"
)
func main() {
log := logrus.New()
hook, err := logrus_sentry.NewSentryHook(YOUR_DSN, []logrus.Level{
logrus.PanicLevel,
logrus.FatalLevel,
logrus.ErrorLevel,
})
if err == nil {
log.Hooks.Add(hook)
}
}
logrus является потокобезопасным
по умолчанию,Logger
отmutex
защищен от одновременной записи. Когда хук вызывается и лог пишется,mutex
будет сохранен. Если вы уверены, что такая блокировка не нужна, вы можете позвонитьlogger.SetNoLock()
Отключить блокировку.
К ситуациям, когда блокировка не требуется, относятся:
- незарегистрированный
hook
,илиhook
Вызов является потокобезопасным. - написать
logger.Out
является потокобезопасным, например.logger.Out
был заблокирован или защищенlogger.Out
этоAppend
Дескриптор открытия файла режима.
Некоторые предложения по написанию и хранению журнала
После того, как вы выбрали библиотеку журналов, используемую в вашем проекте, вам также необходимо спланировать, где в вашем коде вызывать регистратор и как хранить журналы. В этом разделе будет рекомендована некоторая уборкаGo
Лучшие журналы практики, они включают в себя:
- из основного процесса приложения вместо
goroutine
вызовите регистратора. - Записывайте журналы из приложения в локальные файлы, даже если они позже отправляются на платформу централизации журналов.
- Определите нормализованные поля по умолчанию для журналов
- Отправляйте журналы на платформу обработки журналов для анализа и агрегирования.
- использовать
HTTP
Распределенный заголовок содержит уникальный идентификатор микросервиса записи поведения пользователя.
Избегайте использования логгеров в горутинах
избегать создания собственныхgoroutine
Есть две причины обрабатывать запись в журнал. Во-первых, это может вызвать проблемы с параллелизмом, так как копии регистратора будут пытаться получить доступ к одному и тому жеio.Writer
. Во-вторых, библиотеки журналирования обычно запускаются сами.goroutine
, внутренне управляет всеми проблемами параллелизма, одновременно запуская собственныйgoroutine
вызовет только помехи.
всегда записывать лог в файл
Даже при отправке журналов на центральную платформу журналов рекомендуется сначала записывать журналы в файл на локальном компьютере. Это гарантирует, что ваши журналы всегда доступны локально и не будут потеряны по сети. Кроме того, запись в файл означает, что вы можете отделить задачу записи журналов от задачи отправки журналов на центральную платформу журналов. Самому вашему приложению не нужно устанавливать соединение или передавать журналы на платформу ведения журналов.Вы можете оставить эти задачи профессиональному программному обеспечению, например, использоватьElasticsearch
Если вы индексируете данные журнала, вы можете использоватьLogstash
Извлечение данных журнала из файлов журнала.
Централизованно обрабатывать журналы с помощью платформы обработки журналов
Если ваше приложение развернуто на нескольких хост-кластерах, журналы приложения будут распределены по разным компьютерам. Журналы доставляются из локальных файлов на центральную платформу ведения журналов для анализа и агрегирования данных журналов. Что касается выбора услуг обработки журналов, услуги обработки журналов с открытым исходным кодом включают:ELK
Каждое поставщик Cloud Service также имеет свой собственный сервис обработки журнала, вы можете выбрать его в соответствии с вашей собственной ситуацией, попробуйте выбрать сервер журнала того же поставщика, что и облачный сервер, чтобы не потреблять трафик публичной сети.
Отслеживайте журналы Go по службам с уникальными идентификаторами
Для приложений, построенных на распределенных системах, запрос может проходить через несколько служб, каждая из которых записывает свои журналы. В этом случае, чтобы запросить журнал, соответствующий запросу, обычным решением является перенос уникального идентификатора в заголовок запроса и добавление поля уникального идентификатора в регистраторы журналов всех служб в распределенной системе, чтобы каждый письменный журнал будет иметьHTTP
Уникальный идентификатор запроса. При анализе журналов на платформе унифицированных журналов журналы, сгенерированные запросом во всех подчиненных службах, можно запрашивать с помощью уникального идентификатора запроса, записанного в журнале вышестоящей службы.
Ссылка на ссылку: