Несколько советов по сбору, стандартизации и централизации журналов Golang

Go
Несколько советов по сбору, стандартизации и централизации журналов Golang

Часто используется корпоративными организациями и командами, использующими распределенные системы.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Уникальный идентификатор запроса. При анализе журналов на платформе унифицированных журналов журналы, сгенерированные запросом во всех подчиненных службах, можно запрашивать с помощью уникального идентификатора запроса, записанного в журнале вышестоящей службы.

Ссылка на ссылку:

woohoo.data собака get.com/blog/go-log…

GitHub.com/sirupsen/слегка…