[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, ...)
24 KiB
Logger Entry
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
- Architecture
- Performance
- Use Cases
- Quick Start
- Best Practices
- API Reference
- Contributing
- Improvements & Security
- Resources
- AI Transparency
- License
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
- Immutability Pattern: All setter methods return the entry itself for method chaining
- Lazy Evaluation: Logging deferred until Log() or Check() is called
- Flexible Context: Support for timestamps, stack traces, caller info, file/line numbers
- Safety First: Nil-safe operations throughout, no panics in production code
- 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
- Creation:
New(level)creates an entry with initial state - Configuration: Set logger, level, gin context, message mode
- Context: Set time, stack, caller, file, line, message
- Fields: Add, merge, or set custom structured fields
- Errors: Add or set error information
- Data: Attach arbitrary data structures
- Logging: Call
Log()orCheck()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
-racedetector
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=truein 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:
-
Code Quality
- Follow Go best practices and idioms
- Maintain or improve code coverage (target: >80%)
- Pass all tests including race detector
- Use
gofmtandgolint
-
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
- Add tests for new features
- Use Ginkgo v2 / Gomega for test framework
- Ensure zero race conditions
- Aim for 80%+ code coverage
-
Documentation
- Update GoDoc comments for public APIs
- Add examples for new features
- Update README.md and TESTING.md if needed
-
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
-raceflag - ✅ 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:
- Automatic Caller Detection: Runtime stack inspection for automatic file/line detection
- Entry Pooling: Optional object pooling for ultra-high-frequency scenarios
- Sampling Support: Built-in log sampling for rate limiting
- 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
buglabel - Security: Report privately via GitHub Security Advisories
- Enhancements: Use GitHub Issues with
enhancementlabel
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.
Related golib Packages
-
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