code.gitea.io/gitea@v1.22.3/services/context/access_log.go (about) 1 // Copyright 2020 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package context 5 6 import ( 7 "bytes" 8 "fmt" 9 "net" 10 "net/http" 11 "strings" 12 "text/template" 13 "time" 14 15 user_model "code.gitea.io/gitea/models/user" 16 "code.gitea.io/gitea/modules/log" 17 "code.gitea.io/gitea/modules/setting" 18 "code.gitea.io/gitea/modules/web/middleware" 19 ) 20 21 type routerLoggerOptions struct { 22 req *http.Request 23 Identity *string 24 Start *time.Time 25 ResponseWriter http.ResponseWriter 26 Ctx map[string]any 27 RequestID *string 28 } 29 30 const keyOfRequestIDInTemplate = ".RequestID" 31 32 // According to: 33 // TraceId: A valid trace identifier is a 16-byte array with at least one non-zero byte 34 // MD5 output is 16 or 32 bytes: md5-bytes is 16, md5-hex is 32 35 // SHA1: similar, SHA1-bytes is 20, SHA1-hex is 40. 36 // UUID is 128-bit, 32 hex chars, 36 ASCII chars with 4 dashes 37 // So, we accept a Request ID with a maximum character length of 40 38 const maxRequestIDByteLength = 40 39 40 func parseRequestIDFromRequestHeader(req *http.Request) string { 41 requestID := "-" 42 for _, key := range setting.Log.RequestIDHeaders { 43 if req.Header.Get(key) != "" { 44 requestID = req.Header.Get(key) 45 break 46 } 47 } 48 if len(requestID) > maxRequestIDByteLength { 49 requestID = fmt.Sprintf("%s...", requestID[:maxRequestIDByteLength]) 50 } 51 return requestID 52 } 53 54 // AccessLogger returns a middleware to log access logger 55 func AccessLogger() func(http.Handler) http.Handler { 56 logger := log.GetLogger("access") 57 needRequestID := len(setting.Log.RequestIDHeaders) > 0 && strings.Contains(setting.Log.AccessLogTemplate, keyOfRequestIDInTemplate) 58 logTemplate, _ := template.New("log").Parse(setting.Log.AccessLogTemplate) 59 return func(next http.Handler) http.Handler { 60 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 61 start := time.Now() 62 63 var requestID string 64 if needRequestID { 65 requestID = parseRequestIDFromRequestHeader(req) 66 } 67 68 reqHost, _, err := net.SplitHostPort(req.RemoteAddr) 69 if err != nil { 70 reqHost = req.RemoteAddr 71 } 72 73 next.ServeHTTP(w, req) 74 rw := w.(ResponseWriter) 75 76 identity := "-" 77 data := middleware.GetContextData(req.Context()) 78 if signedUser, ok := data[middleware.ContextDataKeySignedUser].(*user_model.User); ok { 79 identity = signedUser.Name 80 } 81 buf := bytes.NewBuffer([]byte{}) 82 err = logTemplate.Execute(buf, routerLoggerOptions{ 83 req: req, 84 Identity: &identity, 85 Start: &start, 86 ResponseWriter: rw, 87 Ctx: map[string]any{ 88 "RemoteAddr": req.RemoteAddr, 89 "RemoteHost": reqHost, 90 "Req": req, 91 }, 92 RequestID: &requestID, 93 }) 94 if err != nil { 95 log.Error("Could not execute access logger template: %v", err.Error()) 96 } 97 98 logger.Info("%s", buf.String()) 99 }) 100 } 101 }