Logging

Back

Loading concept...

📝 Logging in Go: Your Program’s Diary

The Story of the Detective’s Notebook

Imagine you’re a detective solving mysteries. Every time something happens—a door opens, a person enters, a clue appears—you write it down in your notebook. Why? Because later, when you need to figure out what went wrong (or right!), your notes tell the whole story.

That’s exactly what logging is for your Go programs!

Your program is like a busy restaurant kitchen. Things happen fast. Orders come in, dishes go out, sometimes things burn. Without a diary (logs), you’d never know what happened at 2:47 PM when Customer #42’s order went missing!


🎯 What You’ll Learn

  1. Basic Logging — Writing simple notes to track what your program does
  2. Structured Logging — Organized notes that computers (and humans) can search easily

Part 1: Basic Logging — Your First Diary Entries

What is Basic Logging?

Think of basic logging like writing in a simple diary:

“Monday 9am: Woke up. Had breakfast. Went to school.”

Simple. Easy. Gets the job done!

In Go, we use the log package that comes built-in. It’s like a free notebook that every Go program gets!

Your First Log Message

package main

import "log"

func main() {
    log.Println("Hello! I started!")
    log.Println("Doing some work...")
    log.Println("All done! Goodbye!")
}

What happens? Your program writes three notes with timestamps:

2024/01/15 09:00:01 Hello! I started!
2024/01/15 09:00:01 Doing some work...
2024/01/15 09:00:01 All done! Goodbye!

See that date and time? Go adds it automatically, like magic! 🪄

The Three Musketeers of Basic Logging

Go gives you three main ways to write logs:

// 1. Print - Just writes a message
log.Print("Something happened")

// 2. Println - Writes message + new line
log.Println("Something happened")

// 3. Printf - Writes formatted message
name := "Alice"
log.Printf("User %s logged in", name)

Think of them like:

  • Print = Writing on the same line
  • Println = Writing and starting a new line
  • Printf = Writing with blanks to fill in (like Mad Libs!)

When Things Go Wrong — Fatal and Panic

Sometimes things go SO wrong that your program can’t continue. Like a restaurant running out of all food!

// Fatal - Logs message, then STOPS program
log.Fatal("No database! Can't continue!")

// Panic - Logs message, then CRASHES (but can recover)
log.Panic("Something unexpected happened!")

⚠️ Warning: Use these only for SERIOUS problems. It’s like pulling the fire alarm—don’t do it for small stuff!

Saving Logs to a File

By default, logs appear on screen. But what if you want to save them forever? Like putting your diary in a safe!

package main

import (
    "log"
    "os"
)

func main() {
    // Create a log file
    file, err := os.Create("app.log")
    if err != nil {
        log.Fatal("Can't create log file!")
    }
    defer file.Close()

    // Tell Go: "Write logs here!"
    log.SetOutput(file)

    log.Println("This goes to the file!")
}

Adding Extra Info to Logs

Want your logs to show more details? Like writing not just WHAT happened, but WHERE?

// Add file name and line number
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)

log.Println("Where am I?")
// Output: 2024/01/15 09:00:01 main.go:15: Where am I?

Here are the flags you can use:

Flag What it adds Example
Ldate Date 2024/01/15
Ltime Time 09:00:01
Lmicroseconds Tiny time details .123456
Lshortfile File + line main.go:15
Llongfile Full path + line /home/user/main.go:15

Creating Your Own Logger

What if you want DIFFERENT diaries for different things? One for errors, one for regular stuff?

package main

import (
    "log"
    "os"
)

func main() {
    // Error logger - shows [ERROR] prefix
    errorLog := log.New(
        os.Stderr,
        "[ERROR] ",
        log.Ldate|log.Ltime|log.Lshortfile,
    )

    // Info logger - shows [INFO] prefix
    infoLog := log.New(
        os.Stdout,
        "[INFO] ",
        log.Ldate|log.Ltime,
    )

    infoLog.Println("Starting app...")
    errorLog.Println("Something went wrong!")
}

Output:

[INFO] 2024/01/15 09:00:01 Starting app...
[ERROR] 2024/01/15 09:00:01 main.go:20: Something went wrong!

Part 2: Structured Logging — The Smart Filing System

The Problem with Basic Logging

Imagine your diary has 10,000 pages. Someone asks: “Find all entries where Alice did something with error code 500.”

With basic logging, you’d have to read EVERY page! 😫

2024/01/15 09:00:01 User Alice got error 500 on /home
2024/01/15 09:01:23 Bob visited /about
2024/01/15 09:02:45 Error 404 for user Charlie

Good luck searching that mess!

Enter Structured Logging! 🦸‍♂️

Structured logging is like having a SUPER organized filing system. Instead of messy sentences, you get clean data:

{
  "time": "2024-01-15T09:00:01Z",
  "level": "error",
  "user": "Alice",
  "code": 500,
  "path": "/home",
  "message": "Page failed to load"
}

Now a computer can INSTANTLY find all entries where user = "Alice" AND code = 500!

Go’s Built-in slog Package (Go 1.21+)

Go now has a built-in structured logger called slog. It’s like getting a premium filing cabinet for free!

package main

import "log/slog"

func main() {
    slog.Info("User logged in",
        "user", "Alice",
        "ip", "192.168.1.1",
    )
}

