Логирование необходимо любому приложению. Встроенный модуль логирования имеется практически во всех современных языках. И Golang не исключение: модуль log используется прямо «из коробки».

fmt и log

В модуле log имеется три типа диагностических сообщений:

  1. Print  —  запись диагностических сообщений.
  2. Panic  —  запись диагностических сообщений и выполнение функции panic().
  3. Fatal  —  запись диагностических сообщений и завершение работы с кодом.

Хотя назначение этих сообщений и функций Print, Panic, Fatal в fmt одинаковое, различия имеются:

package main

import (
"fmt"
"log"
)

func main() {

fmt.Print("First print statement")
fmt.Printf("Second print statement")
fmt.Println("Third print statement")

log.Print("First Log message")
log.Printf("Second Log message")
log.Println("Third Log message")

l := log.Default()
l.Print("First Log message")
l.Printf("Second Log message")
l.Println("Third Log message")

}

// Вывод
// Первый оператор печатиВторой оператор печатиТретий оператор печати
// 2022/12/26 20:21:36 Первое диагностическое сообщение
// 2022/12/26 20:21:36 Второе диагностическое сообщение
// 2022/12/26 20:21:36 Третье диагностическое сообщение
// 2022/12/26 20:21:36 Первое диагностическое сообщение
// 2022/12/26 20:21:36 Второе диагностическое сообщение
// 2022/12/26 20:21:36 Третье диагностическое сообщение

Итак, у модуля log то же назначение, что и у функций print/panic/fatal модуля fmt. Первое отличие: модулем log в конце всегда добавляется символ новой строки, если его нет. Именно поэтому все три оператора печати fmt оказались в одной строке, а все диагностические сообщения  —  в разных.

Поэтому в модуле log функции Print и Println эквивалентны. Их реализации тоже абсолютно одинаковы.

Другое очевидное отличие: в начало модулем log добавляется информация о логе. По умолчанию это дата и время.

Настройка формата логов

package main

import (
"log"
"os"
)

func main() {
InfoLog := log.New(os.Stdout, "Info: ", log.Ldate|log.Ltime|log.Lmicroseconds|log.Lshortfile)
InfoLog.Print("First Log message")

DebugLog := log.New(os.Stdout, "Debug: ", log.Ldate|log.Ltime|log.Llongfile)
DebugLog.Print("First Log message")

ErrorLog := log.Default()
ErrorLog.SetPrefix("Error: ")
ErrorLog.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds | log.Lshortfile)
f, err := os.OpenFile("error.log", os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
panic(err)
}
defer f.Close()
ErrorLog.SetOutput(f)
ErrorLog.Print("First Log message")
}

Вывод в файл error.log:

Ошибка: 2022/12/26 21:50:22.715153 main.go:23: Первое диагностическое сообщение

Вывод в STDOUT, стандартный вывод терминала:

Информация: 2022/12/26 21:50:22.715100 main.go:10: Первое диагностическое сообщение
Отладка: 2022/12/26 21:50:22 /home/golang/logger/main.go:13: Первое диагностическое сообщение

В первом объекте log при помощи New создан новый объект журнала и присвоен переменной InfoLog. Функции New требуется три аргумента:

  1. os.Stdout  —  этим определяется, что лог запишется в терминал.
  2. “Info:”  —  это строковое значение, которое добавляется в начало лог-инструкции.
  3. log.Ldate|log.Ltime|log.Lmicroseconds|log.Lshortfile  —  этим определяется все, что между “Info:” и сообщением. Здесь генерируется значение 2022/12/25 21:50:22.715100 main.go:10.

Аналогично DebugLog инициализируется при помощью New с похожими аргументами.

Все аргументы, передаваемые в New, задаются отдельно при помощи функций SetOutput, SetPrefix и SetFlags. Этими же функциями задаются пользовательские значения в ErrorLog, в котором “error.log” задается лог-файлом.

f, err := os.OpenFile("error.log", os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
panic(err)
}
ErrorLog.SetOutput(f)

Функции SetOutput требуется объект типа io.Writer. То есть используется объект любого type, которым реализованы функции Write. Из os.OpenFile возвращается объект *os.File, соответствующий интерфейсу io.Writer.

Заключение

Настройка логов  —  фундаментальная, неотъемлемая часть программирования. В примерах мы показали, что встроенными модулями log эта задача решается быстро.

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Naitik Gala: Writing logs in Golang

Предыдущая статья17 полезных скриптов автоматизации на Python. Часть 1
Следующая статьяVPR — первый процессор на RISC-V от Nordic