level

package
v1.19.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 11, 2026 License: MIT Imports: 3 Imported by: 0

README

Logger Level

Go Version License Coverage

Type-safe logging level definitions with parsing, validation, and logrus integration, providing standardized severity levels for structured logging applications.


Table of Contents


Overview

The level package provides a type-safe representation of logging severity levels with multiple formats (string, code, integer) and seamless logrus integration. It offers flexible parsing from various input sources while maintaining strict type safety and performance.

Design Philosophy
  1. Type Safety: Strong typing with custom Level type prevents invalid values
  2. Framework Compatibility: Direct conversion to logrus levels for zero-overhead integration
  3. Multiple Representations: String, integer, and code formats for different use cases
  4. Parse Flexibility: Case-insensitive parsing from strings and integers
  5. Simplicity: Minimal API surface with clear semantics and zero dependencies
Key Features
  • Type-Safe Levels: Compile-time safety with custom uint8 type
  • Multiple Formats: String ("Info"), Code ("Info"), Integer (4) representations
  • Logrus Integration: Direct conversion to logrus.Level
  • Flexible Parsing: Case-insensitive string and integer parsing
  • Level Comparison: Ordered severity for threshold filtering
  • Zero Dependencies: Only Go stdlib and logrus
  • High Performance: <10ns for conversions, O(1) operations

Architecture

Component Diagram
┌────────────────────────────────────────────────────────┐
│                    Level Package                       │
├────────────────────────────────────────────────────────┤
│                                                        │
│  Level Type (uint8)                                    │
│  ┌──────────────────────────────────────────────────┐  │
│  │  Constants (Severity Order)                      │  │
│  │  ┌────────────────────────────────────────────┐  │  │
│  │  │  PanicLevel   = 0  (Most severe)           │  │  │
│  │  │  FatalLevel   = 1                          │  │  │
│  │  │  ErrorLevel   = 2                          │  │  │
│  │  │  WarnLevel    = 3                          │  │  │
│  │  │  InfoLevel    = 4  (Default)               │  │  │
│  │  │  DebugLevel   = 5                          │  │  │
│  │  │  NilLevel     = 6  (Disable logging)       │  │  │
│  │  └────────────────────────────────────────────┘  │  │
│  └──────────────────────────────────────────────────┘  │
│                                                        │
│  Parsing Functions                                     │
│  ┌──────────────────────────────────────────────────┐  │
│  │  Parse(s string) → Level                         │  │
│  │  ParseFromInt(i int) → Level                     │  │
│  │  ParseFromUint32(u uint32) → Level               │  │
│  │  ListLevels() → []string                         │  │
│  └──────────────────────────────────────────────────┘  │
│                                                        │
│  Conversion Methods                                    │
│  ┌──────────────────────────────────────────────────┐  │
│  │  String() → string    ("Critical", "Info", ...)  │  │
│  │  Code() → string      ("Crit", "Info", ...)      │  │
│  │  Int() → int          (0, 4, ...)                │  │
│  │  Uint8() → uint8      (0, 4, ...)                │  │
│  │  Uint32() → uint32    (0, 4, ...)                │  │
│  │  Logrus() → logrus.Level                         │  │
│  └──────────────────────────────────────────────────┘  │
│                                                        │
└────────────────────────────────────────────────────────┘
Level Hierarchy

Levels are ordered from most severe (0) to least severe (6):

┌───────────────────────────────────────────────────────┐
│  Level        │ Value │ String    │ Code   │ Logrus   │
├───────────────┼───────┼───────────┼────────┼──────────┤
│  PanicLevel   │   0   │ Critical  │ Crit   │ Panic    │
│  FatalLevel   │   1   │ Fatal     │ Fatal  │ Fatal    │
│  ErrorLevel   │   2   │ Error     │ Err    │ Error    │
│  WarnLevel    │   3   │ Warning   │ Warn   │ Warning  │
│  InfoLevel    │   4   │ Info      │ Info   │ Info     │
│  DebugLevel   │   5   │ Debug     │ Debug  │ Debug    │
│  NilLevel     │   6   │ (empty)   │ (empty)│ MaxInt32 │
└───────────────────────────────────────────────────────┘

