lolly/internal/logging/logging_bench_test.go
xfy 8ed800271d test: 迁移基准测试循环到 Go 1.24 b.Loop() API
- 所有 *_bench_test.go 文件从 for i := 0; i < b.N; i++ 改为 for b.Loop()
- 部分测试文件从 for i := 0; i < N; ... 改为 for range N 或 for i := range N
- 涵盖模块: cache, handler, http2, http3, loadbalance, logging, lua,
  middleware/accesslog, middleware/bodylimit, middleware/rewrite,
  middleware/security, netutil, resolver, server, ssl, stream

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 13:50:15 +08:00

253 lines
6.7 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package logging 提供日志功能的性能测试。
//
// 该文件包含日志相关的基准测试,用于评估关键操作的性能:
// - JSON 格式访问日志记录
// - 模板格式访问日志记录
// - 变量展开开销
// - 日志级别解析
//
// 主要用途:
//
// 用于监控日志系统的性能特征,优化内存分配和执行时间。
//
// 注意事项:
// - 使用 -benchmem 标志查看内存分配统计
// - 测试使用模拟数据避免外部依赖
//
// 作者xfy
package logging
import (
"testing"
"time"
"github.com/valyala/fasthttp"
"rua.plus/lolly/internal/config"
)
// BenchmarkLoggerLogAccessJSON 测试 JSON 格式访问日志记录性能。
// 这是最常见的访问日志格式,使用 zerolog 直接输出结构化数据。
// 预期结果:极低内存分配(< 1 allocs/op高性能。
func BenchmarkLoggerLogAccessJSON(b *testing.B) {
logger := New(&config.LoggingConfig{
Access: config.AccessLogConfig{
Path: "stdout",
Format: "json",
},
Error: config.ErrorLogConfig{
Path: "stdout",
Level: "info",
},
})
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI("/api/users?id=123")
ctx.Request.Header.SetMethod("GET")
ctx.Request.Header.Set("User-Agent", "benchmark-agent/1.0")
ctx.Request.Header.Set("Referer", "http://example.com/")
b.ResetTimer()
b.ReportAllocs()
for b.Loop() {
logger.LogAccess(ctx, 200, 1024, 150*time.Millisecond)
}
}
// BenchmarkLoggerLogAccessTemplate 测试模板格式访问日志记录性能。
// 重点测试变量展开和字符串处理的开销,通常会有更多内存分配。
// 使用 Nginx 风格的日志格式模板。
func BenchmarkLoggerLogAccessTemplate(b *testing.B) {
logger := New(&config.LoggingConfig{
Access: config.AccessLogConfig{
Path: "stdout",
Format: "$remote_addr - $remote_user [$time_local] \"$request\" $status $body_bytes_sent \"$http_referer\" \"$http_user_agent\" $request_time",
},
Error: config.ErrorLogConfig{
Path: "stdout",
Level: "info",
},
})
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI("/api/users?id=123")
ctx.Request.Header.SetMethod("GET")
ctx.Request.Header.Set("User-Agent", "benchmark-agent/1.0")
ctx.Request.Header.Set("Referer", "http://example.com/")
b.ResetTimer()
b.ReportAllocs()
for b.Loop() {
logger.LogAccess(ctx, 200, 1024, 150*time.Millisecond)
}
}
// BenchmarkLoggerLogAccessSimpleTemplate 测试简单模板的访问日志记录性能。
// 相比复杂模板,变量数量更少,字符串拼接开销更低。
func BenchmarkLoggerLogAccessSimpleTemplate(b *testing.B) {
logger := New(&config.LoggingConfig{
Access: config.AccessLogConfig{
Path: "stdout",
Format: "$remote_addr $request $status $body_bytes_sent",
},
Error: config.ErrorLogConfig{
Path: "stdout",
Level: "info",
},
})
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI("/api/test")
ctx.Request.Header.SetMethod("POST")
b.ResetTimer()
b.ReportAllocs()
for b.Loop() {
logger.LogAccess(ctx, 201, 512, 50*time.Millisecond)
}
}
// BenchmarkFormatAccessLog 直接测试 formatAccessLog 函数性能。
// 隔离变量展开的开销,不经过日志输出层。
func BenchmarkFormatAccessLog(b *testing.B) {
logger := New(&config.LoggingConfig{
Access: config.AccessLogConfig{
Path: "stdout",
Format: "$remote_addr - $remote_user [$time_local] \"$request\" $status $body_bytes_sent \"$http_referer\" \"$http_user_agent\" $request_time",
},
Error: config.ErrorLogConfig{
Path: "stdout",
Level: "info",
},
})
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI("/api/v1/resources?name=test&limit=10")
ctx.Request.Header.SetMethod("PUT")
ctx.Request.Header.Set("User-Agent", "Mozilla/5.0 (compatible; Benchmark/1.0)")
ctx.Request.Header.Set("Referer", "https://example.com/dashboard")
b.ResetTimer()
b.ReportAllocs()
for b.Loop() {
_ = logger.formatAccessLog(ctx, 200, 2048, 250*time.Microsecond)
}
}
// BenchmarkFormatAccessLogMinimal 测试最小模板的 formatAccessLog 性能。
// 用于评估变量系统的基准开销。
func BenchmarkFormatAccessLogMinimal(b *testing.B) {
logger := New(&config.LoggingConfig{
Access: config.AccessLogConfig{
Path: "stdout",
Format: "$status",
},
Error: config.ErrorLogConfig{
Path: "stdout",
Level: "info",
},
})
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI("/ping")
ctx.Request.Header.SetMethod("GET")
b.ResetTimer()
b.ReportAllocs()
for b.Loop() {
_ = logger.formatAccessLog(ctx, 200, 4, 1*time.Microsecond)
}
}
// BenchmarkParseLevel 测试日志级别解析性能。
// 在初始化时调用,预期极快且零分配。
func BenchmarkParseLevel(b *testing.B) {
levels := []string{"debug", "info", "warn", "error", "DEBUG", "INFO", "WARN", "ERROR", "unknown", ""}
b.ResetTimer()
b.ReportAllocs()
for i := 0; b.Loop(); i++ {
_ = parseLevel(levels[i%len(levels)])
}
}
// BenchmarkParseLevelLowercase 测试小写日志级别解析性能。
// 最常见的输入场景,应该是最快路径。
func BenchmarkParseLevelLowercase(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
for b.Loop() {
_ = parseLevel("info")
}
}
// BenchmarkParseLevelUppercase 测试大写日志级别解析性能。
// 测试 strings.ToLower 的开销。
func BenchmarkParseLevelUppercase(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
for b.Loop() {
_ = parseLevel("INFO")
}
}
// BenchmarkLoggerLogAccessWithUser 测试带有用户认证的访问日志性能。
// 额外的 ctx.UserValue 查找会增加开销。
func BenchmarkLoggerLogAccessWithUser(b *testing.B) {
logger := New(&config.LoggingConfig{
Access: config.AccessLogConfig{
Path: "stdout",
Format: "$remote_addr - $remote_user [$time_local] \"$request\" $status $body_bytes_sent",
},
Error: config.ErrorLogConfig{
Path: "stdout",
Level: "info",
},
})
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI("/admin/dashboard")
ctx.Request.Header.SetMethod("GET")
ctx.SetUserValue("remote_user", "admin_user_12345")
b.ResetTimer()
b.ReportAllocs()
for b.Loop() {
logger.LogAccess(ctx, 200, 1024, 100*time.Millisecond)
}
}
// BenchmarkLoggerLogAccessEmptyFormat 测试空格式(默认 JSON的访问日志性能。
// 验证空字符串回退到 JSON 的性能。
func BenchmarkLoggerLogAccessEmptyFormat(b *testing.B) {
logger := New(&config.LoggingConfig{
Access: config.AccessLogConfig{
Path: "stdout",
Format: "",
},
Error: config.ErrorLogConfig{
Path: "stdout",
Level: "info",
},
})
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI("/health")
ctx.Request.Header.SetMethod("GET")
b.ResetTimer()
b.ReportAllocs()
for b.Loop() {
logger.LogAccess(ctx, 200, 2, 5*time.Microsecond)
}
}