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",
|
||
})
|
||
}
|