logrus, ежедневная библиотека для Go

Go

Введение

предыдущий постПредставляет библиотеку ведения журналов в стандартной библиотеке Go.log. Наконец, мы также упомянули, чтоlogБиблиотека предоставляет только три набора интерфейсов, и функция слишком проста. Сегодня давайте представим «звездную библиотеку» в лог-библиотеке —logrus. На момент написания этой статьи (2020.02.07),logrusКоличество звезд на GitHub достигло 13,8 тыс.logrusполностью соответствует стандартуlogБиблиотека также поддерживает два формата вывода журнала: текст и JSON. Многие известные проекты с открытым исходным кодом используют эту библиотеку, например, знаменитый докер.

быстрый в использовании

Сначала необходимо установить сторонние библиотеки:

$ go get github.com/sirupsen/logrus

После использования:

package main

import (
  "github.com/sirupsen/logrus"
)

func main() {
  logrus.SetLevel(logrus.TraceLevel)

  logrus.Trace("trace msg")
  logrus.Debug("debug msg")
  logrus.Info("info msg")
  logrus.Warn("warn msg")
  logrus.Error("error msg")
  logrus.Fatal("fatal msg")
  logrus.Panic("panic msg")
}

logrusочень прост в использовании, со стандартной библиотекойlogпохожий.logrusПоддерживается больше уровней ведения журнала:

  • Panic: войти, тоpanic.
  • Fatal: Неустранимая ошибка, при возникновении ошибки программа не может работать нормально. После вывода журнала программа завершает работу;
  • Error: Журнал ошибок, нужно проверить причину;
  • Warn: Предупреждающее сообщение, напоминающее программистам об осторожности;
  • Info: Журналы ключевых операций, основных процессов;
  • Debug: вывод отладочной информации в общие программы;
  • Trace: очень подробная информация, обычно не используемая;

Уровень лога увеличивается последовательно сверху вниз,Traceмаксимум,Panicминимум.logrusСуществует уровень журнала, выше которого журналы не выводятся. Уровень по умолчаниюInfoLevel. Итак, чтобы увидетьTraceиDebugвойти, мыmainПервая строка функции устанавливает уровень журнала наTraceLevel.

Запускаем программу, выводим:

$ go run main.go
time="2020-02-07T21:22:42+08:00" level=trace msg="trace msg"
time="2020-02-07T21:22:42+08:00" level=debug msg="debug msg"
time="2020-02-07T21:22:42+08:00" level=info msg="info msg"
time="2020-02-07T21:22:42+08:00" level=info msg="warn msg"
time="2020-02-07T21:22:42+08:00" level=error msg="error msg"
time="2020-02-07T21:22:42+08:00" level=fatal msg="fatal msg"
exit status 1

так какlogrus.Fatalприведет к выходу программы, следующиеlogrus.Panicне будет реализовано.

Кроме того, в выводе мы наблюдаем три ключевых фрагмента информации:time,levelиmsg:

  • time: время вывода журнала;
  • level: уровень журнала;
  • msg: информация журнала.

сделанный на заказ

имя выходного файла

перечислитьlogrus.SetReportCaller(true)Установите для добавления информации об имени файла и методе в выходной журнал:

package main

import (
  "github.com/sirupsen/logrus"
)

func main() {
  logrus.SetReportCaller(true)

  logrus.Info("info msg")
}

Вывод имеет еще два поляfileдля звонкаlogrusимя файла соответствующего метода,methodДля имени метода:

$ go run main.go
time="2020-02-07T21:46:03+08:00" level=info msg="info msg" func=main.main file="D:/code/golang/src/github.com/darjun/go-daily-lib/logrus/caller/main.go:10"

добавить поле

Иногда необходимо добавить в вывод какие-то поля, что можно сделать, вызвавlogrus.WithFieldиlogrus.WithFieldsвыполнить.logrus.WithFieldsпринять одинlogrus.Fieldsпараметр типа, базовый слой которого фактическиmap[string]interface{}:

