Files
golib/logger/entry/README.md
nabbar 3837f0b2bb Improvements, test & documentatons (2025-12 #1)
[file/bandwidth]
- ADD documentation: add enhanced README and TESTING guidelines
- ADD tests: complete test suites with benchmarks, concurrency, and edge cases

[file/perm]
- ADD documentation: add enhanced README and TESTING guidelines
- ADD tests: complete test suites with benchmarks, concurrency, and edge cases
- ADD function to parse form "rwx-wxr-x" or "-rwx-w-r-x"
- ADD function to ParseFileMode to convert os.FileMode to file.Perm

[file/progress]
- ADD documentation: add enhanced README and TESTING guidelines
- ADD tests: complete test suites with benchmarks, concurrency, and edge cases

[ioutils/...]
- UPDATE documentation: update enhanced README and TESTING guidelines
- UPDATE tests: complete test suites with benchmarks, concurrency, and edge cases

[logger/...]
- UPDATE documentation: update enhanced README and TESTING guidelines
- ADD documentation: add enhanced README and TESTING guidelines for sub
  packages
- UPDATE tests: complete test suites with benchmarks, concurrency, and edge cases
- UPDATE config: remove FileBufferSize from OptionFile (rework hookfile)
- UPDATE fields: expose Store function in interface
- REWORK hookfile: rework package, use aggregator to allow multi write and
  single file
- FIX hookstderr: fix bug with NonColorable
- FIX hookstdout: fix bug with NonColorable
- FIX hookwriter: fix bug with NonColorable

[network/protocol]
- ADD function IsTCP, IsUDP, IsUnixLike to check type of protocol

[runner]
- FIX typo

[socket]
- UPDATE documentation: update enhanced README and TESTING guidelines
- ADD documentation: add enhanced README and TESTING guidelines for sub
  packages
- UPDATE tests: complete test suites with benchmarks, concurrency, and edge cases
- REWORK server: use context compatible io.reader, io.writer, io.closer
  instead of reader / writer
- REWORK server: simplify, optimize server
- REMOVE reader, writer type
- ADD context: add new interface in root socket interface to expose
  context interface that extend context, io reader/writer/closer,
dediacted function to server (IsConnected, ...)
2025-12-02 02:56:20 +01:00

24 KiB

Logger Entry

Go Version License Coverage

Flexible, chainable logger entry wrapper for structured logging with logrus, providing thread-safe entry construction with context information, custom fields, errors, and Gin framework integration.


Table of Contents


Overview

The entry package provides a high-level, fluent API for constructing structured log entries with logrus. It wraps logrus entries to enable method chaining, rich context information, custom fields, multiple errors, and automatic Gin framework error registration.

Design Philosophy

  1. Immutability Pattern: All setter methods return the entry itself for method chaining
  2. Lazy Evaluation: Logging deferred until Log() or Check() is called
  3. Flexible Context: Support for timestamps, stack traces, caller info, file/line numbers
  4. Safety First: Nil-safe operations throughout, no panics in production code
  5. Integration Ready: Built-in support for Gin web framework error handling

Key Features

  • Fluent API: Chain methods for concise, readable entry construction
  • Multiple Log Levels: Debug, Info, Warn, Error, Fatal, Panic, and Nil
  • Rich Context: Time, stack, caller, file, line, and message fields
  • Custom Fields: Structured key-value pairs for detailed logging
  • Error Handling: Multiple errors with automatic nil filtering
  • Data Attachment: Arbitrary data structures for complex logging
  • Gin Integration: Automatic error registration in Gin context
  • Message-Only Mode: Simple logging without structured fields
  • Thread-Safe Construction: Each entry is independent (not shared)
  • 85.8% Test Coverage: 135 comprehensive test specifications

Architecture

Component Diagram

┌──────────────────────────────────────┐
│           Entry Interface            │
│  - Configuration methods             │
│  - Field management                  │
│  - Error management                  │
│  - Logging methods                   │
└──────────────┬───────────────────────┘
               │
               ▼
┌──────────────────────────────────────┐
│         entry Implementation         │
│                                      │
│  ┌────────────────────────────────┐  │
│  │  Configuration State           │  │
│  │  - Logger function             │  │
│  │  - Gin context pointer         │  │
│  │  - Message-only flag           │  │
│  └────────────────────────────────┘  │
│               │                      │
│               ▼                      │
│  ┌────────────────────────────────┐  │
│  │  Context Information           │  │
│  │  - Time, Stack, Caller         │  │
│  │  - File, Line, Message         │  │
│  └────────────────────────────────┘  │
│               │                      │
│               ▼                      │
│  ┌────────────────────────────────┐  │
│  │  Data & Fields                 │  │
│  │  - Custom fields               │  │
│  │  - Errors slice                │  │
│  │  - Arbitrary data              │  │
│  └────────────────────────────────┘  │
│               │                      │
│               ▼                      │
│       Log to logrus                  │
└──────────────────────────────────────┘

Data Flow

New(level) → Configuration → Context → Fields → Errors → Data → Log()
     │              │            │         │        │       │      │
     │              │            │         │        │       │      ├─▶ Check errors
     │              │            │         │        │       │      │
     │              │            │         │        │       │      ├─▶ Build logrus entry
     │              │            │         │        │       │      │
     │              │            │         │        │       │      ├─▶ Add fields
     │              │            │         │        │       │      │
     │              │            │         │        │       │      ├─▶ Log at level
     │              │            │         │        │       │      │
     │              │            │         │        │       │      └─▶ Register in Gin
     │              │            │         │        │       │
     ▼              ▼            ▼         ▼        ▼       ▼
 Level        Logger      Time/Stack  Fields   Errors   Data
              Context     Caller/File  (map)   (slice)  (any)

Entry Lifecycle

  1. Creation: New(level) creates an entry with initial state
  2. Configuration: Set logger, level, gin context, message mode
  3. Context: Set time, stack, caller, file, line, message
  4. Fields: Add, merge, or set custom structured fields
  5. Errors: Add or set error information
  6. Data: Attach arbitrary data structures
  7. Logging: Call Log() or Check() to output to logrus

Performance

Benchmarks

Based on actual test results with 135 specifications:

Operation Overhead Allocations Notes
Entry Creation ~50ns 1 alloc Lightweight struct initialization
Method Chaining ~10ns/call 0 allocs Pointer returns, no copying
Field Operations ~100ns 0-1 allocs Depends on field type
Error Addition ~80ns 0-1 allocs Slice append
Log() Call ~5-50µs 2-5 allocs Logrus processing dominates
Check() Call ~5-50µs 2-5 allocs Includes Log() overhead

Memory Usage

Entry struct:         ~300 bytes (base structure)
Per custom field:     ~48 bytes (key + value + overhead)
Per error:            ~40 bytes (interface + pointer)
Typical entry:        ~500-800 bytes (with 5 fields, 1-2 errors)

Memory Profile:

  • No memory pooling (entries are short-lived)
  • Fields stored in golib/logger/fields package
  • Errors stored as slice of error interfaces
  • Data stored as interface{} (type-erased)

Scalability

  • Concurrent Creation: Unlimited (each entry is independent)
  • Fields Per Entry: Tested up to 100 fields
  • Errors Per Entry: Tested up to 50 errors
  • Not Thread-Safe: Each entry should be used by single goroutine
  • Zero Race Conditions: All tests pass with -race detector

Use Cases

1. Application Logging

Problem: Need structured logging with context information across application layers.

logger := logrus.New()
fields := logfld.New(nil)

entry.New(loglvl.InfoLevel).
    SetLogger(func() *logrus.Logger { return logger }).
    FieldSet(fields).
    FieldAdd("component", "api").
    FieldAdd("version", "1.0.0").
    SetEntryContext(time.Now(), 0, "", "", 0, "Application started").
    Log()

2. Error Tracking with Context

Problem: Log errors with full context and stack information.

fields := logfld.New(nil)

entry.New(loglvl.ErrorLevel).
    SetLogger(func() *logrus.Logger { return logger }).
    FieldSet(fields).
    ErrorAdd(true, dbError, cacheError).
    SetEntryContext(time.Now(), 12345, "ProcessRequest", "handler.go", 42, "Request failed").
    FieldAdd("user_id", userID).
    FieldAdd("request_id", reqID).
    Log()

3. HTTP Request Logging with Gin

Problem: Log HTTP errors and automatically register them in Gin context.

func handler(c *gin.Context) {
    fields := logfld.New(nil)
    
    e := entry.New(loglvl.ErrorLevel).
        SetLogger(func() *logrus.Logger { return logger }).
        SetGinContext(c).
        FieldSet(fields).
        FieldAdd("method", c.Request.Method).
        FieldAdd("path", c.Request.URL.Path)
    
    if err := processRequest(c); err != nil {
        e.ErrorAdd(true, err).
            SetEntryContext(time.Now(), 0, "", "", 0, "Request processing failed").
            Log()  // Error logged and added to c.Errors
        c.JSON(500, gin.H{"error": "Internal server error"})
        return
    }
    
    c.JSON(200, gin.H{"status": "ok"})
}

4. Conditional Error Logging

Problem: Log at different levels based on whether errors occurred.

fields := logfld.New(nil)

e := entry.New(loglvl.ErrorLevel).
    SetLogger(func() *logrus.Logger { return logger }).
    FieldSet(fields).
    ErrorAdd(true, err1, err2)

// Check() logs at ErrorLevel if errors exist, InfoLevel otherwise
if e.Check(loglvl.InfoLevel) {
    // Has errors - logged at ErrorLevel
    return fmt.Errorf("operation failed")
} else {
    // No errors - logged at InfoLevel
    return nil
}

5. Debug Logging with Full Context

Problem: Detailed debug logging with stack traces and caller information.

fields := logfld.New(nil)

entry.New(loglvl.DebugLevel).
    SetLogger(func() *logrus.Logger { return logger }).
    FieldSet(fields).
    SetEntryContext(
        time.Now(),           // timestamp
        runtime.NumGoroutine(), // goroutine count
        "DebugFunction",      // caller
        "debug.go",           // file
        156,                  // line
        "Debug checkpoint reached",
    ).
    DataSet(debugData).
    Log()

Quick Start

Installation

go get github.com/nabbar/golib/logger/entry

Basic Logging

package main

import (
    "time"
    
    logent "github.com/nabbar/golib/logger/entry"
    logfld "github.com/nabbar/golib/logger/fields"
    loglvl "github.com/nabbar/golib/logger/level"
    "github.com/sirupsen/logrus"
)

func main() {
    logger := logrus.New()
    fields := logfld.New(nil)
    
    logent.New(loglvl.InfoLevel).
        SetLogger(func() *logrus.Logger { return logger }).
        FieldSet(fields).
        SetEntryContext(time.Now(), 0, "", "", 0, "Hello, World!").
        Log()
}

Error Logging

err := doSomething()
if err != nil {
    fields := logfld.New(nil)
    
    logent.New(loglvl.ErrorLevel).
        SetLogger(func() *logrus.Logger { return logger }).
        FieldSet(fields).
        ErrorAdd(true, err).
        SetEntryContext(time.Now(), 0, "main", "main.go", 42, "Operation failed").
        Log()
}

Structured Fields

fields := logfld.New(nil)

logent.New(loglvl.InfoLevel).
    SetLogger(func() *logrus.Logger { return logger }).
    FieldSet(fields).
    FieldAdd("user_id", 12345).
    FieldAdd("action", "login").
    FieldAdd("ip", "192.168.1.1").
    FieldAdd("success", true).
    SetEntryContext(time.Now(), 0, "", "", 0, "User logged in").
    Log()

Gin Integration

import (
    "github.com/gin-gonic/gin"
    logent "github.com/nabbar/golib/logger/entry"
)

func errorHandler(c *gin.Context) {
    fields := logfld.New(nil)
    
    logent.New(loglvl.ErrorLevel).
        SetLogger(func() *logrus.Logger { return logger }).
        SetGinContext(c).  // Errors auto-registered in c.Errors
        FieldSet(fields).
        ErrorAdd(true, someError).
        SetEntryContext(time.Now(), 0, "", "", 0, "Request failed").
        Log()
}

Conditional Logging

fields := logfld.New(nil)

e := logent.New(loglvl.ErrorLevel).
    SetLogger(func() *logrus.Logger { return logger }).
    FieldSet(fields).
    ErrorAdd(true, err)

// Log at ErrorLevel if errors, InfoLevel if no errors
hasErrors := e.Check(loglvl.InfoLevel)
if hasErrors {
    // Handle error case
}

Best Practices

DO

  • Always call FieldSet() before field operations: Required for FieldAdd, FieldMerge, FieldClean
  • Use method chaining: More readable and concise entry construction
  • Filter nil errors in production: Use cleanNil=true in ErrorAdd
  • Create new entry per log statement: Entries are not designed for reuse
  • Set valid logger function: Ensure logger function returns non-nil logger
  • Use structured fields: Better than string concatenation in messages
  • Add context information: Time, caller, file, line for debugging

DON'T

  • Don't share entries across goroutines: Entries are not thread-safe
  • Don't call field methods without FieldSet: Will return nil and log nothing
  • Don't ignore FatalLevel effects: Triggers os.Exit(1) after logging
  • Don't mutate fields/errors after logging: Unpredictable behavior
  • Don't use PanicLevel in production: Only for exceptional situations
  • Don't reuse entries: Create fresh entry for each log statement
  • Don't rely on automatic caller detection: Must provide manually

Field Management Best Practices

// GOOD: Initialize fields before use
fields := logfld.New(nil)
e := entry.New(loglvl.InfoLevel).FieldSet(fields)
e.FieldAdd("key", "value")

// BAD: No FieldSet() call
e := entry.New(loglvl.InfoLevel)
e.FieldAdd("key", "value")  // Returns nil!

Error Handling Best Practices

// GOOD: Filter nil errors
e.ErrorAdd(true, err1, err2, err3)  // Nils are skipped

// LESS OPTIMAL: Include nils (for debugging)
e.ErrorAdd(false, err1, err2, err3)  // Nils included

API Reference

Entry Interface

type Entry interface {
    // Configuration
    SetLogger(fct func() *logrus.Logger) Entry
    SetLevel(lvl loglvl.Level) Entry
    SetMessageOnly(flag bool) Entry
    SetGinContext(ctx *gin.Context) Entry
    
    // Context
    SetEntryContext(etime time.Time, stack uint64, caller, file string, 
                    line uint64, msg string) Entry
    
    // Data
    DataSet(data interface{}) Entry
    
    // Fields
    FieldSet(fields logfld.Fields) Entry
    FieldAdd(key string, val interface{}) Entry
    FieldMerge(fields logfld.Fields) Entry
    FieldClean(keys ...string) Entry
    
    // Errors
    ErrorSet(err []error) Entry
    ErrorAdd(cleanNil bool, err ...error) Entry
    ErrorClean() Entry
    
    // Logging
    Check(lvlNoErr loglvl.Level) bool
    Log()
}

Configuration Methods

SetLogger: Sets logger function (required for logging)

  • Returns: Entry for chaining
  • Nil-safe: Returns nil if entry is nil

SetLevel: Changes log level dynamically

  • Parameters: Log level (Debug, Info, Warn, Error, Fatal, Panic, Nil)
  • Returns: Entry for chaining

SetMessageOnly: Enables simple message-only logging

  • Parameters: true for message-only, false for structured
  • Returns: Entry for chaining

SetGinContext: Enables Gin error registration

  • Parameters: Pointer to gin.Context
  • Returns: Entry for chaining

SetEntryContext: Sets all context information at once

  • Parameters: time, stack, caller, file, line, message
  • Returns: Entry for chaining

Field Management

FieldSet: Initializes or replaces entry fields (required first)

  • Parameters: Fields object from golib/logger/fields
  • Returns: Entry for chaining

FieldAdd: Adds single key-value pair

  • Parameters: key (string), value (interface{})
  • Returns: Entry for chaining, or nil if fields not set

FieldMerge: Merges another Fields object

  • Parameters: Fields object to merge
  • Returns: Entry for chaining, or nil if fields not set

FieldClean: Removes specific keys

  • Parameters: Variable number of keys to remove
  • Returns: Entry for chaining, or nil if fields not set

Error Management

ErrorSet: Replaces entire error slice

  • Parameters: Slice of errors
  • Returns: Entry for chaining

ErrorAdd: Appends errors to slice

  • Parameters: cleanNil (bool), errors (variadic)
  • Returns: Entry for chaining

ErrorClean: Removes all errors

  • Returns: Entry for chaining

Logging Methods

Check: Logs entry and returns true if errors exist

  • Parameters: fallback level if no errors
  • Returns: true if errors present, false otherwise
  • Side effect: Calls Log()

Log: Performs actual logging to logrus

  • No parameters
  • No return value
  • Guard conditions: entry, logger, and fields must be set

Contributing

Contributions are welcome! Please follow these guidelines:

  1. 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
  2. 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
  3. Testing

    • Add tests for new features
    • Use Ginkgo v2 / Gomega for test framework
    • Ensure zero race conditions
    • Aim for 80%+ code coverage
  4. Documentation

    • Update GoDoc comments for public APIs
    • Add examples for new features
    • Update README.md and TESTING.md if needed
  5. Pull Request Process

    • Fork the repository
    • Create a feature branch
    • Write clear commit messages
    • Ensure all tests pass
    • Update documentation
    • 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

  • 85.8% test coverage (target: >80%)
  • Zero race conditions detected with -race flag
  • Thread-safe by design (single entry per goroutine)
  • Nil-safe operations throughout the codebase
  • Memory-efficient with minimal allocations

Security Considerations

  • No panic in production: All methods handle nil gracefully
  • Error sanitization: Consider logging sensitive errors separately
  • Field validation: Ensure no sensitive data in structured fields
  • Gin integration: Errors exposed in Gin context may be visible in responses

Future Enhancements (Non-urgent)

The following enhancements could be considered for future versions:

  1. Automatic Caller Detection: Runtime stack inspection for automatic file/line detection
  2. Entry Pooling: Optional object pooling for ultra-high-frequency scenarios
  3. Sampling Support: Built-in log sampling for rate limiting
  4. Batch Logging: Optional batching for improved throughput

These are optional improvements and not required for production use. The current implementation is stable and performant.

Reporting Issues

  • Bugs: Use GitHub Issues with bug label
  • Security: Report privately via GitHub Security Advisories
  • Enhancements: Use GitHub Issues with enhancement label

See TESTING.md#reporting-bugs--vulnerabilities for templates.


Resources

Package Documentation

  • GoDoc - Complete API reference with function signatures, method descriptions, and runnable examples. Essential for understanding the public interface and usage patterns.

  • doc.go - In-depth package documentation including design philosophy, architecture diagrams, entry lifecycle, and best practices. Provides detailed explanations of internal mechanisms and production usage guidelines.

  • TESTING.md - Comprehensive test suite documentation covering test architecture, BDD methodology with Ginkgo v2, coverage analysis (85.8%), and guidelines for writing new tests. Includes troubleshooting and bug reporting templates.

  • github.com/nabbar/golib/logger/fields - Field management for structured logging. Provides thread-safe field operations, JSON serialization, and logrus integration. Required dependency for all entry field operations.

  • github.com/nabbar/golib/logger/level - Log level definitions and utilities. Provides Level enum with Debug, Info, Warn, Error, Fatal, Panic, and Nil levels. Includes conversion to/from logrus levels.

  • github.com/nabbar/golib/errors - Error handling utilities used for error unwrapping and slice extraction. Provides Error interface with enhanced error management capabilities.

External References

  • Logrus Documentation - Official logrus documentation. The entry package wraps logrus for enhanced usability while preserving all core logrus functionality and flexibility.

  • Gin Web Framework - High-performance HTTP web framework. The entry package provides automatic error registration in Gin context for seamless HTTP error handling.

  • Structured Logging Best Practices - Industry best practices for structured logging. Explains benefits of key-value pairs over string concatenation for log analysis.

  • Go Logging Guidelines - Dave Cheney's guide to logging in Go. Covers log levels, context, and production logging best practices.


AI Transparency

In compliance with EU AI Act Article 50.4: AI assistance was used for documentation, code review, test generation, and bug resolution under human supervision. All core functionality is human-designed and validated. The package architecture, API design, and implementation logic are entirely human-created.


License

MIT License - See LICENSE file for details.

Copyright (c) 2025 Nicolas JUHEL


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