Design Choices:

  • Lower values = higher severity (enables simple level <= threshold checks)
  • NilLevel (6) disables logging when converted to logrus
  • String() returns full name, Code() returns compact form
  • Case-insensitive parsing for user-friendly configuration

Performance

Benchmarks

Performance results from typical usage scenarios (AMD Ryzen 9 7900X3D):

Operation Time/op Throughput Notes
Parse("info") ~10 ns 100M ops/s String parsing
ParseFromInt(4) ~5 ns 200M ops/s Integer parsing
String() ~5 ns 200M ops/s String conversion
Code() ~5 ns 200M ops/s Code conversion
Logrus() ~5 ns 200M ops/s Logrus conversion
Int() ~2 ns 500M ops/s Integer conversion

Key Insights:

  • Ultra-Fast: All operations complete in <10ns
  • Zero Allocations: All operations are stack-based
  • Constant Time: O(1) for all operations (switch statement)
  • Cache-Friendly: uint8 type fits in CPU register
Memory Usage
Component Size Notes
Level value 1 byte uint8 storage
Parse result 1 byte No allocation
Conversion 0 bytes Stack-based
Total 1 byte Per level value

Memory Characteristics:

  • Minimal Footprint: 1 byte per level value
  • Zero Heap: All operations use stack memory
  • Cache Efficient: Fits in CPU registers
  • No Allocations: Conversions are allocation-free
Scalability

Concurrency:

  • ✅ Thread-safe: All operations are read-only after initialization
  • ✅ No synchronization needed: Immutable constants
  • ✅ No contention: No shared mutable state
  • ✅ Tested: All tests pass with -race detector (0 races)

Performance Characteristics:

  • Parse operations scale linearly with CPU cores
  • No lock contention or synchronization overhead
  • Suitable for high-throughput logging (millions of ops/s)
  • Constant memory usage regardless of scale

Use Cases

1. Configuration File Parsing

Problem: Parse log levels from YAML/JSON configuration files with user-friendly strings.

Solution: Use Parse() for case-insensitive string parsing.

Advantages:

  • Accepts multiple formats ("info", "INFO", "Info")
  • Returns default (InfoLevel) for invalid inputs
  • No error handling needed for configuration defaults
  • Fast parsing suitable for startup configuration

Suited for: Web applications, microservices, CLI tools loading configuration files.

2. Dynamic Log Level Changes

Problem: Change log levels at runtime based on user input or signals.

Solution: Parse user input and update logger configuration dynamically.

Advantages:

  • Validation through parsing (invalid → default)
  • List available levels with ListLevels()
  • Immediate effect on logging behavior
  • No restart required

Suited for: Production systems requiring runtime debugging, admin panels, monitoring tools.

3. Log Level Filtering

Problem: Filter log messages based on severity threshold.

Solution: Compare level values using integer comparison.

Advantages:

  • Simple comparison: if currentLevel <= threshold
  • Ordered by severity (0 = most severe)
  • Fast integer comparison (<5ns)
  • Type-safe operations

Suited for: Log aggregators, filtering proxies, monitoring systems requiring threshold-based filtering.

4. Logrus Integration

Problem: Use standardized level definitions with logrus logger.

Solution: Convert Level to logrus.Level using Logrus() method.

Advantages:

  • Direct mapping to logrus levels
  • Zero-overhead conversion
  • Type-safe integration
  • NilLevel disables logging (returns MaxInt32)

Suited for: Applications using logrus for structured logging, libraries exposing logging configuration.

5. Multi-Format Level Display

Problem: Display levels in different formats (full name, code, integer) for different audiences.

Solution: Use String() for humans, Code() for compact output, Int() for storage/APIs.

Advantages:

  • Multiple representations without duplication
  • Consistent conversion from single source
  • Flexible output formatting
  • Suitable for dashboards, logs, APIs

