event.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. package log
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "heckel.io/ntfy/v2/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).Log(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) *Event {
  45. return e.Log(ErrorLevel, message, v...)
  46. }
  47. // Warn logs the event with log level warn
  48. func (e *Event) Warn(message string, v ...any) *Event {
  49. return e.Log(WarnLevel, message, v...)
  50. }
  51. // Info logs the event with log level info
  52. func (e *Event) Info(message string, v ...any) *Event {
  53. return e.Log(InfoLevel, message, v...)
  54. }
  55. // Debug logs the event with log level debug
  56. func (e *Event) Debug(message string, v ...any) *Event {
  57. return e.Log(DebugLevel, message, v...)
  58. }
  59. // Trace logs the event with log level trace
  60. func (e *Event) Trace(message string, v ...any) *Event {
  61. return e.Log(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. // FieldIf adds a custom field and value to the log event if the given level is loggable
  96. func (e *Event) FieldIf(key string, value any, level Level) *Event {
  97. if e.Loggable(level) {
  98. return e.Field(key, value)
  99. }
  100. return e
  101. }
  102. // Fields adds a map of fields to the log event
  103. func (e *Event) Fields(fields Context) *Event {
  104. if e.fields == nil {
  105. e.fields = make(Context)
  106. }
  107. for k, v := range fields {
  108. e.fields[k] = v
  109. }
  110. return e
  111. }
  112. // With adds the fields of the given Contexter structs to the log event by calling their Context method
  113. func (e *Event) With(contexters ...Contexter) *Event {
  114. if e.contexters == nil {
  115. e.contexters = contexters
  116. } else {
  117. e.contexters = append(e.contexters, contexters...)
  118. }
  119. return e
  120. }
  121. // Render returns the rendered log event as a string, or an empty string. The event is only rendered,
  122. // if either the global log level is >= l, or if the log level in one of the overrides matches
  123. // the level.
  124. //
  125. // If no overrides are defined (default), the Contexter array is not applied unless the event
  126. // is actually logged. If overrides are defined, then Contexters have to be applied in any case
  127. // to determine if they match. This is super complicated, but required for efficiency.
  128. func (e *Event) Render(l Level, message string, v ...any) string {
  129. appliedContexters := e.maybeApplyContexters()
  130. if !e.Loggable(l) {
  131. return ""
  132. }
  133. e.Message = fmt.Sprintf(message, v...)
  134. e.Level = l
  135. e.Timestamp = util.FormatTime(e.time)
  136. if !appliedContexters {
  137. e.applyContexters()
  138. }
  139. if CurrentFormat() == JSONFormat {
  140. return e.JSON()
  141. }
  142. return e.String()
  143. }
  144. // Log logs the event to the defined output, or does nothing if Render returns an empty string
  145. func (e *Event) Log(l Level, message string, v ...any) *Event {
  146. if m := e.Render(l, message, v...); m != "" {
  147. log.Println(m)
  148. }
  149. return e
  150. }
  151. // Loggable returns true if the given log level is lower or equal to the current log level
  152. func (e *Event) Loggable(l Level) bool {
  153. return e.globalLevelWithOverride() <= l
  154. }
  155. // IsTrace returns true if the current log level is TraceLevel
  156. func (e *Event) IsTrace() bool {
  157. return e.Loggable(TraceLevel)
  158. }
  159. // IsDebug returns true if the current log level is DebugLevel or below
  160. func (e *Event) IsDebug() bool {
  161. return e.Loggable(DebugLevel)
  162. }
  163. // JSON returns the event as a JSON representation
  164. func (e *Event) JSON() string {
  165. b, _ := json.Marshal(e)
  166. s := string(b)
  167. if len(e.fields) > 0 {
  168. b, _ := json.Marshal(e.fields)
  169. s = fmt.Sprintf("{%s,%s}", s[1:len(s)-1], string(b[1:len(b)-1]))
  170. }
  171. return s
  172. }
  173. // String returns the event as a string
  174. func (e *Event) String() string {
  175. if len(e.fields) == 0 {
  176. return fmt.Sprintf("%s %s", e.Level.String(), e.Message)
  177. }
  178. fields := make([]string, 0)
  179. for k, v := range e.fields {
  180. fields = append(fields, fmt.Sprintf("%s=%v", k, v))
  181. }
  182. sort.Strings(fields)
  183. return fmt.Sprintf("%s %s (%s)", e.Level.String(), e.Message, strings.Join(fields, ", "))
  184. }
  185. func (e *Event) globalLevelWithOverride() Level {
  186. mu.RLock()
  187. l, ov := level, overrides
  188. mu.RUnlock()
  189. if e.fields == nil {
  190. return l
  191. }
  192. for field, fieldOverrides := range ov {
  193. value, exists := e.fields[field]
  194. if exists {
  195. for _, o := range fieldOverrides {
  196. if o.value == "" || o.value == value || o.value == fmt.Sprintf("%v", value) {
  197. return o.level
  198. }
  199. }
  200. }
  201. }
  202. return l
  203. }
  204. func (e *Event) maybeApplyContexters() bool {
  205. mu.RLock()
  206. hasOverrides := len(overrides) > 0
  207. mu.RUnlock()
  208. if hasOverrides {
  209. e.applyContexters()
  210. }
  211. return hasOverrides // = applied
  212. }
  213. func (e *Event) applyContexters() {
  214. for _, c := range e.contexters {
  215. e.Fields(c.Context())
  216. }
  217. }