log.go 3.4 KB

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