134 lines
3.1 KiB
Go
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",
|
|
})
|
|
}
|