Go
June 18, 2023

Логирование в Go на основе уровней при помощи Uber Zap 

Логировать или не логировать

Преимущества ведения журнала

Большинство веб-серверов в технологической отрасли используют ведение журнала. Ведение журнала имеет некоторые преимущества, когда речь идет о веб-приложении.

  • Ведение журнала помогает разработчикам выявлять ошибки и проблемы с производительностью
  • Журналы могут быть реконструированы и использованы для аудита системы.

Что следует логировать

На самом деле это все, что облегчает процесс обнаружения ошибок и отладки. Сообщение журнала может содержать полезную информацию для отладки, такую ​​как метка времени, сообщение об ошибке и т. д.

Что не следует логировать

Информация о безопасности не рекомендуется для логирования, особенно в производственной среде. Например, вы не должны регистрировать информацию о кредитной карте, личную информацию пользователя и т.д. Ну вы поняли. Потому что, если эти журналы утекут, они могут быть использованы хакерами.

Встроенный пакет ведения журнала Go

Go предоставляет разработчикам встроенный пакет ведения журнала. Его можно использовать для простой записи чего-либо в стандартный вывод или в файл.

Простой пример ведения журнала

В следующем коде показано ведение журнала с помощью встроенной библиотеки ведения журнала Go.

package main

import "log"

func main() {
 log.Println("This is logging!")
}

Приведенный выше код выводит на терминал следующий результат. Вы можете видеть, что вместе с сообщением журнала есть информация о дате и времени.

2022/01/08 14:17:45 This is logging!

Вы также можете записывать сообщения в файл! Скопируйте следующий код и запустите.

package main

import (
 "log"
 "os"
)

func main() {
  logFile := openLogFile()
  defer logFile.Close()
 
  log.SetOutput(logFile)
  log.Println("This is logging!")
}

