Введение

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

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

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

Базовое ведение журнала в Golang

Golang поставляется со встроенным стандартным пакетом log, который обеспечивает основные функции регистрации ошибок. Хотя он не предоставляет никаких уровней ведения журнала, таких как debug, warning или error, он по-прежнему имеет множество функций для начала работы с базовым ведением журнала.
Существует пять функций печати — Print(), Printf(), PrintIn(), Fatal() и Fatalf(), а также четыре уровня серьезности — ИНФО, ПРЕДУПРЕЖДЕНИЕ, ОШИБКА и FATAL.
Функции `Print` записывают сообщения журнала с серьезностью INFO, а функции `Fatal` записывают сообщения с серьезностью `FATAL`. Соответственно , мы можем выполнить функции «Printf» или «Fatalf» для форматирования сообщений журнала с переменными и другими данными. Функция «PrintIn» записывает сообщение, за которым следует символ новой строки, на консоль или в стандартный вывод.

Давайте посмотрим на пример, чтобы понять это лучше.

package main

import "log"

func main() {
    log.Println("Welcome to logging in Golang!!")
}

Когда приведенный выше код выполняется, пакет log печатает выходные данные в поток standard error (stderr) и автоматически добавляет метку времени к каждому сообщению журнала.

Вход в файл в Golang

Несмотря на то, что пакет log по умолчанию выводит поток stderr, его можно настроить для записи в любой локальный файл или в любое другое место, которое принимает интерфейс io.Writer. Вы должны либо создать новый файл, либо открыть существующий и указать его в качестве выходного пути журнала, чтобы сохранять сообщения журнала в файле.

package main

import (
    "log"
    "os"
)

func main() {

    file, err := os.OpenFile("appLogs.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
    if err != nil {
        log.Fatal(err)
    }

    log.SetOutput(file)

    log.Println("Welcome to logging in Golang!! Writing logs into a log file.")
}

В приведенном выше коде мы создали файл appLogs.txt или открыли его, если он уже существует, в режиме добавления и только для записи. Когда приведенный выше код выполняется, в файл appLogs.txt записывается следующий вывод:

Написание пользовательских журналов в Golang

Метод log.New() можно использовать для создания пользовательских регистраторов. При использовании функции log.New() в пользовательский регистратор должны быть отправлены три аргумента:

  1. out - Указывает место, куда должны быть записаны данные журнала, например, путь к файлу. Это может быть любой интерфейс, реализующий интерфейс io.Writer.
  2. prefix - строка или текст, который должен быть добавлен в начале каждой строки лога.
  3. flag — это наборы констант, которые позволяют нам определять свойства журналирования.
package main
import (
    "log"
    "os"
)

var (
    WarningLog *log.Logger
    InfoLog   *log.Logger
    ErrorLog   *log.Logger
)

func init() {
    logFile, err := os.OpenFile("appLogs.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
    if err != nil {
        log.Fatal(err)
    }

    InfoLog = log.New(logFile, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile)
    WarningLog = log.New(logFile, "WARNING: ", log.Ldate|log.Ltime|log.Lshortfile)
    ErrorLog = log.New(logFile, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)
}

func main() {
    InfoLog.Println("Starting the application...")
    InfoLog.Println("Something has occurred...")
    WarningLog.Println("WARNING!!!")
    ErrorLog.Println("Some error has occurred...")
}

С помощью destination file path, prefix string и flags, представленных в приведенном выше коде, были сгенерированы три журнала. Эти регистраторы используют метод println() в основной функции для записи записей журнала в файл.

Когда приведенный выше код выполняется, мы получаем следующий вывод, записанный в файл appLogs.txt.

Обработка/регистрация panic() в Golang

Функция panic() в языке Go аналогична исключениям, возникающим во время выполнения при возникновении ошибки. panic() либо вызывается самой программой, когда возникает непредвиденная ошибка, либо программист специально создает исключение для обработки конкретных ошибок.

package main
import "fmt"

// recover function to handle panic
func handlePanic() {

  // detect if panic occurs or not
  a := recover()

  if a != nil {
    fmt.Println("RECOVER ", a)
  }

}

func division(num1, num2 int) {

  // execute the handlePanic even after panic occurs
  defer handlePanic()

  // if num2 is 0, program is terminated due to panic
  if num2 == 0 {
    panic("Cannot divide a number by zero")
  } else {
    result := num1 / num2
    fmt.Println("Result: ", result)
  }
}

func main() {
  division(4, 2)
  division(8, 0)
  division(2, 8)
}

Здесь обратите внимание на две вещи:

  • Мы вызываем handlePanic() перед возникновением паники. Это потому, что программа будет завершена, если она обнаружит панику, и мы хотим выполнить handlePanic() до завершения.
  • Мы используем defer для вызова handlePanic(). Это потому, что мы хотим обработать панику только после ее возникновения, поэтому мы откладываем ее выполнение.

Фреймворки ведения журналов

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

  • Зап
  • Зеролог
  • Логрус
  • вершина/журнал
  • Журнал 15

Я надеюсь, что пост был полезен для понимания ведения журнала в Golang.