event.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. package log
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "heckel.io/ntfy/util"
  6. "log"
  7. "os"
  8. "sort"
  9. "strings"
  10. "time"
  11. )
  12. const (
  13. fieldTag = "tag"
  14. fieldError = "error"
  15. fieldTimeTaken = "time_taken_ms"
  16. fieldExitCode = "exit_code"
  17. tagStdLog = "stdlog"
  18. )
  19. // Event represents a single log event
  20. type Event struct {
  21. Timestamp string `json:"time"`
  22. Level Level `json:"level"`
  23. Message string `json:"message"`
  24. time time.Time
  25. contexters []Contexter
  26. fields Context
  27. }
  28. // newEvent creates a new log event
  29. //
  30. // We delay allocations and processing for efficiency, because most log events
  31. // are never actually rendered, so we don't format the time, or allocate a fields map.
  32. func newEvent() *Event {
  33. return &Event{
  34. time: time.Now(),
  35. }
  36. }
  37. // Fatal logs the event as FATAL, and exits the program with exit code 1
  38. func (e *Event) Fatal(message string, v ...any) {
  39. e.Field(fieldExitCode, 1).maybeLog(FatalLevel, message, v...)
  40. fmt.Fprintf(os.Stderr, message+"\n", v...) // Always output error to stderr
  41. os.Exit(1)
  42. }
  43. // Error logs the event with log level error
  44. func (e *Event) Error(message string, v ...any) {
  45. e.maybeLog(ErrorLevel, message, v...)
  46. }
  47. // Warn logs the event with log level warn
  48. func (e *Event) Warn(message string, v ...any) {
  49. e.maybeLog(WarnLevel, message, v...)
  50. }
  51. // Info logs the event with log level info
  52. func (e *Event) Info(message string, v ...any) {
  53. e.maybeLog(InfoLevel, message, v...)
  54. }
  55. // Debug logs the event with log level debug
  56. func (e *Event) Debug(message string, v ...any) {
  57. e.maybeLog(DebugLevel, message, v...)
  58. }
  59. // Trace logs the event with log level trace
  60. func (e *Event) Trace(message string, v ...any) {
  61. e.maybeLog(TraceLevel, message, v...)
  62. }
  63. // Tag adds a "tag" field to the log event
  64. func (e *Event) Tag(tag string) *Event {
  65. return e.Field(fieldTag, tag)
  66. }
  67. // Time sets the time field
  68. func (e *Event) Time(t time.Time) *Event {
  69. e.time = t
  70. return e
  71. }
  72. // Timing runs f and records the time if took to execute it in "time_taken_ms"
  73. func (e *Event) Timing(f func()) *Event {
  74. start := time.Now()
  75. f()
  76. return e.Field(fieldTimeTaken, time.Since(start).Milliseconds())
  77. }
  78. // Err adds an "error" field to the log event
  79. func (e *Event) Err(err error) *Event {
  80. if err == nil {
  81. return e
  82. } else if c, ok := err.(Contexter); ok {
  83. return e.With(c)
  84. }
  85. return e.Field(fieldError, err.Error())
  86. }
  87. // Field adds a custom field and value to the log event
  88. func (e *Event) Field(key string, value any) *Event {
  89. if e.fields == nil {
  90. e.fields = make(Context)
  91. }
  92. e.fields[key] = value
  93. return e
  94. }
  95. // Fields adds a map of fields to the log event
  96. func (e *Event) Fields(fields Context) *Event {
  97. if e.fields == nil {
  98. e.fields = make(Context)
  99. }
  100. for k, v := range fields {
  101. e.fields[k] = v
  102. }
  103. return e
  104. }
  105. // With adds the fields of the given Contexter structs to the log event by calling their Context method
  106. func (e *Event) With(contexters ...Contexter) *Event {
  107. if e.contexters == nil {
  108. e.contexters = contexters
  109. } else {
  110. e.contexters = append(e.contexters, contexters...)
  111. }
  112. return e
  113. }
  114. // Render returns the rendered log event as a string, or an empty string. The event is only rendered,
  115. // if either the global log level is >= l, or if the log level in one of the overrides matches
  116. // the level.
  117. //
  118. // If no overrides are defined (default), the Contexter array is not applied unless the event
  119. // is actually logged. If overrides are defined, then Contexters have to be applied in any case
  120. // to determine if they match. This is super complicated, but required for efficiency.
  121. func (e *Event) Render(l Level, message string, v ...any) string {
  122. appliedContexters := e.maybeApplyContexters()
  123. if !e.shouldLog(l) {
  124. return ""
  125. }
  126. e.Message = fmt.Sprintf(message, v...)
  127. e.Level = l
  128. e.Timestamp = util.FormatTime(e.time)
  129. if !appliedContexters {
  130. e.applyContexters()
  131. }
  132. if CurrentFormat() == JSONFormat {
  133. return e.JSON()
  134. }
  135. return e.String()
  136. }
  137. // maybeLog logs the event to the defined output, or does nothing if Render returns an empty string
  138. func (e *Event) maybeLog(l Level, message string, v ...any) {
  139. if m := e.Render(l, message, v...); m != "" {
  140. log.Println(m)
  141. }
  142. }
  143. // Loggable returns true if the given log level is lower or equal to the current log level
  144. func (e *Event) Loggable(l Level) bool {
  145. return e.globalLevelWithOverride() <= l
  146. }
  147. // IsTrace returns true if the current log level is TraceLevel
  148. func (e *Event) IsTrace() bool {
  149. return e.Loggable(TraceLevel)
  150. }
  151. // IsDebug returns true if the current log level is DebugLevel or below
  152. func (e *Event) IsDebug() bool {
  153. return e.Loggable(DebugLevel)
  154. }
  155. // JSON returns the event as a JSON representation
  156. func (e *Event) JSON() string {
  157. b, _ := json.Marshal(e)
  158. s := string(b)
  159. if len(e.fields) > 0 {
  160. b, _ := json.Marshal(e.fields)
  161. s = fmt.Sprintf("{%s,%s}", s[1:len(s)-1], string(b[1:len(b)-1]))
  162. }
  163. return s
  164. }
  165. // String returns the event as a string
  166. func (e *Event) String() string {
  167. if len(e.fields) == 0 {
  168. return fmt.Sprintf("%s %s", e.Level.String(), e.Message)
  169. }
  170. fields := make([]string, 0)
  171. for k, v := range e.fields {
  172. fields = append(fields, fmt.Sprintf("%s=%v", k, v))
  173. }
  174. sort.Strings(fields)
  175. return fmt.Sprintf("%s %s (%s)", e.Level.String(), e.Message, strings.Join(fields, ", "))
  176. }
  177. func (e *Event) shouldLog(l Level) bool {
  178. return e.globalLevelWithOverride() <= l
  179. }
  180. func (e *Event) globalLevelWithOverride() Level {
  181. mu.RLock()
  182. l, ov := level, overrides
  183. mu.RUnlock()
  184. if e.fields == nil {
  185. return l
  186. }
  187. for field, fieldOverrides := range ov {
  188. value, exists := e.fields[field]
  189. if exists {
  190. for _, o := range fieldOverrides {
  191. if o.value == "" || o.value == value || o.value == fmt.Sprintf("%v", value) {
  192. return o.level
  193. }
  194. }
  195. }
  196. }
  197. return l
  198. }
  199. func (e *Event) maybeApplyContexters() bool {
  200. mu.RLock()
  201. hasOverrides := len(overrides) > 0
  202. mu.RUnlock()
  203. if hasOverrides {
  204. e.applyContexters()
  205. }
  206. return hasOverrides // = applied
  207. }
  208. func (e *Event) applyContexters() {
  209. for _, c := range e.contexters {
  210. e.Fields(c.Context())
  211. }
  212. }