// github.com/sirupsen/logrus/logrus.go
type Fields map[string]interface{}

Следующая программа добавляет два поля к выходным даннымnameиage:

package main

import (
  "github.com/sirupsen/logrus"
)

func main() {
  logrus.WithFields(logrus.Fields{
    "name": "dj",
    "age": 18,
  }).Info("info msg")
}

Если во все журналы в функции необходимо добавить некоторые поля, вы можете использоватьWithFieldsВозвращаемое значение. Например, в обработчик веб-запроса необходимо добавить логuser_idиipПоле:

package main

import (
  "github.com/sirupsen/logrus"
)

func main() {
  requestLogger := logrus.WithFields(logrus.Fields{
    "user_id": 10010,
    "ip":      "192.168.32.15",
  })

  requestLogger.Info("info msg")
  requestLogger.Error("error msg")
}

По факту,WithFieldsвернутьlogrus.Entryзначение типа, оно будетlogrus.Loggerи установитьlogrus.FieldsСохрани это. перечислитьEntryКогда связанный метод выводит журнал, сохраненныйlogrus.Fieldsтакже будет выводиться соответственно.

перенаправить вывод

По умолчанию журналы выводятся вio.Stderr. можно назватьlogrus.SetOutputпроходить вio.Writerпараметр. Последующие вызовы соответствующих журналов методов будут записываться вio.Writerсередина. Теперь, как и в предыдущей статье, мы можем представитьlogПора что-то делать. проходить вio.MultiWriter, Также записывайте логи вbytes.Buffer, стандартный вывод и файлы:

package main

import (
  "bytes"
  "io"
  "log"
  "os"

  "github.com/sirupsen/logrus"
)

func main() {
  writer1 := &bytes.Buffer{}
  writer2 := os.Stdout
  writer3, err := os.OpenFile("log.txt", os.O_WRONLY|os.O_CREATE, 0755)
  if err != nil {
    log.Fatalf("create file log.txt failed: %v", err)
  }

  logrus.SetOutput(io.MultiWriter(writer1, writer2, writer3))
  logrus.Info("info msg")
}

настроить

На самом деле, учитывая простоту использования, библиотека обычно создает объект со значениями по умолчанию, и самые внешние методы пакета обычно работают с этим объектом по умолчанию.

Мы рассмотрели это в нескольких предыдущих статьях:

Этот метод используется при разработке многих библиотек,logrusтоже так:

// github.com/sirupsen/logrus/exported.go
var (
  std = New()
)

func StandardLogger() *Logger {
  return std
}

func SetOutput(out io.Writer) {
  std.SetOutput(out)
}

func SetFormatter(formatter Formatter) {
  std.SetFormatter(formatter)
}

func SetReportCaller(include bool) {
  std.SetReportCaller(include)
}

func SetLevel(level Level) {
  std.SetLevel(level)
}

Во-первых, определитеLoggerобъектstd,SetOutput/SetFormatter/SetReportCaller/SetLevelЭти методы называютсяstdСоответствующий метод объекта!

Мы, конечно, также можем создать свой собственныйLoggerОбъект, использование и прямой вызовlogrusМетод аналогичен:

package main

import "github.com/sirupsen/logrus"

func main() {
  log := logrus.New()

  log.SetLevel(logrus.InfoLevel)
  log.SetFormatter(&logrus.JSONFormatter{})

  log.Info("info msg")
}

формат журнала

logrusПоддерживаются два формата журнала, текстовый и JSON, по умолчанию используется текстовый формат. в состоянии пройтиlogrus.SetFormatterУстановите формат журнала:

package main

import (
  "github.com/sirupsen/logrus"
)

