Skip to main content
The log package implements simple logging. It provides basic logging functions and customizable loggers.

Basic Logging

import "log"

func main() {
    log.Print("This is a message")
    log.Printf("User %s logged in", "alice")
    log.Println("Server started")
    
    // Fatal logs and calls os.Exit(1)
    // log.Fatal("Critical error")
    
    // Panic logs and calls panic()
    // log.Panic("Panic message")
}

Custom Logger

import (
    "log"
    "os"
)

func createLogger() *log.Logger {
    return log.New(os.Stdout, "[APP] ", log.LstdFlags)
}

func main() {
    logger := createLogger()
    logger.Println("Custom logger message")
}

Log Flags

const (
    Ldate         = 1 << iota // 2009/01/23
    Ltime                     // 01:23:23
    Lmicroseconds             // 01:23:23.123123
    Llongfile                 // /a/b/c/d.go:23
    Lshortfile                // d.go:23
    LUTC                      // Use UTC times
    Lmsgprefix                // Move prefix to message line
    LstdFlags     = Ldate | Ltime
)

func configureLogger() {
    log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
    log.SetPrefix("[MyApp] ")
}

File Logging

func logToFile(filename string) error {
    f, err := os.OpenFile(filename,
        os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        return err
    }
    defer f.Close()
    
    log.SetOutput(f)
    log.Println("This goes to file")
    return nil
}

Multiple Loggers

var (
    Info    *log.Logger
    Warning *log.Logger
    Error   *log.Logger
)

func initLoggers() {
    Info = log.New(os.Stdout, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile)
    Warning = log.New(os.Stdout, "WARNING: ", log.Ldate|log.Ltime|log.Lshortfile)
    Error = log.New(os.Stderr, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)
}

func main() {
    initLoggers()
    
    Info.Println("Application started")
    Warning.Println("Disk space low")
    Error.Println("Failed to connect")
}

Practical Examples

HTTP Request Logger

type LoggingHandler struct {
    handler http.Handler
    logger  *log.Logger
}

func NewLoggingHandler(h http.Handler) *LoggingHandler {
    return &LoggingHandler{
        handler: h,
        logger:  log.New(os.Stdout, "[HTTP] ", log.LstdFlags),
    }
}

func (h *LoggingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    h.logger.Printf("%s %s %s", r.Method, r.URL.Path, r.RemoteAddr)
    h.handler.ServeHTTP(w, r)
}

Structured Logging

type StructuredLogger struct {
    logger *log.Logger
}

func NewStructuredLogger() *StructuredLogger {
    return &StructuredLogger{
        logger: log.New(os.Stdout, "", 0),
    }
}

func (sl *StructuredLogger) Log(level, msg string, fields map[string]interface{}) {
    entry := map[string]interface{}{
        "level": level,
        "msg":   msg,
        "time":  time.Now().Format(time.RFC3339),
    }
    
    for k, v := range fields {
        entry[k] = v
    }
    
    data, _ := json.Marshal(entry)
    sl.logger.Println(string(data))
}

log/slog (Go 1.21+)

New structured logging package.
import "log/slog"

func useSlog() {
    slog.Info("User logged in", "user", "alice", "ip", "192.168.1.1")
    slog.Warn("High memory usage", "percent", 85)
    slog.Error("Database error", "error", err)
}

// JSON output
func jsonLogger() {
    logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
    logger.Info("Application started", "version", "1.0.0")
}

Best Practices

  1. Use appropriate levels - Info for general messages, Error for errors
  2. Include context - Add relevant information to log messages
  3. Don’t log sensitive data - Avoid passwords, tokens, etc.
  4. Configure output - Set appropriate flags and prefix
  5. Use structured logging - For production applications
  6. Rotate log files - Prevent unbounded growth
  7. Handle errors - Check errors when opening log files

Build docs developers (and LLMs) love