Documentation
¶
Overview ¶
Package level defines log severity levels with conversions and parsing capabilities.
Design Philosophy ¶
The level package provides a standardized representation of logging severity levels that is compatible with popular logging frameworks like logrus while maintaining flexibility for custom implementations. The design prioritizes:
- Type Safety: Strong typing with custom Level type prevents invalid values
- Framework Compatibility: Direct conversion to logrus levels
- Multiple Representations: String, integer, and code representations
- Parse Flexibility: Case-insensitive parsing from strings and integers
- Simplicity: Minimal API surface with clear semantics
Architecture ¶
Log Levels (Ordered by Severity):
┌────────────────────────────────────────────────────────────┐ │ Level │ Value │ String │ Code │ Use Case │ ├───────────────┼───────┼───────────┼────────┼───────────────┤ │ PanicLevel │ 0 │ Critical │ Crit │ Panic + trace │ │ FatalLevel │ 1 │ Fatal │ Fatal │ Fatal error │ │ ErrorLevel │ 2 │ Error │ Err │ Error │ │ WarnLevel │ 3 │ Warning │ Warn │ Warning │ │ InfoLevel │ 4 │ Info │ Info │ Information │ │ DebugLevel │ 5 │ Debug │ Debug │ Debug info │ │ NilLevel │ 6 │ (empty) │ (empty)│ Disable log │ └────────────────────────────────────────────────────────────┘
Levels are ordered from most severe (PanicLevel=0) to least severe (DebugLevel=5). NilLevel (6) is special and disables logging entirely.
Representations ¶
Each level has multiple representations for different use cases:
- Type Value: The internal uint8 value (0-6)
- String: Human-readable full name (e.g., "Critical", "Info")
- Code: Short code for compact output (e.g., "Crit", "Err")
- Integer: Numeric representation for comparisons and storage
Parsing ¶
The package supports flexible parsing from various input formats:
String Parsing (Case-Insensitive):
level := level.Parse("info") // InfoLevel
level := level.Parse("ERROR") // ErrorLevel
level := level.Parse("Critical") // PanicLevel
level := level.Parse("unknown") // InfoLevel (default fallback)
Code Parsing:
level := level.Parse("Crit") // PanicLevel
level := level.Parse("Err") // ErrorLevel
level := level.Parse("Warn") // WarnLevel
Integer Parsing:
level := level.ParseFromInt(4) // InfoLevel level := level.ParseFromInt(99) // InfoLevel (invalid = default) level := level.ParseFromUint32(2) // ErrorLevel
Logrus Integration ¶
The package provides direct conversion to logrus levels:
import "github.com/sirupsen/logrus" goLibLevel := level.InfoLevel logrusLevel := goLibLevel.Logrus() // logrus.InfoLevel logger := logrus.New() logger.SetLevel(goLibLevel.Logrus())
NilLevel returns math.MaxInt32 when converted to logrus, effectively disabling all log output.
Use Cases ¶
1. Configuration Parsing
Parse log levels from configuration files:
cfgLevel := config.Get("log.level") // "debug"
level := level.Parse(cfgLevel)
logger.SetLevel(level.Logrus())
2. Log Level Validation
Validate and list available levels:
levels := level.ListLevels()
// ["critical", "fatal", "error", "warning", "info", "debug"]
for _, lvl := range levels {
fmt.Printf("%s is valid\n", lvl)
}
3. Level Comparison
Compare severity levels:
if level.ErrorLevel < level.WarnLevel {
// More severe levels have lower values
}
currentLevel := level.InfoLevel
if level.DebugLevel >= currentLevel {
// Will log at current level
}
4. Dynamic Level Changes
Change log levels at runtime:
func SetLogLevel(lvl string) error {
parsed := level.Parse(lvl)
if parsed.String() == "unknown" {
return fmt.Errorf("invalid level: %s", lvl)
}
logger.SetLevel(parsed.Logrus())
return nil
}
5. Structured Logging
Use levels in structured logging:
entry := logger.WithFields(logrus.Fields{
"level_name": level.InfoLevel.String(),
"level_code": level.InfoLevel.Code(),
"level_int": level.InfoLevel.Int(),
})
Advantages and Limitations ¶
Advantages:
- Simple and focused API with clear semantics
- Type-safe with compile-time validation
- Case-insensitive parsing for user input
- Multiple representations (string, code, int)
- Direct logrus compatibility
- Zero dependencies (except logrus for conversion)
- Immutable level definitions prevent runtime modification
- Small memory footprint (uint8 storage)
Limitations:
- Fixed set of levels (cannot add custom levels)
- String parsing has no whitespace trimming
- NilLevel cannot be parsed from string (by design)
- ParseFromUint32 clamps large values to math.MaxInt
- No trace level (use DebugLevel instead)
- Code() returns same value for unknown levels
Performance Considerations ¶
The package is designed for minimal overhead:
- Level comparisons: O(1) integer comparison
- String parsing: O(1) switch statement with case-insensitive comparison
- Conversions: O(1) direct mapping
- Memory: 1 byte per Level value (uint8)
Parsing Performance:
Parse("info"): ~10ns
ParseFromInt(4): ~5ns
String(): ~5ns
Logrus(): ~5ns
The package is suitable for high-performance logging scenarios where level checks are performed frequently.
Best Practices ¶
✓ Use Parse() for configuration values ✓ Use ParseFromInt() for numeric inputs ✓ Check for "unknown" result when parsing untrusted input ✓ Use String() for human-readable output ✓ Use Code() for compact log prefixes ✓ Use Int() for storage and comparison ✓ Use Logrus() for logrus integration
✗ Don't cast arbitrary integers to Level ✗ Don't expect Parse() to handle whitespace ✗ Don't try to parse NilLevel from strings ✗ Don't rely on unknown level behavior in production
Thread Safety ¶
The Level type is immutable and all operations are thread-safe. Multiple goroutines can safely call any method concurrently:
var level level.Level = level.InfoLevel
go func() { fmt.Println(level.String()) }()
go func() { fmt.Println(level.Int()) }()
go func() { fmt.Println(level.Logrus()) }()
Package-level functions (Parse, ParseFromInt, etc.) are also thread-safe.
Compatibility ¶
The package maintains compatibility with:
- github.com/sirupsen/logrus (primary integration)
- Standard Go integer types (int, uint8, uint32)
- String representations for configuration files
- JSON/YAML serialization (via String()/Parse())
Minimum Go version: 1.18
Examples ¶
See example_test.go for runnable examples demonstrating:
- Basic level creation and conversion
- Parsing from strings and integers
- Logrus integration
- Level comparison
- Configuration parsing
- Dynamic level changes
Example (AllLevels) ¶
Example_allLevels demonstrates all log level constants.
package main
import (
"fmt"
"github.com/nabbar/golib/logger/level"
)
func main() {
levels := []level.Level{
level.PanicLevel,
level.FatalLevel,
level.ErrorLevel,
level.WarnLevel,
level.InfoLevel,
level.DebugLevel,
level.NilLevel,
}
fmt.Println("Level constants:")
for _, lvl := range levels {
fmt.Printf(" %d: %s (%s)\n", lvl.Uint8(), lvl.String(), lvl.Code())
}
}
Output: Level constants: 0: Critical (Crit) 1: Fatal (Fatal) 2: Error (Err) 3: Warning (Warn) 4: Info (Info) 5: Debug (Debug) 6: ()
Example (Basic) ¶
Example_basic demonstrates basic level usage and conversions.
package main
import (
"fmt"
"github.com/nabbar/golib/logger/level"
)
func main() {
// Create a level
lvl := level.InfoLevel
// Get string representation
fmt.Println("String:", lvl.String())
// Get code representation
fmt.Println("Code:", lvl.Code())
// Get integer representation
fmt.Println("Int:", lvl.Int())
// Get uint8 representation
fmt.Println("Uint8:", lvl.Uint8())
}
Output: String: Info Code: Info Int: 4 Uint8: 4
Example (Comparison) ¶
Example_comparison demonstrates comparing log levels.
package main
import (
"fmt"
"github.com/nabbar/golib/logger/level"
)
func main() {
// More severe levels have lower values
fmt.Println("PanicLevel < InfoLevel:", level.PanicLevel < level.InfoLevel)
fmt.Println("ErrorLevel < DebugLevel:", level.ErrorLevel < level.DebugLevel)
// Check if a level should be logged
currentLevel := level.WarnLevel
testLevel := level.ErrorLevel
if testLevel <= currentLevel {
fmt.Println("ErrorLevel would be logged")
}
}
Output: PanicLevel < InfoLevel: true ErrorLevel < DebugLevel: true ErrorLevel would be logged
Example (ConfigurationParsing) ¶
Example_configurationParsing demonstrates parsing log levels from configuration.
package main
import (
"fmt"
"github.com/nabbar/golib/logger/level"
)
func main() {
// Simulate configuration values (use slice to maintain order)
configs := []struct {
key string
value string
}{
{"app.log.level", "debug"},
{"api.log.level", "INFO"},
{"default.log.level", "warning"},
}
for _, cfg := range configs {
lvl := level.Parse(cfg.value)
fmt.Printf("%s: %s (code: %s)\n", cfg.key, lvl.String(), lvl.Code())
}
}
Output: app.log.level: Debug (code: Debug) api.log.level: Info (code: Info) default.log.level: Warning (code: Warn)
Example (DynamicLevelChange) ¶
Example_dynamicLevelChange demonstrates changing log levels at runtime.
package main
import (
"fmt"
"github.com/nabbar/golib/logger/level"
)
func main() {
// Initial level
currentLevel := level.InfoLevel
fmt.Printf("Initial level: %s\n", currentLevel.String())
// Simulate level change request
newLevelStr := "debug"
newLevel := level.Parse(newLevelStr)
if newLevel.String() != "unknown" {
currentLevel = newLevel
fmt.Printf("Changed to: %s\n", currentLevel.String())
}
// Another change
newLevelStr = "error"
newLevel = level.Parse(newLevelStr)
if newLevel.String() != "unknown" {
currentLevel = newLevel
fmt.Printf("Changed to: %s\n", currentLevel.String())
}
}
Output: Initial level: Info Changed to: Debug Changed to: Error
Example (ListLevels) ¶
Example_listLevels demonstrates listing all available levels.
package main
import (
"fmt"
"github.com/nabbar/golib/logger/level"
)
func main() {
levels := level.ListLevels()
fmt.Println("Available levels:")
for _, lvl := range levels {
fmt.Printf(" - %s\n", lvl)
}
}
Output: Available levels: - critical - fatal - error - warning - info - debug
Example (Logrus) ¶
Example_logrus demonstrates integration with logrus.
package main
import (
"fmt"
"github.com/nabbar/golib/logger/level"
"github.com/sirupsen/logrus"
)
func main() {
// Convert to logrus level
goLibLevel := level.InfoLevel
logrusLevel := goLibLevel.Logrus()
fmt.Printf("GoLib level: %s\n", goLibLevel.String())
fmt.Printf("Logrus level: %v\n", logrusLevel)
fmt.Printf("Logrus level matches: %v\n", logrusLevel == logrus.InfoLevel)
}
Output: GoLib level: Info Logrus level: info Logrus level matches: true
Example (Parse) ¶
Example_parse demonstrates parsing levels from strings.
package main
import (
"fmt"
"github.com/nabbar/golib/logger/level"
)
func main() {
// Parse from full name (case-insensitive)
lvl1 := level.Parse("info")
fmt.Println(lvl1.String())
lvl2 := level.Parse("ERROR")
fmt.Println(lvl2.String())
lvl3 := level.Parse("Critical")
fmt.Println(lvl3.String())
// Parse from code
lvl4 := level.Parse("Warn")
fmt.Println(lvl4.String())
// Invalid input returns InfoLevel
lvl5 := level.Parse("unknown")
fmt.Println(lvl5.String())
}
Output: Info Error Critical Warning Info
Example (ParseFromInt) ¶
Example_parseFromInt demonstrates parsing levels from integers.
package main
import (
"fmt"
"github.com/nabbar/golib/logger/level"
)
func main() {
// Parse from integer value
lvl1 := level.ParseFromInt(0)
fmt.Println(lvl1.String())
lvl2 := level.ParseFromInt(4)
fmt.Println(lvl2.String())
lvl3 := level.ParseFromInt(6)
fmt.Println(lvl3.String())
// Invalid value returns InfoLevel
lvl4 := level.ParseFromInt(99)
fmt.Println(lvl4.String())
}
Output: Critical Info Info
Example (ParseFromUint32) ¶
Example_parseFromUint32 demonstrates parsing levels from uint32.
package main
import (
"fmt"
"github.com/nabbar/golib/logger/level"
)
func main() {
// Parse from uint32 value
lvl1 := level.ParseFromUint32(2)
fmt.Println(lvl1.String())
lvl2 := level.ParseFromUint32(5)
fmt.Println(lvl2.String())
// Large values are clamped
lvl3 := level.ParseFromUint32(99)
fmt.Println(lvl3.String())
}
Output: Error Debug Info
Example (Roundtrip) ¶
Example_roundtrip demonstrates roundtrip conversions.
package main
import (
"fmt"
"github.com/nabbar/golib/logger/level"
)
func main() {
// Start with a level
original := level.WarnLevel
// Convert to string and back
str := original.String()
parsed := level.Parse(str)
fmt.Printf("String roundtrip: %s -> %s -> %s\n",
original.String(), str, parsed.String())
// Convert to int and back
i := original.Int()
fromInt := level.ParseFromInt(i)
fmt.Printf("Int roundtrip: %s -> %d -> %s\n",
original.String(), i, fromInt.String())
// Convert to uint32 and back
u32 := original.Uint32()
fromUint32 := level.ParseFromUint32(u32)
fmt.Printf("Uint32 roundtrip: %s -> %d -> %s\n",
original.String(), u32, fromUint32.String())
}
Output: String roundtrip: Warning -> Warning -> Warning Int roundtrip: Warning -> 3 -> Warning Uint32 roundtrip: Warning -> 3 -> Warning
Example (Validation) ¶
Example_validation demonstrates input validation.
package main
import (
"fmt"
"github.com/nabbar/golib/logger/level"
)
func main() {
testInputs := []string{"info", "DEBUG", "invalid", "", "trace"}
fmt.Println("Validation results:")
for _, input := range testInputs {
lvl := level.Parse(input)
// Parse returns InfoLevel for invalid inputs
if lvl == level.InfoLevel {
// Check if this was actually "info" or a fallback
if input != "info" && input != "INFO" {
fmt.Printf(" %q -> %s (fallback for invalid)\n", input, lvl.String())
continue
}
}
fmt.Printf(" %q -> %s\n", input, lvl.String())
}
}
Output: Validation results: "info" -> Info "DEBUG" -> Debug "invalid" -> Info (fallback for invalid) "" -> Info (fallback for invalid) "trace" -> Info (fallback for invalid)
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ListLevels ¶
func ListLevels() []string
ListLevels returns a slice containing lowercase string representations of all standard log levels. The returned slice contains: ["critical", "fatal", "error", "warning", "info", "debug"] NilLevel is not included as it's not meant to be parsed or used in configuration. All returned strings can be parsed back using Parse().
Types ¶
type Level ¶
type Level uint8
Level represents a logging severity level as an uint8 value. It provides methods for conversion to various formats (string, int, logrus.Level) and parsing from multiple input types. Levels are ordered from most severe (PanicLevel=0) to least severe (DebugLevel=5). NilLevel (6) is a special value that disables logging.
const ( // PanicLevel is the highest severity level (value: 0). // Used for critical errors that will trigger a panic with stack trace. // String representation: "Critical", Code: "Crit" PanicLevel Level = iota // FatalLevel represents fatal errors (value: 1). // Used for errors that will cause the application to exit. // String representation: "Fatal", Code: "Fatal" FatalLevel // ErrorLevel represents errors (value: 2). // Used when the operation fails and execution stops, returning control to the caller. // String representation: "Error", Code: "Err" ErrorLevel // WarnLevel represents warnings (value: 3). // Used when an issue occurs but execution continues with degraded functionality. // String representation: "Warning", Code: "Warn" WarnLevel // InfoLevel represents informational messages (value: 4). // Used for general information about application state, events, or successful operations. // This is the default level returned by Parse() for invalid inputs. // String representation: "Info", Code: "Info" InfoLevel // DebugLevel is the lowest severity level for normal logging (value: 5). // Used for detailed diagnostic information useful during development and troubleshooting. // String representation: "Debug", Code: "Debug" DebugLevel // NilLevel is a special level that disables all logging (value: 6). // It cannot be parsed from string and returns empty strings for String() and Code(). // Converts to math.MaxInt32 when used with Logrus(). NilLevel )
func Parse ¶
Parse converts a string to its corresponding Level value. Parsing is case-insensitive and supports both full names and short codes:
- "Critical", "CRITICAL", "critical", "Crit" -> PanicLevel
- "Fatal", "FATAL", "fatal" -> FatalLevel
- "Error", "ERROR", "error", "Err" -> ErrorLevel
- "Warning", "WARNING", "warning", "Warn" -> WarnLevel
- "Info", "INFO", "info" -> InfoLevel
- "Debug", "DEBUG", "debug" -> DebugLevel
Returns InfoLevel for any unrecognized input (empty string, invalid values, etc.). Note: Parse does not trim leading/trailing whitespace. Note: NilLevel cannot be parsed from string and will return InfoLevel.
func ParseFromInt ¶ added in v1.19.0
ParseFromInt converts an integer to its corresponding Level value. Valid inputs: 0=PanicLevel, 1=FatalLevel, 2=ErrorLevel, 3=WarnLevel, 4=InfoLevel, 5=DebugLevel, 6=NilLevel. Returns InfoLevel for any value outside the valid range (negative or > 6). This function is useful for deserializing levels from numeric storage or APIs.
func ParseFromUint32 ¶ added in v1.19.0
ParseFromUint32 converts a uint32 to its corresponding Level value. Valid inputs: 0=PanicLevel, 1=FatalLevel, 2=ErrorLevel, 3=WarnLevel, 4=InfoLevel, 5=DebugLevel, 6=NilLevel. Values >= math.MaxInt are clamped to math.MaxInt before conversion (platform-dependent). Returns InfoLevel for any value outside the valid range (> 6). This function is useful for deserializing levels from 32-bit numeric storage.
func (Level) Code ¶ added in v1.19.0
Code converts the Level to its short code representation. Returns:
- PanicLevel -> "Crit"
- FatalLevel -> "Fatal"
- ErrorLevel -> "Err"
- WarnLevel -> "Warn"
- InfoLevel -> "Info"
- DebugLevel -> "Debug"
- NilLevel -> "" (empty string)
- Unknown levels -> "unknown"
Short codes are useful for compact log output or when space is limited. The returned code can be parsed back using Parse().
func (Level) Int ¶ added in v1.19.0
Int converts the Level to an int value. Examples: PanicLevel -> 0, FatalLevel -> 1, InfoLevel -> 4, NilLevel -> 6. This is useful for level comparison, storage, or when interfacing with systems that expect integer level values. Use ParseFromInt() to convert back to Level.
func (Level) Logrus ¶
Logrus converts the Level to its equivalent logrus.Level value. Mappings:
- PanicLevel -> logrus.PanicLevel
- FatalLevel -> logrus.FatalLevel
- ErrorLevel -> logrus.ErrorLevel
- WarnLevel -> logrus.WarnLevel
- InfoLevel -> logrus.InfoLevel
- DebugLevel -> logrus.DebugLevel
- NilLevel -> math.MaxInt32 (effectively disables logging)
- Unknown levels -> math.MaxInt32
This method enables seamless integration with the logrus logging library. Use this to set logrus logger levels: logger.SetLevel(level.InfoLevel.Logrus())
func (Level) String ¶
String converts the Level to its full human-readable string representation. Returns:
- PanicLevel -> "Critical"
- FatalLevel -> "Fatal"
- ErrorLevel -> "Error"
- WarnLevel -> "Warning"
- InfoLevel -> "Info"
- DebugLevel -> "Debug"
- NilLevel -> "" (empty string)
- Unknown levels -> "unknown"
The returned string can be parsed back using Parse(). This method implements the fmt.Stringer interface.
func (Level) Uint8 ¶
Uint8 converts the Level to its underlying uint8 value. Examples: PanicLevel -> 0, FatalLevel -> 1, InfoLevel -> 4, NilLevel -> 6. This is useful for compact binary serialization or when interfacing with systems that expect 8-bit level values.
func (Level) Uint32 ¶ added in v1.19.0
Uint32 converts the Level to a uint32 value. Examples: PanicLevel -> 0, FatalLevel -> 1, InfoLevel -> 4, NilLevel -> 6. This is useful when interfacing with 32-bit APIs or for compatibility with systems that use uint32 for level representation. Use ParseFromUint32() to convert back to Level.