Examples
Complete real-world examples of using the logging package
6 minute read
This guide provides complete, real-world examples of using the logging package in various scenarios.
Basic Application
Simple application with structured logging.
package main
import (
"context"
"os"
"rivaas.dev/logging"
)
func main() {
// Create logger
logger := logging.MustNew(
logging.WithJSONHandler(),
logging.WithLevel(logging.LevelInfo),
logging.WithServiceName("myapp"),
logging.WithServiceVersion("v1.0.0"),
)
defer logger.Shutdown(context.Background())
// Application logic
logger.Info("application started",
"port", 8080,
"environment", os.Getenv("ENV"),
)
// Simulate work
processData(logger)
logger.Info("application stopped")
}
func processData(logger *logging.Logger) {
logger.Info("processing data", "items", 100)
// Process logic...
logger.Info("data processing completed", "processed", 100)
}
HTTP Server
HTTP server with request logging.
package main
import (
"context"
"net/http"
"time"
"rivaas.dev/logging"
)
func main() {
logger := logging.MustNew(
logging.WithJSONHandler(),
logging.WithServiceName("api-server"),
)
defer logger.Shutdown(context.Background())
mux := http.NewServeMux()
// Add logging middleware
mux.HandleFunc("/", loggingMiddleware(logger, handleRoot))
mux.HandleFunc("/api/users", loggingMiddleware(logger, handleUsers))
logger.Info("server starting", "port", 8080)
http.ListenAndServe(":8080", mux)
}
func loggingMiddleware(logger *logging.Logger, next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// Wrap response writer to capture status
wrapped := &responseWriter{ResponseWriter: w, statusCode: 200}
next(wrapped, r)
logger.LogRequest(r,
"status", wrapped.statusCode,
"duration_ms", time.Since(start).Milliseconds(),
)
}
}
type responseWriter struct {
http.ResponseWriter
statusCode int
}
func (w *responseWriter) WriteHeader(code int) {
w.statusCode = code
w.ResponseWriter.WriteHeader(code)
}
func handleRoot(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, World!"))
}
func handleUsers(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"users": []}`))
}
Router Integration
Full router integration with tracing.
package main
import (
"context"
"rivaas.dev/app"
"rivaas.dev/logging"
"rivaas.dev/tracing"
"rivaas.dev/router/middleware/accesslog"
)
func main() {
// Create app with full observability
a, err := app.New(
app.WithServiceName("user-api"),
app.WithServiceVersion("v2.0.0"),
app.WithObservability(
app.WithLogging(
logging.WithJSONHandler(),
logging.WithLevel(logging.LevelInfo),
),
app.WithTracing(
tracing.WithOTLP("localhost:4317"),
),
),
)
if err != nil {
panic(err)
}
defer a.Shutdown(context.Background())
router := a.Router()
logger := a.Logger()
// Add access log middleware
router.Use(accesslog.New(
accesslog.WithExcludePaths("/health"),
))
// Health endpoint
router.GET("/health", func(c *router.Context) {
c.JSON(200, map[string]string{"status": "healthy"})
})
// API endpoints
api := router.Group("/api/v1")
{
api.GET("/users", getUsers(logger))
api.POST("/users", createUser(logger))
}
logger.Info("server starting", "port", 8080)
a.Run(":8080")
}
func getUsers(logger *logging.Logger) router.HandlerFunc {
return func(c *router.Context) {
log := c.Logger()
log.Info("fetching users")
users := fetchUsers()
log.Info("users fetched", "count", len(users))
c.JSON(200, users)
}
}
func createUser(logger *logging.Logger) router.HandlerFunc {
return func(c *router.Context) {
log := c.Logger()
var user User
if err := c.BindJSON(&user); err != nil {
log.Error("invalid request", "error", err)
c.JSON(400, map[string]string{"error": "invalid request"})
return
}
if err := saveUser(user); err != nil {
log.Error("failed to save user", "error", err)
c.JSON(500, map[string]string{"error": "internal error"})
return
}
log.Info("user created", "user_id", user.ID)
c.JSON(201, user)
}
}
Multiple Loggers
Different loggers for different purposes.
package main
import (
"context"
"os"
"rivaas.dev/logging"
)
type Application struct {
appLogger *logging.Logger
debugLogger *logging.Logger
auditLogger *logging.Logger
}
func NewApplication() *Application {
// Application logger - JSON for production
appLogger := logging.MustNew(
logging.WithJSONHandler(),
logging.WithLevel(logging.LevelInfo),
logging.WithServiceName("myapp"),
)
// Debug logger - Console with source info
debugLogger := logging.MustNew(
logging.WithConsoleHandler(),
logging.WithDebugLevel(),
logging.WithSource(true),
)
// Audit logger - Separate file for compliance
auditFile, _ := os.OpenFile("audit.log",
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
auditLogger := logging.MustNew(
logging.WithJSONHandler(),
logging.WithOutput(auditFile),
logging.WithServiceName("myapp-audit"),
)
return &Application{
appLogger: appLogger,
debugLogger: debugLogger,
auditLogger: auditLogger,
}
}
func (a *Application) Run() {
defer a.appLogger.Shutdown(context.Background())
defer a.debugLogger.Shutdown(context.Background())
defer a.auditLogger.Shutdown(context.Background())
// Normal application log
a.appLogger.Info("application started")
// Debug information
a.debugLogger.Debug("initialization complete",
"config_loaded", true,
"db_connected", true,
)
// Audit event
a.auditLogger.Info("user action",
"user_id", "123",
"action", "login",
"success", true,
)
}
func main() {
app := NewApplication()
app.Run()
}
Environment-Based Configuration
Configure logging based on environment.
package main
import (
"os"
"rivaas.dev/logging"
)
func createLogger() *logging.Logger {
env := os.Getenv("ENV")
var opts []logging.Option
switch env {
case "development":
opts = []logging.Option{
logging.WithConsoleHandler(),
logging.WithDebugLevel(),
logging.WithSource(true),
}
case "staging":
opts = []logging.Option{
logging.WithJSONHandler(),
logging.WithLevel(logging.LevelInfo),
logging.WithServiceName(os.Getenv("SERVICE_NAME")),
logging.WithEnvironment("staging"),
}
case "production":
opts = []logging.Option{
logging.WithJSONHandler(),
logging.WithLevel(logging.LevelWarn),
logging.WithServiceName(os.Getenv("SERVICE_NAME")),
logging.WithServiceVersion(os.Getenv("VERSION")),
logging.WithEnvironment("production"),
logging.WithSampling(logging.SamplingConfig{
Initial: 1000,
Thereafter: 100,
Tick: time.Minute,
}),
}
default:
opts = []logging.Option{
logging.WithJSONHandler(),
logging.WithLevel(logging.LevelInfo),
}
}
return logging.MustNew(opts...)
}
func main() {
logger := createLogger()
defer logger.Shutdown(context.Background())
logger.Info("application started", "environment", os.Getenv("ENV"))
}
Worker Pool with Per-Worker Logging
Logging in concurrent workers.
package main
import (
"context"
"fmt"
"sync"
"time"
"rivaas.dev/logging"
)
type Worker struct {
id int
logger *logging.Logger
}
func NewWorker(id int, baseLogger *logging.Logger) *Worker {
// Create worker-specific logger
workerLogger := baseLogger.With("worker_id", id)
return &Worker{
id: id,
logger: baseLogger,
}
}
func (w *Worker) Process(job Job) {
start := time.Now()
w.logger.Info("job started",
"worker_id", w.id,
"job_id", job.ID,
)
// Process job
time.Sleep(100 * time.Millisecond)
w.logger.LogDuration("job completed", start,
"worker_id", w.id,
"job_id", job.ID,
)
}
type Job struct {
ID int
}
func main() {
logger := logging.MustNew(
logging.WithJSONHandler(),
logging.WithServiceName("worker-pool"),
)
defer logger.Shutdown(context.Background())
// Create worker pool
numWorkers := 4
jobs := make(chan Job, 100)
var wg sync.WaitGroup
// Start workers
for i := 0; i < numWorkers; i++ {
wg.Add(1)
worker := NewWorker(i, logger)
go func() {
defer wg.Done()
for job := range jobs {
worker.Process(job)
}
}()
}
// Send jobs
for i := 0; i < 10; i++ {
jobs <- Job{ID: i}
}
close(jobs)
wg.Wait()
logger.Info("all jobs completed")
}
Error Handling with Context
Comprehensive error logging.
package main
import (
"context"
"errors"
"time"
"rivaas.dev/logging"
)
type Service struct {
logger *logging.Logger
}
func NewService(logger *logging.Logger) *Service {
return &Service{logger: logger}
}
func (s *Service) ProcessPayment(ctx context.Context, payment Payment) error {
log := s.logger.With(
"payment_id", payment.ID,
"amount", payment.Amount,
)
log.Info("processing payment")
// Validation
if err := s.validatePayment(payment); err != nil {
log.LogError(err, "payment validation failed",
"step", "validation",
)
return err
}
// Process with retry
var lastErr error
for retry := 0; retry < 3; retry++ {
if err := s.chargePayment(payment); err != nil {
lastErr = err
log.LogError(err, "payment charge failed",
"retry", retry,
"max_retries", 3,
)
time.Sleep(time.Second * time.Duration(retry+1))
continue
}
log.Info("payment processed successfully")
return nil
}
// Critical failure - log with stack trace
s.logger.ErrorWithStack("payment processing failed after retries",
lastErr, true,
"payment_id", payment.ID,
"retries", 3,
)
return lastErr
}
func (s *Service) validatePayment(payment Payment) error {
if payment.Amount <= 0 {
return errors.New("invalid amount")
}
return nil
}
func (s *Service) chargePayment(payment Payment) error {
// Simulate charging
return nil
}
type Payment struct {
ID string
Amount float64
}
Testing Example
Complete testing setup.
package myservice_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"rivaas.dev/logging"
)
func TestUserService(t *testing.T) {
th := logging.NewTestHelper(t)
svc := NewUserService(th.Logger)
t.Run("create user", func(t *testing.T) {
th.Reset()
user, err := svc.CreateUser("alice", "alice@example.com")
require.NoError(t, err)
require.NotNil(t, user)
// Verify logging
th.AssertLog(t, "INFO", "user created", map[string]any{
"username": "alice",
"email": "alice@example.com",
})
})
t.Run("duplicate user", func(t *testing.T) {
th.Reset()
_, err := svc.CreateUser("alice", "alice@example.com")
require.Error(t, err)
// Verify error logging
assert.True(t, th.ContainsLog("user creation failed"))
assert.True(t, th.ContainsAttr("error", "user already exists"))
})
}
Next Steps
- Review Best Practices for production patterns
- See Testing for test utilities
- Explore the API Reference
For more examples, check the examples directory on GitHub.
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.