func main() {
  logrus.SetLevel(logrus.TraceLevel)
  logrus.SetFormatter(&logrus.JSONFormatter{})

  logrus.Trace("trace msg")
  logrus.Debug("debug msg")
  logrus.Info("info msg")
  logrus.Warn("warn msg")
  logrus.Error("error msg")
  logrus.Fatal("fatal msg")
  logrus.Panic("panic msg")
}

Программа выводит логи в формате JSON:

$ go run main.go 
{"level":"trace","msg":"trace msg","time":"2020-02-07T21:40:04+08:00"}
{"level":"debug","msg":"debug msg","time":"2020-02-07T21:40:04+08:00"}
{"level":"info","msg":"info msg","time":"2020-02-07T21:40:04+08:00"}
{"level":"info","msg":"warn msg","time":"2020-02-07T21:40:04+08:00"}
{"level":"error","msg":"error msg","time":"2020-02-07T21:40:04+08:00"}
{"level":"fatal","msg":"fatal msg","time":"2020-02-07T21:40:04+08:00"}
exit status 1

сторонний формат

кроме встроенногоTextFormatterиJSONFormatter, и многие сторонние форматы поддерживаются. Здесь мы вводимnested-logrus-formatter.

Сначала установите:

$ go get github.com/antonfisher/nested-logrus-formatter

После использования:

package main

import (
  nested "github.com/antonfisher/nested-logrus-formatter"
  "github.com/sirupsen/logrus"
)

func main() {
  logrus.SetFormatter(&nested.Formatter{
    HideKeys:    true,
    FieldsOrder: []string{"component", "category"},
  })

  logrus.Info("info msg")
}

Вывод программы:

Feb  8 15:22:59.077 [INFO] info msg

nestedФормат предоставляет несколько полей для настройки поведения:

// github.com/antonfisher/nested-logrus-formatter/formatter.go
type Formatter struct {
  FieldsOrder     []string
  TimestampFormat string  
  HideKeys        bool    
  NoColors        bool    
  NoFieldsColors  bool    
  ShowFullLevel   bool    
  TrimMessages    bool    
}
  • дефолт,logrusПоле в выходном журналеkey=valueтакая форма. использоватьnestedформат, мы можем установитьHideKeysзаtrueскрыть ключ, вывести только значение;
  • дефолт,logrusэто алфавитное поле вывода ключей, которое может быть установленоFieldsOrderОпределите порядок выходных полей;
  • установивTimestampFormatУстановите формат даты.
package main

import (
  "time"

  nested "github.com/antonfisher/nested-logrus-formatter"
  "github.com/sirupsen/logrus"
)

func main() {
  logrus.SetFormatter(&nested.Formatter{
    // HideKeys:        true,
    TimestampFormat: time.RFC3339,
    FieldsOrder:     []string{"name", "age"},
  })

  logrus.WithFields(logrus.Fields{
    "name": "dj",
    "age":  18,
  }).Info("info msg")
}

Если ключи не скрыты, программа выводит:

$ 2020-02-08T15:40:07+08:00 [INFO] [name:dj] [age:18] info msg

Скрытые ключи, вывод программы:

$ 2020-02-08T15:41:58+08:00 [INFO] [dj] [18] info msg

Обратите внимание, что мы устанавливаем формат времени наtime.RFC3339,Сейчас2006-01-02T15:04:05Z07:00эта форма.

путем реализации интерфейсаlogrus.FormatterВы можете реализовать свой собственный формат.

// github.com/sirupsen/logrus/formatter.go
type Formatter interface {
  Format(*Entry) ([]byte, error)
}

установить крючок

так же может бытьlogrusУстановите хук, конкретный метод хука будет выполняться перед каждым выводом журнала. Таким образом, мы можем добавить поля вывода для вывода журналов в разные места назначения в зависимости от уровня.logrusтакже имеет встроенныйsyslogхук, который выводит логи наsyslogсередина. Здесь мы реализуем хук, чтобы добавить хук в выходной журнал.app=awesome-webполе.

Хуки должны быть реализованыlogrus.Hookинтерфейс:

