Files
kra/internal/server/middleware/logger.go
T
2026-01-07 10:35:49 +08:00

134 lines
3.1 KiB
Go

package middleware
import (
"bytes"
"context"
"encoding/json"
"io"
"time"
"github.com/go-kratos/kratos/v2/log"
"github.com/go-kratos/kratos/v2/middleware"
"github.com/go-kratos/kratos/v2/transport"
kratoshttp "github.com/go-kratos/kratos/v2/transport/http"
)
// LogLayout 日志layout
type LogLayout struct {
Time time.Time `json:"time"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
Path string `json:"path"`
Query string `json:"query,omitempty"`
Body string `json:"body,omitempty"`
IP string `json:"ip"`
UserAgent string `json:"user_agent"`
Error string `json:"error,omitempty"`
Cost time.Duration `json:"cost"`
Source string `json:"source"`
}
// LoggerConfig 日志中间件配置
type LoggerConfig struct {
// Filter 用户自定义过滤
Filter func(ctx context.Context, path string) bool
// FilterKeyword 关键字过滤
FilterKeyword func(layout *LogLayout) bool
// AuthProcess 鉴权处理
AuthProcess func(ctx context.Context, layout *LogLayout)
// Print 日志处理
Print func(LogLayout)
// Source 服务唯一标识
Source string
}
// Logger 日志中间件
func Logger(cfg LoggerConfig) middleware.Middleware {
if cfg.Print == nil {
cfg.Print = func(layout LogLayout) {
v, _ := json.Marshal(layout)
log.Info(string(v))
}
}
if cfg.Source == "" {
cfg.Source = "Kratos"
}
return func(handler middleware.Handler) middleware.Handler {
return func(ctx context.Context, req interface{}) (interface{}, error) {
var (
path string
query string
body string
ip string
userAgent string
)
if tr, ok := transport.FromServerContext(ctx); ok {
path = tr.Operation()
if header := tr.RequestHeader(); header != nil {
userAgent = header.Get("User-Agent")
}
if ht, ok := tr.(kratoshttp.Transporter); ok {
r := ht.Request()
query = r.URL.RawQuery
ip = getClientIP(r)
// 读取body(仅在未过滤时)
if cfg.Filter == nil || !cfg.Filter(ctx, path) {
bodyBytes, _ := io.ReadAll(r.Body)
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
body = string(bodyBytes)
}
}
}
// 检查是否需要过滤
if cfg.Filter != nil && cfg.Filter(ctx, path) {
return handler(ctx, req)
}
start := time.Now()
reply, err := handler(ctx, req)
cost := time.Since(start)
layout := LogLayout{
Time: time.Now(),
Path: path,
Query: query,
Body: body,
IP: ip,
UserAgent: userAgent,
Cost: cost,
Source: cfg.Source,
}
if err != nil {
layout.Error = err.Error()
}
// 处理鉴权信息
if cfg.AuthProcess != nil {
cfg.AuthProcess(ctx, &layout)
}
// 关键字过滤
if cfg.FilterKeyword != nil {
cfg.FilterKeyword(&layout)
}
// 输出日志
cfg.Print(layout)
return reply, err
}
}
}
// DefaultLogger 默认日志中间件
func DefaultLogger() middleware.Middleware {
return Logger(LoggerConfig{
Source: "Kratos",
})
}