Configuration
4 minute read
This guide covers all configuration options available in the logging package. It covers handler selection to service metadata.
Handler Configuration
Choose the appropriate handler type for your environment.
Handler Types
// JSON structured logging. Default and best for production.
logging.WithJSONHandler()
// Text key=value logging.
logging.WithTextHandler()
// Human-readable colored console. Best for development.
logging.WithConsoleHandler()
See Basic Usage for detailed handler comparison.
Log Level Configuration
Control which log messages are output.
Setting Minimum Level
// Set specific level
logging.WithLevel(logging.LevelDebug)
logging.WithLevel(logging.LevelInfo)
logging.WithLevel(logging.LevelWarn)
logging.WithLevel(logging.LevelError)
// Convenience function for debug
logging.WithDebugLevel()
Example:
log := logging.MustNew(
logging.WithJSONHandler(),
logging.WithLevel(logging.LevelInfo), // Info, Warn, Error only
)
See Dynamic Log Levels to change levels at runtime.
Output Destination
By default, logs write to os.Stdout. Customize the output destination:
File Output
logFile, err := os.OpenFile("app.log",
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
log.Fatal(err)
}
defer logFile.Close()
logger := logging.MustNew(
logging.WithJSONHandler(),
logging.WithOutput(logFile),
)
Custom Writer
Any io.Writer can be used:
var buf bytes.Buffer
logger := logging.MustNew(
logging.WithJSONHandler(),
logging.WithOutput(&buf),
)
Multiple Writers
Use io.MultiWriter to write to multiple destinations:
logFile, _ := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
logger := logging.MustNew(
logging.WithJSONHandler(),
logging.WithOutput(io.MultiWriter(os.Stdout, logFile)),
)
Service Information
Add service metadata automatically to every log entry.
Service Metadata
logger := logging.MustNew(
logging.WithJSONHandler(),
logging.WithServiceName("my-api"),
logging.WithServiceVersion("v1.0.0"),
logging.WithEnvironment("production"),
)
logger.Info("server started", "port", 8080)
Output:
{
"level": "INFO",
"msg": "server started",
"service": "my-api",
"version": "v1.0.0",
"env": "production",
"port": 8080
}
Why Service Metadata?
- Filtering: Query logs by service in aggregation tools
- Correlation: Track logs across distributed services
- Versioning: Identify which version produced logs
- Environment: Distinguish between dev/staging/prod logs
Reading Service Information
Access configured service info programmatically:
serviceName := logger.ServiceName()
version := logger.ServiceVersion()
env := logger.Environment()
Source Code Location
Add file and line information to log entries for debugging.
Enable Source Location
logger := logging.MustNew(
logging.WithJSONHandler(),
logging.WithSource(true),
)
logger.Info("debug message")
Output:
{
"level": "INFO",
"msg": "debug message",
"source": {
"file": "main.go",
"line": 42
}
}
Performance note: Source location adds overhead. Enable only for debugging.
Custom Attribute Replacer
Transform or filter log attributes with a custom function.
Redacting Custom Fields
logger := logging.MustNew(
logging.WithJSONHandler(),
logging.WithReplaceAttr(func(groups []string, a slog.Attr) slog.Attr {
// Redact credit card numbers
if a.Key == "credit_card" {
return slog.String(a.Key, "***REDACTED***")
}
// Transform time format
if a.Key == "time" {
if t, ok := a.Value.Any().(time.Time); ok {
return slog.String(a.Key, t.Format(time.RFC3339))
}
}
return a
}),
)
Built-in Redaction
The following fields are automatically redacted:
passwordtokensecretapi_keyauthorization
log.Info("authentication",
"username", "john",
"password", "secret123", // Automatically redacted
)
// Output: {...,"username":"john","password":"***REDACTED***"}
Dropping Attributes
Return an empty slog.Attr to drop an attribute:
logging.WithReplaceAttr(func(groups []string, a slog.Attr) slog.Attr {
if a.Key == "internal_field" {
return slog.Attr{} // Drop this field
}
return a
})
Global Logger Registration
By default, loggers are not registered globally, allowing multiple independent logger instances.
Register as Global Default
logger := logging.MustNew(
logging.WithJSONHandler(),
logging.WithServiceName("my-api"),
logging.WithGlobalLogger(), // Register as slog default
)
defer logger.Shutdown(context.Background())
// Now third-party libraries using slog will use your logger
slog.Info("using global logger", "key", "value")
When to Use Global Registration
Use global registration when:
- Third-party libraries use
slogdirectly - You prefer
slog.Info()overlogger.Info() - Migrating from direct
slogusage
Don’t use global registration when:
- Running tests with isolated loggers
- Creating libraries (avoid affecting global state)
- Using multiple logging configurations
Default Behavior
Without WithGlobalLogger(), each logger is independent:
logger1 := logging.MustNew(logging.WithJSONHandler())
logger2 := logging.MustNew(logging.WithConsoleHandler())
logger1.Info("from logger1") // JSON output
logger2.Info("from logger2") // Console output
slog.Info("from default slog") // Standard slog output (independent)
Custom Logger
Provide your own slog.Logger for advanced scenarios.
Using Custom slog.Logger
customLogger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelDebug,
AddSource: true,
}))
logger := logging.MustNew(
logging.WithCustomLogger(customLogger),
)
Limitations:
- Dynamic level changes (
SetLevel) not supported with custom loggers - Service metadata must be added to custom logger directly
Debug Mode
Enable comprehensive debugging with a single option.
Enable Debug Mode
logger := logging.MustNew(
logging.WithJSONHandler(),
logging.WithDebugMode(true),
)
Automatically enables:
- Debug log level (
WithDebugLevel()) - Source code location (
WithSource(true))
Use cases:
- Troubleshooting production issues
- Development environments
- Detailed debugging sessions
Complete Configuration Example
Putting all options together:
package main
import (
"os"
"rivaas.dev/logging"
"log/slog"
)
func main() {
// Production configuration
prodLogger := logging.MustNew(
logging.WithJSONHandler(),
logging.WithLevel(logging.LevelInfo),
logging.WithServiceName("payment-api"),
logging.WithServiceVersion("v2.1.0"),
logging.WithEnvironment("production"),
logging.WithOutput(os.Stdout),
)
defer prodLogger.Shutdown(context.Background())
// Development configuration
devLogger := logging.MustNew(
logging.WithConsoleHandler(),
logging.WithDebugLevel(),
logging.WithSource(true),
logging.WithServiceName("payment-api"),
logging.WithEnvironment("development"),
)
defer devLogger.Shutdown(context.Background())
// Choose based on environment
var logger *logging.Logger
if os.Getenv("ENV") == "production" {
logger = prodLogger
} else {
logger = devLogger
}
logger.Info("application started")
}
Configuration Best Practices
Production Settings
logger := logging.MustNew(
logging.WithJSONHandler(), // Machine-parseable
logging.WithLevel(logging.LevelInfo), // No debug spam
logging.WithServiceName("my-api"), // Service identification
logging.WithServiceVersion(version), // Version tracking
logging.WithEnvironment("production"), // Environment filtering
)
Development Settings
logger := logging.MustNew(
logging.WithConsoleHandler(), // Human-readable
logging.WithDebugLevel(), // See everything
logging.WithSource(true), // File:line info
)
Testing Settings
buf := &bytes.Buffer{}
logger := logging.MustNew(
logging.WithJSONHandler(),
logging.WithOutput(buf),
logging.WithLevel(logging.LevelDebug),
)
// Inspect buf for assertions
Next Steps
- Learn Context Logging for trace correlation
- Explore Log Sampling to reduce volume
- See Dynamic Log Levels for runtime changes
- Review Best Practices for production use
For complete option details, see the Options Reference.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.