Logging in Go Applications: Best Practices in Practice

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Why Logging in Go is Not Just fmt.Println



Logging is the eyes and ears of any application running in production. In the world of Go (Golang), the approach to logging has undergone significant evolution: from simple console output via fmt.Println to powerful structured loggers capable of producing JSON-compatible records for centralized log collection systems (ELK, Grafana Loki, Datadog).



In this article, we will break down best practices for logging in Go in practice, not in theory. You will learn how to configure logging levels, use structured logs, avoid "noise," and handle errors correctly. All code examples are ready to use in your projects.



1. The Standard log Library: When It's Enough and When It's Not



Go provides a built-in log package from the standard library. It is simple but has significant limitations.



Basic Example

package main

import ( "log" "os")

func main() { // Configuring output to a file file, _ := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) defer file.Close() log.SetOutput(file)

log.Println("Application started") log.Printf("User %s authorized", "admin") log.Fatal("Critical error: failed to connect to DB")}


Pros:

  • No need to install third-party dependencies.
  • Simplicity of use for small scripts or CLI utilities.


Cons:

  • No logging levels (only Print, Fatal, Panic).
  • Fixed output format (date, time, message).
  • No structured fields (key-value).
  • Poorly parsed by log collection systems.


Conclusion: for production services, microservice architecture, or any application requiring monitoring, the standard log is not suitable. Switch to structured logging.



2. Structured Logging with zerolog: The Modern Standard



The zerolog library is one of the most popular and fastest loggers in Go. It supports logging levels, JSON output by default, and minimal memory allocation.



Installation

go get -u github.com/rs/zerolog/log


Configuration and Usage Example

package main

import ( "os" "time"

"github.com/rs/zerolog" "github.com/rs/zerolog/log")

func main() { // Configuring time format zerolog.TimeFieldFormat = time.RFC3339

// Console output with colors (for development) log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339})

// For production — JSON to stdout // log.Logger = zerolog.New(os.Stdout).With().Timestamp().Logger()

log.Info().Msg("Server started on port 8080") log.Debug().Str("module", "auth").Msg("User login attempt") log.Error().Err(err).Str("user_id", "12345").Msg("Authorization error") log.Fatal().Int("attempt", 3).Msg("Maximum number of attempts exceeded")}


Logging Levels in zerolog

  • Trace (log.Trace()) — for deep debugging.
  • Debug (log.Debug()) — debug information.
  • Info (log.Info()) — normal events.
  • Warn (log.Warn()) — warnings (not errors).
  • Error (log.Error()) — errors requiring attention.
  • Fatal (log.Fatal()) — fatal errors (terminates the program).
  • Panic (log.Panic()) — panic with stack trace output.


Best practice: in production, set a minimum

Blogs

Book Recommendations