e1.4.4 Fix module path and dependency issues, compatibility package added.

This commit is contained in:
2025-07-08 22:13:11 -04:00
parent 7ce7158841
commit ccbe65bf40
22 changed files with 776 additions and 29 deletions

View File

@ -1,7 +1,7 @@
# Log # Log
A high-performance, buffered, rotating file logger for Go applications, configured via A high-performance, buffered, rotating file logger for Go applications, configured via
the [LixenWraith/config](https://github.com/LixenWraith/config) package or simple overrides. Designed for the [lixenwraith/config](https://github.com/lixenwraith/config) package or simple overrides. Designed for
production-grade reliability with features like disk management, log retention, and lock-free asynchronous processing production-grade reliability with features like disk management, log retention, and lock-free asynchronous processing
using atomic operations and channels. using atomic operations and channels.
@ -14,7 +14,7 @@ using atomic operations and channels.
- **Lock-free Asynchronous Logging:** Non-blocking log operations with minimal application impact. Logs are sent via a - **Lock-free Asynchronous Logging:** Non-blocking log operations with minimal application impact. Logs are sent via a
buffered channel, processed by a dedicated background goroutine. Uses atomic operations for state management, avoiding buffered channel, processed by a dedicated background goroutine. Uses atomic operations for state management, avoiding
mutexes in the hot path. mutexes in the hot path.
- **External Configuration:** Fully configured using `github.com/LixenWraith/config`, supporting both TOML files and CLI - **External Configuration:** Fully configured using `github.com/lixenwraith/config`, supporting both TOML files and CLI
overrides with centralized management. Also supports simple initialization with defaults and string overrides via overrides with centralized management. Also supports simple initialization with defaults and string overrides via
`InitWithDefaults`. `InitWithDefaults`.
- **Automatic File Rotation:** Seamlessly rotates log files when they reach configurable size limits (`max_size_mb`), - **Automatic File Rotation:** Seamlessly rotates log files when they reach configurable size limits (`max_size_mb`),
@ -47,8 +47,8 @@ using atomic operations and channels.
## Installation ## Installation
```bash ```bash
go get github.com/LixenWraith/log go get github.com/lixenwraith/log
go get github.com/LixenWraith/config go get github.com/lixenwraith/config
``` ```
## Basic Usage ## Basic Usage
@ -59,7 +59,7 @@ This example shows minimal initialization using defaults with a single override,
package main package main
import ( import (
"github.com/LixenWraith/log" "github.com/lixenwraith/log"
) )
func main() { func main() {
@ -287,7 +287,7 @@ err := logger.InitWithDefaults(
condition is resolved. condition is resolved.
- **Configuration Dependencies:** - **Configuration Dependencies:**
For full configuration management (TOML file loading, CLI overrides, etc.), the `github.com/LixenWraith/config` package is required when using the `Init` method. For simpler initialization without this external dependency, use `InitWithDefaults`. For full configuration management (TOML file loading, CLI overrides, etc.), the `github.com/lixenwraith/config` package is required when using the `Init` method. For simpler initialization without this external dependency, use `InitWithDefaults`.
- **Retention Accuracy:** Log retention relies on file modification times, which could potentially be affected by - **Retention Accuracy:** Log retention relies on file modification times, which could potentially be affected by
external file system operations. external file system operations.

188
compat/README.md Normal file
View File

@ -0,0 +1,188 @@
# Compatibility Adapters for lixenwraith/log
This package provides compatibility adapters to use the `github.com/lixenwraith/log` logger with popular Go networking frameworks:
- **gnet v2**: High-performance event-driven networking framework
- **fasthttp**: Fast HTTP implementation
## Features
- ✅ Full interface compatibility with both frameworks
- ✅ Preserves structured logging capabilities
- ✅ Configurable fatal behavior for gnet
- ✅ Automatic log level detection for fasthttp
- ✅ Optional structured field extraction from printf formats
- ✅ Thread-safe and high-performance
- ✅ Shared logger instance for multiple adapters
## Installation
```bash
go get github.com/lixenwraith/log
```
## Quick Start
### Basic Usage with gnet
```go
import (
"github.com/lixenwraith/log"
"github.com/lixenwraith/log/compat"
"github.com/panjf2000/gnet/v2"
)
// Create and configure logger
logger := log.NewLogger()
logger.InitWithDefaults("directory=/var/log/gnet", "level=-4")
defer logger.Shutdown()
// Create gnet adapter
adapter := compat.NewGnetAdapter(logger)
// Use with gnet
gnet.Run(eventHandler, "tcp://127.0.0.1:9000", gnet.WithLogger(adapter))
```
### Basic Usage with fasthttp
```go
import (
"github.com/lixenwraith/log"
"github.com/lixenwraith/log/compat"
"github.com/valyala/fasthttp"
)
// Create and configure logger
logger := log.NewLogger()
logger.InitWithDefaults("directory=/var/log/fasthttp")
defer logger.Shutdown()
// Create fasthttp adapter
adapter := compat.NewFastHTTPAdapter(logger)
// Use with fasthttp
server := &fasthttp.Server{
Handler: requestHandler,
Logger: adapter,
}
server.ListenAndServe(":8080")
```
### Using the Builder Pattern
```go
// Create adapters with shared configuration
builder := compat.NewBuilder().
WithOptions(
"directory=/var/log/app",
"level=0",
"format=json",
"max_size_mb=100",
)
gnetAdapter, fasthttpAdapter, err := builder.Build()
if err != nil {
panic(err)
}
defer builder.GetLogger().Shutdown()
```
## Advanced Features
### Structured Field Extraction
The structured adapters can extract key-value pairs from printf-style format strings:
```go
// Use structured adapter
adapter := compat.NewStructuredGnetAdapter(logger)
// These calls will extract structured fields:
adapter.Infof("client=%s port=%d", "192.168.1.1", 8080)
// Logs: {"client": "192.168.1.1", "port": 8080, "source": "gnet"}
adapter.Errorf("user: %s, action: %s, error: %s", "john", "login", "invalid password")
// Logs: {"user": "john", "action": "login", "error": "invalid password", "source": "gnet"}
```
### Custom Fatal Handling
```go
adapter := compat.NewGnetAdapter(logger,
compat.WithFatalHandler(func(msg string) {
// Custom cleanup
saveState()
notifyAdmin(msg)
os.Exit(1)
}),
)
```
### Custom Level Detection for fasthttp
```go
adapter := compat.NewFastHTTPAdapter(logger,
compat.WithDefaultLevel(log.LevelInfo),
compat.WithLevelDetector(func(msg string) int64 {
if strings.Contains(msg, "CRITICAL") {
return log.LevelError
}
return 0 // Use default detection
}),
)
```
## Configuration Examples
### High-Performance Configuration
```go
builder := compat.NewBuilder().
WithOptions(
"directory=/var/log/highperf",
"level=0", // Info and above
"format=json", // Structured logs
"buffer_size=4096", // Large buffer
"flush_interval_ms=1000", // Less frequent flushes
"enable_periodic_sync=false", // Disable periodic sync
)
```
### Development Configuration
```go
builder := compat.NewBuilder().
WithOptions(
"directory=./logs",
"level=-4", // Debug level
"format=txt", // Human-readable
"show_timestamp=true",
"show_level=true",
"trace_depth=3", // Include call traces
"flush_interval_ms=100", // Frequent flushes
)
```
### Production Configuration with Monitoring
```go
builder := compat.NewBuilder().
WithOptions(
"directory=/var/log/prod",
"level=0",
"format=json",
"max_size_mb=1000", // 1GB files
"max_total_size_mb=10000", // 10GB total
"retention_period_hrs=168", // 7 days
"heartbeat_level=2", // Process + disk heartbeats
"heartbeat_interval_s=300", // 5 minutes
)
```
## Performance Considerations
1. **Printf Overhead**: The adapters must format printf-style strings, adding minimal overhead
2. **Structured Extraction**: The structured adapters use regex matching, which adds ~1-2μs per call
3. **Level Detection**: FastHTTP adapter's level detection adds <100ns for simple string checks
4. **Buffering**: The underlying logger's buffering minimizes I/O impact

72
compat/builder.go Normal file
View File

@ -0,0 +1,72 @@
// FILE: compat/builder.go
package compat
import (
"github.com/lixenwraith/log"
"github.com/panjf2000/gnet/v2"
"github.com/valyala/fasthttp"
)
// Builder provides a convenient way to create configured loggers for both frameworks
type Builder struct {
logger *log.Logger
options []string // InitWithDefaults options
}
// NewBuilder creates a new adapter builder
func NewBuilder() *Builder {
return &Builder{
logger: log.NewLogger(),
}
}
// WithOptions adds configuration options for the underlying logger
func (b *Builder) WithOptions(opts ...string) *Builder {
b.options = append(b.options, opts...)
return b
}
// Build initializes the logger and returns adapters for both frameworks
func (b *Builder) Build() (*GnetAdapter, *FastHTTPAdapter, error) {
// Initialize the logger
if err := b.logger.InitWithDefaults(b.options...); err != nil {
return nil, nil, err
}
// Create adapters
gnetAdapter := NewGnetAdapter(b.logger)
fasthttpAdapter := NewFastHTTPAdapter(b.logger)
return gnetAdapter, fasthttpAdapter, nil
}
// BuildStructured initializes the logger and returns structured adapters
func (b *Builder) BuildStructured() (*StructuredGnetAdapter, *FastHTTPAdapter, error) {
// Initialize the logger
if err := b.logger.InitWithDefaults(b.options...); err != nil {
return nil, nil, err
}
// Create adapters
gnetAdapter := NewStructuredGnetAdapter(b.logger)
fasthttpAdapter := NewFastHTTPAdapter(b.logger)
return gnetAdapter, fasthttpAdapter, nil
}
// GetLogger returns the underlying logger for direct access
func (b *Builder) GetLogger() *log.Logger {
return b.logger
}
// Example usage functions
// ConfigureGnetServer configures a gnet server with the logger
func ConfigureGnetServer(adapter *GnetAdapter, opts ...gnet.Option) []gnet.Option {
return append(opts, gnet.WithLogger(adapter))
}
// ConfigureFastHTTPServer configures a fasthttp server with the logger
func ConfigureFastHTTPServer(adapter *FastHTTPAdapter, server *fasthttp.Server) {
server.Logger = adapter
}

103
compat/fasthttp.go Normal file
View File

@ -0,0 +1,103 @@
// FILE: compat/fasthttp.go
package compat
import (
"fmt"
"strings"
"github.com/lixenwraith/log"
)
// FastHTTPAdapter wraps lixenwraith/log.Logger to implement fasthttp's Logger interface
type FastHTTPAdapter struct {
logger *log.Logger
defaultLevel int64
levelDetector func(string) int64 // Function to detect log level from message
}
// NewFastHTTPAdapter creates a new fasthttp-compatible logger adapter
func NewFastHTTPAdapter(logger *log.Logger, opts ...FastHTTPOption) *FastHTTPAdapter {
adapter := &FastHTTPAdapter{
logger: logger,
defaultLevel: log.LevelInfo,
levelDetector: DetectLogLevel, // Default level detection
}
for _, opt := range opts {
opt(adapter)
}
return adapter
}
// FastHTTPOption allows customizing adapter behavior
type FastHTTPOption func(*FastHTTPAdapter)
// WithDefaultLevel sets the default log level for Printf calls
func WithDefaultLevel(level int64) FastHTTPOption {
return func(a *FastHTTPAdapter) {
a.defaultLevel = level
}
}
// WithLevelDetector sets a custom function to detect log level from message content
func WithLevelDetector(detector func(string) int64) FastHTTPOption {
return func(a *FastHTTPAdapter) {
a.levelDetector = detector
}
}
// Printf implements fasthttp's Logger interface
func (a *FastHTTPAdapter) Printf(format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
// Detect log level from message content
level := a.defaultLevel
if a.levelDetector != nil {
detected := a.levelDetector(msg)
if detected != 0 {
level = detected
}
}
// Log with appropriate level
switch level {
case log.LevelDebug:
a.logger.Debug("msg", msg, "source", "fasthttp")
case log.LevelWarn:
a.logger.Warn("msg", msg, "source", "fasthttp")
case log.LevelError:
a.logger.Error("msg", msg, "source", "fasthttp")
default:
a.logger.Info("msg", msg, "source", "fasthttp")
}
}
// DetectLogLevel attempts to detect log level from message content
func DetectLogLevel(msg string) int64 {
msgLower := strings.ToLower(msg)
// Check for error indicators
if strings.Contains(msgLower, "error") ||
strings.Contains(msgLower, "failed") ||
strings.Contains(msgLower, "fatal") ||
strings.Contains(msgLower, "panic") {
return log.LevelError
}
// Check for warning indicators
if strings.Contains(msgLower, "warn") ||
strings.Contains(msgLower, "warning") ||
strings.Contains(msgLower, "deprecated") {
return log.LevelWarn
}
// Check for debug indicators
if strings.Contains(msgLower, "debug") ||
strings.Contains(msgLower, "trace") {
return log.LevelDebug
}
// Default to info level
return log.LevelInfo
}

79
compat/gnet.go Normal file
View File

@ -0,0 +1,79 @@
// FILE: compat/gnet.go
package compat
import (
"fmt"
"os"
"time"
"github.com/lixenwraith/log"
)
// GnetAdapter wraps lixenwraith/log.Logger to implement gnet's logging.Logger interface
type GnetAdapter struct {
logger *log.Logger
fatalHandler func(msg string) // Customizable fatal behavior
}
// NewGnetAdapter creates a new gnet-compatible logger adapter
func NewGnetAdapter(logger *log.Logger, opts ...GnetOption) *GnetAdapter {
adapter := &GnetAdapter{
logger: logger,
fatalHandler: func(msg string) {
os.Exit(1) // Default behavior matches gnet expectations
},
}
for _, opt := range opts {
opt(adapter)
}
return adapter
}
// GnetOption allows customizing adapter behavior
type GnetOption func(*GnetAdapter)
// WithFatalHandler sets a custom fatal handler
func WithFatalHandler(handler func(string)) GnetOption {
return func(a *GnetAdapter) {
a.fatalHandler = handler
}
}
// Debugf logs at debug level with printf-style formatting
func (a *GnetAdapter) Debugf(format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
a.logger.Debug("msg", msg, "source", "gnet")
}
// Infof logs at info level with printf-style formatting
func (a *GnetAdapter) Infof(format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
a.logger.Info("msg", msg, "source", "gnet")
}
// Warnf logs at warn level with printf-style formatting
func (a *GnetAdapter) Warnf(format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
a.logger.Warn("msg", msg, "source", "gnet")
}
// Errorf logs at error level with printf-style formatting
func (a *GnetAdapter) Errorf(format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
a.logger.Error("msg", msg, "source", "gnet")
}
// Fatalf logs at error level and triggers fatal handler
func (a *GnetAdapter) Fatalf(format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
a.logger.Error("msg", msg, "source", "gnet", "fatal", true)
// Ensure log is flushed before exit
_ = a.logger.Flush(100 * time.Millisecond)
if a.fatalHandler != nil {
a.fatalHandler(msg)
}
}

131
compat/structured.go Normal file
View File

@ -0,0 +1,131 @@
// FILE: compat/structured.go
package compat
import (
"fmt"
"regexp"
"strings"
"github.com/lixenwraith/log"
)
// parseFormat attempts to extract structured fields from printf-style format strings
// This is useful for preserving structured logging semantics
func parseFormat(format string, args []interface{}) []interface{} {
// Pattern to detect common structured patterns like "key=%v" or "key: %v"
keyValuePattern := regexp.MustCompile(`(\w+)\s*[:=]\s*%[vsdqxXeEfFgGpbcU]`)
matches := keyValuePattern.FindAllStringSubmatchIndex(format, -1)
if len(matches) == 0 || len(matches) > len(args) {
// Fallback to simple message if pattern doesn't match
return []interface{}{"msg", fmt.Sprintf(format, args...)}
}
// Build structured fields
fields := make([]interface{}, 0, len(matches)*2+2)
lastEnd := 0
argIndex := 0
for _, match := range matches {
// Add any text before this match as part of the message
if match[0] > lastEnd {
prefix := format[lastEnd:match[0]]
if strings.TrimSpace(prefix) != "" {
if len(fields) == 0 {
fields = append(fields, "msg", strings.TrimSpace(prefix))
}
}
}
// Extract key name
keyStart := match[2]
keyEnd := match[3]
key := format[keyStart:keyEnd]
// Get corresponding value
if argIndex < len(args) {
fields = append(fields, key, args[argIndex])
argIndex++
}
lastEnd = match[1]
}
// Handle remaining format string and args
if lastEnd < len(format) {
remainingFormat := format[lastEnd:]
remainingArgs := args[argIndex:]
if len(remainingArgs) > 0 {
remaining := fmt.Sprintf(remainingFormat, remainingArgs...)
if strings.TrimSpace(remaining) != "" {
if len(fields) == 0 {
fields = append(fields, "msg", strings.TrimSpace(remaining))
} else {
// Append to existing message
for i := 0; i < len(fields); i += 2 {
if fields[i] == "msg" {
fields[i+1] = fmt.Sprintf("%v %s", fields[i+1], strings.TrimSpace(remaining))
break
}
}
}
}
}
}
return fields
}
// StructuredGnetAdapter provides enhanced structured logging for gnet
type StructuredGnetAdapter struct {
*GnetAdapter
extractFields bool
}
// NewStructuredGnetAdapter creates a gnet adapter with structured field extraction
func NewStructuredGnetAdapter(logger *log.Logger, opts ...GnetOption) *StructuredGnetAdapter {
return &StructuredGnetAdapter{
GnetAdapter: NewGnetAdapter(logger, opts...),
extractFields: true,
}
}
// Debugf logs with structured field extraction
func (a *StructuredGnetAdapter) Debugf(format string, args ...interface{}) {
if a.extractFields {
fields := parseFormat(format, args)
a.logger.Debug(append(fields, "source", "gnet")...)
} else {
a.GnetAdapter.Debugf(format, args...)
}
}
// Infof logs with structured field extraction
func (a *StructuredGnetAdapter) Infof(format string, args ...interface{}) {
if a.extractFields {
fields := parseFormat(format, args)
a.logger.Info(append(fields, "source", "gnet")...)
} else {
a.GnetAdapter.Infof(format, args...)
}
}
// Warnf logs with structured field extraction
func (a *StructuredGnetAdapter) Warnf(format string, args ...interface{}) {
if a.extractFields {
fields := parseFormat(format, args)
a.logger.Warn(append(fields, "source", "gnet")...)
} else {
a.GnetAdapter.Warnf(format, args...)
}
}
// Errorf logs with structured field extraction
func (a *StructuredGnetAdapter) Errorf(format string, args ...interface{}) {
if a.extractFields {
fields := parseFormat(format, args)
a.logger.Error(append(fields, "source", "gnet")...)
} else {
a.GnetAdapter.Errorf(format, args...)
}
}

View File

@ -1,4 +1,4 @@
// --- File: config.go --- // FILE: config.go
package log package log
import ( import (

75
example/fasthttp/main.go Normal file
View File

@ -0,0 +1,75 @@
// FILE: examples/fasthttp/main.go
package main
import (
"fmt"
"strings"
"time"
"github.com/lixenwraith/log"
"github.com/lixenwraith/log/compat"
"github.com/valyala/fasthttp"
)
func main() {
// Create and configure logger
logger := log.NewLogger()
err := logger.InitWithDefaults(
"directory=/var/log/fasthttp",
"level=0",
"format=txt",
"buffer_size=2048",
)
if err != nil {
panic(err)
}
defer logger.Shutdown()
// Create fasthttp adapter with custom level detection
fasthttpAdapter := compat.NewFastHTTPAdapter(
logger,
compat.WithDefaultLevel(log.LevelInfo),
compat.WithLevelDetector(customLevelDetector),
)
// Configure fasthttp server
server := &fasthttp.Server{
Handler: requestHandler,
Logger: fasthttpAdapter,
// Other server settings
Name: "MyServer",
Concurrency: fasthttp.DefaultConcurrency,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 120 * time.Second,
TCPKeepalive: true,
ReduceMemoryUsage: true,
}
// Start server
fmt.Println("Starting server on :8080")
if err := server.ListenAndServe(":8080"); err != nil {
panic(err)
}
}
func requestHandler(ctx *fasthttp.RequestCtx) {
ctx.SetContentType("text/plain")
fmt.Fprintf(ctx, "Hello, world! Path: %s\n", ctx.Path())
}
func customLevelDetector(msg string) int64 {
// Custom logic to detect log levels
// Can inspect specific fasthttp message patterns
if strings.Contains(msg, "connection cannot be served") {
return log.LevelWarn
}
if strings.Contains(msg, "error when serving connection") {
return log.LevelError
}
// Use default detection
return compat.detectLogLevel(msg)
}

47
example/gnet/main.go Normal file
View File

@ -0,0 +1,47 @@
// FILE: example/gnet/main.go
package main
import (
"github.com/lixenwraith/log"
"github.com/lixenwraith/log/compat"
"github.com/panjf2000/gnet/v2"
)
// Example gnet event handler
type echoServer struct {
gnet.BuiltinEventEngine
}
func (es *echoServer) OnTraffic(c gnet.Conn) gnet.Action {
buf, _ := c.Next(-1)
c.Write(buf)
return gnet.None
}
func main() {
// Method 1: Simple adapter
logger := log.NewLogger()
err := logger.InitWithDefaults(
"directory=/var/log/gnet",
"level=-4", // Debug level
"format=json",
)
if err != nil {
panic(err)
}
defer logger.Shutdown()
gnetAdapter := compat.NewGnetAdapter(logger)
// Configure gnet server with the logger
err = gnet.Run(
&echoServer{},
"tcp://127.0.0.1:9000",
gnet.WithMulticore(true),
gnet.WithLogger(gnetAdapter),
gnet.WithReusePort(true),
)
if err != nil {
panic(err)
}
}

View File

@ -1,3 +1,4 @@
// FILE: example/heartbeat/main.go
package main package main
import ( import (
@ -5,7 +6,7 @@ import (
"os" "os"
"time" "time"
"github.com/LixenWraith/log" "github.com/lixenwraith/log"
) )
func main() { func main() {

View File

@ -1,3 +1,4 @@
// FILE: example/reconfig/main.go
package main package main
import ( import (
@ -5,7 +6,7 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/LixenWraith/log" "github.com/lixenwraith/log"
) )
// Simulate rapid reconfiguration // Simulate rapid reconfiguration

View File

@ -1,3 +1,4 @@
// FILE: example/simple/main.go
package main package main
import ( import (
@ -6,8 +7,8 @@ import (
"sync" "sync"
"time" "time"
"github.com/LixenWraith/config" "github.com/lixenwraith/config"
"github.com/LixenWraith/log" "github.com/lixenwraith/log"
) )
const configFile = "simple_config.toml" const configFile = "simple_config.toml"
@ -51,7 +52,7 @@ func main() {
// Load config from file (and potentially CLI args - none provided here) // Load config from file (and potentially CLI args - none provided here)
// The log package will register its keys during Init // The log package will register its keys during Init
_, err = cfg.Load(configFile, nil) // os.Args[1:] could be used here err = cfg.Load(configFile, nil) // os.Args[1:] could be used here
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "Failed to load config: %v. Using defaults.\n", err) fmt.Fprintf(os.Stderr, "Failed to load config: %v. Using defaults.\n", err)
// Proceeding, log.Init will use registered defaults // Proceeding, log.Init will use registered defaults

View File

@ -1,3 +1,4 @@
// FILE: example/stress/main.go
package main package main
import ( import (
@ -11,8 +12,8 @@ import (
"syscall" "syscall"
"time" "time"
"github.com/LixenWraith/config" "github.com/lixenwraith/config"
"github.com/LixenWraith/log" "github.com/lixenwraith/log"
) )
const ( const (
@ -121,7 +122,7 @@ func main() {
// defer os.RemoveAll(logsDir) // Remove to keep the log directory // defer os.RemoveAll(logsDir) // Remove to keep the log directory
cfg := config.New() cfg := config.New()
_, err = cfg.Load(configFile, nil) err = cfg.Load(configFile, nil)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "Failed to load config: %v.\n", err) fmt.Fprintf(os.Stderr, "Failed to load config: %v.\n", err)
os.Exit(1) os.Exit(1)

View File

@ -1,4 +1,4 @@
// --- File: format.go --- // FILE: format.go
package log package log
import ( import (

19
go.mod
View File

@ -1,10 +1,23 @@
module github.com/LixenWraith/log module github.com/lixenwraith/log
go 1.24.2 go 1.24.4
require github.com/LixenWraith/config v0.0.0-20250426061518-532233ac282c require (
github.com/lixenwraith/config v0.0.0-20250701170607-8515fa0543b6
github.com/panjf2000/gnet/v2 v2.9.1
github.com/valyala/fasthttp v1.63.0
)
require ( require (
github.com/BurntSushi/toml v1.5.0 // indirect github.com/BurntSushi/toml v1.5.0 // indirect
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/panjf2000/ants/v2 v2.11.3 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/sync v0.15.0 // indirect
golang.org/x/sys v0.33.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
) )

38
go.sum
View File

@ -1,6 +1,40 @@
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/LixenWraith/config v0.0.0-20250426061518-532233ac282c h1:3JcYZVGF+OOfm72eOE2LLFHU9ERYSKs76jJOxpB/4FQ= github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/LixenWraith/config v0.0.0-20250426061518-532233ac282c/go.mod h1:3cBfqRthYrMGIkea2GXyMVPNxVWSYt2FkeWG1Zyv2Ow= github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/lixenwraith/config v0.0.0-20250701170607-8515fa0543b6 h1:qE4SpAJWFaLkdRyE0FjTPBBRYE7LOvcmRCB5p86W73Q=
github.com/lixenwraith/config v0.0.0-20250701170607-8515fa0543b6/go.mod h1:4wPJ3HnLrYrtUwTinngCsBgtdIXsnxkLa7q4KAIbwY8=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/panjf2000/ants/v2 v2.11.3 h1:AfI0ngBoXJmYOpDh9m516vjqoUu2sLrIVgppI9TZVpg=
github.com/panjf2000/ants/v2 v2.11.3/go.mod h1:8u92CYMUc6gyvTIw8Ru7Mt7+/ESnJahz5EVtqfrilek=
github.com/panjf2000/gnet/v2 v2.9.1 h1:bKewICy/0xnQ9PMzNaswpe/Ah14w1TrRk91LHTcbIlA=
github.com/panjf2000/gnet/v2 v2.9.1/go.mod h1:WQTxDWYuQ/hz3eccH0FN32IVuvZ19HewEWx0l62fx7E=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.63.0 h1:DisIL8OjB7ul2d7cBaMRcKTQDYnrGy56R4FCiuDP0Ns=
github.com/valyala/fasthttp v1.63.0/go.mod h1:REc4IeW+cAEyLrRPa5A81MIjvz0QE1laoTX2EaPHKJM=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -1,4 +1,4 @@
// --- File: interface.go --- // FILE: interface.go
package log package log
import ( import (

View File

@ -1,4 +1,4 @@
// --- File: logger.go --- // FILE: logger.go
package log package log
import ( import (
@ -9,7 +9,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/LixenWraith/config" "github.com/lixenwraith/config"
) )
// Logger is the core struct that encapsulates all logger functionality // Logger is the core struct that encapsulates all logger functionality

View File

@ -1,4 +1,4 @@
// --- File: processor.go --- // FILE: processor.go
package log package log
import ( import (

View File

@ -1,4 +1,4 @@
// --- File: state.go --- // FILE: state.go
package log package log
import ( import (
@ -9,7 +9,7 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/LixenWraith/config" "github.com/lixenwraith/config"
) )
// State encapsulates the runtime state of the logger // State encapsulates the runtime state of the logger
@ -220,4 +220,4 @@ func (l *Logger) Flush(timeout time.Duration) error {
case <-time.After(timeout): case <-time.After(timeout):
return fmtErrorf("timeout waiting for flush confirmation (%v)", timeout) return fmtErrorf("timeout waiting for flush confirmation (%v)", timeout)
} }
} }

View File

@ -1,3 +1,4 @@
// FILE: storage.go
package log package log
import ( import (

View File

@ -1,4 +1,4 @@
// --- File: utility.go --- // FILE: utility.go
package log package log
import ( import (