Test / test (push) Canceled after 0s
Test / fuzz (./compat, FuzzParseFormat) (push) Canceled after 0s
Test / fuzz (./formatter, FuzzFormatJSON) (push) Canceled after 0s
Test / fuzz (./formatter, FuzzFormatTextPolicy) (push) Canceled after 0s
Test / fuzz (./sanitizer, FuzzSanitize) (push) Canceled after 0s
Test / fuzz (./sanitizer, FuzzSerializerJSON) (push) Canceled after 0s
391 lines
9.1 KiB
Markdown
391 lines
9.1 KiB
Markdown
# Compatibility adapters
|
|
|
|
The `compat` package provides logging adapters for gnet v2, fasthttp, and Fiber v2. Framework dependencies are not imported by this module; applications supply them.
|
|
|
|
## Shared logger setup
|
|
|
|
Configure and start the logger before passing an adapter to a framework:
|
|
|
|
```go
|
|
logger, err := log.NewBuilder().
|
|
Directory("/var/log/service").
|
|
EnableFile(true).
|
|
Format("json").
|
|
LevelString("info").
|
|
Build()
|
|
if err != nil { return err }
|
|
if err := logger.Start(); err != nil {
|
|
_ = logger.Shutdown()
|
|
return err
|
|
}
|
|
defer logger.Shutdown(5 * time.Second)
|
|
|
|
builder := compat.NewBuilder().WithLogger(logger)
|
|
gnetAdapter, err := builder.BuildGnet()
|
|
if err != nil { return err }
|
|
fastHTTPAdapter, err := builder.BuildFastHTTP()
|
|
if err != nil { return err }
|
|
fiberAdapter, err := builder.BuildFiber()
|
|
if err != nil { return err }
|
|
```
|
|
|
|
Alternatively, `compat.NewBuilder().WithConfig(cfg)` creates an initialized logger on the first build. Retrieve it with `GetLogger()` and call `Start()` explicitly. `WithLogger` takes precedence over `WithConfig`. Builders and option application are single-goroutine operations; completed adapters may be shared with a shared Logger.
|
|
|
|
## gnet
|
|
|
|
`GnetAdapter` implements `Debugf`, `Infof`, `Warnf`, `Errorf`, and `Fatalf`. Pass it through `gnet.WithLogger(adapter)`:
|
|
|
|
```go
|
|
adapter := compat.NewGnetAdapter(logger)
|
|
err := gnet.Run(eventHandler, "tcp://127.0.0.1:9000", gnet.WithLogger(adapter))
|
|
```
|
|
|
|
Ordinary messages include `msg` and `source` (`gnet`) as positional arguments. With JSON output, these appear in the `fields` array. Use the logger's `LogStructured` or `LogContext` with `FlagKV` directly if a keyed JSON object is required.
|
|
|
|
### Structured field extraction
|
|
|
|
`NewStructuredGnetAdapter` extracts simple `key=%v` or `key: %v` expressions with unindexed, single-argument verbs:
|
|
|
|
```go
|
|
adapter := compat.NewStructuredGnetAdapter(logger)
|
|
adapter.Infof("client=%s port=%d", "192.168.1.1", 8080)
|
|
// JSON fields: ["client", "192.168.1.1", "port", 8080, "source", "gnet"]
|
|
```
|
|
|
|
Text outside the expressions is retained in `msg`. Formats mixing unrelated conversions, argument indexes, width/precision, escaped percent signs, or a different argument count fall back to a complete `fmt.Sprintf` message. Extraction never guesses which argument belongs to a key.
|
|
|
|
## fasthttp
|
|
|
|
`FastHTTPAdapter` implements the `Printf` method accepted by `fasthttp.Server.Logger`:
|
|
|
|
```go
|
|
adapter := compat.NewFastHTTPAdapter(logger)
|
|
server := &fasthttp.Server{
|
|
Handler: requestHandler,
|
|
Logger: adapter,
|
|
}
|
|
```
|
|
|
|
The default detector performs a case-insensitive substring check:
|
|
|
|
| Message contains | Level |
|
|
|---|---|
|
|
| `error`, `failed`, `fatal`, or `panic` | Error |
|
|
| `warn` or `deprecated` | Warn |
|
|
| `debug` or `trace` | Debug |
|
|
| Otherwise | Configured default (Info initially) |
|
|
|
|
This heuristic does not interpret HTTP status numbers. Disable it with `WithLevelDetector(nil)`, or provide a custom function. A detector result of zero selects the configured default; nonzero custom levels are preserved.
|
|
|
|
```go
|
|
adapter := compat.NewFastHTTPAdapter(logger,
|
|
compat.WithDefaultLevel(log.LevelWarn),
|
|
compat.WithLevelDetector(nil),
|
|
)
|
|
```
|
|
|
|
## Fiber
|
|
|
|
`FiberAdapter` provides plain, printf-style (`Infof`), and key/value (`Infow`) logging methods, and implements `io.Writer` through `Write([]byte)`. Trace methods currently map to Debug with a `level`, `trace` marker. Use it directly in Fiber middleware:
|
|
|
|
```go
|
|
adapter := compat.NewFiberAdapter(logger)
|
|
app.Use(func(c *fiber.Ctx) error {
|
|
err := c.Next()
|
|
adapter.Infow("request", "method", c.Method(), "path", c.Path(),
|
|
"status", c.Response().StatusCode())
|
|
return err
|
|
})
|
|
```
|
|
|
|
## Fatal and panic behavior
|
|
|
|
Fatal methods enqueue an Error record, attempt a flush for 100 ms, and invoke the configured handler. Defaults call `os.Exit(1)` for fatal and `panic(msg)` for Fiber panic methods. These effects still occur when the log level suppresses the record. A timed-out or failed flush cannot guarantee that the final message was written.
|
|
|
|
Override behavior with `WithFatalHandler`, `WithFiberFatalHandler`, or `WithFiberPanicHandler`. A nil handler disables that action. Shut down the application/framework before the shared logger so shutdown messages can still be recorded.
|
|
|
|
### Simple integration example suite
|
|
|
|
These client and server examples can be used to test the basic functionality of the adapters. They are not included in the package to avoid dependency creep.
|
|
|
|
|
|
#### gnet server
|
|
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"github.com/lixenwraith/log"
|
|
"github.com/lixenwraith/log/compat"
|
|
"github.com/panjf2000/gnet/v2"
|
|
)
|
|
|
|
type echoServer struct {
|
|
gnet.BuiltinEventEngine
|
|
adapter *compat.GnetAdapter
|
|
}
|
|
|
|
func (es *echoServer) OnTraffic(c gnet.Conn) gnet.Action {
|
|
buf, _ := c.Next(-1)
|
|
if len(buf) > 0 {
|
|
es.adapter.Infof("Echo %d bytes", len(buf))
|
|
c.Write(buf)
|
|
}
|
|
return gnet.None
|
|
}
|
|
|
|
func main() {
|
|
// Minimal logger config
|
|
logger, err := log.NewBuilder().
|
|
Directory("./logs_gnet").
|
|
EnableFile(true).
|
|
Format("json").
|
|
LevelString("info").
|
|
HeartbeatLevel(0).
|
|
Build()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err := logger.Start(); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
adapter, err := compat.NewBuilder().WithLogger(logger).BuildGnet()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
handler := &echoServer{adapter: adapter}
|
|
|
|
fmt.Println("Starting gnet server on :9000")
|
|
fmt.Println("Press Ctrl+C to stop")
|
|
|
|
// Signal handling
|
|
sigChan := make(chan os.Signal, 1)
|
|
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
go func() {
|
|
if err := gnet.Run(handler, "tcp://:9000",
|
|
gnet.WithLogger(adapter),
|
|
); err != nil {
|
|
fmt.Printf("gnet error: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}()
|
|
|
|
<-sigChan
|
|
fmt.Println("\nShutting down...")
|
|
logger.Shutdown()
|
|
}
|
|
```
|
|
|
|
#### fasthttp server
|
|
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"github.com/lixenwraith/log"
|
|
"github.com/lixenwraith/log/compat"
|
|
"github.com/valyala/fasthttp"
|
|
)
|
|
|
|
func main() {
|
|
// Minimal logger config
|
|
logger, err := log.NewBuilder().
|
|
Directory("./logs_fasthttp").
|
|
EnableFile(true).
|
|
Format("json").
|
|
LevelString("info").
|
|
HeartbeatLevel(0).
|
|
Build()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err := logger.Start(); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
adapter, err := compat.NewBuilder().WithLogger(logger).BuildFastHTTP()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
server := &fasthttp.Server{
|
|
Handler: func(ctx *fasthttp.RequestCtx) {
|
|
adapter.Printf("Request: %s %s", ctx.Method(), ctx.Path())
|
|
ctx.WriteString("OK")
|
|
},
|
|
Logger: adapter,
|
|
Name: "TestServer",
|
|
}
|
|
|
|
fmt.Println("Starting FastHTTP server on :8080")
|
|
fmt.Println("Press Ctrl+C to stop")
|
|
|
|
sigChan := make(chan os.Signal, 1)
|
|
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
go func() {
|
|
if err := server.ListenAndServe(":8080"); err != nil {
|
|
fmt.Printf("FastHTTP error: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}()
|
|
|
|
<-sigChan
|
|
fmt.Println("\nShutting down...")
|
|
server.Shutdown()
|
|
logger.Shutdown()
|
|
}
|
|
```
|
|
|
|
#### Fiber server
|
|
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/lixenwraith/log"
|
|
"github.com/lixenwraith/log/compat"
|
|
)
|
|
|
|
func main() {
|
|
// Minimal logger config
|
|
logger, err := log.NewBuilder().
|
|
Directory("./logs_fiber").
|
|
EnableFile(true).
|
|
Format("json").
|
|
LevelString("info").
|
|
HeartbeatLevel(0).
|
|
Build()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err := logger.Start(); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
adapter, err := compat.NewBuilder().WithLogger(logger).BuildFiber()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
app := fiber.New(fiber.Config{
|
|
DisableStartupMessage: true,
|
|
})
|
|
|
|
app.Use(func(c *fiber.Ctx) error {
|
|
adapter.Infow("Request", "method", c.Method(), "path", c.Path())
|
|
return c.Next()
|
|
})
|
|
|
|
app.Get("/", func(c *fiber.Ctx) error {
|
|
return c.SendString("OK")
|
|
})
|
|
|
|
fmt.Println("Starting Fiber server on :3000")
|
|
fmt.Println("Press Ctrl+C to stop")
|
|
|
|
sigChan := make(chan os.Signal, 1)
|
|
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
go func() {
|
|
if err := app.Listen(":3000"); err != nil {
|
|
fmt.Printf("Fiber error: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}()
|
|
|
|
<-sigChan
|
|
fmt.Println("\nShutting down...")
|
|
app.ShutdownWithTimeout(2 * time.Second)
|
|
logger.Shutdown()
|
|
}
|
|
```
|
|
|
|
#### Client
|
|
|
|
Client for all adapter servers.
|
|
|
|
```bash
|
|
# Run with:
|
|
go run client.go -target=gnet
|
|
go run client.go -target=fasthttp
|
|
go run client.go -target=fiber
|
|
```
|
|
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
)
|
|
|
|
var target = flag.String("target", "fiber", "Target: gnet|fasthttp|fiber")
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
|
|
switch *target {
|
|
case "gnet":
|
|
conn, err := net.Dial("tcp", "localhost:9000")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
conn.Write([]byte("TEST"))
|
|
buf := make([]byte, 4)
|
|
conn.Read(buf)
|
|
conn.Close()
|
|
fmt.Println("gnet: received echo")
|
|
|
|
case "fasthttp":
|
|
resp, err := http.Get("http://localhost:8080/")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
body, _ := io.ReadAll(resp.Body)
|
|
resp.Body.Close()
|
|
fmt.Printf("fasthttp: %s\n", body)
|
|
|
|
case "fiber":
|
|
resp, err := http.Get("http://localhost:3000/")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
body, _ := io.ReadAll(resp.Body)
|
|
resp.Body.Close()
|
|
fmt.Printf("fiber: %s\n", body)
|
|
}
|
|
}
|
|
```
|
|
|