func openLogFile() *os.File {
  f, err := os.OpenFile("access.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
  if err != nil {
    log.Fatal(err)
  }
 
  return f
}

Приведенный выше код создает файл с именем access.log. Файл будет содержать сообщение "2022/01/08 14:22:51 This is logging!".

Что не так со встроенной библиотекой Go?

Одним из основных недостатков встроенной библиотеки ведения журналов является то, что она слишком проста. Это хорошо для быстрой разработки, когда разработчик хочет увидеть результат как можно быстрее. Но когда дело доходит до производственной среды. Обычно нам нужно что-то более структурированное, чем простые текстовые сообщения.

Вы можете спросить. «Что значит что-то более структурированное?» Мы можем структурировать журналы, классифицируя их по уровням, что обычно называется ведением журналов на основе уровней.

Что такое ведение журнала на основе уровней?

Приложение обычно выводит много типов информации для журнала. Могут быть отладочные сообщения, ошибки, предупреждения, общая информация и т.д. Вся эта информация, которую мы видим перед собой, может вызвать проблемы, когда дело доходит до отладки или обнаружения ошибок. К счастью, существует концепция приоритизации сообщений журнала по различным уровням важности. Когда журналы классифицируются по уровням, мы можем отфильтровать их, чтобы увидеть только те уровни, которые мы хотим проанализировать.

Например, сообщение об ошибке может иметь более высокий приоритет, чем предупреждающее сообщение, тогда мы могли бы отфильтровать все предупреждения, чтобы увидеть только ошибки.

Введение в молниеносно быстрое Uber Zap, структурированное, ведение журналов на основе уровней в Go

Zap — проект с открытым исходным кодом, разработанный Uber. Это фреймворк для ведения журнала, который работает «молниеносно быстро». Вы можете увидеть информацию о производительности и тестах в репозитории.

Zap поддерживает семь типов уровней ведения журнала: Debug, Info, Warning, Error, DPanic, Panic и Fatal. Описание каждого уровня приведено ниже.

const ( 
	// Журналы DebugLevel, как правило, объемные и обычно отключаются 
	// в рабочей среде. 
	DebugLevel = zapcore.DebugLevel
    
 	// InfoLevel — это приоритет ведения журнала по умолчанию. 
	InfoLevel = zapcore.InfoLevel
    
 	// Журналы WarnLevel более важны, чем Info 
	// но не требуется индивидуальная проверка человеком 
	WarnLevel = zapcore.WarnLevel
    
	// Журналы ErrorLevel имеют высокий приоритет.  
	// Если приложение работает нормально,оно не должно генерировать журналы ошибок
	ErrorLevel = zapcore.ErrorLevel
    
	// Журналы DPanicLevel содержат особенно важные ошибки.  
	// При разработке регистратор паникует после записи сообщения. 
	DPanicLevel = zapcore.DPanicLevel
    
 	// PanicLevel регистрирует сообщение, затем вызывает панику. 
	PanicLevel = zapcore.PanicLevel
    
 	// FatalLevel регистрирует сообщение, затем вызывает os.Exit(1). 
	FatalLevel = zapcore.FatalLevel
 )

Пример ведения журнала с помощью Zap

Отказ от ответственности: я не буду вдаваться в подробности о том, как использовать пакет. Я хочу показать несколько основных примеров того, что вы можете делать с Zap. Подробную документацию вы можете найти здесь.

Ведение журнала с помощью предустановленного регистратора

Обычно вам нужно настроить регистратор перед его использованием. Что, если мы просто хотим использовать его прямо сейчас? К счастью, Zap предлагает предустановленный регистратор для удобного использования. См. пример кода ниже.

package main

import (
 "log"
 "os"
 "go.uber.org/zap"
)

func main() {
  logger, _ := zap.NewDevelopment()
  defer logger.Sync()
 
  logger.Info("Hello Zap!")
  logger.Warn("Beware of getting Zapped! (Pun)")
  logger.Error("I'm out of Zap joke!")
}

Из приведенного выше кода zap.NewDevelopment() возвращается предустановленный регистратор разработки, который печатает сообщения в удобочитаемом формате. После этого мы используем методы Info, Warn и Error для вывода различных уровней журнала. При выполнении кода вы получите следующий результат.

2022-01-08T15:33:54.504+0700    INFO    logging-zap/main.go:13  Hello Zap!
2022-01-08T15:33:54.504+0700    WARN    logging-zap/main.go:14  Beware of getting Zapped! (Pun)
main.main
        /home/siraphob-wsl-ubuntu/go/src/github.com/copsterr/logging-zap/main.go:14
runtime.main
        /usr/local/go/src/runtime/proc.go:255
2022-01-08T15:33:54.504+0700    ERROR   logging-zap/main.go:15  I'm out of Zap joke!
main.main
        /home/siraphob-wsl-ubuntu/go/src/github.com/copsterr/logging-zap/main.go:15
runtime.main
        /usr/local/go/src/runtime/proc.go:255

Для INFO печатается обычное сообщение журнала. Он содержит отметку времени, уровень журнала, местоположение кода и сообщение.

Для WARN и ERROR они также распечатывают трассировку стека. Это полезно для определения места возникновения ошибки.

Настройка регистратора

Иногда предустановленные регистраторы не соответствуют вашим потребностям. Zap позволяет настроить собственный регистратор. Вы можете определить конфигурацию регистратора в файле JSON или YAML, а затем проанализировать его в регистраторе Zap. Пример показан ниже.

package main

import (
 "encoding/json"
 "log"
 "os"
 "go.uber.org/zap"
)

func main() {
 rawJSON := []byte(`{
   "level": "debug",
   "encoding": "json",
   "outputPaths": ["stdout"],
   "errorOutputPaths": ["stderr"],
   "encoderConfig": {
     "messageKey": "message",
     "levelKey": "level",
     "levelEncoder": "lowercase"
   }
 }`)
 
 var cfg zap.Config
 
 if err := json.Unmarshal(rawJSON, &cfg); err != nil {
  panic(err)
 }
 
 logger, err := cfg.Build()
 
 if err != nil {
  panic(err)
 }
 
 defer logger.Sync()
 
 logger.Info("Hi, custom logger!")
 logger.Warn("Custom logger is warning you!")
 logger.Error("Let's do error instead.")
}

Приведенный выше код дает следующий результат.

{"level":"info","message":"Hi, custom logger!"}
{"level":"warn","message":"Custom logger is warning you!"}
{"level":"error","message":"Let's do error instead."}

Как вы можете видеть, регистратор создает выходные данные в JSON, поэтому было бы лучше, если бы мы поместили их в файл журнала. Мы можем добиться этого, изменив конфигурацию регистратора, как показано ниже.

rawJSON := []byte(`{
   "level": "debug",
   "encoding": "json",
   "outputPaths": ["stdout, "/my.log"],       <-- This line
   "errorOutputPaths": ["stderr", "/my.log"], <-- This line
   "encoderConfig": {
     "messageKey": "message",
     "levelKey": "level",
     "levelEncoder": "lowercase"
   }
 }`)

Из приведенного выше фрагмента вы можете видеть, что ключ outputPaths и errorOutputPaths содержит в качестве одного из элементов массива значение файла my.log. Когда вы выполните код, он создаст my.log файл с сообщениями журнала.

Это только вершина айсберга. Вы можете сделать гораздо больше с Zap. Ознакомьтесь с их документацией для получения дополнительной информации.

Резюме

Ведение журнала позволяет разработчикам обнаруживать баги и ошибки. Ведение журнала на основе уровней помогает фильтровать только необходимую информацию. В этой статье я показал вам, как войти с помощью Uber Zap. Тем не менее, это может быть не лучший регистратор, который соответствует вашим потребностям. Существует множество фреймворков для ведения журналов, из которых вы можете выбирать. Правильное использование регистратора может улучшить качество программного обеспечения и повысить производительность процесса разработки. Я надеюсь, что эта статья даст вам некоторые идеи о том, как вести журнал в вашем проекте. Удачного кодирования!

Источник: https://medium.com/codex/level-based-logging-in-go-with-uber-zap-a8a90aa40672