mirror of
https://github.com/lukaszraczylo/kportal.git
synced 2026-06-09 23:59:45 +00:00
271 lines
6.5 KiB
Go
271 lines
6.5 KiB
Go
package httplog
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"testing"
|
|
)
|
|
|
|
// BenchmarkLoggerLog benchmarks the Log function with sync.Pool
|
|
func BenchmarkLoggerLog(b *testing.B) {
|
|
l := &Logger{
|
|
forwardID: "benchmark",
|
|
maxBodyLen: 1024,
|
|
output: io.Discard,
|
|
}
|
|
|
|
entry := Entry{
|
|
Direction: "request",
|
|
RequestID: "req-123",
|
|
Method: "POST",
|
|
Path: "/api/users",
|
|
BodySize: 256,
|
|
Body: `{"name":"test user","email":"test@example.com","data":"some payload data here"}`,
|
|
StatusCode: 200,
|
|
LatencyMs: 42,
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = l.Log(entry)
|
|
}
|
|
}
|
|
|
|
// BenchmarkLoggerLogNoPool simulates logging without sync.Pool
|
|
func BenchmarkLoggerLogNoPool(b *testing.B) {
|
|
l := &Logger{
|
|
forwardID: "benchmark",
|
|
maxBodyLen: 1024,
|
|
output: io.Discard,
|
|
}
|
|
|
|
entry := Entry{
|
|
Direction: "request",
|
|
RequestID: "req-123",
|
|
Method: "POST",
|
|
Path: "/api/users",
|
|
BodySize: 256,
|
|
Body: `{"name":"test user","email":"test@example.com","data":"some payload data here"}`,
|
|
StatusCode: 200,
|
|
LatencyMs: 42,
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// Simulate old behavior: allocate new buffer each time
|
|
data, _ := json.Marshal(entry)
|
|
_, _ = l.output.Write(append(data, '\n'))
|
|
}
|
|
}
|
|
|
|
// BenchmarkReadBodyLimited benchmarks reading body with sync.Pool
|
|
func BenchmarkReadBodyLimited(b *testing.B) {
|
|
bodyData := bytes.Repeat([]byte("a"), 1024)
|
|
transport := &loggingTransport{}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// Create a new ReadCloser for each iteration
|
|
body := io.NopCloser(bytes.NewReader(bodyData))
|
|
_, _ = transport.readBodyLimited(body, 2048)
|
|
}
|
|
}
|
|
|
|
// BenchmarkReadBodyLimitedSmall benchmarks with small bodies (typical API requests)
|
|
func BenchmarkReadBodyLimitedSmall(b *testing.B) {
|
|
bodyData := []byte(`{"id":123,"name":"test","active":true}`)
|
|
transport := &loggingTransport{}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
body := io.NopCloser(bytes.NewReader(bodyData))
|
|
_, _ = transport.readBodyLimited(body, 1024)
|
|
}
|
|
}
|
|
|
|
// BenchmarkReadBodyLimitedLarge benchmarks with large bodies
|
|
func BenchmarkReadBodyLimitedLarge(b *testing.B) {
|
|
bodyData := bytes.Repeat([]byte("x"), 65536) // 64KB
|
|
transport := &loggingTransport{}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
body := io.NopCloser(bytes.NewReader(bodyData))
|
|
_, _ = transport.readBodyLimited(body, 65536)
|
|
}
|
|
}
|
|
|
|
// BenchmarkBufferPoolGetPut benchmarks the buffer pool itself
|
|
func BenchmarkBufferPoolGetPut(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
bufPtr := bufferPool.Get().(*[]byte)
|
|
// Reset and use the buffer to simulate real usage
|
|
*bufPtr = (*bufPtr)[:0]
|
|
*bufPtr = append(*bufPtr, "test data..."...)
|
|
bufferPool.Put(bufPtr)
|
|
}
|
|
})
|
|
}
|
|
|
|
// BenchmarkLogBufferPoolGetPut benchmarks the log buffer pool
|
|
func BenchmarkLogBufferPoolGetPut(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
buf := logBufferPool.Get().(*bytes.Buffer)
|
|
buf.Reset()
|
|
buf.WriteString("test log entry")
|
|
logBufferPool.Put(buf)
|
|
}
|
|
})
|
|
}
|
|
|
|
// BenchmarkFlattenHeaders benchmarks header flattening with pooling
|
|
func BenchmarkFlattenHeaders(b *testing.B) {
|
|
headers := http.Header{
|
|
"Content-Type": []string{"application/json"},
|
|
"Accept": []string{"text/html", "application/json"},
|
|
"User-Agent": []string{"test-client/1.0"},
|
|
"X-Request-ID": []string{"abc-123-def"},
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = flattenHeaders(headers)
|
|
}
|
|
}
|
|
|
|
// BenchmarkTruncateBody benchmarks body truncation with pooled buffers
|
|
func BenchmarkTruncateBody(b *testing.B) {
|
|
body := "this is a very long body that should be truncated for logging purposes"
|
|
maxLen := 20
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = truncateBody(body, maxLen)
|
|
}
|
|
}
|
|
|
|
// BenchmarkTruncateBodyNoPool simulates truncation without pooling
|
|
func BenchmarkTruncateBodyNoPool(b *testing.B) {
|
|
body := "this is a very long body that should be truncated for logging purposes"
|
|
maxLen := 20
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
if len(body) > maxLen {
|
|
_ = body[:maxLen] + "...(truncated)"
|
|
}
|
|
}
|
|
}
|
|
|
|
// BenchmarkLoggerLogWithTruncation benchmarks logging with body truncation
|
|
func BenchmarkLoggerLogWithTruncation(b *testing.B) {
|
|
l := &Logger{
|
|
forwardID: "benchmark",
|
|
maxBodyLen: 50,
|
|
output: io.Discard,
|
|
}
|
|
|
|
entry := Entry{
|
|
Direction: "request",
|
|
RequestID: "req-123",
|
|
Method: "POST",
|
|
Path: "/api/users",
|
|
Body: `{"name":"test user","email":"test@example.com","data":"some payload data here for truncation"}`,
|
|
BodySize: 100,
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = l.Log(entry)
|
|
}
|
|
}
|
|
|
|
// BenchmarkReadBufferPool benchmarks the read buffer pool
|
|
func BenchmarkReadBufferPool(b *testing.B) {
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
bufPtr := readBufferPool.Get().(*[]byte)
|
|
buf := *bufPtr
|
|
_ = len(buf) // Use the buffer
|
|
readBufferPool.Put(bufPtr)
|
|
}
|
|
})
|
|
}
|
|
|
|
// BenchmarkReadBodyLimitedParallel benchmarks body reading under concurrent load
|
|
func BenchmarkReadBodyLimitedParallel(b *testing.B) {
|
|
bodyData := bytes.Repeat([]byte("x"), 4096)
|
|
transport := &loggingTransport{}
|
|
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
body := io.NopCloser(bytes.NewReader(bodyData))
|
|
_, _ = transport.readBodyLimited(body, 8192)
|
|
}
|
|
})
|
|
}
|
|
|
|
// BenchmarkLoggerLogParallel benchmarks logging under concurrent load
|
|
func BenchmarkLoggerLogParallel(b *testing.B) {
|
|
l := &Logger{
|
|
forwardID: "benchmark",
|
|
maxBodyLen: 1024,
|
|
output: io.Discard,
|
|
}
|
|
|
|
entry := Entry{
|
|
Direction: "request",
|
|
RequestID: "req-123",
|
|
Method: "POST",
|
|
Path: "/api/users",
|
|
Body: `{"name":"test user"}`,
|
|
BodySize: 100,
|
|
}
|
|
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
_ = l.Log(entry)
|
|
}
|
|
})
|
|
}
|
|
|
|
// BenchmarkCompleteFlow benchmarks the complete logging flow
|
|
func BenchmarkCompleteFlow(b *testing.B) {
|
|
l := &Logger{
|
|
forwardID: "benchmark",
|
|
maxBodyLen: 1024,
|
|
output: io.Discard,
|
|
}
|
|
|
|
headers := http.Header{
|
|
"Content-Type": []string{"application/json"},
|
|
"Accept": []string{"application/json"},
|
|
}
|
|
|
|
bodyData := []byte(`{"id":123,"name":"test"}`)
|
|
transport := &loggingTransport{}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// Simulate full request logging flow
|
|
entry := Entry{
|
|
Direction: "request",
|
|
RequestID: "req-123",
|
|
Method: "POST",
|
|
Path: "/api/users",
|
|
Headers: flattenHeaders(headers),
|
|
BodySize: len(bodyData),
|
|
Body: string(bodyData),
|
|
}
|
|
_ = l.Log(entry)
|
|
|
|
// Simulate body reading
|
|
body := io.NopCloser(bytes.NewReader(bodyData))
|
|
_, _ = transport.readBodyLimited(body, 2048)
|
|
}
|
|
}
|