Suited for: Monitoring dashboards, log viewers, REST APIs exposing logging configuration.


Quick Start

Installation
go get github.com/nabbar/golib/logger/level

Requirements:

  • Go 1.24 or higher
  • Compatible with Linux, macOS, Windows
Basic Level Usage

Create and use levels:

package main

import (
    "fmt"
    "github.com/nabbar/golib/logger/level"
)

func main() {
    // Use predefined constants
    currentLevel := level.InfoLevel
    
    fmt.Printf("Level value: %d\n", currentLevel)          // 4
    fmt.Printf("Level string: %s\n", currentLevel.String()) // "Info"
    fmt.Printf("Level code: %s\n", currentLevel.Code())     // "Info"
}
Parsing from Strings

Parse levels from configuration:

package main

import (
    "fmt"
    "github.com/nabbar/golib/logger/level"
)

func main() {
    // Case-insensitive parsing
    lvl1 := level.Parse("info")      // InfoLevel
    lvl2 := level.Parse("ERROR")     // ErrorLevel
    lvl3 := level.Parse("Critical")  // PanicLevel
    lvl4 := level.Parse("unknown")   // InfoLevel (fallback)
    
    fmt.Printf("Parsed: %s, %s, %s, %s\n",
        lvl1.String(), lvl2.String(), lvl3.String(), lvl4.String())
    
    // List available levels
    levels := level.ListLevels()
    fmt.Printf("Available levels: %v\n", levels)
    // ["critical", "fatal", "error", "warning", "info", "debug"]
}
Logrus Integration

Use with logrus logger:

package main

import (
    "github.com/nabbar/golib/logger/level"
    "github.com/sirupsen/logrus"
)

func main() {
    logger := logrus.New()
    
    // Parse level from config
    configLevel := "debug"
    lvl := level.Parse(configLevel)
    
    // Set logger level
    logger.SetLevel(lvl.Logrus())
    
    // Use logger
    logger.Debug("Debug message")  // Will be logged
    logger.Info("Info message")    // Will be logged
}
Level Comparison

Filter messages by severity:

package main

import (
    "fmt"
    "github.com/nabbar/golib/logger/level"
)

func shouldLog(messageLevel, threshold level.Level) bool {
    // Lower values = higher severity
    return messageLevel <= threshold
}

func main() {
    threshold := level.InfoLevel
    
    fmt.Printf("Error logged: %v\n", shouldLog(level.ErrorLevel, threshold))  // true
    fmt.Printf("Debug logged: %v\n", shouldLog(level.DebugLevel, threshold))  // false
}
Configuration Parsing

Parse from different sources:

package main

import (
    "fmt"
    "github.com/nabbar/golib/logger/level"
)

func main() {
    // From string (config file)
    strLevel := level.Parse("warning")
    
    // From integer (database/API)
    intLevel := level.ParseFromInt(2)  // ErrorLevel
    
    // From uint32 (network protocol)
    uintLevel := level.ParseFromUint32(4)  // InfoLevel
    
    fmt.Printf("String: %s, Int: %s, Uint: %s\n",
        strLevel.String(), intLevel.String(), uintLevel.String())
}

Best Practices

Testing

The package includes comprehensive tests with 98.0% code coverage and 94 test specifications using BDD methodology (Ginkgo v2 + Gomega).

For detailed test documentation, see TESTING.md.

✅ DO

Use Parse() for configuration values:

// ✅ GOOD: Case-insensitive, defaults to InfoLevel
configLevel := os.Getenv("LOG_LEVEL")
level := level.Parse(configLevel)
logger.SetLevel(level.Logrus())

Use constants for known levels:

// ✅ GOOD: Type-safe, compile-time checked
currentLevel := level.InfoLevel
if messageLevel <= currentLevel {
    log(message)
}

Use String() for human output:

// ✅ GOOD: Human-readable
fmt.Printf("Current log level: %s\n", level.InfoLevel.String())  // "Info"

Use Code() for compact display:

