log.go 3.4 KB

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