Migration Guides

Switch from other popular Go logging libraries to Rivaas logging

This guide helps you migrate from other popular Go logging libraries to Rivaas logging.

Overview

Switching to Rivaas logging is straightforward. The package offers better performance and stdlib integration while maintaining familiar patterns.

Common migrations:

From logrus

logrus is a popular structured logger, but Rivaas logging offers better performance and native Go integration.

Basic Setup

BEFORE (logrus):

import "github.com/sirupsen/logrus"

log := logrus.New()
log.SetFormatter(&logrus.JSONFormatter{})
log.SetLevel(logrus.InfoLevel)
log.SetOutput(os.Stdout)

log.WithFields(logrus.Fields{
    "user_id": "123",
    "action": "login",
}).Info("User logged in")

AFTER (rivaas/logging):

import "rivaas.dev/logging"

log := logging.MustNew(
    logging.WithJSONHandler(),
    logging.WithLevel(logging.LevelInfo),
    logging.WithOutput(os.Stdout),
)

log.Info("User logged in",
    "user_id", "123",
    "action", "login",
)

Key Differences

Featurelogrusrivaas/logging
Format config&logrus.JSONFormatter{}logging.WithJSONHandler()
FieldsWithFields(logrus.Fields{})Inline key-value pairs
Levellogrus.InfoLevellogging.LevelInfo
Performance~2000 ns/op~500 ns/op
DependenciesMany externalGo stdlib only

Migration Steps

  1. Replace import:
// Old
import "github.com/sirupsen/logrus"

// New
import "rivaas.dev/logging"
  1. Update initialization:
// Old
log := logrus.New()
log.SetFormatter(&logrus.JSONFormatter{})
log.SetLevel(logrus.InfoLevel)

// New
log := logging.MustNew(
    logging.WithJSONHandler(),
    logging.WithLevel(logging.LevelInfo),
)
  1. Convert WithFields calls:
// Old
log.WithFields(logrus.Fields{
    "user_id": "123",
    "action": "login",
}).Info("message")

// New
log.Info("message",
    "user_id", "123",
    "action", "login",
)
  1. Update log levels:
// Old
logrus.DebugLevel -> logging.LevelDebug
logrus.InfoLevel  -> logging.LevelInfo
logrus.WarnLevel  -> logging.LevelWarn
logrus.ErrorLevel -> logging.LevelError

From zap

zap is very fast, but Rivaas logging offers similar performance with a simpler API.

Basic Setup

BEFORE (zap):

import "go.uber.org/zap"

logger, _ := zap.NewProduction()
defer logger.Sync()

logger.Info("User logged in",
    zap.String("user_id", "123"),
    zap.String("action", "login"),
    zap.Int("status", 200),
)

AFTER (rivaas/logging):

import "rivaas.dev/logging"

logger := logging.MustNew(logging.WithJSONHandler())
defer logger.Shutdown(context.Background())

logger.Info("User logged in",
    "user_id", "123",
    "action", "login",
    "status", 200,
)

Key Differences

Featurezaprivaas/logging
Typed fieldszap.String("key", val)Direct values
Shutdownlogger.Sync()logger.Shutdown(ctx)
API styleTyped wrappersNative Go types
Performance~450 ns/op~500 ns/op
ComplexityHighLow

Migration Steps

  1. Replace import:
// Old
import "go.uber.org/zap"

// New
import "rivaas.dev/logging"
  1. Simplify initialization:
// Old
logger, _ := zap.NewProduction()

// New
logger := logging.MustNew(logging.WithJSONHandler())
  1. Remove type wrappers:
// Old
logger.Info("message",
    zap.String("name", name),
    zap.Int("count", count),
    zap.Bool("enabled", enabled),
)

// New
logger.Info("message",
    "name", name,
    "count", count,
    "enabled", enabled,
)
  1. Update shutdown:
// Old
defer logger.Sync()

// New
defer logger.Shutdown(context.Background())

From zerolog

zerolog is very fast, but Rivaas logging is simpler and uses stdlib.

Basic Setup

BEFORE (zerolog):

import "github.com/rs/zerolog"

logger := zerolog.New(os.Stdout).With().
    Str("service", "myapp").
    Str("version", "1.0.0").
    Logger()

logger.Info().
    Str("user_id", "123").
    Str("action", "login").
    Msg("User logged in")

AFTER (rivaas/logging):

import "rivaas.dev/logging"

logger := logging.MustNew(
    logging.WithJSONHandler(),
    logging.WithServiceName("myapp"),
    logging.WithServiceVersion("1.0.0"),
)

logger.Info("User logged in",
    "user_id", "123",
    "action", "login",
)

Key Differences

