entry

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: 9 Imported by: 0

README

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

Documentation

Overview

Package entry provides a flexible, chainable logger entry wrapper for structured logging with logrus.

Overview

The entry package wraps logrus entries to provide a fluent, chainable API for constructing structured log entries with context information, custom fields, errors, and arbitrary data. It supports integration with Gin web framework for automatic error registration.

Design Philosophy

The package follows these core principles:

  1. Immutability Pattern: All setter methods return the entry itself, enabling method chaining while maintaining a fluent API design.

  2. Lazy Evaluation: The actual logging to logrus is deferred until the Log() or Check() method is called, allowing entries to be built incrementally.

  3. Flexible Context: Entries can include timestamps, stack traces, caller information, file/line numbers, custom fields, arbitrary data, and multiple errors.

  4. Safety First: All methods handle nil entries gracefully, returning nil or appropriate defaults rather than panicking.

Key Features

  • Fluent API: Chain methods for concise entry construction
  • Multiple log levels: Debug, Info, Warn, Error, Fatal, Panic, and Nil
  • Rich context: Time, stack, caller, file, line, and message
  • Custom fields: Add structured key-value pairs
  • Error handling: Multiple errors with nil filtering
  • Data attachment: Attach arbitrary data structures
  • Gin integration: Automatic error registration in Gin context
  • Message-only mode: Simple logging without structured fields

Architecture

The package consists of the following components:

┌──────────────────────────────────────┐
│           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                  │
└──────────────────────────────────────┘

Entry Lifecycle

A typical entry follows this 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

Logging Behavior

The Log() method behavior depends on the entry configuration:

Normal Mode (clean=false):

  • Includes all context fields (time, stack, caller, file, line)
  • Includes custom fields and data
  • Formats errors as comma-separated string
  • Respects log level filtering
  • Registers errors in Gin context if configured

Message-Only Mode (clean=true):

  • Outputs only the message text
  • Ignores all context fields and custom fields
  • Uses simple Info level logging
  • Suitable for console output or simple logs

Special Cases:

  • NilLevel entries are never logged
  • Nil logger or nil fields prevent logging
  • FatalLevel triggers os.Exit(1) after logging

Level Hierarchy

Log levels from most to least verbose:

  • PanicLevel: System panic situations
  • FatalLevel: Fatal errors (triggers exit)
  • ErrorLevel: Error conditions
  • WarnLevel: Warning conditions
  • InfoLevel: Informational messages
  • DebugLevel: Debug information
  • TraceLevel: Detailed trace information
  • NilLevel: Disabled logging

Field Management

Fields are managed through the logger/fields package and provide:

  • Type-safe key-value storage
  • Merge operations for combining field sets
  • Deletion of specific keys
  • Integration with logrus.Fields

Fields must be initialized with FieldSet() before using FieldAdd(), FieldMerge(), or FieldClean().

Error Handling

The package supports multiple error patterns:

Standard Errors:

  • Add individual errors with ErrorAdd()
  • Set entire error slice with ErrorSet()
  • Clear errors with ErrorClean()

Error Filtering:

  • ErrorAdd(cleanNil=true, ...) filters out nil errors
  • ErrorAdd(cleanNil=false, ...) includes nil errors

Wrapped Errors:

  • Automatically unwraps github.com/nabbar/golib/errors
  • Extracts error slices from wrapped errors
  • Supports fmt.Errorf %w wrapping

Integration Points

Logrus Integration:

  • Requires logger function returning *logrus.Logger
  • Uses logrus.Entry for actual logging
  • Supports all logrus formatters and hooks

Gin Integration:

  • SetGinContext() enables automatic error registration
  • Errors are added to Gin context error slice
  • Useful for HTTP error handling and middleware

Performance Considerations

Memory:

  • Entry struct is lightweight (~300 bytes base)
  • Fields and errors use slice allocation
  • No memory pooling (entries are short-lived)

CPU:

  • Method chaining has minimal overhead
  • Field operations delegate to logger/fields package
  • Log() method performs string building and formatting