Output:

2024/01/15 09:00:01 INFO User logged in user=Alice ip=192.168.1.1

See how organized that is? Each piece of info is labeled!

Log Levels — Importance Tags

Not all logs are equally important. Structured logging uses LEVELS:

slog.Debug("Tiny detail for developers")
slog.Info("Normal, everything's fine")
slog.Warn("Hmm, this might be a problem")
slog.Error("Something went wrong!")

Think of it like:

  • Debug = 📝 Notes to yourself
  • Info = ✅ “All good!”
  • Warn = ⚠️ “Keep an eye on this…”
  • Error = 🚨 “Something broke!”

JSON Output — Computers Love This!

Want your logs as pure JSON? Computers can read JSON super fast!

package main

import (
    "log/slog"
    "os"
)

func main() {
    // Create JSON logger
    logger := slog.New(
        slog.NewJSONHandler(os.Stdout, nil),
    )

    logger.Info("User action",
        "user", "Alice",
        "action", "login",
        "success", true,
    )
}

Output:

{"time":"2024-01-15T09:00:01Z","level":"INFO","msg":"User action","user":"Alice","action":"login","success":true}

Text Output — Humans Love This!

Sometimes humans need to read logs too. Text format is friendlier:

logger := slog.New(
    slog.NewTextHandler(os.Stdout, nil),
)

logger.Info("Server started", "port", 8080)

Output:

time=2024-01-15T09:00:01Z level=INFO msg="Server started" port=8080

Grouping Related Information

Sometimes you have related data. Group it!

slog.Info("Request completed",
    slog.Group("user",
        "id", 123,
        "name", "Alice",
    ),
    slog.Group("response",
        "status", 200,
        "time_ms", 45,
    ),
)

Output (JSON):

{
  "msg": "Request completed",
  "user": {"id": 123, "name": "Alice"},
  "response": {"status": 200, "time_ms": 45}
}

Setting Minimum Log Level

Don’t want to see Debug messages in production? Set a minimum level!

opts := &slog.HandlerOptions{
    Level: slog.LevelWarn, // Only Warn and Error
}

logger := slog.New(
    slog.NewJSONHandler(os.Stdout, opts),
)

logger.Debug("You won't see me!")
logger.Info("Me neither!")
logger.Warn("But you'll see THIS!")
logger.Error("And definitely THIS!")

Adding Context That Sticks

Want some info to appear in ALL your logs? Use With:

// Create logger with default values
logger := slog.Default().With(
    "service", "user-api",
    "version", "1.2.3",
)

logger.Info("Starting...")
logger.Info("Stopping...")

Both messages will include service=user-api and version=1.2.3!

Logging Errors Properly

When errors happen, log them with context:

user := "Alice"
err := errors.New("database connection failed")

slog.Error("Failed to load user",
    "user", user,
    "error", err,
)

Output:

{
  "level": "ERROR",
  "msg": "Failed to load user",
  "user": "Alice",
  "error": "database connection failed"
}

🎭 Basic vs Structured: When to Use Each?

graph TD A["Need Logging?"] --> B{Small Script?} B -->|Yes| C["Basic Logging&lt;br/&gt;log package"] B -->|No| D{Need to Search<br/>Logs Later?} D -->|Yes| E["Structured Logging&lt;br/&gt;slog package"] D -->|No| C E --> F{Machine Processing?} F -->|Yes| G["JSON Handler"] F -->|No| H["Text Handler"]
Situation Use This
Quick script, learning Basic log
Production app Structured slog
Need to search logs JSON format
Reading logs yourself Text format
Debugging slog.Debug()
Normal operations slog.Info()
Potential problems slog.Warn()
Actual errors slog.Error()

🚀 Real-World Example: Web Server Logging

Let’s put it all together with a mini web server:

package main

import (
    "log/slog"
    "net/http"
    "os"
    "time"
)

func main() {
    // Setup logger
    logger := slog.New(
        slog.NewJSONHandler(os.Stdout, nil),
    ).With("service", "web")

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()

        // Log the request
        logger.Info("Request received",
            "method", r.Method,
            "path", r.URL.Path,
            "ip", r.RemoteAddr,
        )

        w.Write([]byte("Hello!"))

        // Log completion
        logger.Info("Request completed",
            "path", r.URL.Path,
            "duration_ms", time.Since(start).Milliseconds(),
        )
    })

    logger.Info("Server starting", "port", 8080)
    http.ListenAndServe(":8080", nil)
}

🎯 Key Takeaways

  1. Basic Logging (log package)

    • Built into Go, no extra downloads
    • Great for simple programs
    • Use Print, Println, Printf for messages
    • Use Fatal and Panic only for serious errors
    • Customize with flags and custom loggers
  2. Structured Logging (slog package)

    • Organize data with key-value pairs
    • Use log levels: Debug → Info → Warn → Error
    • JSON for machines, Text for humans
    • Group related data together
    • Add persistent context with With()

Remember: Good logs are like a good detective’s notebook. When something goes wrong at 3 AM, you’ll thank yourself for writing clear, organized logs! 🕵️‍♀️


Now you’re ready to make your Go programs talk! Next up: Play with logging in the interactive lab! 🎮

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.