1
0
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:
Joey 2024-02-09 12:27:21 +01:00 committed by GitHub
parent 847a4a959d
commit 2b03f47fae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 160 additions and 30 deletions

View File

@ -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
View File

@ -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.

View File

@ -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

View File

@ -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
}
}

View File

@ -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) {

View File

@ -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