Concurrency:

  • Entries are NOT thread-safe
  • Create separate entry per goroutine
  • Logger function should return thread-safe logger

Use Cases

Simple Logging:

entry.New(loglvl.InfoLevel).
    SetLogger(loggerFunc).
    FieldSet(fields).
    SetEntryContext(time.Now(), 0, "", "", 0, "Hello").
    Log()

Error Logging:

entry.New(loglvl.ErrorLevel).
    SetLogger(loggerFunc).
    FieldSet(fields).
    ErrorAdd(true, err1, err2).
    SetEntryContext(time.Now(), 0, "func", "file.go", 42, "Failed").
    Log()

Structured Logging with Data:

entry.New(loglvl.InfoLevel).
    SetLogger(loggerFunc).
    FieldSet(fields).
    FieldAdd("user_id", userID).
    DataSet(requestData).
    SetEntryContext(time.Now(), 0, "", "", 0, "Request processed").
    Log()

Gin Error Registration:

e := entry.New(loglvl.ErrorLevel).
    SetLogger(loggerFunc).
    SetGinContext(c).
    FieldSet(fields).
    ErrorAdd(true, dbError).
    SetEntryContext(time.Now(), 0, "", "", 0, "Database failed")
e.Log() // Registers error in c.Errors

Conditional Logging:

e := entry.New(loglvl.ErrorLevel).
    SetLogger(loggerFunc).
    FieldSet(fields).
    ErrorAdd(true, err)
if e.Check(loglvl.InfoLevel) {
    // Has errors, logged at ErrorLevel
} else {
    // No errors, logged at InfoLevel
}

Best Practices

DO:

  • Always call FieldSet() before FieldAdd/FieldMerge/FieldClean
  • Use method chaining for concise entry construction
  • Filter nil errors with cleanNil=true in production
  • Create new entry per log statement
  • Set logger function that returns valid logger

DON'T:

  • Share entries across goroutines
  • Call FieldAdd/FieldMerge/FieldClean without FieldSet
  • Ignore FatalLevel effects (os.Exit)
  • Mutate fields/errors after logging
  • Use PanicLevel in production code

Limitations

  • No automatic caller detection (must provide manually)
  • No log buffering or batching
  • No built-in sampling or rate limiting
  • Fields must be initialized before use
  • Not thread-safe (by design)

Dependencies

Standard Library:

  • os: For exit on fatal level
  • strings: For error message joining
  • time: For timestamp handling

External:

  • github.com/sirupsen/logrus: Core logging engine
  • github.com/gin-gonic/gin: Gin framework integration
  • github.com/nabbar/golib/logger/fields: Field management
  • github.com/nabbar/golib/logger/level: Log level definitions
  • github.com/nabbar/golib/logger/types: Type constants
  • github.com/nabbar/golib/errors: Error utilities

Package Status

This package is production-ready and stable. It is widely used in nabbar/golib ecosystem for structured logging across multiple packages.

Example (BasicLogging)

Example_basicLogging demonstrates the simplest usage of the entry package for basic structured logging with logrus.

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() {
	// Create a logger
	logger := logrus.New()
	logger.SetLevel(logrus.InfoLevel)

	// Create fields for structured data
	fields := logfld.New(nil)

	// Create and log an entry
	logent.New(loglvl.InfoLevel).
		SetLogger(func() *logrus.Logger { return logger }).
		FieldSet(fields).
		SetEntryContext(time.Now(), 0, "", "", 0, "Application started").
		Log()
}
Example (ComplexWorkflow)