Featurezerologrivaas/logging
API styleChainingFunctional options
Context.With().Str().Logger()WithServiceName()
Fields.Str("k", v).Msg()Inline pairs
Performance~400 ns/op~500 ns/op
ReadabilityMediumHigh

Migration Steps

  1. Replace import:
// Old
import "github.com/rs/zerolog"

// New
import "rivaas.dev/logging"
  1. Simplify initialization:
// Old
logger := zerolog.New(os.Stdout).With().
    Str("service", "myapp").
    Str("version", "1.0.0").
    Logger()

// New
logger := logging.MustNew(
    logging.WithJSONHandler(),
    logging.WithServiceName("myapp"),
    logging.WithServiceVersion("1.0.0"),
)
  1. Remove chaining:
// Old
logger.Info().
    Str("user_id", "123").
    Str("action", "login").
    Msg("User logged in")

// New
logger.Info("User logged in",
    "user_id", "123",
    "action", "login",
)

From stdlib log

Standard library log is simple but unstructured. Rivaas logging adds structure while using stdlib slog.

Basic Setup

BEFORE (stdlib log):

import "log"

log.SetOutput(os.Stdout)
log.SetPrefix("[INFO] ")
log.Printf("User %s logged in from %s", userID, ipAddress)

AFTER (rivaas/logging):

import "rivaas.dev/logging"

logger := logging.MustNew(
    logging.WithJSONHandler(),
    logging.WithLevel(logging.LevelInfo),
)

logger.Info("User logged in",
    "user_id", userID,
    "ip_address", ipAddress,
)

Key Benefits

Featurestdlib logrivaas/logging
StructureNoYes
Log levelsNoYes
FormatsText onlyJSON, Text, Console
PerformanceFastFast
ParsingManualAutomatic

Migration Steps

  1. Replace import:
// Old
import "log"

// New
import "rivaas.dev/logging"
  1. Update initialization:
// Old
log.SetOutput(os.Stdout)
log.SetPrefix("[INFO] ")

// New
logger := logging.MustNew(
    logging.WithJSONHandler(),
    logging.WithLevel(logging.LevelInfo),
)
  1. Convert Printf to structured:
// Old
log.Printf("User %s logged in from %s", userID, ipAddress)

// New
logger.Info("User logged in",
    "user_id", userID,
    "ip_address", ipAddress,
)

Migration Checklist

Use this checklist when migrating:

  • Replace logger initialization
  • Update all log calls to structured format
  • Replace log level constants
  • Update context/field methods (WithFields → inline)
  • Replace typed field methods (zap.String → direct values)
  • Update error handling (Sync → Shutdown)
  • Test with new logger
  • Update imports
  • Remove old logger dependency from go.mod
  • Update documentation and examples

Gradual Migration

Migrate gradually to minimize risk.

Phase 1: Parallel Logging

Run both loggers side-by-side:

// Keep old logger
oldLogger := logrus.New()

// Add new logger
newLogger := logging.MustNew(logging.WithJSONHandler())

// Log to both
func logInfo(msg string, fields map[string]any) {
    // Old logger
    oldLogger.WithFields(logrus.Fields(fields)).Info(msg)
    
    // New logger
    args := make([]any, 0, len(fields)*2)
    for k, v := range fields {
        args = append(args, k, v)
    }
    newLogger.Info(msg, args...)
}

Phase 2: Feature Flag

Use feature flag to switch between loggers:

func getLogger() Logger {
    if os.Getenv("USE_NEW_LOGGER") == "true" {
        return logging.MustNew(logging.WithJSONHandler())
    }
    return logrus.New()
}

Phase 3: Full Migration

Once validated, remove old logger completely.

Performance Comparison

Benchmark results (lower is better):

Loggerns/opallocs/opDependencies
stdlib slog45000
rivaas/logging50001 (OTel)
zap4500Many
zerolog4000Several
logrus20005Many

Note: rivaas/logging overhead is minimal compared to stdlib slog while adding valuable features.

Common Migration Issues

Issue: Missing Fields

Problem: Fields not appearing in logs.

Solution: Check field names match new format:

// Wrong - using old field format
log.Info("message", logrus.Fields{"key": "value"})

// Right - inline key-value pairs
log.Info("message", "key", "value")

Issue: Log Level Not Working

Problem: Debug logs not appearing.

Solution: Check log level configuration:

logger := logging.MustNew(
    logging.WithJSONHandler(),
    logging.WithDebugLevel(),  // Make sure to set debug level
)

Issue: Performance Regression

Problem: Logging slower than expected.

Solution: Check for common issues:

  • Logging in tight loops
  • Source location enabled in production
  • Not using appropriate log level

Getting Help

If you encounter issues during migration:

  1. Check the Troubleshooting guide
  2. Review Examples for patterns
  3. See Best Practices for recommendations
  4. Consult the API Reference

Next Steps

For complete API details, see the API Reference.