|
1 | 1 | # Log |
2 | 2 |
|
3 | | -The `cosmossdk.io/log` provides a zerolog logging implementation for the Cosmos SDK and Cosmos SDK modules. |
| 3 | +The `cosmossdk.io/log` package provides a structured logging implementation for the Cosmos SDK using Go's standard `log/slog` with OpenTelemetry integration. |
| 4 | + |
| 5 | +## Features |
| 6 | + |
| 7 | +- **Dual output**: Logs are sent to both console and OpenTelemetry simultaneously |
| 8 | +- **Pretty console output**: Human-readable colored output powered by [zerolog](https://github.com/rs/zerolog) |
| 9 | +- **OpenTelemetry integration**: Full observability with trace correlation |
| 10 | +- **Verbose mode**: Dynamic log level switching for operations like chain upgrades |
| 11 | +- **Module filtering**: Filter logs by module and level |
| 12 | + |
| 13 | +## Usage |
| 14 | + |
| 15 | +### Basic Usage |
| 16 | + |
| 17 | +```go |
| 18 | +import "cosmossdk.io/log" |
| 19 | + |
| 20 | +// Create a logger with default settings (console + OTEL) |
| 21 | +logger := log.NewLogger("my-app") |
| 22 | + |
| 23 | +// Log messages |
| 24 | +logger.Info("server started", "port", 8080) |
| 25 | +logger.Debug("processing request", "id", "abc123") |
| 26 | +logger.Error("operation failed", "error", err) |
| 27 | + |
| 28 | +// Add context to all subsequent logs |
| 29 | +moduleLogger := logger.With("module", "auth") |
| 30 | +moduleLogger.Info("user authenticated", "user_id", 42) |
| 31 | +``` |
| 32 | + |
| 33 | +### With Context (Trace Correlation) |
| 34 | + |
| 35 | +```go |
| 36 | +// Use context-aware methods for trace/span correlation |
| 37 | +logger.InfoContext(ctx, "handling request", "path", "/api/v1/users") |
| 38 | +logger.ErrorContext(ctx, "request failed", "error", err) |
| 39 | +``` |
| 40 | + |
| 41 | +### Configuration Options |
| 42 | + |
| 43 | +```go |
| 44 | +logger := log.NewLogger("my-app", |
| 45 | + // Set minimum log level |
| 46 | + log.WithLevel(slog.LevelDebug), |
| 47 | + |
| 48 | + // Enable verbose mode support (for upgrades, etc.) |
| 49 | + log.WithVerboseLevel(slog.LevelDebug), |
| 50 | + |
| 51 | + // Customize console output |
| 52 | + log.WithColor(true), // Enable colored output (default: true) |
| 53 | + log.WithTimeFormat(time.RFC3339), // Custom time format (default: time.Kitchen) |
| 54 | + log.WithConsoleWriter(os.Stdout), // Custom output writer (default: os.Stderr) |
| 55 | + |
| 56 | + // Output JSON instead of pretty text |
| 57 | + log.WithJSONOutput(), |
| 58 | + |
| 59 | + // Disable console (OTEL only) |
| 60 | + log.WithoutConsole(), |
| 61 | + |
| 62 | + // Custom OTEL provider |
| 63 | + log.WithLoggerProvider(provider), |
| 64 | + |
| 65 | + // Filter logs by module/level |
| 66 | + log.WithFilter(filterFunc), |
| 67 | +) |
| 68 | +``` |
| 69 | + |
| 70 | +### Verbose Mode |
| 71 | + |
| 72 | +Verbose mode allows dynamic log level switching, useful during chain upgrades: |
| 73 | + |
| 74 | +```go |
| 75 | +logger := log.NewLogger("cosmos-sdk", |
| 76 | + log.WithLevel(slog.LevelInfo), |
| 77 | + log.WithVerboseLevel(slog.LevelDebug), |
| 78 | + log.WithFilter(myFilter), |
| 79 | +) |
| 80 | + |
| 81 | +// Cast to VerboseModeLogger to access SetVerboseMode |
| 82 | +if vl, ok := logger.(log.VerboseModeLogger); ok { |
| 83 | + // Enable verbose mode - lowers level to Debug and disables filter |
| 84 | + vl.SetVerboseMode(true) |
| 85 | + |
| 86 | + // ... perform upgrade ... |
| 87 | + |
| 88 | + // Disable verbose mode - restores original level and filter |
| 89 | + vl.SetVerboseMode(false) |
| 90 | +} |
| 91 | +``` |
| 92 | + |
| 93 | +### Log Level Filtering |
| 94 | + |
| 95 | +Parse log level configuration strings: |
| 96 | + |
| 97 | +```go |
| 98 | +// Format: "module:level,module:level,..." or just "level" for default |
| 99 | +filterFunc, err := log.ParseLogLevel("info,consensus:debug,p2p:error") |
| 100 | + |
| 101 | +logger := log.NewLogger("my-app", |
| 102 | + log.WithFilter(filterFunc), |
| 103 | +) |
| 104 | +``` |
| 105 | + |
| 106 | +### Custom Logger |
| 107 | + |
| 108 | +Wrap an existing `*slog.Logger`: |
| 109 | + |
| 110 | +```go |
| 111 | +slogger := slog.New(myCustomHandler) |
| 112 | +logger := log.NewCustomLogger(slogger) |
| 113 | +``` |
| 114 | + |
| 115 | +### No-Op Logger |
| 116 | + |
| 117 | +For testing or disabled logging: |
| 118 | + |
| 119 | +```go |
| 120 | +logger := log.NewNopLogger() |
| 121 | +``` |
| 122 | + |
| 123 | +## Architecture |
| 124 | + |
| 125 | +``` |
| 126 | +┌─────────────────────────────────────────────────────┐ |
| 127 | +│ slog.Logger │ |
| 128 | +├─────────────────────────────────────────────────────┤ |
| 129 | +│ multiHandler │ |
| 130 | +├────────────────────────┬────────────────────────────┤ |
| 131 | +│ zerologHandler │ otelHandler │ |
| 132 | +│ (console output) │ (OpenTelemetry export) │ |
| 133 | +│ │ │ |
| 134 | +│ • Pretty formatting │ • Gets ALL logs │ |
| 135 | +│ • Color support │ • No filtering │ |
| 136 | +│ • Level filtering │ • Trace correlation │ |
| 137 | +│ • Module filtering │ │ |
| 138 | +│ • Verbose mode │ │ |
| 139 | +└────────────────────────┴────────────────────────────┘ |
| 140 | +``` |
| 141 | + |
| 142 | +**Key design decisions:** |
| 143 | +- Console output respects level and filter settings |
| 144 | +- OpenTelemetry receives all logs unfiltered (for full observability) |
| 145 | +- Verbose mode only affects console output |
| 146 | + |
| 147 | +## Logger Interface |
| 148 | + |
| 149 | +```go |
| 150 | +type Logger interface { |
| 151 | + Info(msg string, keyVals ...any) |
| 152 | + Warn(msg string, keyVals ...any) |
| 153 | + Error(msg string, keyVals ...any) |
| 154 | + Debug(msg string, keyVals ...any) |
| 155 | + |
| 156 | + // Context-aware methods for trace correlation |
| 157 | + InfoContext(ctx context.Context, msg string, keyVals ...any) |
| 158 | + WarnContext(ctx context.Context, msg string, keyVals ...any) |
| 159 | + ErrorContext(ctx context.Context, msg string, keyVals ...any) |
| 160 | + DebugContext(ctx context.Context, msg string, keyVals ...any) |
| 161 | + |
| 162 | + With(keyVals ...any) Logger |
| 163 | + Impl() any // Returns underlying *slog.Logger |
| 164 | +} |
| 165 | +``` |
4 | 166 |
|
5 | | -To use a logger wrapping an instance of the standard library's `log/slog` package, use `cosmossdk.io/log/slog`. |
|
0 commit comments