Example_complexWorkflow demonstrates a complete, complex logging workflow combining all major features.

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() {
	// Setup
	logger := logrus.New()
	logger.SetFormatter(&logrus.JSONFormatter{})

	baseFields := logfld.New(nil)
	baseFields.Add("service", "payment-api")
	baseFields.Add("environment", "production")

	// Simulate processing
	userID := 12345
	amount := 99.99
	var processingErr error

	// Build comprehensive log entry
	entry := logent.New(loglvl.InfoLevel).
		SetLogger(func() *logrus.Logger { return logger }).
		FieldSet(baseFields).
		FieldAdd("user_id", userID).
		FieldAdd("amount", amount).
		FieldAdd("currency", "USD")

	// Add transaction data
	txData := map[string]interface{}{
		"transaction_id": "tx-789",
		"payment_method": "credit_card",
		"timestamp":      time.Now().Unix(),
	}
	entry.DataSet(txData)

	// Check for errors
	if processingErr != nil {
		entry.SetLevel(loglvl.ErrorLevel).
			ErrorAdd(true, processingErr).
			SetEntryContext(time.Now(), 0, "ProcessPayment", "payment.go", 156, "Payment processing failed")
	} else {
		entry.SetEntryContext(time.Now(), 0, "ProcessPayment", "payment.go", 180, "Payment processed successfully")
	}

	// Log the entry
	entry.Log()
}
Example (ConditionalLogging)

Example_conditionalLogging demonstrates using Check() for conditional logging with different levels based on error presence.

package main

import (
	"fmt"

	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)

	// Simulate operation that may fail
	var err error
	// err = performOperation()

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

	// Check will log at ErrorLevel if errors exist, InfoLevel otherwise
	hasErrors := entry.Check(loglvl.InfoLevel)

	if hasErrors {
		fmt.Println("Operation failed with errors")
	} else {
		fmt.Println("Operation succeeded")
	}
}
Example (ContextInformation)

Example_contextInformation demonstrates logging with detailed context information including stack traces, caller information, and file/line numbers.

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)

	// Log with full context information
	logent.New(loglvl.DebugLevel).
		SetLogger(func() *logrus.Logger { return logger }).
		FieldSet(fields).
		SetEntryContext(
			time.Now(),                // timestamp
			12345,                     // goroutine stack number
			"ProcessRequest",          // caller function name
			"handler.go",              // file name
			78,                        // line number
			"Processing user request", // message
		).
		Log()
}
Example (DataAttachment)

Example_dataAttachment demonstrates attaching arbitrary data structures.

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)

	// Create data structure
	data := map[string]interface{}{
		"request_id":  "req-123",
		"duration_ms": 450,
		"status_code": 200,
	}

	logent.New(loglvl.InfoLevel).
		SetLogger(func() *logrus.Logger { return logger }).
		FieldSet(fields).
		DataSet(data).
		SetEntryContext(time.Now(), 0, "", "", 0, "Request completed").
		Log()
}
Example (ErrorLogging)

Example_errorLogging demonstrates logging with error information.

package main

import (
	"errors"
	"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()
	logger.SetLevel(logrus.ErrorLevel)

	fields := logfld.New(nil)

	// Create an error entry
	err := errors.New("database connection failed")

	logent.New(loglvl.ErrorLevel).
		SetLogger(func() *logrus.Logger { return logger }).
		FieldSet(fields).
		ErrorAdd(true, err). // cleanNil=true filters out nil errors
		SetEntryContext(time.Now(), 0, "ConnectDB", "db.go", 42, "Failed to connect to database").
		Log()
}
Example (ErrorManagement)

Example_errorManagement demonstrates various error management operations.

package main

import (
	"errors"
	"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)

	// Create entry
	entry := logent.New(loglvl.ErrorLevel).
		SetLogger(func() *logrus.Logger { return logger }).
		FieldSet(fields)

	// Add errors incrementally
	entry.ErrorAdd(true, errors.New("first error"))
	entry.ErrorAdd(true, errors.New("second error"))

	// Log current errors
	entry.SetEntryContext(time.Now(), 0, "", "", 0, "Multiple errors").Log()

	// Clean errors and add new ones
	entry.ErrorClean().
		ErrorAdd(true, errors.New("new error after cleanup")).
		SetEntryContext(time.Now(), 0, "", "", 0, "After cleanup").
		Log()

	// Set errors directly with a slice
	errs := []error{
		errors.New("error from slice 1"),
		errors.New("error from slice 2"),
	}
	entry.ErrorSet(errs).
		SetEntryContext(time.Now(), 0, "", "", 0, "Set errors").
		Log()
}
Example (FieldManagement)

