
Logie
A flexible structured logging package built on top of zerolog with module-level log control.
Installation
go get github.com/Michi01/logie
Features
- Module-based logging with independent log levels
- Global and per-module log level control
- Structured logging with fields
- Console output formatting
- Built-in support for common fields (hostname, PID, Go version)
Quick Start
package main
import "github.com/Michi01/logie"
func main() {
// Get default logger with console output
logger := logie.DefaultLogger()
// Basic logging
logger.Info("Hello, World!")
// Logging with fields
logger.WithField("user", "john").Info("User logged in")
}
Creating Loggers
Default Logger
logger := logie.DefaultLogger()
The default logger includes:
- Console output with timestamps
- Hostname
- Process ID
- Go version
- Application version (if BuildVersion is set)
Custom Logger
output := zerolog.ConsoleWriter{
Out: os.Stdout,
TimeFormat: time.RFC3339,
}
logger := logie.New(output)
Module-Based Logging
logger := logie.New(
output,
logie.WithModule("auth"),
)
Log Levels
Available levels: Panic, Fatal, Error, Warn, Info, Debug, Trace
Environment Configuration
package main
import (
"os"
"strings"
"github.com/rs/zerolog"
"github.com/Michi01/logie"
)
func init() {
// Set global log level from environment
if levelStr := os.Getenv("LOG_LEVEL"); levelStr != "" {
level := parseLogLevel(levelStr, zerolog.InfoLevel)
logie.SetGlobalLevel(level)
}
// Configure module levels from environment
// Format: MODULE_LOG_LEVEL_<MODULE>=<LEVEL>
// Example: MODULE_LOG_LEVEL_AUTH=debug
for _, env := range os.Environ() {
if !strings.HasPrefix(env, "MODULE_LOG_LEVEL_") {
continue
}
parts := strings.SplitN(env, "=", 2)
if len(parts) != 2 {
continue
}
module := strings.TrimPrefix(parts[0], "MODULE_LOG_LEVEL_")
module = strings.ToLower(module)
level := parseLogLevel(parts[1], zerolog.InfoLevel)
logie.SetModuleLevel(module, level)
}
}
// parseLogLevel converts a string level to zerolog.Level with a default fallback
func parseLogLevel(level string, defaultLevel zerolog.Level) zerolog.Level {
switch strings.ToLower(level) {
case "panic":
return zerolog.PanicLevel
case "fatal":
return zerolog.FatalLevel
case "error":
return zerolog.ErrorLevel
case "warn":
return zerolog.WarnLevel
case "info":
return zerolog.InfoLevel
case "debug":
return zerolog.DebugLevel
default:
return defaultLevel
}
}
### Setting Global Level
```go
logie.SetGlobalLevel(zerolog.DebugLevel)
Setting Module Level
logie.SetModuleLevel("auth", zerolog.DebugLevel)
Adding Context
Single Field
logger.WithField("user_id", 123).Info("User action")
Multiple Fields
logger.WithFields(map[string]interface{}{
"user_id": 123,
"action": "login",
}).Info("User action")
All log levels support formatted messages:
logger.Infof("Processing item %d of %d", current, total)
logger.Errorf("Failed to process: %v", err)
Environment Variables
LOG_LEVEL: Sets the global logging level (default: "info")
MODULE_LOG_LEVEL_<MODULE>: Sets the log level for a specific module
- Example:
MODULE_LOG_LEVEL_AUTH=debug
- Example:
MODULE_LOG_LEVEL_DATABASE=error
Usage Patterns
Global Logger
package main
import "github.com/Michi01/logie"
var log *logie.Logger
func init() {
log = logie.DefaultLogger()
}
func main() {
log.Info("Application started")
}
Dependency Injection Pattern
// auth/auth.go
package auth
type Service struct {
log *logie.Logger
}
func NewService(logger *logie.Logger) *Service {
return &Service{
log: logger.WithModule("auth"),
}
}
func (s *Service) Login(username string) {
s.log.WithField("user", username).Info("Login attempt")
}
// database/db.go
package database
type Repository struct {
log *logie.Logger
}
func NewRepository(logger *logie.Logger) *Repository {
return &Repository{
log: logger.WithModule("database"),
}
}
// main.go
package main
func main() {
logger := logie.DefaultLogger()
authService := auth.NewService(logger)
dbRepo := database.NewRepository(logger)
// Both services now have their own logger instance with module context
}
Package-Level Logger
// payments/logger.go
package payments
import "github.com/Michi01/logie"
var log *logie.Logger
func init() {
log = logie.DefaultLogger().WithModule("payments")
}
// payments/service.go
package payments
func ProcessPayment(amount float64) {
log.WithField("amount", amount).Info("Processing payment")
}
Best Practices
- Use modules to organize logs by component
- Add contextual fields using WithField/WithFields
- Set appropriate log levels for different environments
- Use structured fields instead of embedding data in messages
- Prefer dependency injection for better testability
- Use package-level loggers only when dependency injection isn't practical
Thread Safety
All operations in Logie are thread-safe and can be used concurrently.