Documentation Index Fetch the complete documentation index at: https://mnah05-boiler-go-21-79.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Boiler-Go uses zerolog for fast, structured logging. The logger supports multiple output destinations including console and file logging.
Logger architecture
The logging system is implemented in pkg/logger/logger.go with three main components:
Global logger Singleton instance for fallback logging
Output configuration Flexible stdout and file output
Thread safety Safe concurrent access with sync.Once
Output configuration
The logger supports three output modes configured via LOG_OUTPUT environment variable:
type OutputConfig struct {
// FilePath is the path to the log file. If empty, no file logging.
FilePath string
// Stdout enables console output.
Stdout bool
// StdoutOnly disables file output and only logs to stdout.
StdoutOnly bool
}
Output modes
stdout (default)
file
both
Logs only to console with human-readable format: This is the default mode and recommended for development.
Logs only to file, no console output: LOG_OUTPUT = file
LOG_FILE = logs/app.log
Without console output, you won’t see logs in real-time during development.
Logs to both console and file: LOG_OUTPUT = both
LOG_FILE = logs/app.log
Use this mode in production to get real-time visibility while maintaining persistent logs.
Creating loggers
Basic logger
Create a logger with default stdout configuration:
import " boiler-go/pkg/logger "
logg := logger . New ()
logg . Info (). Msg ( "application starting" )
Both cmd/api/main.go and cmd/worker/main.go implement the newLogger() function:
func newLogger ( cfg * config . Config , defaultFile string ) zerolog . Logger {
outputCfg := logger . OutputConfig {}
switch cfg . LogOutput {
case "stdout" :
outputCfg . Stdout = true
outputCfg . StdoutOnly = true
case "file" :
outputCfg . Stdout = false
outputCfg . StdoutOnly = false
outputCfg . FilePath = cfg . LogFile
if outputCfg . FilePath == "" {
outputCfg . FilePath = defaultFile
}
case "both" :
outputCfg . Stdout = true
outputCfg . StdoutOnly = false
outputCfg . FilePath = cfg . LogFile
if outputCfg . FilePath == "" {
outputCfg . FilePath = defaultFile
}
}
return logger . NewWithOutput ( outputCfg )
}
Usage in applications
// cmd/api/main.go:56
logg := newLogger ( cfg , "logs/api.log" )
The console writer uses a human-readable format with timestamps:
zerolog . ConsoleWriter {
Out : os . Stdout ,
TimeFormat : "2006-01-02 15:04:05" ,
}
Example output:
2024-03-15 14:23:10 INF server starting port=8080
2024-03-15 14:23:10 INF database connected
2024-03-15 14:23:10 INF redis connected
Structured logging
Zerolog supports structured fields for better log analysis:
logg . Info ().
Str ( "user_id" , "123" ). // String field
Int ( "count" , 42 ). // Integer field
Dur ( "duration" , elapsed ). // Duration field
Err ( err ). // Error field
Bool ( "success" , true ). // Boolean field
Time ( "created_at" , time . Now ()). // Time field
Msg ( "operation completed" )
Real-world examples
Server startup
Error logging
Task processing
With duration
// cmd/api/main.go:102-104
logg . Info ().
Str ( "port" , cfg . AppPort ).
Msg ( "server starting" )
Log levels
Zerolog supports multiple log levels (default level is Info):
logg . Debug (). Msg ( "detailed debugging information" )
logg . Info (). Msg ( "informational message" )
logg . Warn (). Msg ( "warning message" )
logg . Error (). Err ( err ). Msg ( "error occurred" )
logg . Fatal (). Err ( err ). Msg ( "critical error, exiting" ) // Calls os.Exit(1)
Fatal() calls os.Exit(1) after logging. Use it only for unrecoverable errors during startup.
Fatal logging examples
Configuration error
Startup failure
// cmd/api/main.go:63
if err := db . Open ( dbCtx , cfg ); err != nil {
logg . Fatal (). Err ( err ). Msg ( "failed to initialize database" )
}
File logging
When file output is enabled, the logger automatically:
Creates the log directory if it doesn’t exist
Opens the file in append mode
Falls back to stdout if file operations fail
// pkg/logger/logger.go:49-56
dir := filepath . Dir ( cfg . FilePath )
if dir != "" && dir != "." {
if err := os . MkdirAll ( dir , 0755 ); err != nil {
fmt . Fprintf ( os . Stderr , "failed to create log directory: %v \n " , err )
return zerolog . New ( os . Stdout ). With (). Timestamp (). Logger ()
}
}
file , err := os . OpenFile ( cfg . FilePath , os . O_CREATE | os . O_WRONLY | os . O_APPEND , 0666 )
The logger creates parent directories automatically. You don’t need to manually create logs/ directories.
Multi-writer output
When logging to both stdout and file, zerolog uses MultiLevelWriter:
// pkg/logger/logger.go:79
output = zerolog . MultiLevelWriter ( writers ... )
This ensures logs are written atomically to all destinations.
Global logger
The package provides a global logger for cases where dependency injection isn’t available:
import " boiler-go/pkg/logger "
func someFunction () {
logg := logger . Global ()
logg . Info (). Msg ( "using global logger" )
}
Prefer passing loggers explicitly over using the global instance. This makes testing easier and dependencies clearer.
The global logger is initialized lazily:
// pkg/logger/logger.go:87-92
func Global () zerolog . Logger {
globalOnce . Do ( func () {
global = New ()
})
return global
}
Best practices
Pass loggers as parameters : Avoid relying on the global logger
Use structured fields : Include contextual information in every log
Log at appropriate levels : Reserve Error for actual errors, use Info for normal operation
Include correlation IDs : Add request IDs to track requests across services
Don’t log sensitive data : Avoid logging passwords, tokens, or PII
Request logging example
func HandleRequest ( w http . ResponseWriter , r * http . Request , logg zerolog . Logger ) {
requestID := generateRequestID ()
logg . Info ().
Str ( "request_id" , requestID ).
Str ( "method" , r . Method ).
Str ( "path" , r . URL . Path ).
Str ( "remote_addr" , r . RemoteAddr ).
Msg ( "request started" )
// Process request...
logg . Info ().
Str ( "request_id" , requestID ).
Int ( "status" , 200 ).
Msg ( "request completed" )
}
Configuration reference
Output destination: stdout, file, or both
File path when using file or both mode. Defaults:
API: logs/api.log
Worker: logs/worker.log