log.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. package log
  2. import (
  3. "io"
  4. "log"
  5. "os"
  6. "strings"
  7. "sync"
  8. "time"
  9. )
  10. // Defaults for package level variables
  11. var (
  12. DefaultLevel = InfoLevel
  13. DefaultFormat = TextFormat
  14. DefaultOutput = &peekLogWriter{os.Stderr}
  15. )
  16. var (
  17. level = DefaultLevel
  18. format = DefaultFormat
  19. overrides = make(map[string][]*levelOverride)
  20. output io.Writer = DefaultOutput
  21. filename = ""
  22. mu = &sync.RWMutex{}
  23. )
  24. // init sets the default log output (including log.SetOutput)
  25. //
  26. // This has to be explicitly called, because DefaultOutput is a peekLogWriter,
  27. // which wraps os.Stderr.
  28. func init() {
  29. SetOutput(DefaultOutput)
  30. }
  31. // Fatal prints the given message, and exits the program
  32. func Fatal(message string, v ...any) {
  33. newEvent().Fatal(message, v...)
  34. }
  35. // Error prints the given message, if the current log level is ERROR or lower
  36. func Error(message string, v ...any) {
  37. newEvent().Error(message, v...)
  38. }
  39. // Warn prints the given message, if the current log level is WARN or lower
  40. func Warn(message string, v ...any) {
  41. newEvent().Warn(message, v...)
  42. }
  43. // Info prints the given message, if the current log level is INFO or lower
  44. func Info(message string, v ...any) {
  45. newEvent().Info(message, v...)
  46. }
  47. // Debug prints the given message, if the current log level is DEBUG or lower
  48. func Debug(message string, v ...any) {
  49. newEvent().Debug(message, v...)
  50. }
  51. // Trace prints the given message, if the current log level is TRACE
  52. func Trace(message string, v ...any) {
  53. newEvent().Trace(message, v...)
  54. }
  55. // With creates a new log event and adds the fields of the given Contexter structs
  56. func With(contexts ...Contexter) *Event {
  57. return newEvent().With(contexts...)
  58. }
  59. // Field creates a new log event and adds a custom field and value to it
  60. func Field(key string, value any) *Event {
  61. return newEvent().Field(key, value)
  62. }
  63. // Fields creates a new log event and adds a map of fields to it
  64. func Fields(fields Context) *Event {
  65. return newEvent().Fields(fields)
  66. }
  67. // Tag creates a new log event and adds a "tag" field to it
  68. func Tag(tag string) *Event {
  69. return newEvent().Tag(tag)
  70. }
  71. // Time creates a new log event and sets the time field
  72. func Time(time time.Time) *Event {
  73. return newEvent().Time(time)
  74. }
  75. // Timing runs f and records the time if took to execute it in "time_taken_ms"
  76. func Timing(f func()) *Event {
  77. return newEvent().Timing(f)
  78. }
  79. // CurrentLevel returns the current log level
  80. func CurrentLevel() Level {
  81. mu.RLock()
  82. defer mu.RUnlock()
  83. return level
  84. }
  85. // SetLevel sets a new log level
  86. func SetLevel(newLevel Level) {
  87. mu.Lock()
  88. defer mu.Unlock()
  89. level = newLevel
  90. }
  91. // SetLevelOverride adds a log override for the given field
  92. func SetLevelOverride(field string, value string, level Level) {
  93. mu.Lock()
  94. defer mu.Unlock()
  95. if _, ok := overrides[field]; !ok {
  96. overrides[field] = make([]*levelOverride, 0)
  97. }
  98. overrides[field] = append(overrides[field], &levelOverride{value: value, level: level})
  99. }
  100. // ResetLevelOverrides removes all log level overrides
  101. func ResetLevelOverrides() {
  102. mu.Lock()
  103. defer mu.Unlock()
  104. overrides = make(map[string][]*levelOverride)
  105. }
  106. // CurrentFormat returns the current log format
  107. func CurrentFormat() Format {
  108. mu.RLock()
  109. defer mu.RUnlock()
  110. return format
  111. }
  112. // SetFormat sets a new log format
  113. func SetFormat(newFormat Format) {
  114. mu.Lock()
  115. defer mu.Unlock()
  116. format = newFormat
  117. if newFormat == JSONFormat {
  118. DisableDates()
  119. }
  120. }
  121. // SetOutput sets the log output writer
  122. func SetOutput(w io.Writer) {
  123. mu.Lock()
  124. defer mu.Unlock()
  125. output = &peekLogWriter{w}
  126. if f, ok := w.(*os.File); ok {
  127. filename = f.Name()
  128. } else {
  129. filename = ""
  130. }
  131. log.SetOutput(output)
  132. }
  133. // File returns the log file, if any, or an empty string otherwise
  134. func File() string {
  135. mu.RLock()
  136. defer mu.RUnlock()
  137. return filename
  138. }
  139. // IsFile returns true if the output is a non-default file
  140. func IsFile() bool {
  141. mu.RLock()
  142. defer mu.RUnlock()
  143. return filename != ""
  144. }
  145. // DisableDates disables the date/time prefix
  146. func DisableDates() {
  147. log.SetFlags(0)
  148. }
  149. // Loggable returns true if the given log level is lower or equal to the current log level
  150. func Loggable(l Level) bool {
  151. return CurrentLevel() <= l
  152. }
  153. // IsTrace returns true if the current log level is TraceLevel
  154. func IsTrace() bool {
  155. return Loggable(TraceLevel)
  156. }
  157. // IsDebug returns true if the current log level is DebugLevel or below
  158. func IsDebug() bool {
  159. return Loggable(DebugLevel)
  160. }
  161. // peekLogWriter is an io.Writer which will peek at the rendered log event,
  162. // and ensure that the rendered output is valid JSON. This is a hack!
  163. type peekLogWriter struct {
  164. w io.Writer
  165. }
  166. func (w *peekLogWriter) Write(p []byte) (n int, err error) {
  167. if len(p) == 0 || p[0] == '{' || CurrentFormat() == TextFormat {
  168. return w.w.Write(p)
  169. }
  170. m := newEvent().Tag(tagStdLog).Render(InfoLevel, strings.TrimSpace(string(p)))
  171. if m == "" {
  172. return 0, nil
  173. }
  174. return w.w.Write([]byte(m + "\n"))
  175. }