Example_fieldManagement demonstrates managing custom fields throughout entry lifecycle.

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()

	// Create base fields
	baseFields := logfld.New(nil)
	baseFields.Add("app", "myapp")
	baseFields.Add("version", "1.0.0")

	// Create additional fields
	reqFields := logfld.New(nil)
	reqFields.Add("request_id", "req-456")

	// Build entry with merged fields
	entry := logent.New(loglvl.InfoLevel).
		SetLogger(func() *logrus.Logger { return logger }).
		FieldSet(baseFields).
		FieldAdd("endpoint", "/api/users").
		FieldMerge(reqFields).
		SetEntryContext(time.Now(), 0, "", "", 0, "API request")

	// Log the entry
	entry.Log()

	// Clean specific fields for reuse
	entry.FieldClean("request_id").
		FieldAdd("request_id", "req-457").
		SetEntryContext(time.Now(), 0, "", "", 0, "Next request").
		Log()
}
Example (LevelControl)

Example_levelControl demonstrates controlling log levels dynamically.

package main

import (
	"errors"
	"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()
	logger.SetLevel(logrus.DebugLevel) // Allow all levels

	fields := logfld.New(nil)

	// Create entry and change level
	entry := logent.New(loglvl.DebugLevel).
		SetLogger(func() *logrus.Logger { return logger }).
		FieldSet(fields)

	// Log at debug level
	entry.SetEntryContext(time.Now(), 0, "", "", 0, "Debug message").Log()

	// Change to info level and log again
	entry.SetLevel(loglvl.InfoLevel).
		SetEntryContext(time.Now(), 0, "", "", 0, "Info message").
		Log()

	// Change to error level
	entry.SetLevel(loglvl.ErrorLevel).
		ErrorAdd(true, errors.New("error occurred")).
		SetEntryContext(time.Now(), 0, "", "", 0, "Error message").
		Log()
}
Example (MessageOnly)

Example_messageOnly demonstrates simple message-only logging without structured fields.

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)

	// Log only the message, ignoring all fields
	logent.New(loglvl.InfoLevel).
		SetLogger(func() *logrus.Logger { return logger }).
		FieldSet(fields).
		SetMessageOnly(true).
		SetEntryContext(time.Now(), 0, "", "", 0, "Simple console message").
		Log()
}
Example (MethodChaining)

Example_methodChaining demonstrates the fluent API for building complex entries.

package main

import (
	"errors"
	"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)

	// Build complex entry with method chaining
	logent.New(loglvl.WarnLevel).
		SetLogger(func() *logrus.Logger { return logger }).
		FieldSet(fields).
		FieldAdd("component", "auth").
		FieldAdd("attempt", 3).
		ErrorAdd(true, errors.New("invalid credentials")).
		DataSet(map[string]string{"username": "user@example.com"}).
		SetEntryContext(time.Now(), 0, "Authenticate", "auth.go", 100, "Authentication failed").
		Log()
}
Example (MultipleErrors)

Example_multipleErrors demonstrates handling multiple errors in a single entry.

package main

import (
	"errors"
	"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)

	// Collect multiple errors
	err1 := errors.New("connection timeout")
	err2 := errors.New("retry failed")

	logent.New(loglvl.ErrorLevel).
		SetLogger(func() *logrus.Logger { return logger }).
		FieldSet(fields).
		ErrorAdd(true, err1, err2).
		SetEntryContext(time.Now(), 0, "", "", 0, "Multiple failures occurred").
		Log()
}
Example (StructuredData)

Example_structuredData demonstrates adding custom structured data to log entries.

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()

	// Create fields with custom data
	fields := logfld.New(nil)

	// Create entry with custom fields
	logent.New(loglvl.InfoLevel).
		SetLogger(func() *logrus.Logger { return logger }).
		FieldSet(fields).
		FieldAdd("user_id", 12345).
		FieldAdd("action", "login").
		FieldAdd("ip_address", "192.168.1.1").
		SetEntryContext(time.Now(), 0, "", "", 0, "User logged in").
		Log()
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Entry

