На данный момент в файле main.go мы выводим лог с помощью функций log.Printf() и log.Fatal().

В Go, обе эти функции выводят сообщения через стандартный логгер, который по умолчанию добавляет к сообщениям префиксы с локальной датой и временем и записывает их в стандартный поток ошибок (который должен отображаться в окне терминала). Функция log.Fatal() также вызовет os.Exit(1) после того как выведет в терминал сообщение об ошибке, это приведет к мгновенному завершению работы приложения.

Премиум 👑 канал по Golang

Рекомендуем вам супер TELEGRAM канал по Golang где собраны все материалы для качественного изучения языка. Удивите всех своими знаниями на собеседовании! 😎

Подписаться на канал

Уроки, статьи и Видео

Мы публикуем в паблике ВК и Telegram качественные обучающие материалы для быстрого изучения Go. Подпишитесь на нас в ВК и в Telegram. Поддержите сообщество Go программистов.

Go в ВК ЧАТ в Telegram


Содержание статьи

Логгирование можно поделить на два различных типа, или уровня. К первому типу относятся информационные сообщения (вроде «Запуск сервера на :4000«), а ко второму типу относятся сообщения об ошибках.

Давайте усовершенствуем наше приложение, добавив возможность многоуровнего логирования, чтобы информационные сообщения и сообщения об ошибках обрабатывались по-разному. А именно:

  • Информационным сообщениям добавим префикс "INFO". Такое сообщение будет выводиться в стандартный поток вывода (stdout);
  • Сообщениям об ошибках добавим префикс "ERROR". Такие сообщения будут выводиться в стандартный поток ошибок (stderr) вместе с соответствующим названием файла и номером строки, которая вызвала логгер для записи (это поможет в отладке на будущее).

Есть несколько способов использования разных логгеров, но самый простой и понятный подход заключается в использовании функции log.New() для создания двух новых настраиваемых логгеров.

Откройте файл main.go и обновите его код следующим образом:

Отлично… проверим эти изменения в действии!

Попробуйте запустить приложение, затем откройте другое окно терминала и попробуйте запустить его во второй раз. В результате должна появится сообщение об ошибке, потому что сетевой адрес, который наш сервер хочет прослушать (":4000"), уже используется другим процессом.

Логи во втором терминале должны выглядеть следующим образом:

Обратите внимание, что у этих двух сообщений разные префиксы — чтобы их можно было легко различить в терминале — и наше сообщение об ошибке также включает в себя название файла и номер строки (main.go:37), которая вызывает логгер для записи возникнувшей ошибки.

На заметку: Если вы хотите включить весь путь файла в лог  вместо просто названия файла, при создании логгера можно использовать флаг log.Llongfile вместо log.Lshortfile. Вы также можете заставить свой логгер  использовать UTC дату (вместо локальной), добавив флаг log.LUTC.

Разделение логирования для разных задач

Большое преимущество логирования сообщений в стандартных потоках (stdout и stderr), как у нас, заключается в том, что само приложение и логирование не связаны. Само приложение не занимается маршрутизацией или хранением логов, и это может упростить управление логами, которое будет различаться в зависимости от среды.

Стандартные потоки отображаются в терминале, поэтому вывод логов можно легко посмотреть после запуска приложения из терминала.

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

Например, можно перенаправить потоки из stdout и stderr в файлы на диске при запуске приложения из терминала следующим образом:

Логирование ошибок от HTTP-сервера

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

Нам требуется инициализировать новую структуру http.Server, содержащую параметры конфигурации для сервера, вместо использования http.ListenAndServe().

Проще всего будет показать это на примере:

Дополнительные методы логирования

До сих пор мы использовали методы Println(), Printf() и Fatal() для записи логов. Однако Go предоставляет ряд других методов, с которыми стоит ознакомиться.

Как правило, лучше избегать использования методов Panic() и Fatal() за пределами функции main(). Вместо этого рекомендуется возвращать возникшие ошибки, а паниковать или принудительно завершать приложение непосредственно из самого main().

Конкурентное логирование в Golang

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

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

Логирование сообщений в файл

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

Скачать исходный код сайта

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

Скачать: snippetbox-11.zip