gitea源码

access_log.go 3.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package context
  4. import (
  5. "bytes"
  6. "net"
  7. "net/http"
  8. "strings"
  9. "text/template"
  10. "time"
  11. user_model "code.gitea.io/gitea/models/user"
  12. "code.gitea.io/gitea/modules/log"
  13. "code.gitea.io/gitea/modules/setting"
  14. "code.gitea.io/gitea/modules/web/middleware"
  15. )
  16. type accessLoggerTmplData struct {
  17. Identity *string
  18. Start *time.Time
  19. ResponseWriter struct {
  20. Status, Size int
  21. }
  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 = requestID[:maxRequestIDByteLength] + "..."
  43. }
  44. return requestID
  45. }
  46. type accessLogRecorder struct {
  47. logger log.BaseLogger
  48. logTemplate *template.Template
  49. needRequestID bool
  50. }
  51. func (lr *accessLogRecorder) record(start time.Time, respWriter ResponseWriter, req *http.Request) {
  52. var requestID string
  53. if lr.needRequestID {
  54. requestID = parseRequestIDFromRequestHeader(req)
  55. }
  56. reqHost, _, err := net.SplitHostPort(req.RemoteAddr)
  57. if err != nil {
  58. reqHost = req.RemoteAddr
  59. }
  60. identity := "-"
  61. data := middleware.GetContextData(req.Context())
  62. if signedUser, ok := data[middleware.ContextDataKeySignedUser].(*user_model.User); ok {
  63. identity = signedUser.Name
  64. }
  65. buf := bytes.NewBuffer([]byte{})
  66. tmplData := accessLoggerTmplData{
  67. Identity: &identity,
  68. Start: &start,
  69. Ctx: map[string]any{
  70. "RemoteAddr": req.RemoteAddr,
  71. "RemoteHost": reqHost,
  72. "Req": req,
  73. },
  74. RequestID: &requestID,
  75. }
  76. tmplData.ResponseWriter.Status = respWriter.WrittenStatus()
  77. tmplData.ResponseWriter.Size = respWriter.WrittenSize()
  78. err = lr.logTemplate.Execute(buf, tmplData)
  79. if err != nil {
  80. log.Error("Could not execute access logger template: %v", err.Error())
  81. }
  82. lr.logger.Log(1, &log.Event{Level: log.INFO}, "%s", buf.String())
  83. }
  84. func newAccessLogRecorder() *accessLogRecorder {
  85. return &accessLogRecorder{
  86. logger: log.GetLogger("access"),
  87. logTemplate: template.Must(template.New("log").Parse(setting.Log.AccessLogTemplate)),
  88. needRequestID: len(setting.Log.RequestIDHeaders) > 0 && strings.Contains(setting.Log.AccessLogTemplate, keyOfRequestIDInTemplate),
  89. }
  90. }
  91. // AccessLogger returns a middleware to log access logger
  92. func AccessLogger() func(http.Handler) http.Handler {
  93. recorder := newAccessLogRecorder()
  94. return func(next http.Handler) http.Handler {
  95. return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
  96. start := time.Now()
  97. next.ServeHTTP(w, req)
  98. recorder.record(start, w.(ResponseWriter), req)
  99. })
  100. }
  101. }