You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

access_log.go 2.8KB

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