// Package middleware ... // // Description : middleware ... // // Author : go_developer@163.com<白茶清欢> // // Date : 2022-07-14 10:53 package middleware import ( "git.zhangdeman.cn/zhangdeman/wrapper" "strings" "git.zhangdeman.cn/zhangdeman/gin/define" "github.com/gin-gonic/gin" "go.uber.org/zap" ) // fillCfg 填充默认配置 // // Author : go_developer@163.com<白茶清欢> // // Date : 15:37 2023/12/29 func fillCfg(cfg *AccessConfig) { if nil == cfg { return } if nil == cfg.IsRecordLog { cfg.IsRecordLog = defaultIsRecordLog } if nil == cfg.ExtraFieldList { cfg.ExtraFieldList = make([]string, 0) } if nil == cfg.RequestHeaderList { cfg.RequestHeaderList = make([]string, 0) } if nil == cfg.ResponseHeaderList { cfg.ResponseHeaderList = make([]string, 0) } if nil == cfg.ExtraFieldList { cfg.ExtraFieldList = make([]string, 0) } cfg.ExtraFieldList = append(cfg.ExtraFieldList, define.RecordRequestDataField, define.RecordResponseDataField) } // getLogRequestHeader 获取记录的请求header // // Author : go_developer@163.com<白茶清欢> // // Date : 15:59 2023/12/29 func getLogRequestHeader(ctx *gin.Context, cfg *AccessConfig) map[string][]string { // 请求header headerTable := make(map[string][]string) if len(cfg.RequestHeaderList) == 0 { // 全部记录 for headerKey, _ := range ctx.Request.Header { cfg.RequestHeaderList = append(cfg.RequestHeaderList, headerKey) } } for _, key := range cfg.RequestHeaderList { headerTable[key] = ctx.Request.Header.Values(key) } return headerTable } // getLogResponseHeader 记录相应header // // Author : go_developer@163.com<白茶清欢> // // Date : 16:01 2023/12/29 func getLogResponseHeader(ctx *gin.Context, cfg *AccessConfig) map[string][]string { // 响应header responseHeaderTable := make(map[string][]string) if len(cfg.ResponseHeaderList) == 0 { // 全部记录 for headerKey, _ := range ctx.Writer.Header() { cfg.ResponseHeaderList = append(cfg.ResponseHeaderList, headerKey) } zap.Any("pkg_gin_response_header", ctx.Writer.Header()) } for _, key := range cfg.ResponseHeaderList { responseHeaderTable[key] = ctx.Writer.Header().Values(key) } return responseHeaderTable } // LogRequest 记录请求日志 // // Author : go_developer@163.com<白茶清欢> // // Date : 10:55 2022/7/14 func LogRequest(cfg *AccessConfig) gin.HandlerFunc { fillCfg(cfg) return func(ctx *gin.Context) { // 未传入配置或者未传入日志实例 if nil == cfg || nil == cfg.Logger || !cfg.IsRecordLog(ctx) { ctx.Next() return } ctx.Next() startRequestTime := wrapper.OwnTime(ctx.GetTime(define.StartRequestTimeField)) logDataList := []zap.Field{ // 开始请求时间 zap.Any(define.StartRequestTimeField, startRequestTime.FormatUnixMilli()), } // 结束时间 finishRequestTime := wrapper.OwnTime(ctx.GetTime(define.FinishRequestTimeField)) logDataList = append( logDataList, zap.Any(define.FinishRequestTimeField, finishRequestTime.FormatUnixMilli()), // 请求完成时间 zap.Int64("pkg_gin_request_cost", finishRequestTime.UnixMilli()-startRequestTime.UnixMilli()), // 接口耗时 zap.Any("pkg_gin_request_header", getLogRequestHeader(ctx, cfg)), // 请求header zap.Any("pkg_gin_response_header", getLogResponseHeader(ctx, cfg)), // 响应header ) // 扩展数据 for _, field := range cfg.ExtraFieldList { val, _ := ctx.Get(field) logDataList = append(logDataList, zap.Any(field, val)) } cfg.Logger.Info("请求日志记录", logDataList...) if nil == cfg.FinishHook { return } // hook 不为nil, 自动触发 cfg.FinishHook( ctx, []byte(ctx.GetString(define.RecordRequestDataField)), ctx.GetStringMap(define.RecordResponseDataField), finishRequestTime.UnixMilli()-startRequestTime.UnixMilli(), ) } } // AccessConfig 访问记录的配置 // // Author : go_developer@163.com<白茶清欢> // // Date : 11:26 2022/7/14 type AccessConfig struct { Logger *zap.Logger // 日志实例 RequestHeaderList []string // 要记录哪些header , 不传全部记录 ResponseHeaderList []string // 要记录哪些响应header, 不传全部记录 IsRecordLog func(ctx *gin.Context) bool // 验证当前请求是否记录日志 ExtraFieldList []string // 记录的扩展字段列表,请将相关数据使用 ctx.Set 写入上下文中, 日志会自动记录 FinishHook func(ctx *gin.Context, requestData []byte, responseData map[string]interface{}, cost int64) // 请求处理完成之后, 触发的hook函数 } // defaultIsRecordLog 默认仅记录 json api 日志 // // Author : go_developer@163.com<白茶清欢> // // Date : 11:31 2022/7/14 func defaultIsRecordLog(ctx *gin.Context) bool { if strings.Contains(ctx.Writer.Header().Get("Content-type"), "application/json") { return true } return false }