// ✅ GOOD: Compact log prefixes
logEntry := fmt.Sprintf("[%s] %s", level.ErrorLevel.Code(), message)  // "[Err] ..."

Use Int() for storage/comparison:

// ✅ GOOD: Efficient storage and comparison
config.LogLevel = level.InfoLevel.Int()  // Store as integer
if level.ParseFromInt(config.LogLevel) <= threshold {
    // Compare using integers
}

Check ListLevels() for validation:

// ✅ GOOD: List valid levels for user
levels := level.ListLevels()
fmt.Printf("Valid levels: %s\n", strings.Join(levels, ", "))
❌ DON'T

Don't cast arbitrary integers to Level:

// ❌ BAD: Unchecked cast
var lvl level.Level = 99  // Invalid value!

// ✅ GOOD: Use parsing functions
lvl := level.ParseFromInt(99)  // Returns InfoLevel (safe default)

Don't expect Parse() to handle whitespace:

// ❌ BAD: No whitespace trimming
lvl := level.Parse(" info ")  // Returns InfoLevel (fallback)

// ✅ GOOD: Trim before parsing
lvl := level.Parse(strings.TrimSpace(configValue))

Don't try to parse NilLevel from strings:

// ❌ BAD: NilLevel not parseable from string
lvl := level.Parse("nil")  // Returns InfoLevel (fallback)

// ✅ GOOD: Use constant directly
lvl := level.NilLevel

Don't rely on unknown level behavior in production:

// ❌ BAD: Assuming unknown returns specific value
lvl := level.Parse(userInput)
// No validation, might get InfoLevel fallback

// ✅ GOOD: Validate input
lvl := level.Parse(userInput)
if lvl.String() == "unknown" {
    return fmt.Errorf("invalid log level: %s", userInput)
}

Don't use String() for code comparison:

// ❌ BAD: String comparison
if level.String() == "Info" { ... }

// ✅ GOOD: Direct comparison
if level == level.InfoLevel { ... }

API Reference

Level Type
type Level uint8

Custom type representing logging severity levels. Ordered from most severe (0) to least severe (6).

Properties:

  • Underlying type: uint8 (1 byte)
  • Comparable: Supports ==, !=, <, <=, >, >=
  • Serializable: Can be stored as integer
  • Thread-safe: Immutable after creation
Constants
const (
    PanicLevel  Level = 0  // Critical errors causing panic
    FatalLevel  Level = 1  // Fatal errors causing exit
    ErrorLevel  Level = 2  // Error conditions
    WarnLevel   Level = 3  // Warning conditions
    InfoLevel   Level = 4  // Informational messages (default)
    DebugLevel  Level = 5  // Debug-level messages
    NilLevel    Level = 6  // Disable logging
)

Constants Usage:

  • Lower values indicate higher severity
  • InfoLevel (4) is the recommended default
  • NilLevel (6) disables logging when converted to logrus
  • All constants are immutable and thread-safe
Parsing Functions

ListLevels() []string

  • Returns list of valid level strings
  • Excludes NilLevel (not user-parseable)
  • Returned slice: ["critical", "fatal", "error", "warning", "info", "debug"]
  • Use for validation, help messages, dropdowns

Parse(s string) Level

  • Parses string to Level (case-insensitive)
  • Returns InfoLevel for invalid input (safe default)
  • Accepts: full names ("Critical", "Info") and codes ("Crit", "Err")
  • No error return (always succeeds with fallback)

ParseFromInt(i int) Level

  • Converts integer to Level
  • Valid range: 0-6
  • Returns InfoLevel for out-of-range values
  • Use for database/API integer representations

ParseFromUint32(u uint32) Level

  • Converts uint32 to Level
  • Clamps large values to math.MaxInt
  • Returns InfoLevel for out-of-range values
  • Use for network protocols, binary formats
Conversion Methods

Level Methods:

func (l Level) Uint8() uint8        // Returns underlying uint8 value
func (l Level) Uint32() uint32      // Returns value as uint32
func (l Level) Int() int            // Returns value as int
func (l Level) String() string      // Returns full name ("Info", "Error", ...)
func (l Level) Code() string        // Returns code ("Info", "Err", ...)
func (l Level) Logrus() logrus.Level // Converts to logrus.Level

