mirror of
https://github.com/gofiber/fiber.git
synced 2025-02-06 23:31:55 +00:00
🚀 Performance improvements (#2838)
* Add new supporter
* Add new test condition
* Add Handler Type
* Update app.go
* Update group.go
* Add Handler Type
* Update ViewEngine
* Update Templates Interface
* Update template examples
* Update fasthttp to v1.13.1
* Default cookie SameSite to Lax
* - static file routing fixed for fasthttp 1.13
- fix expected cookie values in tests
* Update template examples
* Update fasthttp to v1.13.1
Co-Authored-By: Thomas van Vugt <thomasvvugt@users.noreply.github.com>
* Cookie SameSite defaults to Lax
Co-Authored-By: Thomas van Vugt <thomasvvugt@users.noreply.github.com>
Co-Authored-By: Queru <pascal@queru.net>
* Fix router bug
Co-Authored-By: RW <renewerner87@googlemail.com>
* Remove unused code
Co-Authored-By: RW <renewerner87@googlemail.com>
* Add more static tests
Co-Authored-By: RW <renewerner87@googlemail.com>
* Update app_test.go
Co-Authored-By: RW <renewerner87@googlemail.com>
* Update Static tests
Co-Authored-By: RW <renewerner87@googlemail.com>
* Update app_test.go
Co-Authored-By: RW <renewerner87@googlemail.com>
* Update app_test.go
Co-Authored-By: RW <renewerner87@googlemail.com>
* Fix handler next calls
Co-Authored-By: RW <renewerner87@googlemail.com>
* Update router.go
Co-Authored-By: RW <renewerner87@googlemail.com>
* Update ctx.go
Co-Authored-By: RW <renewerner87@googlemail.com>
* Update app_test.go
Co-Authored-By: RW <renewerner87@googlemail.com>
* Remove nextHandler
Co-Authored-By: RW <renewerner87@googlemail.com>
* Remove lencount
Co-Authored-By: RW <renewerner87@googlemail.com>
* Add ErrorHandler
* Add ErrorHandler tests
* Add recover by default
* Enable recover by default
* Add App()
* Add ErrorHandler
* Enable recover by default
* Add ErrorHandler
* Add App() & Middleware
* Add RequestID
* Add new supporters
* Update shields
* Add mw
* Update basic_auth.go
* Update README.md
* Update spacing
* Update basic_auth_test.go
* Update ctx_test.go
* Add tests
* Update middleware
* up
* Small improvements
Use optimized `utils.ToString` and avoid `once.Do`
**Before**
```
BenchmarkLogfKeyAndValues/test_logf_with_debug_level_and_key-values-24 7323432 153.8 ns/op 89 B/op 1 allocs/op
BenchmarkLogfKeyAndValues/test_logf_with_info_level_and_key-values-24 8171703 144.5 ns/op 81 B/op 1 allocs/op
BenchmarkLogfKeyAndValues/test_logf_with_warn_level_and_key-values-24 8207860 142.8 ns/op 81 B/op 1 allocs/op
BenchmarkLogfKeyAndValues/test_logf_with_format_and_key-values-24 7500332 159.1 ns/op 135 B/op 2 allocs/op
BenchmarkLogfKeyAndValues/test_logf_with_one_key-24 10024760 131.0 ns/op 155 B/op 2 allocs/op
```
**After**
```
BenchmarkLogfKeyAndValues/test_logf_with_debug_level_and_key-values-24 13797813 77.42 ns/op 77 B/op 0 allocs/op
BenchmarkLogfKeyAndValues/test_logf_with_info_level_and_key-values-24 15375350 75.43 ns/op 73 B/op 1 allocs/op
BenchmarkLogfKeyAndValues/test_logf_with_warn_level_and_key-values-24 14926300 75.28 ns/op 75 B/op 1 allocs/op
BenchmarkLogfKeyAndValues/test_logf_with_format_and_key-values-24 12860275 90.27 ns/op 134 B/op 2 allocs/op
BenchmarkLogfKeyAndValues/test_logf_with_one_key-24 15649615 74.98 ns/op 100 B/op 1 allocs/op
```
* Fix WithCtxCaller test
* Fix lint
* Fix lint
* Replace Sprintf with byebufferpool in ctx.String()
# Original fn using Sprintf
Benchmark_Ctx_String-24 3846717 318.0 ns/op 152 B/op 8 allocs/op
Benchmark_Ctx_String-24 3780208 315.9 ns/op 152 B/op 8 allocs/op
Benchmark_Ctx_String-24 3627513 315.1 ns/op 152 B/op 8 allocs/op
Benchmark_Ctx_String-24 3712863 317.4 ns/op 152 B/op 8 allocs/op
// Modified using bytebufferpool
Benchmark_Ctx_String-24 8131666 149.3 ns/op 96 B/op 5 allocs/op
Benchmark_Ctx_String-24 7626406 148.3 ns/op 96 B/op 5 allocs/op
Benchmark_Ctx_String-24 8194621 149.2 ns/op 96 B/op 5 allocs/op
Benchmark_Ctx_String-24 8297750 156.6 ns/op 96 B/op 5 allocs/op
* Fix linting
* Use bytebufferpool in default logger
* Fix linting
* Lint fix
* Update linter.yml
* Update linter.yml
* Disable caching as recommended by golangci-lint
* 🩹 fix lint errors
---------
Co-authored-by: ReneWerner87 <ReneWerner87@googlemail.com>
Co-authored-by: Thomas van Vugt <thomasvvugt@users.noreply.github.com>
Co-authored-by: Queru <pascal@queru.net>
Co-authored-by: ReneWerner87 <rene@gofiber.io>
Co-authored-by: Juan Calderon-Perez <835733+gaby@users.noreply.github.com>
This commit is contained in:
parent
847a4a959d
commit
2b03f47fae
7
.github/workflows/linter.yml
vendored
7
.github/workflows/linter.yml
vendored
@ -7,8 +7,14 @@ on:
|
||||
- master
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
# Required: allow read access to the content for analysis.
|
||||
contents: read
|
||||
# Optional: allow read access to pull request. Use with `only-new-issues` option.
|
||||
pull-requests: read
|
||||
# Optional: Allow write access to checks to allow the action to annotate code in the PR.
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
golangci:
|
||||
@ -21,6 +27,7 @@ jobs:
|
||||
with:
|
||||
# NOTE: Keep this in sync with the version from go.mod
|
||||
go-version: "1.20.x"
|
||||
cache: false
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
|
41
ctx.go
41
ctx.go
@ -1605,14 +1605,39 @@ func (c *DefaultCtx) Status(status int) Ctx {
|
||||
//
|
||||
// The returned value may be useful for logging.
|
||||
func (c *DefaultCtx) String() string {
|
||||
return fmt.Sprintf(
|
||||
"#%016X - %s <-> %s - %s %s",
|
||||
c.fasthttp.ID(),
|
||||
c.fasthttp.LocalAddr(),
|
||||
c.fasthttp.RemoteAddr(),
|
||||
c.fasthttp.Request.Header.Method(),
|
||||
c.fasthttp.URI().FullURI(),
|
||||
)
|
||||
// Get buffer from pool
|
||||
buf := bytebufferpool.Get()
|
||||
|
||||
// Start with the ID, converting it to a hex string without fmt.Sprintf
|
||||
buf.WriteByte('#') //nolint:errcheck // It is fine to ignore the error
|
||||
// Convert ID to hexadecimal
|
||||
id := strconv.FormatUint(c.fasthttp.ID(), 16)
|
||||
// Pad with leading zeros to ensure 16 characters
|
||||
for i := 0; i < (16 - len(id)); i++ {
|
||||
buf.WriteByte('0') //nolint:errcheck // It is fine to ignore the error
|
||||
}
|
||||
buf.WriteString(id) //nolint:errcheck // It is fine to ignore the error
|
||||
buf.WriteString(" - ") //nolint:errcheck // It is fine to ignore the error
|
||||
|
||||
// Add local and remote addresses directly
|
||||
buf.WriteString(c.fasthttp.LocalAddr().String()) //nolint:errcheck // It is fine to ignore the error
|
||||
buf.WriteString(" <-> ") //nolint:errcheck // It is fine to ignore the error
|
||||
buf.WriteString(c.fasthttp.RemoteAddr().String()) //nolint:errcheck // It is fine to ignore the error
|
||||
buf.WriteString(" - ") //nolint:errcheck // It is fine to ignore the error
|
||||
|
||||
// Add method and URI
|
||||
buf.Write(c.fasthttp.Request.Header.Method()) //nolint:errcheck // It is fine to ignore the error
|
||||
buf.WriteByte(' ') //nolint:errcheck // It is fine to ignore the error
|
||||
buf.Write(c.fasthttp.URI().FullURI()) //nolint:errcheck // It is fine to ignore the error
|
||||
|
||||
// Allocate string
|
||||
str := buf.String()
|
||||
|
||||
// Reset buffer
|
||||
buf.Reset()
|
||||
bytebufferpool.Put(buf)
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
// Type sets the Content-Type HTTP header to the MIME type specified by the file extension.
|
||||
|
14
ctx_test.go
14
ctx_test.go
@ -4631,6 +4631,20 @@ func Test_Ctx_String(t *testing.T) {
|
||||
require.Equal(t, "#0000000000000000 - 0.0.0.0:0 <-> 0.0.0.0:0 - GET http:///", c.String())
|
||||
}
|
||||
|
||||
// go test -v -run=^$ -bench=Benchmark_Ctx_String -benchmem -count=4
|
||||
func Benchmark_Ctx_String(b *testing.B) {
|
||||
var str string
|
||||
app := New()
|
||||
ctx := app.NewCtx(&fasthttp.RequestCtx{})
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
str = ctx.String()
|
||||
}
|
||||
require.Equal(b, "#0000000000000000 - 0.0.0.0:0 <-> 0.0.0.0:0 - GET http:///", str)
|
||||
}
|
||||
|
||||
func TestCtx_ParamsInt(t *testing.T) {
|
||||
// Create a test context and set some strings (or params)
|
||||
// create a fake app to be used within this test
|
||||
|
@ -6,8 +6,8 @@ import (
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/gofiber/utils/v2"
|
||||
"github.com/valyala/bytebufferpool"
|
||||
)
|
||||
|
||||
@ -75,8 +75,6 @@ func (l *defaultLogger) privateLogw(lv Level, format string, keysAndValues []any
|
||||
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 {
|
||||
@ -84,14 +82,12 @@ func (l *defaultLogger) privateLogw(lv Level, format string, keysAndValues []any
|
||||
}
|
||||
|
||||
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
|
||||
if i > 0 || format != "" {
|
||||
_ = buf.WriteByte(' ') //nolint:errcheck // It is fine to ignore the error
|
||||
}
|
||||
_, _ = fmt.Fprintf(buf, " %s=%v", keysAndValues[i], keysAndValues[i+1])
|
||||
_, _ = buf.WriteString(keysAndValues[i].(string)) //nolint:errcheck // It is fine to ignore the error
|
||||
_ = buf.WriteByte('=') //nolint:errcheck // It is fine to ignore the error
|
||||
_, _ = buf.WriteString(utils.ToString(keysAndValues[i+1])) //nolint:errcheck // It is fine to ignore the error
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,6 +156,60 @@ func Test_LogfKeyAndValues(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLogfKeyAndValues(b *testing.B) {
|
||||
tests := []struct {
|
||||
name string
|
||||
level Level
|
||||
format string
|
||||
keysAndValues []any
|
||||
}{
|
||||
{
|
||||
name: "test logf with debug level and key-values",
|
||||
level: LevelDebug,
|
||||
format: "",
|
||||
keysAndValues: []any{"name", "Bob", "age", 30},
|
||||
},
|
||||
{
|
||||
name: "test logf with info level and key-values",
|
||||
level: LevelInfo,
|
||||
format: "",
|
||||
keysAndValues: []any{"status", "ok", "code", 200},
|
||||
},
|
||||
{
|
||||
name: "test logf with warn level and key-values",
|
||||
level: LevelWarn,
|
||||
format: "",
|
||||
keysAndValues: []any{"error", "not found", "id", 123},
|
||||
},
|
||||
{
|
||||
name: "test logf with format and key-values",
|
||||
level: LevelWarn,
|
||||
format: "test",
|
||||
keysAndValues: []any{"error", "not found", "id", 123},
|
||||
},
|
||||
{
|
||||
name: "test logf with one key",
|
||||
level: LevelWarn,
|
||||
format: "",
|
||||
keysAndValues: []any{"error"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
b.Run(tt.name, func(b *testing.B) {
|
||||
var buf bytes.Buffer
|
||||
l := &defaultLogger{
|
||||
stdlog: log.New(&buf, "", 0),
|
||||
level: tt.level,
|
||||
depth: 4,
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
l.privateLogw(tt.level, tt.format, tt.keysAndValues)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_WithContextCaller(t *testing.T) {
|
||||
logger = &defaultLogger{
|
||||
stdlog: log.New(os.Stderr, "", log.Lshortfile),
|
||||
@ -169,7 +223,7 @@ func Test_WithContextCaller(t *testing.T) {
|
||||
WithContext(ctx).Info("")
|
||||
Info("")
|
||||
|
||||
require.Equal(t, "default_test.go:169: [Info] \ndefault_test.go:170: [Info] \n", string(w.b))
|
||||
require.Equal(t, "default_test.go:223: [Info] \ndefault_test.go:224: [Info] \n", string(w.b))
|
||||
}
|
||||
|
||||
func Test_SetLevel(t *testing.T) {
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
@ -47,17 +48,50 @@ func defaultLoggerInstance(c fiber.Ctx, data *Data, cfg Config) error {
|
||||
if data.ChainErr != nil {
|
||||
formatErr = " | " + data.ChainErr.Error()
|
||||
}
|
||||
_, _ = buf.WriteString( //nolint:errcheck // This will never fail
|
||||
fmt.Sprintf("%s | %3d | %13v | %15s | %-7s | %-"+data.ErrPaddingStr+"s %s\n",
|
||||
data.Timestamp.Load().(string),
|
||||
c.Response().StatusCode(),
|
||||
data.Stop.Sub(data.Start),
|
||||
c.IP(),
|
||||
c.Method(),
|
||||
c.Path(),
|
||||
formatErr,
|
||||
),
|
||||
)
|
||||
|
||||
// Helper function to append fixed-width string with padding
|
||||
fixedWidth := func(s string, width int, rightAlign bool) {
|
||||
if rightAlign {
|
||||
for i := len(s); i < width; i++ {
|
||||
_ = buf.WriteByte(' ') //nolint:errcheck // It is fine to ignore the error
|
||||
}
|
||||
_, _ = buf.WriteString(s) //nolint:errcheck // It is fine to ignore the error
|
||||
} else {
|
||||
_, _ = buf.WriteString(s) //nolint:errcheck // It is fine to ignore the error
|
||||
for i := len(s); i < width; i++ {
|
||||
_ = buf.WriteByte(' ') //nolint:errcheck // It is fine to ignore the error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Timestamp
|
||||
_, _ = buf.WriteString(data.Timestamp.Load().(string)) //nolint:errcheck // It is fine to ignore the error
|
||||
_, _ = buf.WriteString(" | ") //nolint:errcheck // It is fine to ignore the error
|
||||
|
||||
// Status Code with 3 fixed width, right aligned
|
||||
fixedWidth(strconv.Itoa(c.Response().StatusCode()), 3, true)
|
||||
_, _ = buf.WriteString(" | ") //nolint:errcheck // It is fine to ignore the error
|
||||
|
||||
// Duration with 13 fixed width, right aligned
|
||||
fixedWidth(data.Stop.Sub(data.Start).String(), 13, true)
|
||||
_, _ = buf.WriteString(" | ") //nolint:errcheck // It is fine to ignore the error
|
||||
|
||||
// Client IP with 15 fixed width, right aligned
|
||||
fixedWidth(c.IP(), 15, true)
|
||||
_, _ = buf.WriteString(" | ") //nolint:errcheck // It is fine to ignore the error
|
||||
|
||||
// HTTP Method with 7 fixed width, left aligned
|
||||
fixedWidth(c.Method(), 7, false)
|
||||
_, _ = buf.WriteString(" | ") //nolint:errcheck // It is fine to ignore the error
|
||||
|
||||
// Path with dynamic padding for error message, left aligned
|
||||
errPadding, _ := strconv.Atoi(data.ErrPaddingStr) //nolint:errcheck // It is fine to ignore the error
|
||||
fixedWidth(c.Path(), errPadding, false)
|
||||
|
||||
// Error message
|
||||
_, _ = buf.WriteString(" ") //nolint:errcheck // It is fine to ignore the error
|
||||
_, _ = buf.WriteString(formatErr) //nolint:errcheck // It is fine to ignore the error
|
||||
_, _ = buf.WriteString("\n") //nolint:errcheck // It is fine to ignore the error
|
||||
}
|
||||
|
||||
// Write buffer to output
|
||||
|
Loading…
x
Reference in New Issue
Block a user