github.com/jtzjtz/kit@v1.0.2/middleware/access_log/access_log.go (about) 1 package access_log 2 3 /*日志采集*/ 4 import ( 5 "bytes" 6 "fmt" 7 "github.com/gin-gonic/gin" 8 "github.com/json-iterator/go" 9 "github.com/jtzjtz/kit/http" 10 "github.com/jtzjtz/kit/log" 11 "io/ioutil" 12 "os" 13 "strconv" 14 "strings" 15 "time" 16 ) 17 18 var json = jsoniter.ConfigCompatibleWithStandardLibrary 19 var accessChannel chan *accessResponse 20 21 const ( 22 LOG_ROOT_PATH = "/data/accesslog/" 23 LOG_EXTENSION = ".log" 24 ) 25 26 type result struct { 27 Code int `json:"code"` 28 Msg string `json:"msg"` 29 Data interface{} `json:"data"` 30 } 31 type accessResponse struct { 32 Ctx *gin.Context 33 RequestBody string 34 ResponseBody string 35 RequestTime string 36 RequestTimeUnix string 37 HandleCount int 38 } 39 type bodyLogWriter struct { 40 gin.ResponseWriter 41 body *bytes.Buffer 42 } 43 44 func (w bodyLogWriter) Write(b []byte) (int, error) { 45 w.body.Write(b) 46 return w.ResponseWriter.Write(b) 47 } 48 func (w bodyLogWriter) WriteString(s string) (int, error) { 49 w.body.WriteString(s) 50 return w.ResponseWriter.WriteString(s) 51 } 52 53 type accessLogParams struct { 54 channelBufferNum int32 //channel 缓存区 55 goroutineHandleNum int32 //goruntine 协程启动数量,消费处理log日志信息 56 logFullRes bool //是否记录全部响应数据 57 timeOutSendMail int32 //发送邮件的过期时间 58 noNeedRouteSendMail string //不需要发送邮件地址路由地址 59 } 60 61 //设置默认值 62 var ap = accessLogParams{ 63 channelBufferNum: 5, 64 goroutineHandleNum: 1, 65 logFullRes: false, 66 timeOutSendMail: 0, 67 noNeedRouteSendMail: "", 68 } 69 70 //设置参数 71 func SetAp(channelBufferNum, goroutineHandleNum int32, logFullRes bool) { 72 ap.channelBufferNum = channelBufferNum 73 ap.goroutineHandleNum = goroutineHandleNum 74 ap.logFullRes = logFullRes 75 } 76 77 //设置请求超时处理参数 78 func SetTimeOutAp(timeOutSendMail int32, noNeedRouteSendMail string) { 79 ap.timeOutSendMail = timeOutSendMail 80 ap.noNeedRouteSendMail = noNeedRouteSendMail 81 } 82 83 //异步处理日志信息 84 func handleAccessLog(env, serviceName string, acsChannel chan *accessResponse) { 85 for access := range acsChannel { 86 ctx := access.Ctx 87 88 if strings.Contains(ctx.Request.RequestURI, "ping") == true || strings.Contains(ctx.Request.RequestURI, "favicon") == true || ctx.Request.RequestURI == "/" { 89 //过滤不需要记日志的url地址 90 continue 91 } 92 scmResCode := 0 93 scmResMsg := "" 94 responseBody := access.ResponseBody 95 if responseBody != "" { 96 res := result{} 97 err := json.Unmarshal([]byte(responseBody), &res) 98 if err == nil { 99 scmResCode = res.Code 100 if ap.logFullRes == true { 101 scmResMsg = responseBody 102 } else { 103 scmResMsg = res.Msg 104 } 105 } 106 } 107 requestBody := access.RequestBody 108 requestTime, _ := strconv.ParseInt(access.RequestTimeUnix, 10, 64) 109 110 requestUri := ctx.Request.RequestURI 111 costTimeStr := fmt.Sprintf("%.3f", float64(time.Now().UnixNano()-requestTime)/1e9) 112 //costTime, _ := strconv.ParseFloat(costTimeStr, 64) 113 //if ap.timeOutSendMail > 0 && int32(costTime) > ap.timeOutSendMail && strings.Contains(ap.noNeedRouteSendMail, requestUri) == false { 114 // emailContent := fmt.Sprintf("请求地址:%s 请求方式:%s <br/>请求时间:%s<br/>请求耗时:%#v<br/> 请求参数:%s<br/> 响应状态码:%d<br/> 响应数据:%s<br/>", ctx.Request.RequestURI, ctx.Request.Method, access.RequestTime, costTime, requestBody, ctx.Writer.Status(), scmResMsg) 115 // println(emailContent) //发送邮件通知 116 //} 117 accessMap := make(map[string]string) 118 119 accessMap["X-App-Info"] = ctx.GetHeader("X-App-Info") 120 accessMap["X-SESSION-ID"] = ctx.GetHeader("X-SESSION-ID") 121 122 accessMap["request_cost"] = costTimeStr //请求消耗时间 123 accessMap["method"] = ctx.Request.Method 124 if ctx.GetHeader("Authorization") != "" { 125 accessMap["token"] = ctx.GetHeader("Authorization") 126 } else { 127 accessMap["token"] = ctx.GetHeader("X-Token") 128 129 } 130 accessMap["app_ver"] = ctx.GetHeader("X-App-Version") 131 accessMap["request_length"] = strconv.Itoa(int(ctx.Request.ContentLength)) 132 accessMap["content_type"] = ctx.ContentType() 133 accessMap["client_ip"] = ctx.GetHeader("X-Real-Ip") 134 accessMap["request_timestamp"] = access.RequestTime 135 accessMap["request_body"] = requestBody // post get 的参数 136 accessMap["host"] = ctx.Request.Host 137 accessMap["service_name"] = serviceName //微服务名字 138 hostName, _ := os.Hostname() 139 accessMap["server_name"] = hostName //当前服务器hostname 140 accessMap["Forwarded-Host"] = ctx.GetHeader("X-Forwarded-For") 141 accessMap["request_uri"] = requestUri 142 accessMap["response_status"] = strconv.Itoa(ctx.Writer.Status()) //http status 143 accessMap["user_agent"] = ctx.Request.UserAgent() //用户端类型 144 accessMap["http_referer"] = ctx.Request.Referer() 145 accessMap["response_code"] = strconv.Itoa(scmResCode) //业务code 146 accessMap["response_msg"] = scmResMsg //返回的数据 147 accessMap["traceid"] = http.GetTraceId(ctx) 148 accessMap["customerid"] = ctx.GetHeader("CustomerId") 149 accessMap["env"] = env 150 log.LogAccess(accessMap) 151 152 } 153 } 154 155 //记录日志中间件 156 func AccessLogMiddleware(env, projectName string) gin.HandlerFunc { 157 accessChannel = make(chan *accessResponse, ap.channelBufferNum) 158 for i := 0; i < int(ap.goroutineHandleNum); i++ { 159 go func(i int) { 160 handleAccessLog(env, projectName, accessChannel) 161 }(i) 162 } 163 164 return func(ctx *gin.Context) { 165 requestTime := time.Now().String() 166 requestTimeUnix := strconv.FormatInt(time.Now().UnixNano(), 10) 167 requestBody := "" 168 if ctx.Request.Method == "POST" { 169 requestBodyByte, err := ctx.GetRawData() 170 if err == nil { 171 requestBody = string(requestBodyByte) 172 ctx.Request.Body = ioutil.NopCloser(bytes.NewBuffer(requestBodyByte)) //把读过的字节流重新放到body 173 } 174 } 175 blw := &bodyLogWriter{body: bytes.NewBufferString(""), ResponseWriter: ctx.Writer} 176 ctx.Writer = blw 177 ctx.Next() 178 accessChannel <- &accessResponse{Ctx: ctx, RequestBody: requestBody, ResponseBody: blw.body.String(), RequestTime: requestTime, RequestTimeUnix: requestTimeUnix} 179 } 180 }