// github.com/sirupsen/logrus/hooks.go
type Hook interface {
  Levels() []Level
  Fire(*Entry) error
}

Levels()Метод возвращает интересующий уровень журнала, хук не будет срабатывать при выводе других журналов.FireМетод ловушки вызывается перед выводом журнала.

package main

import (
  "github.com/sirupsen/logrus"
)

type AppHook struct {
  AppName string
}

func (h *AppHook) Levels() []logrus.Level {
  return logrus.AllLevels
}

func (h *AppHook) Fire(entry *logrus.Entry) error {
  entry.Data["app"] = h.AppName
  return nil
}

func main() {
  h := &AppHook{AppName: "awesome-web"}
  logrus.AddHook(h)

  logrus.Info("info msg")
}

просто нужноFireПри реализации метода дляentry.DataДобавление полей выводится в лог.

Вывод программы:

$ time="2020-02-08T15:51:52+08:00" level=info msg="info msg" app=awesome-web

logrusЕсть много сторонних хуков, мы можем использовать некоторые хуки для отправки логов в хранилище, например, redis/mongodb:

Здесь мы демонстрируем Redis и заинтересованы в проверке остальных самостоятельно. Установить первымlogrus-redis-hook:

$ go get github.com/rogierlommers/logrus-redis-hook

Затем напишите программу:

package main

import (
  "io/ioutil"

  logredis "github.com/rogierlommers/logrus-redis-hook"
  "github.com/sirupsen/logrus"
)

func init() {
  hookConfig := logredis.HookConfig{
    Host:     "localhost",
    Key:      "mykey",
    Format:   "v0",
    App:      "aweosome",
    Hostname: "localhost",
    TTL:      3600,
  }

  hook, err := logredis.NewHook(hookConfig)
  if err == nil {
    logrus.AddHook(hook)
  } else {
    logrus.Errorf("logredis error: %q", err)
  }
}

func main() {
  logrus.Info("just some info logging...")

  logrus.WithFields(logrus.Fields{
    "animal": "walrus",
    "foo":    "bar",
    "this":   "that",
  }).Info("additional fields are being logged as well")

  logrus.SetOutput(ioutil.Discard)
  logrus.Info("This will only be sent to Redis")
}

Для корректной работы программы нам также необходимо установитьredis.

Прямое использование на окнахchocoУстановите редис:

PS C:\Users\Administrator> choco install redis-64
Chocolatey v0.10.15
Installing the following packages:
redis-64
By installing you accept licenses for the packages.
Progress: Downloading redis-64 3.0.503... 100%

redis-64 v3.0.503 [Approved]
redis-64 package files install completed. Performing other installation steps.
 ShimGen has successfully created a shim for redis-benchmark.exe
 ShimGen has successfully created a shim for redis-check-aof.exe
 ShimGen has successfully created a shim for redis-check-dump.exe
 ShimGen has successfully created a shim for redis-cli.exe
 ShimGen has successfully created a shim for redis-server.exe
 The install of redis-64 was successful.
  Software install location not explicitly set, could be in package or
  default install location if installer.

Chocolatey installed 1/1 packages.
 See the log for details (C:\ProgramData\chocolatey\logs\chocolatey.log).

прямой вводredis-server, запустите сервер:

После запуска программы используемredis-cliПроверять:

Мы виделиmykeyЯвляетсяlist, каждый раз, когда приходит лог,listДобавить элемент позже.

Суммировать

В этой статье описываетсяlogrusбазовое использование.logrusРасширяемость отлично подходит для внедрения сторонних форматов и улучшений Hook. Также популярен в сообществе.

Ссылаться на

  1. logrusРепозиторий GitHub
  2. Hooks

я

мой блог

Добро пожаловать, чтобы обратить внимание на мою общедоступную учетную запись WeChat [GoUpUp], учитесь вместе и добивайтесь прогресса вместе ~

Эта статья опубликована в блогеOpenWriteвыпуск!