Skip to content

go.missing_structured_logging

Observability Low

Detects usage of fmt.Println/log.Print instead of structured logging (zerolog/zap/slog).

Unstructured logs are hard to work with:

  • Not queryable — Can’t search for specific fields
  • Not aggregatable — Can’t count or group events
  • Not alertable — Can’t set conditions on values
  • Parsing hell — Regex extraction is fragile

Modern observability requires structured data with consistent fields.

// ❌ Before
fmt.Printf("User %s logged in\n", userID)
log.Printf("Error: %v", err)

These are impossible to filter or aggregate in your logging system.

// ✅ After (zerolog)
import "github.com/rs/zerolog/log"
log.Info().Str("user_id", userID).Msg("user logged in")
log.Error().Err(err).Msg("operation failed")

Now you can query user_id:123 or count login events by user.

  • fmt.Print, fmt.Printf, fmt.Println
  • log.Print, log.Printf, log.Println
  • log.Fatal, log.Panic (also replace with structured)

Unfault converts print statements to zerolog calls.

// zerolog - Fast, structured, zero allocation
import "github.com/rs/zerolog/log"
log.Info().Str("key", "value").Msg("message")
// zap - Uber's high-performance logger
import "go.uber.org/zap"
logger, _ := zap.NewProduction()
logger.Info("message", zap.String("key", "value"))
// slog - Standard library (Go 1.21+)
import "log/slog"
slog.Info("message", "key", "value")
// zerolog with pretty console for dev
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
// Production JSON output
log.Logger = zerolog.New(os.Stdout).With().Timestamp().Logger()