1
0
mirror of https://github.com/gofiber/fiber.git synced 2025-02-06 19:11:45 +00:00

🚀 Add Logger interface and fiberlog (#2499)

* add log for fiber

* replace log in fiber

* add Log use to adapt for log libraries

* Update app.go

Co-authored-by: Tomás Warynyca <41587659+tomaswarynyca@users.noreply.github.com>

* wip: add log docs

* add WithLogger use to print key and value

* remove CtxLogger and add WithContext use to bind Context

* fix errcheck

* fix errcheck

* update log.md

---------

Co-authored-by: Tomás Warynyca <41587659+tomaswarynyca@users.noreply.github.com>
This commit is contained in:
Jiun Lee 2023-06-26 14:16:57 +08:00 committed by GitHub
parent 5967d36bc0
commit fefc533834
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 865 additions and 42 deletions

10
app.go
View File

@ -14,7 +14,6 @@ import (
"encoding/xml"
"errors"
"fmt"
"log"
"net"
"net/http"
"net/http/httputil"
@ -24,6 +23,7 @@ import (
"sync"
"time"
"github.com/gofiber/fiber/v2/log"
"github.com/gofiber/fiber/v2/utils"
"github.com/valyala/fasthttp"
@ -521,7 +521,7 @@ func New(config ...Config) *App {
if app.config.ETag {
if !IsChild() {
log.Printf("[Warning] Config.ETag is deprecated since v2.0.6, please use 'middleware/etag'.\n")
log.Warn("Config.ETag is deprecated since v2.0.6, please use 'middleware/etag'.")
}
}
@ -589,7 +589,7 @@ func (app *App) handleTrustedProxy(ipAddress string) {
if strings.Contains(ipAddress, "/") {
_, ipNet, err := net.ParseCIDR(ipAddress)
if err != nil {
log.Printf("[Warning] IP range %q could not be parsed: %v\n", ipAddress, err)
log.Warnf("IP range %q could not be parsed: %v", ipAddress, err)
} else {
app.config.trustedProxyRanges = append(app.config.trustedProxyRanges, ipNet)
}
@ -987,7 +987,7 @@ func (app *App) init() *App {
// Only load templates if a view engine is specified
if app.config.Views != nil {
if err := app.config.Views.Load(); err != nil {
log.Printf("[Warning]: failed to load views: %v\n", err)
log.Warnf("failed to load views: %v", err)
}
}
@ -1084,7 +1084,7 @@ func (app *App) serverErrorHandler(fctx *fasthttp.RequestCtx, err error) {
}
if catch := app.ErrorHandler(c, err); catch != nil {
log.Printf("serverErrorHandler: failed to call ErrorHandler: %v\n", catch)
log.Errorf("serverErrorHandler: failed to call ErrorHandler: %v", catch)
_ = c.SendStatus(StatusInternalServerError) //nolint:errcheck // It is fine to ignore the error here
return
}

156
docs/api/log.md Normal file
View File

@ -0,0 +1,156 @@
---
id: log
title: Log
description: Fiber's built-in log package
sidebar_position: 8
---
## Log
We can use logs to observe program behavior, diagnose problems, or configure corresponding alarms.
And defining a well structured log can improve search efficiency and facilitate handling of problems.
Fiber provides a default way to print logs in the standard output.
It also provides several global functions, such as `log.Info`, `log.Errorf`, `log.Warnw`, etc.
## Log levels
```go
const (
LevelTrace Level = iota
LevelDebug
LevelInfo
LevelWarn
LevelError
LevelFatal
LevelPanic
)
```
## Custom log
Fiber provides the `AllLogger` interface for adapting the various log libraries.
```go
type CommonLogger interface {
Logger
FormatLogger
WithLogger
}
type AllLogger interface {
CommonLogger
ControlLogger
WithLogger
}
```
## Print log
Note: The method of calling the Fatal level will interrupt the program running after printing the log, please use it with caution.
Directly print logs of different levels, which will be entered into messageKey, the default is msg.
```go
log.Info("Hello, World!")
log.Debug("Are you OK?")
log.Info("42 is the answer to life, the universe, and everything")
log.Warn("We are under attack!")
log.Error("Houston, we have a problem.")
log.Fatal("So Long, and Thanks for All the Fislog.")
log.Panic("The system is down.")
```
Format and print logs of different levels, all methods end with f
```go
log.Debugf("Hello %s", "boy")
log.Infof("%d is the answer to life, the universe, and everything", 233)
log.Warnf("We are under attack %s!", "boss")
log.Errorf("%s, we have a problem.", "Master Shifu")
log.Fatalf("So Long, and Thanks for All the %s.", "banana")
```
Print a message with the key and value, or `KEYVALS UNPAIRED` if the key and value are not a pair.
```go
log.Debugw("", "Hello", "boy")
log.Infow("", "number", 233)
log.Warnw("", "job", "boss")
log.Errorw("", "name", "Master Shifu")
log.Fatalw("", "fruit", "banana")
```
## Global log
If you are in a project and just want to use a simple log function that can be printed at any time in the global, we provide a global log.
```go
import "github.com/gofiber/fiber/v2/log"
log.Info("info")
log.Warn("warn")
```
The above is using the default `log.DefaultLogger` standard output.
You can also find an already implemented adaptation under contrib, or use your own implemented Logger and use `log.SetLogger` to set the global log logger.
```go
import (
"log"
fiberlog "github.com/gofiber/fiber/v2/log"
)
var _ log.AllLogger = (*customLogger)(nil)
type customLogger struct {
stdlog *log.Logger
}
// ...
// inject your custom logger
fiberlog.SetLogger(customLogger)
```
## Set Level
`log.SetLevel` sets the level of logs below which logs will not be output.
The default logger is LevelTrace.
Note that this method is not **concurrent-safe**.
```go
import "github.com/gofiber/fiber/v2/log"
log.SetLevel(log.LevelInfo)
```
## Set output
`log.SetOutput` sets the output destination of the logger. The default logger types the log in the console.
```go
var logger AllLogger = &defaultLogger{
stdlog: log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile|log.Lmicroseconds),
depth: 4,
}
```
Set the output destination to the file.
```go
// Output to ./test.log file
f, err := os.OpenFile("test.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
return
}
log.SetOutput(f)
```
Set the output destination to the console and file.
```go
// Output to ./test.log file
file, _ := os.OpenFile("test.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
iw := io.MultiWriter(os.Stdout, file)
log.SetOutput(iw)
```
## Bind context
Set the context, using the following method will return a `CommonLogger` instance bound to the specified context
```go
commonLogger := log.WithContext(ctx)
commonLogger.Info("info")
```

View File

@ -10,7 +10,6 @@ import (
"fmt"
"hash/crc32"
"io"
"log"
"net"
"os"
"path/filepath"
@ -19,6 +18,7 @@ import (
"time"
"unsafe"
"github.com/gofiber/fiber/v2/log"
"github.com/gofiber/fiber/v2/utils"
"github.com/valyala/bytebufferpool"
@ -75,7 +75,7 @@ func readContent(rf io.ReaderFrom, name string) (int64, error) {
}
defer func() {
if err = f.Close(); err != nil {
log.Printf("Error closing file: %s\n", err)
log.Errorf("Error closing file: %s", err)
}
}()
if n, err := rf.ReadFrom(f); err != nil {
@ -192,7 +192,7 @@ func setETag(c *Ctx, weak bool) { //nolint: revive // Accepting a bool param is
if clientEtag[2:] == etag || clientEtag[2:] == etag[2:] {
// W/1 == 1 || W/1 == W/1
if err := c.SendStatus(StatusNotModified); err != nil {
log.Printf("setETag: failed to SendStatus: %v\n", err)
log.Errorf("setETag: failed to SendStatus: %v", err)
}
c.fasthttp.ResetBody()
return
@ -204,7 +204,7 @@ func setETag(c *Ctx, weak bool) { //nolint: revive // Accepting a bool param is
if strings.Contains(clientEtag, etag) {
// 1 == 1
if err := c.SendStatus(StatusNotModified); err != nil {
log.Printf("setETag: failed to SendStatus: %v\n", err)
log.Errorf("setETag: failed to SendStatus: %v", err)
}
c.fasthttp.ResetBody()
return

View File

@ -1,7 +1,7 @@
package fiber
import (
"log"
"github.com/gofiber/fiber/v2/log"
)
// OnRouteHandler Handlers define a function to create hooks for Fiber.
@ -194,7 +194,7 @@ func (h *Hooks) executeOnListenHooks(listenData ListenData) error {
func (h *Hooks) executeOnShutdownHooks() {
for _, v := range h.onShutdown {
if err := v(); err != nil {
log.Printf("failed to call shutdown hook: %v\n", err)
log.Errorf("failed to call shutdown hook: %v", err)
}
}
}
@ -202,7 +202,7 @@ func (h *Hooks) executeOnShutdownHooks() {
func (h *Hooks) executeOnForkHooks(pid int) {
for _, v := range h.onFork {
if err := v(pid); err != nil {
log.Printf("failed to call fork hook: %v\n", err)
log.Errorf("failed to call fork hook: %v", err)
}
}
}

View File

@ -4,7 +4,6 @@ import (
"fmt"
"html/template"
"io"
"log"
"net/http"
"os"
"path/filepath"
@ -12,6 +11,7 @@ import (
"sync"
"github.com/gofiber/fiber/v2/internal/template/utils"
"github.com/gofiber/fiber/v2/log"
)
// Engine struct
@ -113,7 +113,7 @@ func (e *Engine) Debug(enabled bool) *Engine {
// Parse is deprecated, please use Load() instead
func (e *Engine) Parse() error {
log.Println("[Warning] Parse() is deprecated, please use Load() instead.")
log.Warn("Parse() is deprecated, please use Load() instead.")
return e.Load()
}
@ -170,7 +170,7 @@ func (e *Engine) Load() error {
}
// Debugging
if e.debug {
log.Printf("views: parsed template: %s\n", name)
log.Infof("views: parsed template: %s", name)
}
return err
}

View File

@ -9,7 +9,6 @@ import (
"crypto/x509"
"errors"
"fmt"
"log"
"net"
"os"
"path/filepath"
@ -20,6 +19,7 @@ import (
"strings"
"text/tabwriter"
"github.com/gofiber/fiber/v2/log"
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
"github.com/mattn/go-runewidth"
@ -49,7 +49,7 @@ func (app *App) Listener(ln net.Listener) error {
// Prefork is not supported for custom listeners
if app.config.Prefork {
log.Printf("[Warning] Prefork isn't supported for custom listeners.\n")
log.Warn("Prefork isn't supported for custom listeners.")
}
// Start listening

205
log/default.go Normal file
View File

@ -0,0 +1,205 @@
package log
import (
"context"
"fmt"
"io"
"log"
"os"
"sync"
"github.com/valyala/bytebufferpool"
)
var _ AllLogger = (*defaultLogger)(nil)
type defaultLogger struct {
stdlog *log.Logger
level Level
depth int
}
// privateLog logs a message at a given level log the default logger.
// when the level is fatal, it will exit the program.
func (l *defaultLogger) privateLog(lv Level, fmtArgs []interface{}) {
if l.level > lv {
return
}
level := lv.toString()
buf := bytebufferpool.Get()
_, _ = buf.WriteString(level) //nolint:errcheck // It is fine to ignore the error
_, _ = buf.WriteString(fmt.Sprint(fmtArgs...)) //nolint:errcheck // It is fine to ignore the error
_ = l.stdlog.Output(l.depth, buf.String()) //nolint:errcheck // It is fine to ignore the error
buf.Reset()
bytebufferpool.Put(buf)
if lv == LevelFatal {
os.Exit(1) //nolint:revive // we want to exit the program when Fatal is called
}
}
// privateLog logs a message at a given level log the default logger.
// when the level is fatal, it will exit the program.
func (l *defaultLogger) privateLogf(lv Level, format string, fmtArgs []interface{}) {
if l.level > lv {
return
}
level := lv.toString()
buf := bytebufferpool.Get()
_, _ = buf.WriteString(level) //nolint:errcheck // It is fine to ignore the error
if len(fmtArgs) > 0 {
_, _ = fmt.Fprintf(buf, format, fmtArgs...)
} else {
_, _ = fmt.Fprint(buf, fmtArgs...)
}
_ = l.stdlog.Output(l.depth, buf.String()) //nolint:errcheck // It is fine to ignore the error
buf.Reset()
bytebufferpool.Put(buf)
if lv == LevelFatal {
os.Exit(1) //nolint:revive // we want to exit the program when Fatal is called
}
}
// privateLogw logs a message at a given level log the default logger.
// when the level is fatal, it will exit the program.
func (l *defaultLogger) privateLogw(lv Level, format string, keysAndValues []interface{}) {
if l.level > lv {
return
}
level := lv.toString()
buf := bytebufferpool.Get()
_, _ = buf.WriteString(level) //nolint:errcheck // It is fine to ignore the error
// Write format privateLog buffer
if format != "" {
_, _ = buf.WriteString(format) //nolint:errcheck // It is fine to ignore the error
}
var once sync.Once
isFirst := true
// Write keys and values privateLog buffer
if len(keysAndValues) > 0 {
if (len(keysAndValues) & 1) == 1 {
keysAndValues = append(keysAndValues, "KEYVALS UNPAIRED")
}
for i := 0; i < len(keysAndValues); i += 2 {
if format == "" && isFirst {
once.Do(func() {
_, _ = fmt.Fprintf(buf, "%s=%v", keysAndValues[i], keysAndValues[i+1])
isFirst = false
})
continue
}
_, _ = fmt.Fprintf(buf, " %s=%v", keysAndValues[i], keysAndValues[i+1])
}
}
_ = l.stdlog.Output(l.depth, buf.String()) //nolint:errcheck // It is fine to ignore the error
buf.Reset()
bytebufferpool.Put(buf)
if lv == LevelFatal {
os.Exit(1) //nolint:revive // we want to exit the program when Fatal is called
}
}
func (l *defaultLogger) Trace(v ...interface{}) {
l.privateLog(LevelTrace, v)
}
func (l *defaultLogger) Debug(v ...interface{}) {
l.privateLog(LevelDebug, v)
}
func (l *defaultLogger) Info(v ...interface{}) {
l.privateLog(LevelInfo, v)
}
func (l *defaultLogger) Warn(v ...interface{}) {
l.privateLog(LevelWarn, v)
}
func (l *defaultLogger) Error(v ...interface{}) {
l.privateLog(LevelError, v)
}
func (l *defaultLogger) Fatal(v ...interface{}) {
l.privateLog(LevelFatal, v)
}
func (l *defaultLogger) Panic(v ...interface{}) {
l.privateLog(LevelPanic, v)
}
func (l *defaultLogger) Tracef(format string, v ...interface{}) {
l.privateLogf(LevelTrace, format, v)
}
func (l *defaultLogger) Debugf(format string, v ...interface{}) {
l.privateLogf(LevelDebug, format, v)
}
func (l *defaultLogger) Infof(format string, v ...interface{}) {
l.privateLogf(LevelInfo, format, v)
}
func (l *defaultLogger) Warnf(format string, v ...interface{}) {
l.privateLogf(LevelWarn, format, v)
}
func (l *defaultLogger) Errorf(format string, v ...interface{}) {
l.privateLogf(LevelError, format, v)
}
func (l *defaultLogger) Fatalf(format string, v ...interface{}) {
l.privateLogf(LevelFatal, format, v)
}
func (l *defaultLogger) Panicf(format string, v ...interface{}) {
l.privateLogf(LevelPanic, format, v)
}
func (l *defaultLogger) Tracew(msg string, keysAndValues ...interface{}) {
l.privateLogw(LevelTrace, msg, keysAndValues)
}
func (l *defaultLogger) Debugw(msg string, keysAndValues ...interface{}) {
l.privateLogw(LevelDebug, msg, keysAndValues)
}
func (l *defaultLogger) Infow(msg string, keysAndValues ...interface{}) {
l.privateLogw(LevelInfo, msg, keysAndValues)
}
func (l *defaultLogger) Warnw(msg string, keysAndValues ...interface{}) {
l.privateLogw(LevelWarn, msg, keysAndValues)
}
func (l *defaultLogger) Errorw(msg string, keysAndValues ...interface{}) {
l.privateLogw(LevelError, msg, keysAndValues)
}
func (l *defaultLogger) Fatalw(msg string, keysAndValues ...interface{}) {
l.privateLogw(LevelFatal, msg, keysAndValues)
}
func (l *defaultLogger) Panicw(msg string, keysAndValues ...interface{}) {
l.privateLogw(LevelPanic, msg, keysAndValues)
}
func (l *defaultLogger) WithContext(_ context.Context) CommonLogger {
return l
}
func (l *defaultLogger) SetLevel(level Level) {
l.level = level
}
func (l *defaultLogger) SetOutput(writer io.Writer) {
l.stdlog.SetOutput(writer)
}
// DefaultLogger returns the default logger.
func DefaultLogger() AllLogger {
return logger
}

196
log/default_test.go Normal file
View File

@ -0,0 +1,196 @@
package log
import (
"bytes"
"context"
"log"
"os"
"testing"
"github.com/gofiber/fiber/v2/utils"
)
const work = "work"
func initDefaultLogger() {
logger = &defaultLogger{
stdlog: log.New(os.Stderr, "", 0),
depth: 4,
}
}
type byteSliceWriter struct {
b []byte
}
func (w *byteSliceWriter) Write(p []byte) (int, error) {
w.b = append(w.b, p...)
return len(p), nil
}
func Test_DefaultLogger(t *testing.T) {
initDefaultLogger()
var w byteSliceWriter
SetOutput(&w)
Trace("trace work")
Debug("received work order")
Info("starting work")
Warn("work may fail")
Error("work failed")
Panic("work panic")
utils.AssertEqual(t, "[Trace] trace work\n"+
"[Debug] received work order\n"+
"[Info] starting work\n"+
"[Warn] work may fail\n"+
"[Error] work failed\n"+
"[Panic] work panic\n", string(w.b))
}
func Test_DefaultFormatLogger(t *testing.T) {
initDefaultLogger()
var w byteSliceWriter
SetOutput(&w)
Tracef("trace %s", work)
Debugf("received %s order", work)
Infof("starting %s", work)
Warnf("%s may fail", work)
Errorf("%s failed", work)
Panicf("%s panic", work)
utils.AssertEqual(t, "[Trace] trace work\n"+
"[Debug] received work order\n"+
"[Info] starting work\n"+
"[Warn] work may fail\n"+
"[Error] work failed\n"+
"[Panic] work panic\n", string(w.b))
}
func Test_CtxLogger(t *testing.T) {
initDefaultLogger()
var w byteSliceWriter
SetOutput(&w)
ctx := context.Background()
WithContext(ctx).Tracef("trace %s", work)
WithContext(ctx).Debugf("received %s order", work)
WithContext(ctx).Infof("starting %s", work)
WithContext(ctx).Warnf("%s may fail", work)
WithContext(ctx).Errorf("%s failed", work)
WithContext(ctx).Panicf("%s panic", work)
utils.AssertEqual(t, "[Trace] trace work\n"+
"[Debug] received work order\n"+
"[Info] starting work\n"+
"[Warn] work may fail\n"+
"[Error] work failed\n"+
"[Panic] work panic\n", string(w.b))
}
func Test_LogfKeyAndValues(t *testing.T) {
tests := []struct {
name string
level Level
format string
fmtArgs []interface{}
keysAndValues []interface{}
wantOutput string
}{
{
name: "test logf with debug level and key-values",
level: LevelDebug,
format: "",
fmtArgs: nil,
keysAndValues: []interface{}{"name", "Bob", "age", 30},
wantOutput: "[Debug] name=Bob age=30\n",
},
{
name: "test logf with info level and key-values",
level: LevelInfo,
format: "",
fmtArgs: nil,
keysAndValues: []interface{}{"status", "ok", "code", 200},
wantOutput: "[Info] status=ok code=200\n",
},
{
name: "test logf with warn level and key-values",
level: LevelWarn,
format: "",
fmtArgs: nil,
keysAndValues: []interface{}{"error", "not found", "id", 123},
wantOutput: "[Warn] error=not found id=123\n",
},
{
name: "test logf with format and key-values",
level: LevelWarn,
format: "test",
fmtArgs: nil,
keysAndValues: []interface{}{"error", "not found", "id", 123},
wantOutput: "[Warn] test error=not found id=123\n",
},
{
name: "test logf with one key",
level: LevelWarn,
format: "",
fmtArgs: nil,
keysAndValues: []interface{}{"error"},
wantOutput: "[Warn] error=KEYVALS UNPAIRED\n",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var buf bytes.Buffer
l := &defaultLogger{
stdlog: log.New(&buf, "", 0),
level: tt.level,
depth: 4,
}
l.privateLogw(tt.level, tt.format, tt.keysAndValues)
utils.AssertEqual(t, tt.wantOutput, buf.String())
})
}
}
func Test_SetLevel(t *testing.T) {
setLogger := &defaultLogger{
stdlog: log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile|log.Lmicroseconds),
depth: 4,
}
setLogger.SetLevel(LevelTrace)
utils.AssertEqual(t, LevelTrace, setLogger.level)
utils.AssertEqual(t, LevelTrace.toString(), setLogger.level.toString())
setLogger.SetLevel(LevelDebug)
utils.AssertEqual(t, LevelDebug, setLogger.level)
utils.AssertEqual(t, LevelDebug.toString(), setLogger.level.toString())
setLogger.SetLevel(LevelInfo)
utils.AssertEqual(t, LevelInfo, setLogger.level)
utils.AssertEqual(t, LevelInfo.toString(), setLogger.level.toString())
setLogger.SetLevel(LevelWarn)
utils.AssertEqual(t, LevelWarn, setLogger.level)
utils.AssertEqual(t, LevelWarn.toString(), setLogger.level.toString())
setLogger.SetLevel(LevelError)
utils.AssertEqual(t, LevelError, setLogger.level)
utils.AssertEqual(t, LevelError.toString(), setLogger.level.toString())
setLogger.SetLevel(LevelFatal)
utils.AssertEqual(t, LevelFatal, setLogger.level)
utils.AssertEqual(t, LevelFatal.toString(), setLogger.level.toString())
setLogger.SetLevel(LevelPanic)
utils.AssertEqual(t, LevelPanic, setLogger.level)
utils.AssertEqual(t, LevelPanic.toString(), setLogger.level.toString())
setLogger.SetLevel(8)
utils.AssertEqual(t, 8, int(setLogger.level))
utils.AssertEqual(t, "[?8] ", setLogger.level.toString())
}

141
log/fiberlog.go Normal file
View File

@ -0,0 +1,141 @@
package log
import (
"context"
"io"
)
// Fatal calls the default logger's Fatal method and then os.Exit(1).
func Fatal(v ...interface{}) {
logger.Fatal(v...)
}
// Error calls the default logger's Error method.
func Error(v ...interface{}) {
logger.Error(v...)
}
// Warn calls the default logger's Warn method.
func Warn(v ...interface{}) {
logger.Warn(v...)
}
// Info calls the default logger's Info method.
func Info(v ...interface{}) {
logger.Info(v...)
}
// Debug calls the default logger's Debug method.
func Debug(v ...interface{}) {
logger.Debug(v...)
}
// Trace calls the default logger's Trace method.
func Trace(v ...interface{}) {
logger.Trace(v...)
}
// Panic calls the default logger's Panic method.
func Panic(v ...interface{}) {
logger.Panic(v...)
}
// Fatalf calls the default logger's Fatalf method and then os.Exit(1).
func Fatalf(format string, v ...interface{}) {
logger.Fatalf(format, v...)
}
// Errorf calls the default logger's Errorf method.
func Errorf(format string, v ...interface{}) {
logger.Errorf(format, v...)
}
// Warnf calls the default logger's Warnf method.
func Warnf(format string, v ...interface{}) {
logger.Warnf(format, v...)
}
// Infof calls the default logger's Infof method.
func Infof(format string, v ...interface{}) {
logger.Infof(format, v...)
}
// Debugf calls the default logger's Debugf method.
func Debugf(format string, v ...interface{}) {
logger.Debugf(format, v...)
}
// Tracef calls the default logger's Tracef method.
func Tracef(format string, v ...interface{}) {
logger.Tracef(format, v...)
}
// Panicf calls the default logger's Tracef method.
func Panicf(format string, v ...interface{}) {
logger.Panicf(format, v...)
}
// Tracew logs a message with some additional context. The variadic key-value
// pairs are treated as they are privateLog With.
func Tracew(msg string, keysAndValues ...interface{}) {
logger.Tracew(msg, keysAndValues...)
}
// Debugw logs a message with some additional context. The variadic key-value
// pairs are treated as they are privateLog With.
func Debugw(msg string, keysAndValues ...interface{}) {
logger.Debugw(msg, keysAndValues...)
}
// Infow logs a message with some additional context. The variadic key-value
// pairs are treated as they are privateLog With.
func Infow(msg string, keysAndValues ...interface{}) {
logger.Infow(msg, keysAndValues...)
}
// Warnw logs a message with some additional context. The variadic key-value
// pairs are treated as they are privateLog With.
func Warnw(msg string, keysAndValues ...interface{}) {
logger.Warnw(msg, keysAndValues...)
}
// Errorw logs a message with some additional context. The variadic key-value
// pairs are treated as they are privateLog With.
func Errorw(msg string, keysAndValues ...interface{}) {
logger.Errorw(msg, keysAndValues...)
}
// Fatalw logs a message with some additional context. The variadic key-value
// pairs are treated as they are privateLog With.
func Fatalw(msg string, keysAndValues ...interface{}) {
logger.Fatalw(msg, keysAndValues...)
}
// Panicw logs a message with some additional context. The variadic key-value
// pairs are treated as they are privateLog With.
func Panicw(msg string, keysAndValues ...interface{}) {
logger.Panicw(msg, keysAndValues...)
}
func WithContext(ctx context.Context) CommonLogger {
return logger.WithContext(ctx)
}
// SetLogger sets the default logger and the system logger.
// Note that this method is not concurrent-safe and must not be called
// after the use of DefaultLogger and global functions privateLog this package.
func SetLogger(v AllLogger) {
logger = v
}
// SetOutput sets the output of default logger and system logger. By default, it is stderr.
func SetOutput(w io.Writer) {
logger.SetOutput(w)
}
// SetLevel sets the level of logs below which logs will not be output.
// The default logger is LevelTrace.
// Note that this method is not concurrent-safe.
func SetLevel(lv Level) {
logger.SetLevel(lv)
}

24
log/fiberlog_test.go Normal file
View File

@ -0,0 +1,24 @@
package log
import (
"log"
"os"
"testing"
"github.com/gofiber/fiber/v2/utils"
)
func Test_DefaultSystemLogger(t *testing.T) {
defaultL := DefaultLogger()
utils.AssertEqual(t, logger, defaultL)
}
func Test_SetLogger(t *testing.T) {
setLog := &defaultLogger{
stdlog: log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile|log.Lmicroseconds),
depth: 6,
}
SetLogger(setLog)
utils.AssertEqual(t, logger, setLog)
}

100
log/log.go Normal file
View File

@ -0,0 +1,100 @@
package log
import (
"context"
"fmt"
"io"
"log"
"os"
)
var logger AllLogger = &defaultLogger{
stdlog: log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile|log.Lmicroseconds),
depth: 4,
}
// Logger is a logger interface that provides logging function with levels.
type Logger interface {
Trace(v ...interface{})
Debug(v ...interface{})
Info(v ...interface{})
Warn(v ...interface{})
Error(v ...interface{})
Fatal(v ...interface{})
Panic(v ...interface{})
}
// FormatLogger is a logger interface that output logs with a format.
type FormatLogger interface {
Tracef(format string, v ...interface{})
Debugf(format string, v ...interface{})
Infof(format string, v ...interface{})
Warnf(format string, v ...interface{})
Errorf(format string, v ...interface{})
Fatalf(format string, v ...interface{})
Panicf(format string, v ...interface{})
}
// WithLogger is a logger interface that output logs with a message and key-value pairs.
type WithLogger interface {
Tracew(msg string, keysAndValues ...interface{})
Debugw(msg string, keysAndValues ...interface{})
Infow(msg string, keysAndValues ...interface{})
Warnw(msg string, keysAndValues ...interface{})
Errorw(msg string, keysAndValues ...interface{})
Fatalw(msg string, keysAndValues ...interface{})
Panicw(msg string, keysAndValues ...interface{})
}
type CommonLogger interface {
Logger
FormatLogger
WithLogger
}
// ControlLogger provides methods to config a logger.
type ControlLogger interface {
SetLevel(Level)
SetOutput(io.Writer)
}
// AllLogger is the combination of Logger, FormatLogger, CtxLogger and ControlLogger.
// Custom extensions can be made through AllLogger
type AllLogger interface {
CommonLogger
ControlLogger
WithContext(ctx context.Context) CommonLogger
}
// Level defines the priority of a log message.
// When a logger is configured with a level, any log message with a lower
// log level (smaller by integer comparison) will not be output.
type Level int
// The levels of logs.
const (
LevelTrace Level = iota
LevelDebug
LevelInfo
LevelWarn
LevelError
LevelFatal
LevelPanic
)
var strs = []string{
"[Trace] ",
"[Debug] ",
"[Info] ",
"[Warn] ",
"[Error] ",
"[Fatal] ",
"[Panic] ",
}
func (lv Level) toString() string {
if lv >= LevelTrace && lv <= LevelPanic {
return strs[lv]
}
return fmt.Sprintf("[?%d] ", lv)
}

View File

@ -1,10 +1,10 @@
package cache
import (
"log"
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/log"
"github.com/gofiber/fiber/v2/utils"
)
@ -102,11 +102,11 @@ func configDefault(config ...Config) Config {
// Set default values
if cfg.Store != nil {
log.Printf("[Warning] - [CACHE] Store is deprecated, please use Storage\n")
log.Warn("[CACHE] Store is deprecated, please use Storage")
cfg.Storage = cfg.Store
}
if cfg.Key != nil {
log.Printf("[Warning] - [CACHE] Key is deprecated, please use KeyGenerator\n")
log.Warn("[CACHE] Key is deprecated, please use KeyGenerator")
cfg.KeyGenerator = cfg.Key
}
if cfg.Next == nil {

View File

@ -1,11 +1,11 @@
package cors
import (
"log"
"strconv"
"strings"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/log"
)
// Config defines the config for middleware.
@ -98,7 +98,7 @@ func New(config ...Config) fiber.Handler {
// Warning logs if both AllowOrigins and AllowOriginsFunc are set
if cfg.AllowOrigins != ConfigDefault.AllowOrigins && cfg.AllowOriginsFunc != nil {
log.Printf("[Warning] - [CORS] Both 'AllowOrigins' and 'AllowOriginsFunc' have been defined.\n")
log.Warn("[CORS] Both 'AllowOrigins' and 'AllowOriginsFunc' have been defined.")
}
// Convert string to slice

View File

@ -1,12 +1,12 @@
package csrf
import (
"log"
"net/textproto"
"strings"
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/log"
"github.com/gofiber/fiber/v2/utils"
)
@ -132,15 +132,15 @@ func configDefault(config ...Config) Config {
// Set default values
if cfg.TokenLookup != "" {
log.Printf("[Warning] - [CSRF] TokenLookup is deprecated, please use KeyLookup\n")
log.Warn("[CSRF] TokenLookup is deprecated, please use KeyLookup")
cfg.KeyLookup = cfg.TokenLookup
}
if int(cfg.CookieExpires.Seconds()) > 0 {
log.Printf("[Warning] - [CSRF] CookieExpires is deprecated, please use Expiration\n")
log.Warn("[CSRF] CookieExpires is deprecated, please use Expiration")
cfg.Expiration = cfg.CookieExpires
}
if cfg.Cookie != nil {
log.Printf("[Warning] - [CSRF] Cookie is deprecated, please use Cookie* related fields\n")
log.Warn("[CSRF] Cookie is deprecated, please use Cookie* related fields")
if cfg.Cookie.Name != "" {
cfg.CookieName = cfg.Cookie.Name
}

View File

@ -2,10 +2,10 @@ package idempotency
import (
"fmt"
"log"
"strings"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/log"
"github.com/gofiber/fiber/v2/utils"
)
@ -92,7 +92,7 @@ func New(config ...Config) fiber.Handler {
}
defer func() {
if err := cfg.Lock.Unlock(key); err != nil {
log.Printf("[Error] - [IDEMPOTENCY] failed to unlock key %q: %v", key, err)
log.Errorf("[IDEMPOTENCY] failed to unlock key %q: %v", key, err)
}
}()

View File

@ -1,10 +1,10 @@
package limiter
import (
"log"
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/log"
)
// Config defines the config for middleware.
@ -95,15 +95,15 @@ func configDefault(config ...Config) Config {
// Set default values
if int(cfg.Duration.Seconds()) > 0 {
log.Printf("[Warning] - [LIMITER] Duration is deprecated, please use Expiration\n")
log.Warn("[LIMITER] Duration is deprecated, please use Expiration")
cfg.Expiration = cfg.Duration
}
if cfg.Key != nil {
log.Printf("[Warning] - [LIMITER] Key is deprecated, please us KeyGenerator\n")
log.Warn("[LIMITER] Key is deprecated, please us KeyGenerator")
cfg.KeyGenerator = cfg.Key
}
if cfg.Store != nil {
log.Printf("[Warning] - [LIMITER] Store is deprecated, please use Storage\n")
log.Warn("[LIMITER] Store is deprecated, please use Storage")
cfg.Storage = cfg.Store
}
if cfg.Next == nil {

View File

@ -3,13 +3,13 @@ package proxy
import (
"bytes"
"crypto/tls"
"log"
"net/url"
"strings"
"sync"
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/log"
"github.com/gofiber/fiber/v2/utils"
"github.com/valyala/fasthttp"
@ -17,7 +17,7 @@ import (
// New is deprecated
func New(config Config) fiber.Handler {
log.Printf("[Warning] - [PROXY] proxy.New is deprecated, please use proxy.Balancer instead\n")
log.Warn("[PROXY] proxy.New is deprecated, please use proxy.Balancer instead")
return Balancer(config)
}

View File

@ -2,11 +2,11 @@ package session
import (
"fmt"
"log"
"strings"
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/log"
"github.com/gofiber/fiber/v2/utils"
)
@ -97,7 +97,7 @@ func configDefault(config ...Config) Config {
cfg.Expiration = ConfigDefault.Expiration
}
if cfg.CookieName != "" {
log.Printf("[Warning] - [SESSION] CookieName is deprecated, please use KeyLookup\n")
log.Warn("[SESSION] CookieName is deprecated, please use KeyLookup")
cfg.KeyLookup = fmt.Sprintf("cookie:%s", cfg.CookieName)
}
if cfg.KeyLookup == "" {

View File

@ -3,10 +3,11 @@ package timeout
import (
"context"
"errors"
"log"
"sync"
"time"
"github.com/gofiber/fiber/v2/log"
"github.com/gofiber/fiber/v2"
)
@ -18,7 +19,7 @@ var once sync.Once
// Find documentation and sample usage on https://docs.gofiber.io/api/middleware/timeout
func New(handler fiber.Handler, timeout time.Duration) fiber.Handler {
once.Do(func() {
log.Printf("[Warning] - [TIMEOUT] timeout contains data race issues, not ready for production!")
log.Warn("[TIMEOUT] timeout contains data race issues, not ready for production!")
})
if timeout <= 0 {
@ -32,11 +33,11 @@ func New(handler fiber.Handler, timeout time.Duration) fiber.Handler {
go func() {
defer func() {
if err := recover(); err != nil {
log.Printf("[Warning] - [TIMEOUT] recover error %v", err)
log.Errorf("[TIMEOUT] recover error %v", err)
}
}()
if err := handler(ctx); err != nil {
log.Printf("[Warning] - [TIMEOUT] handler error %v", err)
log.Errorf("[TIMEOUT] handler error %v", err)
}
ch <- struct{}{}
}()

View File

@ -4,7 +4,6 @@ import (
"crypto/tls"
"errors"
"fmt"
"log"
"os"
"os/exec"
"runtime"
@ -13,6 +12,7 @@ import (
"sync/atomic"
"time"
"github.com/gofiber/fiber/v2/log"
"github.com/valyala/fasthttp/reuseport"
)
@ -77,7 +77,7 @@ func (app *App) prefork(network, addr string, tlsConfig *tls.Config) error {
for _, proc := range childs {
if err := proc.Process.Kill(); err != nil {
if !errors.Is(err, os.ErrProcessDone) {
log.Printf("prefork: failed to kill child: %v\n", err)
log.Errorf("prefork: failed to kill child: %v", err)
}
}
}