v0.1.4 formatter race fix, fiber adapter added, default config changed, docs updated
This commit is contained in:
@ -98,6 +98,15 @@ func (b *Builder) BuildFastHTTP(opts ...FastHTTPOption) (*FastHTTPAdapter, error
|
||||
return NewFastHTTPAdapter(l, opts...), nil
|
||||
}
|
||||
|
||||
// BuildFiber creates a Fiber v2.54.x adapter
|
||||
func (b *Builder) BuildFiber(opts ...FiberOption) (*FiberAdapter, error) {
|
||||
l, err := b.getLogger()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewFiberAdapter(l, opts...), nil
|
||||
}
|
||||
|
||||
// GetLogger returns the underlying *log.Logger instance
|
||||
// If a logger has not been provided or created yet, it will be initialized
|
||||
func (b *Builder) GetLogger() (*log.Logger, error) {
|
||||
@ -106,7 +115,7 @@ func (b *Builder) GetLogger() (*log.Logger, error) {
|
||||
|
||||
// --- Example Usage ---
|
||||
//
|
||||
// The following demonstrates how to integrate lixenwraith/log with gnet and fasthttp
|
||||
// The following demonstrates how to integrate lixenwraith/log with gnet, fasthttp, and Fiber
|
||||
// using a single, shared logger instance
|
||||
//
|
||||
// // 1. Create and configure application's main logger
|
||||
@ -127,6 +136,9 @@ func (b *Builder) GetLogger() (*log.Logger, error) {
|
||||
// fasthttpLogger, err := builder.BuildFastHTTP()
|
||||
// if err != nil { /* handle error */ }
|
||||
//
|
||||
// fiberLogger, err := builder.BuildFiber()
|
||||
// if err != nil { /* handle error */ }
|
||||
//
|
||||
// // 4. Configure your servers with the adapters
|
||||
//
|
||||
// // For gnet:
|
||||
@ -142,4 +154,16 @@ func (b *Builder) GetLogger() (*log.Logger, error) {
|
||||
// },
|
||||
// Logger: fasthttpLogger,
|
||||
// }
|
||||
// go server.ListenAndServe(":8080")
|
||||
// go server.ListenAndServe(":8080")
|
||||
//
|
||||
// // For Fiber v2.54.x:
|
||||
// // The adapter is passed to fiber.New() via the config
|
||||
// app := fiber.New(fiber.Config{
|
||||
// AppName: "My Application",
|
||||
// })
|
||||
// app.UpdateConfig(fiber.Config{
|
||||
// AppName: "My Application",
|
||||
// })
|
||||
// // Note: Set the logger after app creation if needed
|
||||
// // fiber uses internal logging, adapter can be used in custom middleware
|
||||
// go app.Listen(":3000")
|
||||
@ -22,6 +22,7 @@ func createTestCompatBuilder(t *testing.T) (*Builder, *log.Logger, string) {
|
||||
Directory(tmpDir).
|
||||
Format("json").
|
||||
LevelString("debug").
|
||||
EnableFile(true).
|
||||
Build()
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -224,4 +225,126 @@ func TestFastHTTPAdapter(t *testing.T) {
|
||||
assert.Equal(t, "source", fields[2])
|
||||
assert.Equal(t, "fasthttp", fields[3])
|
||||
}
|
||||
}
|
||||
|
||||
// TestFiberAdapter tests the Fiber adapter's logging output across all log levels
|
||||
func TestFiberAdapter(t *testing.T) {
|
||||
builder, logger, tmpDir := createTestCompatBuilder(t)
|
||||
defer logger.Shutdown()
|
||||
|
||||
var fatalCalled bool
|
||||
var panicCalled bool
|
||||
adapter, err := builder.BuildFiber(
|
||||
WithFiberFatalHandler(func(msg string) {
|
||||
fatalCalled = true
|
||||
}),
|
||||
WithFiberPanicHandler(func(msg string) {
|
||||
panicCalled = true
|
||||
}),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test formatted logging (Tracef, Debugf, Infof, Warnf, Errorf, Fatalf, Panicf)
|
||||
adapter.Tracef("fiber trace id=%d", 1)
|
||||
adapter.Debugf("fiber debug id=%d", 2)
|
||||
adapter.Infof("fiber info id=%d", 3)
|
||||
adapter.Warnf("fiber warn id=%d", 4)
|
||||
adapter.Errorf("fiber error id=%d", 5)
|
||||
adapter.Fatalf("fiber fatal id=%d", 6)
|
||||
adapter.Panicf("fiber panic id=%d", 7)
|
||||
|
||||
err = logger.Flush(time.Second)
|
||||
require.NoError(t, err)
|
||||
|
||||
lines := readLogFile(t, tmpDir, 7)
|
||||
|
||||
expected := []struct {
|
||||
level string
|
||||
msg string
|
||||
}{
|
||||
{"DEBUG", "fiber trace id=1"},
|
||||
{"DEBUG", "fiber debug id=2"},
|
||||
{"INFO", "fiber info id=3"},
|
||||
{"WARN", "fiber warn id=4"},
|
||||
{"ERROR", "fiber error id=5"},
|
||||
{"ERROR", "fiber fatal id=6"},
|
||||
{"ERROR", "fiber panic id=7"},
|
||||
}
|
||||
|
||||
require.Len(t, lines, 7, "Should have 7 fiber log lines")
|
||||
|
||||
for i, line := range lines {
|
||||
var entry map[string]any
|
||||
err := json.Unmarshal([]byte(line), &entry)
|
||||
require.NoError(t, err, "Failed to parse log line: %s", line)
|
||||
|
||||
assert.Equal(t, expected[i].level, entry["level"])
|
||||
fields := entry["fields"].([]any)
|
||||
assert.Equal(t, "msg", fields[0])
|
||||
assert.Equal(t, expected[i].msg, fields[1])
|
||||
assert.Equal(t, "source", fields[2])
|
||||
assert.Equal(t, "fiber", fields[3])
|
||||
}
|
||||
assert.True(t, fatalCalled, "Custom fatal handler should have been called")
|
||||
assert.True(t, panicCalled, "Custom panic handler should have been called")
|
||||
}
|
||||
|
||||
// TestFiberAdapterStructuredLogging tests Fiber's structured logging (WithLogger methods)
|
||||
func TestFiberAdapterStructuredLogging(t *testing.T) {
|
||||
builder, logger, tmpDir := createTestCompatBuilder(t)
|
||||
defer logger.Shutdown()
|
||||
|
||||
adapter, err := builder.BuildFiber()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test structured logging with key-value pairs
|
||||
adapter.Infow("request served", "status", 200, "client_ip", "127.0.0.1", "method", "GET")
|
||||
adapter.Debugw("query executed", "duration_ms", 42, "query", "SELECT * FROM users")
|
||||
|
||||
err = logger.Flush(time.Second)
|
||||
require.NoError(t, err)
|
||||
|
||||
lines := readLogFile(t, tmpDir, 2)
|
||||
require.Len(t, lines, 2, "Should have 2 fiber structured log lines")
|
||||
|
||||
// Check first structured log (Infow)
|
||||
var entry1 map[string]any
|
||||
err = json.Unmarshal([]byte(lines[0]), &entry1)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "INFO", entry1["level"])
|
||||
fields1 := entry1["fields"].([]any)
|
||||
assert.Equal(t, "msg", fields1[0])
|
||||
assert.Equal(t, "request served", fields1[1])
|
||||
assert.Equal(t, "source", fields1[2])
|
||||
assert.Equal(t, "fiber", fields1[3])
|
||||
assert.Equal(t, "status", fields1[4])
|
||||
assert.Equal(t, 200.0, fields1[5]) // JSON numbers are float64
|
||||
assert.Equal(t, "client_ip", fields1[6])
|
||||
assert.Equal(t, "127.0.0.1", fields1[7])
|
||||
|
||||
// Check second structured log (Debugw)
|
||||
var entry2 map[string]any
|
||||
err = json.Unmarshal([]byte(lines[1]), &entry2)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "DEBUG", entry2["level"])
|
||||
fields2 := entry2["fields"].([]any)
|
||||
assert.Equal(t, "msg", fields2[0])
|
||||
assert.Equal(t, "query executed", fields2[1])
|
||||
assert.Equal(t, "source", fields2[2])
|
||||
assert.Equal(t, "fiber", fields2[3])
|
||||
assert.Equal(t, "duration_ms", fields2[4])
|
||||
assert.Equal(t, 42.0, fields2[5]) // JSON numbers are float64
|
||||
}
|
||||
|
||||
// TestFiberBuilderIntegration ensures Fiber adapter can be built from builder
|
||||
func TestFiberBuilderIntegration(t *testing.T) {
|
||||
builder, logger, _ := createTestCompatBuilder(t)
|
||||
defer logger.Shutdown()
|
||||
|
||||
fiberAdapter, err := builder.BuildFiber()
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, fiberAdapter)
|
||||
assert.Equal(t, logger, fiberAdapter.logger)
|
||||
}
|
||||
254
compat/fiber.go
Normal file
254
compat/fiber.go
Normal file
@ -0,0 +1,254 @@
|
||||
// FILE: lixenwraith/log/compat/fiber.go
|
||||
package compat
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/lixenwraith/log"
|
||||
)
|
||||
|
||||
// FiberAdapter wraps lixenwraith/log.Logger to implement Fiber's CommonLogger interface
|
||||
// This provides compatibility with Fiber v2.54.x logging requirements
|
||||
type FiberAdapter struct {
|
||||
logger *log.Logger
|
||||
fatalHandler func(msg string) // Customizable fatal behavior
|
||||
panicHandler func(msg string) // Customizable panic behavior
|
||||
}
|
||||
|
||||
// NewFiberAdapter creates a new Fiber-compatible logger adapter
|
||||
func NewFiberAdapter(logger *log.Logger, opts ...FiberOption) *FiberAdapter {
|
||||
adapter := &FiberAdapter{
|
||||
logger: logger,
|
||||
fatalHandler: func(msg string) {
|
||||
os.Exit(1) // Default behavior
|
||||
},
|
||||
panicHandler: func(msg string) {
|
||||
panic(msg) // Default behavior
|
||||
},
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(adapter)
|
||||
}
|
||||
|
||||
return adapter
|
||||
}
|
||||
|
||||
// FiberOption allows customizing adapter behavior
|
||||
type FiberOption func(*FiberAdapter)
|
||||
|
||||
// WithFiberFatalHandler sets a custom fatal handler
|
||||
func WithFiberFatalHandler(handler func(string)) FiberOption {
|
||||
return func(a *FiberAdapter) {
|
||||
a.fatalHandler = handler
|
||||
}
|
||||
}
|
||||
|
||||
// WithFiberPanicHandler sets a custom panic handler
|
||||
func WithFiberPanicHandler(handler func(string)) FiberOption {
|
||||
return func(a *FiberAdapter) {
|
||||
a.panicHandler = handler
|
||||
}
|
||||
}
|
||||
|
||||
// --- Logger interface implementation (7 methods) ---
|
||||
|
||||
// Trace logs at trace/debug level
|
||||
func (a *FiberAdapter) Trace(v ...any) {
|
||||
msg := fmt.Sprint(v...)
|
||||
a.logger.Debug("msg", msg, "source", "fiber", "level", "trace")
|
||||
}
|
||||
|
||||
// Debug logs at debug level
|
||||
func (a *FiberAdapter) Debug(v ...any) {
|
||||
msg := fmt.Sprint(v...)
|
||||
a.logger.Debug("msg", msg, "source", "fiber")
|
||||
}
|
||||
|
||||
// Info logs at info level
|
||||
func (a *FiberAdapter) Info(v ...any) {
|
||||
msg := fmt.Sprint(v...)
|
||||
a.logger.Info("msg", msg, "source", "fiber")
|
||||
}
|
||||
|
||||
// Warn logs at warn level
|
||||
func (a *FiberAdapter) Warn(v ...any) {
|
||||
msg := fmt.Sprint(v...)
|
||||
a.logger.Warn("msg", msg, "source", "fiber")
|
||||
}
|
||||
|
||||
// Error logs at error level
|
||||
func (a *FiberAdapter) Error(v ...any) {
|
||||
msg := fmt.Sprint(v...)
|
||||
a.logger.Error("msg", msg, "source", "fiber")
|
||||
}
|
||||
|
||||
// Fatal logs at error level and triggers fatal handler
|
||||
func (a *FiberAdapter) Fatal(v ...any) {
|
||||
msg := fmt.Sprint(v...)
|
||||
a.logger.Error("msg", msg, "source", "fiber", "fatal", true)
|
||||
|
||||
// Ensure log is flushed before exit
|
||||
_ = a.logger.Flush(100 * time.Millisecond)
|
||||
|
||||
if a.fatalHandler != nil {
|
||||
a.fatalHandler(msg)
|
||||
}
|
||||
}
|
||||
|
||||
// Panic logs at error level and triggers panic handler
|
||||
func (a *FiberAdapter) Panic(v ...any) {
|
||||
msg := fmt.Sprint(v...)
|
||||
a.logger.Error("msg", msg, "source", "fiber", "panic", true)
|
||||
|
||||
// Ensure log is flushed before panic
|
||||
_ = a.logger.Flush(100 * time.Millisecond)
|
||||
|
||||
if a.panicHandler != nil {
|
||||
a.panicHandler(msg)
|
||||
}
|
||||
}
|
||||
|
||||
// Write makes FiberAdapter implement io.Writer interface
|
||||
// This allows it to be used with fiber.Config.ErrorHandler output redirection
|
||||
func (a *FiberAdapter) Write(p []byte) (n int, err error) {
|
||||
msg := string(p)
|
||||
// Trim trailing newline if present
|
||||
if len(msg) > 0 && msg[len(msg)-1] == '\n' {
|
||||
msg = msg[:len(msg)-1]
|
||||
}
|
||||
a.logger.Info("msg", msg, "source", "fiber")
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// --- FormatLogger interface implementation (7 methods) ---
|
||||
|
||||
// Tracef logs at trace/debug level with printf-style formatting
|
||||
func (a *FiberAdapter) Tracef(format string, v ...any) {
|
||||
msg := fmt.Sprintf(format, v...)
|
||||
a.logger.Debug("msg", msg, "source", "fiber", "level", "trace")
|
||||
}
|
||||
|
||||
// Debugf logs at debug level with printf-style formatting
|
||||
func (a *FiberAdapter) Debugf(format string, v ...any) {
|
||||
msg := fmt.Sprintf(format, v...)
|
||||
a.logger.Debug("msg", msg, "source", "fiber")
|
||||
}
|
||||
|
||||
// Infof logs at info level with printf-style formatting
|
||||
func (a *FiberAdapter) Infof(format string, v ...any) {
|
||||
msg := fmt.Sprintf(format, v...)
|
||||
a.logger.Info("msg", msg, "source", "fiber")
|
||||
}
|
||||
|
||||
// Warnf logs at warn level with printf-style formatting
|
||||
func (a *FiberAdapter) Warnf(format string, v ...any) {
|
||||
msg := fmt.Sprintf(format, v...)
|
||||
a.logger.Warn("msg", msg, "source", "fiber")
|
||||
}
|
||||
|
||||
// Errorf logs at error level with printf-style formatting
|
||||
func (a *FiberAdapter) Errorf(format string, v ...any) {
|
||||
msg := fmt.Sprintf(format, v...)
|
||||
a.logger.Error("msg", msg, "source", "fiber")
|
||||
}
|
||||
|
||||
// Fatalf logs at error level and triggers fatal handler
|
||||
func (a *FiberAdapter) Fatalf(format string, v ...any) {
|
||||
msg := fmt.Sprintf(format, v...)
|
||||
a.logger.Error("msg", msg, "source", "fiber", "fatal", true)
|
||||
|
||||
// Ensure log is flushed before exit
|
||||
_ = a.logger.Flush(100 * time.Millisecond)
|
||||
|
||||
if a.fatalHandler != nil {
|
||||
a.fatalHandler(msg)
|
||||
}
|
||||
}
|
||||
|
||||
// Panicf logs at error level and triggers panic handler
|
||||
func (a *FiberAdapter) Panicf(format string, v ...any) {
|
||||
msg := fmt.Sprintf(format, v...)
|
||||
a.logger.Error("msg", msg, "source", "fiber", "panic", true)
|
||||
|
||||
// Ensure log is flushed before panic
|
||||
_ = a.logger.Flush(100 * time.Millisecond)
|
||||
|
||||
if a.panicHandler != nil {
|
||||
a.panicHandler(msg)
|
||||
}
|
||||
}
|
||||
|
||||
// --- WithLogger interface implementation (7 methods) ---
|
||||
|
||||
// Tracew logs at trace/debug level with structured key-value pairs
|
||||
func (a *FiberAdapter) Tracew(msg string, keysAndValues ...any) {
|
||||
fields := make([]any, 0, len(keysAndValues)+6)
|
||||
fields = append(fields, "msg", msg, "source", "fiber", "level", "trace")
|
||||
fields = append(fields, keysAndValues...)
|
||||
a.logger.Debug(fields...)
|
||||
}
|
||||
|
||||
// Debugw logs at debug level with structured key-value pairs
|
||||
func (a *FiberAdapter) Debugw(msg string, keysAndValues ...any) {
|
||||
fields := make([]any, 0, len(keysAndValues)+4)
|
||||
fields = append(fields, "msg", msg, "source", "fiber")
|
||||
fields = append(fields, keysAndValues...)
|
||||
a.logger.Debug(fields...)
|
||||
}
|
||||
|
||||
// Infow logs at info level with structured key-value pairs
|
||||
func (a *FiberAdapter) Infow(msg string, keysAndValues ...any) {
|
||||
fields := make([]any, 0, len(keysAndValues)+4)
|
||||
fields = append(fields, "msg", msg, "source", "fiber")
|
||||
fields = append(fields, keysAndValues...)
|
||||
a.logger.Info(fields...)
|
||||
}
|
||||
|
||||
// Warnw logs at warn level with structured key-value pairs
|
||||
func (a *FiberAdapter) Warnw(msg string, keysAndValues ...any) {
|
||||
fields := make([]any, 0, len(keysAndValues)+4)
|
||||
fields = append(fields, "msg", msg, "source", "fiber")
|
||||
fields = append(fields, keysAndValues...)
|
||||
a.logger.Warn(fields...)
|
||||
}
|
||||
|
||||
// Errorw logs at error level with structured key-value pairs
|
||||
func (a *FiberAdapter) Errorw(msg string, keysAndValues ...any) {
|
||||
fields := make([]any, 0, len(keysAndValues)+4)
|
||||
fields = append(fields, "msg", msg, "source", "fiber")
|
||||
fields = append(fields, keysAndValues...)
|
||||
a.logger.Error(fields...)
|
||||
}
|
||||
|
||||
// Fatalw logs at error level with structured key-value pairs and triggers fatal handler
|
||||
func (a *FiberAdapter) Fatalw(msg string, keysAndValues ...any) {
|
||||
fields := make([]any, 0, len(keysAndValues)+6)
|
||||
fields = append(fields, "msg", msg, "source", "fiber", "fatal", true)
|
||||
fields = append(fields, keysAndValues...)
|
||||
a.logger.Error(fields...)
|
||||
|
||||
// Ensure log is flushed before exit
|
||||
_ = a.logger.Flush(100 * time.Millisecond)
|
||||
|
||||
if a.fatalHandler != nil {
|
||||
a.fatalHandler(msg)
|
||||
}
|
||||
}
|
||||
|
||||
// Panicw logs at error level with structured key-value pairs and triggers panic handler
|
||||
func (a *FiberAdapter) Panicw(msg string, keysAndValues ...any) {
|
||||
fields := make([]any, 0, len(keysAndValues)+6)
|
||||
fields = append(fields, "msg", msg, "source", "fiber", "panic", true)
|
||||
fields = append(fields, keysAndValues...)
|
||||
a.logger.Error(fields...)
|
||||
|
||||
// Ensure log is flushed before panic
|
||||
_ = a.logger.Flush(100 * time.Millisecond)
|
||||
|
||||
if a.panicHandler != nil {
|
||||
a.panicHandler(msg)
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
// FILE: lixenwraith/log/compat/structured.go
|
||||
// FILE: lixenwraith/log/compat/structured_gnet.go
|
||||
package compat
|
||||
|
||||
import (
|
||||
Reference in New Issue
Block a user