Method Details:

  • Uint8(): Direct access to underlying value, fastest conversion
  • Uint32(): For protocols requiring uint32, zero-allocation cast
  • Int(): For storage in databases, configuration files
  • String(): Human-readable full name, use for logs, UI
  • Code(): Compact form, use for log prefixes, narrow displays
  • Logrus(): Direct mapping to logrus levels, NilLevel → math.MaxInt32

Return Values by Level:

Level Uint8() Int() String() Code() Logrus()
PanicLevel 0 0 "Critical" "Crit" logrus.PanicLevel
FatalLevel 1 1 "Fatal" "Fatal" logrus.FatalLevel
ErrorLevel 2 2 "Error" "Err" logrus.ErrorLevel
WarnLevel 3 3 "Warning" "Warn" logrus.WarnLevel
InfoLevel 4 4 "Info" "Info" logrus.InfoLevel
DebugLevel 5 5 "Debug" "Debug" logrus.DebugLevel
NilLevel 6 6 "" "" math.MaxInt32
Unknown - - "unknown" "unknown" logrus.InfoLevel

Contributing

Contributions are welcome! Please follow these guidelines:

Code Quality
  • Follow Go best practices and idioms
  • Maintain or improve code coverage (target: >80%)
  • Pass all tests including race detector
  • Use gofmt and golint
AI Usage Policy
  • AI must NEVER be used to generate package code or core functionality
  • AI assistance is limited to:
    • Testing (writing and improving tests)
    • Debugging (troubleshooting and bug resolution)
    • Documentation (comments, README, TESTING.md)
  • All AI-assisted work must be reviewed and validated by humans
Testing Requirements
  • Add tests for new features
  • Use Ginkgo v2 / Gomega for test framework
  • Ensure zero race conditions with -race flag
  • Update examples for new functionality
Documentation Requirements
  • Update GoDoc comments for public APIs
  • Add runnable examples for new features
  • Update README.md and TESTING.md if needed
  • Include usage examples in doc.go
Pull Request Process
  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Write clear commit messages
  4. Ensure all tests pass (go test -race ./...)
  5. Update documentation
  6. Submit PR with description of changes

Improvements & Security

Current Status

The package is production-ready with no urgent improvements or security vulnerabilities identified.

Code Quality Metrics
  • 98.0% test coverage (target: >80%)
  • Zero race conditions detected with -race flag
  • Thread-safe by design (immutable constants)
  • Memory-safe with proper validation
  • Standard interfaces for maximum compatibility
Security Considerations

No Security Vulnerabilities Identified:

  • No external dependencies (only Go stdlib and logrus)
  • No network operations or file system access
  • No cryptographic operations
  • No user input parsing vulnerabilities (safe defaults)
  • Integer parsing prevents overflow (ParseFromUint32 clamps values)

Best Practices Applied:

  • Input validation with safe defaults (Parse returns InfoLevel for invalid)
  • No panic on invalid input
  • Immutable constants prevent modification
  • Type safety prevents invalid values at compile time
  • Defensive nil checks in Logrus() method
Future Enhancements (Non-urgent)

The following enhancements could be considered for future versions:

Level Features:

  1. Trace Level: Add TraceLevel below DebugLevel for ultra-verbose logging
  2. Custom Levels: Support for user-defined levels beyond predefined set
  3. Level Ranges: Parse level ranges ("error-fatal") for filtering
  4. Level Groups: Predefined level groups (PRODUCTION, DEVELOPMENT)

Parsing Improvements:

  1. Whitespace Handling: Automatic trimming in Parse()
  2. Alias Support: Additional aliases ("err" → ErrorLevel, "warn" → WarnLevel)
  3. Localization: Support for non-English level names
  4. Strict Parsing: Optional ParseStrict() returning error for invalid input

