log.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. package server
  2. import (
  3. "fmt"
  4. "github.com/emersion/go-smtp"
  5. "github.com/gorilla/websocket"
  6. "heckel.io/ntfy/v2/log"
  7. "heckel.io/ntfy/v2/util"
  8. "net/http"
  9. "strings"
  10. "unicode/utf8"
  11. )
  12. // Log tags
  13. const (
  14. tagStartup = "startup"
  15. tagHTTP = "http"
  16. tagPublish = "publish"
  17. tagSubscribe = "subscribe"
  18. tagFirebase = "firebase"
  19. tagSMTP = "smtp" // Receive email
  20. tagEmail = "email" // Send email
  21. tagTwilio = "twilio"
  22. tagFileCache = "file_cache"
  23. tagMessageCache = "message_cache"
  24. tagStripe = "stripe"
  25. tagAccount = "account"
  26. tagManager = "manager"
  27. tagResetter = "resetter"
  28. tagWebsocket = "websocket"
  29. tagMatrix = "matrix"
  30. tagWebPush = "webpush"
  31. )
  32. var (
  33. normalErrorCodes = []int{http.StatusNotFound, http.StatusBadRequest, http.StatusTooManyRequests, http.StatusUnauthorized, http.StatusForbidden, http.StatusInsufficientStorage}
  34. rateLimitingErrorCodes = []int{http.StatusTooManyRequests, http.StatusRequestEntityTooLarge}
  35. )
  36. // logr creates a new log event with HTTP request fields
  37. func logr(r *http.Request) *log.Event {
  38. return log.Tag(tagHTTP).Fields(httpContext(r)) // Tag may be overwritten
  39. }
  40. // logv creates a new log event with visitor fields
  41. func logv(v *visitor) *log.Event {
  42. return log.With(v)
  43. }
  44. // logvr creates a new log event with HTTP request and visitor fields
  45. func logvr(v *visitor, r *http.Request) *log.Event {
  46. return logr(r).With(v)
  47. }
  48. // logvrm creates a new log event with HTTP request, visitor fields and message fields
  49. func logvrm(v *visitor, r *http.Request, m *message) *log.Event {
  50. return logvr(v, r).With(m)
  51. }
  52. // logvrm creates a new log event with visitor fields and message fields
  53. func logvm(v *visitor, m *message) *log.Event {
  54. return logv(v).With(m)
  55. }
  56. // logem creates a new log event with email fields
  57. func logem(smtpConn *smtp.Conn) *log.Event {
  58. ev := log.Tag(tagSMTP).Field("smtp_hostname", smtpConn.Hostname())
  59. if smtpConn.Conn() != nil {
  60. ev.Field("smtp_remote_addr", smtpConn.Conn().RemoteAddr().String())
  61. }
  62. return ev
  63. }
  64. func httpContext(r *http.Request) log.Context {
  65. requestURI := r.RequestURI
  66. if requestURI == "" {
  67. requestURI = r.URL.Path
  68. }
  69. return log.Context{
  70. "http_method": r.Method,
  71. "http_path": requestURI,
  72. }
  73. }
  74. func websocketErrorContext(err error) log.Context {
  75. if c, ok := err.(*websocket.CloseError); ok {
  76. return log.Context{
  77. "error": c.Error(),
  78. "error_code": c.Code,
  79. "error_type": "websocket.CloseError",
  80. }
  81. }
  82. return log.Context{
  83. "error": err.Error(),
  84. }
  85. }
  86. func renderHTTPRequest(r *http.Request) string {
  87. peekLimit := 4096
  88. lines := fmt.Sprintf("%s %s %s\n", r.Method, r.URL.RequestURI(), r.Proto)
  89. for key, values := range r.Header {
  90. for _, value := range values {
  91. lines += fmt.Sprintf("%s: %s\n", key, value)
  92. }
  93. }
  94. lines += "\n"
  95. body, err := util.Peek(r.Body, peekLimit)
  96. if err != nil {
  97. lines = fmt.Sprintf("(could not read body: %s)\n", err.Error())
  98. } else if utf8.Valid(body.PeekedBytes) {
  99. lines += string(body.PeekedBytes)
  100. if body.LimitReached {
  101. lines += fmt.Sprintf(" ... (peeked %d bytes)", peekLimit)
  102. }
  103. lines += "\n"
  104. } else {
  105. if body.LimitReached {
  106. lines += fmt.Sprintf("(peeked bytes not UTF-8, peek limit of %d bytes reached, hex: %x ...)\n", peekLimit, body.PeekedBytes)
  107. } else {
  108. lines += fmt.Sprintf("(peeked bytes not UTF-8, %d bytes, hex: %x)\n", len(body.PeekedBytes), body.PeekedBytes)
  109. }
  110. }
  111. r.Body = body // Important: Reset body, so it can be re-read
  112. return strings.TrimSpace(lines)
  113. }