type Entry interface {
	// SetLogger sets the logger function of the entry. The logger function must return
	// a pointer to a logrus.Logger object. If the logger function is nil, the
	// entry will not log anything.
	//
	// Example:
	//   e := New(loglvl.InfoLevel)
	//   e = e.SetLogger(func() *logrus.Logger { return logrus.New() })
	SetLogger(fct func() *logrus.Logger) Entry
	// SetLevel sets the level of the entry. The level determines when the entry is
	// logged. If the level is lower than the logger's level, the entry will not
	// be logged. If the level is loglvl.NilLevel, the entry will not be logged
	// regardless of the logger's level.
	//
	// Example:
	//   e := New(loglvl.InfoLevel)
	//   e = e.SetLevel(loglvl.WarnLevel)
	SetLevel(lvl loglvl.Level) Entry
	// SetMessageOnly sets a flag to log only the message of the entry. If the
	// flag is true, the entry will only log the message. If the flag is
	// false, the entry will log all the fields and the message.
	//
	// Example:
	//   e := New(loglvl.InfoLevel)
	//   e = e.SetMessageOnly(true)
	SetMessageOnly(flag bool) Entry
	// SetEntryContext sets the context fields of the entry. The context fields are
	// logged together with the message. The context fields are: time, stack,
	// caller, file, line, and message.
	//
	// Example:
	//   e := New(loglvl.InfoLevel)
	//   e = e.SetEntryContext(time.Now(), 123, "caller", "file.go", 456, "message")
	SetEntryContext(etime time.Time, stack uint64, caller, file string, line uint64, msg string) Entry
	// SetGinContext sets the gin context pointer of the entry. The gin context pointer
	// is used to register the errors of the current entry into the gin context
	// error slice. If the gin context pointer is nil, the entry will not register any
	// errors into the gin context error slice.
	//
	// Example:
	//   e := New(loglvl.InfoLevel)
	//   e = e.SetGinContext(ginContext)
	//
	// This function is useful when you want to log errors of your application and
	// register them into the gin context error slice.
	SetGinContext(ctx *ginsdk.Context) Entry

	// DataSet sets the data of the entry. The data is logged together with the
	// context fields and the message. The data can be any type that can be
	// marshaled into a JSON object. If the data is nil, no data will be logged.
	//
	// Example:
	//   e := New(loglvl.InfoLevel)
	//   e = e.DataSet(map[string]string{"key1": "value1", "key2": "value2"})
	//
	// This function is useful when you want to log additional data of your application
	// and you want to log it in a structured way.
	DataSet(data interface{}) Entry
	// Check returns true if the level of the entry is greater than or equal to
	// lvlNoErr, and false otherwise.
	//
	// Example:
	//   e := New(loglvl.InfoLevel)
	//   result := e.Check(loglvl.WarnLevel)
	//
	// This function is useful when you want to check if the entry should be logged
	// based on the level of the entry and the logger's level.
	Check(lvlNoErr loglvl.Level) bool
	// Log logs the entry into the logger. The entry is logged together with the
	// context fields and the message. If the logger is nil, the entry will not
	// be logged. If the logger's level is lower than the entry's level, the entry
	// will not be logged. If the logger's level is loglvl.NilLevel, the entry will
	// not be logged regardless of the entry's level.
	//
	// Example:
	//   e := New(loglvl.InfoLevel)
	//   e = e.SetLogger(func() *logrus.Logger { return logrus.New() })
	//   e.Log()
	Log()

	// FieldAdd adds a new field to the entry. The field is a couple of a key
	// (string) and a value (interface{}). The value can be of any type that
	// can be marshaled into a JSON object. The key must be unique, if the
	// key already exists, it will be overwritten with the new value.
	//
	// Example:
	//   e := New(loglvl.InfoLevel)
	//   e = e.FieldAdd("key", "value")
	//
	// This function is useful when you want to log additional data of your
	// application and you want to log it in a structured way.
	FieldAdd(key string, val interface{}) Entry
	// FieldMerge merges the fields of another logfld.Fields object into the
	// current entry. The fields are merged in a shallow way, meaning
	// that if the key already exists in the current entry, it will be
	// overwritten with the new value from the other logfld.Fields object.
	//
	// Example:
	//   e := New(loglvl.InfoLevel)
	//   fields1 := logfld.New(nil)
	//   fields1.Add("key1", "value1")
	//   fields2 := logfld.New(nil)
	//   fields2.Add("key2", "value2")
	//   e = e.FieldMerge(fields1)
	//   e = e.FieldMerge(fields2)
	//
	// This function is useful when you want to log additional data of your
	// application and you want to log it in a structured way.
	FieldMerge(fields logfld.Fields) Entry
	// FieldSet sets the fields of the entry. The fields are a map of key-value
	// pairs where the key is a string and the value is an interface{}.
	// The fields are logged together with the context fields and the message.
	//
	// Example:
	//   e := New(loglvl.InfoLevel)
	//   fields := logfld.New(nil)
	//   fields.Add("key1", "value1")
	//   fields.Add("key2", 123)
	//   e = e.FieldSet(fields)
	//
	// This function is useful when you want to log additional data of your
	// application and you want to log it in a structured way.
	FieldSet(fields logfld.Fields) Entry
	// FieldClean removes the fields specified by the keys from the entry. If
	// the key does not exist, it is ignored. If the keys argument is empty,
	// this function does nothing and returns the entry unchanged.
	//
	// Example:
	//   e := New(loglvl.InfoLevel)
	//   fields := logfld.New(nil)
	//   fields.Add("key1", "value1")
	//   fields.Add("key2", "value2")
	//   e = e.FieldSet(fields)
	//   e = e.FieldClean("key1")
	//
	// This function is useful when you want to log additional data of your
	// application and you want to log it in a structured way.
	FieldClean(keys ...string) Entry

	// ErrorClean removes all errors from the entry. If the entry has no
	// errors, this function does nothing and returns the entry unchanged.
	//
	// Example:
	//   e := New(loglvl.InfoLevel)
	//   e = e.ErrorAdd(errors.New("error1"), errors.New("error2"))
	//   e = e.ErrorClean()
	//
	// This function is useful when you want to clear all errors from the entry
	// and you want to log the entry in a structured way.
	ErrorClean() Entry
	// ErrorSet sets the errors of the entry. The errors are logged together with
	// the context fields and the message. If the errors argument is empty,
	// this function does nothing and returns the entry unchanged.
	//
	// Example:
	//   e := New(loglvl.InfoLevel)
	//   e = e.ErrorSet([]error{errors.New("error1"), errors.New("error2")})
	//
	// This function is useful when you want to log errors of your application
	// and you want to log them in a structured way.
	ErrorSet(err []error) Entry
	// ErrorAdd adds the errors to the entry. The errors are logged together with
	// the context fields and the message. If the cleanNil argument is true, all
	// nil errors are removed from the entry. If the cleanNil argument is false,
	// all errors are added to the entry regardless of whether they are nil or not.
	//
	// Example:
	//   e := New(loglvl.InfoLevel)
	//   e = e.ErrorAdd(true, errors.New("error1"), nil)
	//
	// This function is useful when you want to log errors of your application
	// and you want to log them in a structured way.
	ErrorAdd(cleanNil bool, err ...error) Entry
}

func New

func New(lvl loglvl.Level) Entry

New returns a new Entry with the given level. The new Entry will be set with the current time and an empty error slice. The new Entry will also have a nil logger and gin context, and will not be cleaned. The new Entry will have no additional data and will have no additional fields.

Example:

e := New(loglvl.InfoLevel)
e = e.ErrorAdd(errors.New("error1"), errors.New("error2"))

This function is useful when you want to log messages of your application and you want to log them in a structured way.

Jump to

Keyboard shortcuts

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