Integration:

  1. Zap Integration: Direct conversion to uber/zap levels
  2. Zerolog Integration: Direct conversion to zerolog levels
  3. Standard log Integration: Conversion to standard library log levels
  4. Custom Mappers: Plugin system for custom logger frameworks

These are optional improvements and not required for production use. The current implementation is stable, performant, and feature-complete for its intended use cases.

Suggestions and contributions are welcome via GitHub issues.


Resources

Package Documentation
  • GoDoc - Complete API reference with function signatures, method descriptions, and runnable examples. Essential for understanding the public interface, level constants, and conversion methods. Includes detailed examples for parsing, comparison, and logrus integration.

  • doc.go - In-depth package documentation including design philosophy (type safety, framework compatibility, multiple representations, parse flexibility, simplicity), architecture diagrams (level hierarchy, data flow), representations (type value, string, code, integer), parsing strategies, logrus integration, use cases (5 detailed scenarios), advantages and limitations, performance considerations, best practices, thread safety, compatibility, and comprehensive examples.

  • TESTING.md - Comprehensive test suite documentation covering test architecture (test matrix with 94 specs, detailed inventory), BDD methodology with Ginkgo v2, ISTQB alignment (test levels, types, design techniques), testing pyramid distribution, 98.0% coverage analysis with uncovered code justification (ParseFromUint32 edge case), thread safety assurance, and guidelines for writing new tests. Includes troubleshooting, CI integration examples, and bug reporting templates.

  • github.com/nabbar/golib/logger - Main logger package that uses level definitions. The level package provides standardized severity levels consumed by the logger for log filtering, threshold comparisons, and configuration. Understanding both packages together enables effective logging implementation with proper level management.

  • github.com/nabbar/golib/logger/config - Logger configuration structures using level package. The config package references level strings in LogLevel arrays for output filtering. Integration enables per-output level configuration (stdout, files, syslog) with validation and inheritance.

External References
  • github.com/sirupsen/logrus - Structured logger for Go. The level package provides seamless integration through direct logrus.Level conversion, enabling structured logging with standardized severity levels. All golib level constants map directly to logrus levels except NilLevel which disables logging.

  • Effective Go - Official Go programming guide covering best practices for type definitions, constants, error handling, and package design. The level package follows these conventions for idiomatic Go code, particularly in type safety, immutable constants, and zero-value handling.

Community & Support
  • GitHub Issues - Report bugs, request features, or ask questions about the level package. Check existing issues before creating new ones to avoid duplicates. Use appropriate labels (bug, enhancement, documentation) for faster triage.

  • Contributing Guide - Detailed guidelines for contributing code, tests, and documentation to the project. Includes code style requirements (gofmt, golint), testing procedures (Ginkgo/Gomega, coverage targets >80%), pull request process, and AI usage policy. Essential reading before submitting contributions.


AI Transparency

In compliance with EU AI Act Article 50.4: AI assistance was used for testing, documentation, and bug resolution under human supervision. All core functionality is human-designed and validated.


License

MIT License - See LICENSE file for details.

Copyright (c) 2021 Nicolas JUHEL


Maintained by: Nicolas JUHEL
Package: github.com/nabbar/golib/logger/level
Version: See releases for versioning

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:

  1. Type Safety: Strong typing with custom Level type prevents invalid values
  2. Framework Compatibility: Direct conversion to logrus levels
  3. Multiple Representations: String, integer, and code representations
  4. Parse Flexibility: Case-insensitive parsing from strings and integers
  5. 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:

  1. Type Value: The internal uint8 value (0-6)
  2. String: Human-readable full name (e.g., "Critical", "Info")
  3. Code: Short code for compact output (e.g., "Crit", "Err")
  4. 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

func Parse(l string) Level

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

func ParseFromInt(i int) Level

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

func ParseFromUint32(i uint32) Level

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

func (l Level) Code() string

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

func (l Level) Int() int

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

func (l Level) Logrus() logrus.Level

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

func (l Level) String() 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

func (l Level) Uint8() 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

func (l Level) Uint32() uint32

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL