Logging in Go Applications: Best Practices and Tools

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

Logging is not just recording events to a file. For Go applications running in high-load production environments, logs are the primary source of diagnostic information. Without quality logging, debugging turns into guesswork, and system monitoring becomes impossible.

Go offers developers flexible tools: from the built-in log package to powerful third-party libraries. In this article, we will analyze best practices for logging in Go, learn how to structure records, choose levels, and avoid typical mistakes. The material will be useful for both beginners and experienced developers looking to organize their approach to logging.



Standard log library: basic capabilities and limitations

The log package from the Go standard library is the simplest way to start logging. It provides a ready-made logger that writes to os.Stderr.



Simple usage example

package main

import ( "log")

func main() { log.Println("Application started") log.Printf("User %s logged in", "admin") log.Fatal("Critical error: database unavailable")}

The output will look like this:

2023/10/27 14:35:12 Application started2023/10/27 14:35:12 User admin logged in2023/10/27 14:35:12 Critical error: database unavailable


Limitations of the standard logger

  • Lack of log levels — no separation into Info, Warn, Error. All messages are written to a single stream.
  • Poor structure — the record is a simple string that is difficult to parse automatically.
  • Lack of context — it's not easy to add a request or user identifier.
  • Performance issues — the standard logger blocks the goroutine during writing, which can become a bottleneck.

For small utilities or prototypes, log is perfectly suitable. But for production applications, it's worth considering alternatives.



Structured logging: why is it important?

Structured logging is an approach where each message represents a set of key-value pairs (usually in JSON format). This allows:

  • Automatically parsing logs in collection systems (ELK, Splunk, Grafana Loki).
  • Filtering records by any attributes (level, service, request ID).
  • Building dashboards and alerts based on numeric fields.


Popular libraries for structured logging

  • logrus — one of the first and most popular libraries. Provides levels, formatting (JSON, text), hooks for sending logs to external systems. However, it has high memory consumption.
  • zerolog — a library focused on performance (zero allocation). Great for microservices and high-load systems.
  • zap — from Uber. Very fast, with two modes: SugaredLogger (convenient) and Logger (maximum speed).
  • slog — a standard package added in Go 1.21. Provides structured logging out of the box.


Example using slog (Go 1.21+)

package main

import ( "log/slog" "os")

func main() { logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) logger.Info("Server started", "port", 8080, "env", "production") logger.Error("Database connection error", "db", "postgres", "timeout", 5)}

JSON output:

{"time":"2023-10-27T14:40:00.123Z","level":"INFO","msg":"Server started","port":8080,"env":"production"}{"time":"2023-10-27T14:40:01.456Z","level":"ERROR","msg":"Database connection error","db":"postgres","t                

Blogs

Book Recommendations