Domenico Mastrangelo

Software Engineer / DevOps Engineer

Rust, Golang, Azure, Docker, Kubernetes, Terraform, Github

Enhancing Go Applications with Structured Logging: An Introduction to slog
Leveraging the New Standard Library Package for Improved Debugging and Monitoring

Introduction to slog

The slog package, introduced in Go 1.21, brings structured logging into the Go standard library. It allows developers to log key-value pairs, making logs more searchable and semantically rich. This is particularly useful for applications where logs need to be parsed and analyzed through log analyzers like Datadog, AWS Cloudwatch, or Splunk.

By providing a standardized way to structure data within logs, slog ensures that critical information is easily accessible and can be efficiently processed by these tools, enhancing the observability and maintainability of complex systems..

Benefits of Using slog

slog provides several benefits over traditional logging approaches, including better performance, structured data, and support for context-based logging. It makes it easier to filter and search logs, which is invaluable in identifying issues in production environments.

Getting Started with slog

To use slog in your Go application, you first need to import the slog package. After that, you can start logging messages with various severity levels such as Info, Error, and Debug.

package main

import (
    "log/slog"
    "os"
)

func main() {
    // Create a new JSON logger
    logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
        // Set the base level that will be logged
        // This could be configured based on the environment
        Level:     slog.LevelInfo,

        // Set the filename and line number of the caller
        AddSource: true,

        // Replace a specific attribute with a new value
        // Useful for sanitizing sensitive data
        ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
            if a.Key == "level" {
                return slog.Attr{
                    Key:   "level",
                    Value: slog.StringValue("test123"),
                }
            }
            return a
        },
    }))

    logger.Info("application started", "version", "1.0.0")
    logger.Error("failed to load configuration", "path", "/etc/app/config", "reason", "file not found")
}

https://go.dev/play/p/axqY0dJ95lO

Result

{
    "time": "2009-11-10T23:00:00Z",
    "level": "test123",
    "source": {
        "function": "main.main",
        "file": "/tmp/sandbox514877305/prog.go",
        "line": 23
    },
    "msg": "application started",
    "version": "1.0.0"
}
{
    "time": "2009-11-10T23:00:00Z",
    "level": "test123",
    "source": {
        "function": "main.main",
        "file": "/tmp/sandbox514877305/prog.go",
        "line": 24
    },
    "msg": "failed to load configuration",
    "path": "/etc/app/config",
    "reason": "file not found"
}   

Structured Logging with slog

Structured logging is one of the key features of slog. It allows you to log messages with associated key-value pairs, providing more context for each log entry.

logger.Info("user logged in", "user", "johndoe", "method", "oauth")

Contextual Logging

slog also supports contextual logging, where you can create a new logger from an existing one, carrying over context-specific fields. This is particularly useful for tracing requests through microservices.

requestLogger := logger.With("request_id", "12345")
requestLogger.Info("request processed", "status", "success")

Conclusion

The slog package significantly improves logging in Go applications by introducing structured, leveled, and context-aware logging. By using slog, developers can produce more meaningful, searchable, and actionable logs, leading to more maintainable and debuggable applications.