logger.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. /*
  2. * Public Domain Software
  3. *
  4. * I (Matthias Ladkau) am the author of the source code in this file.
  5. * I have placed the source code in this file in the public domain.
  6. *
  7. * For further information see: http://creativecommons.org/publicdomain/zero/1.0/
  8. */
  9. /*
  10. Package logutil contains a simple leveled logging infrastructure supporting
  11. different log levels, package scopes, formatters and handlers.
  12. The main object is the Logger object which requires a scope. Use
  13. GetLogger(scope string) to get an instance. Log messages are published
  14. by various log methods (e.g. Info).
  15. The logger object is also used to add sinks which consume log messages.
  16. Each sinks requires a formatter which formats / decorades incoming log
  17. messages. Log messages are handled by the most specific scoped sinks which
  18. allow the message level.
  19. Example:
  20. logger = GetLogger("foo.bar")
  21. logger.AddLogSink(Info, SimpleFormatter(), myLogFile)
  22. logger.Info("A log message")
  23. */
  24. package logutil
  25. import (
  26. "fmt"
  27. "io"
  28. "log"
  29. "runtime/debug"
  30. "sort"
  31. "strings"
  32. "sync"
  33. )
  34. /*
  35. fallbackLogger is used if there are error during regular logging
  36. */
  37. var fallbackLogger = log.Print
  38. /*
  39. Level represents a logging level
  40. */
  41. type Level string
  42. /*
  43. Log levels
  44. */
  45. const (
  46. Debug Level = "Debug"
  47. Info = "Info"
  48. Warning = "Warning"
  49. Error = "Error"
  50. )
  51. /*
  52. LogLevelPriority is a map assigning priorities to log level (lower number means a higher priority)
  53. */
  54. var logLevelPriority = map[Level]int{
  55. Debug: 1,
  56. Info: 2,
  57. Warning: 3,
  58. Error: 4,
  59. }
  60. /*
  61. stringToLoglevel is a map assigning log levels to strings.
  62. */
  63. var stringToLoglevel = map[string]Level{
  64. strings.ToLower(fmt.Sprint(Debug)): Debug,
  65. strings.ToLower(fmt.Sprint(Info)): Info,
  66. strings.ToLower(fmt.Sprint(Warning)): Warning,
  67. strings.ToLower(fmt.Sprint(Error)): Error,
  68. }
  69. /*
  70. StringToLoglevel tries to turn a given string into a log level.
  71. */
  72. func StringToLoglevel(loglevelString string) Level {
  73. level, _ := stringToLoglevel[strings.ToLower(loglevelString)]
  74. return level
  75. }
  76. /*
  77. Logger is the main logging object which is used to add sinks and publish
  78. log messages. A log messages is only handled by the most appropriate sink
  79. in terms of level and scope. Multiple sinks can be registered for the same
  80. level and scope.
  81. */
  82. type Logger interface {
  83. /*
  84. AddLogSink adds a log sink to a logger. A log sink can be a file or console
  85. which satisfies the io.Writer interface.
  86. */
  87. AddLogSink(loglevel Level, formatter Formatter, appender io.Writer)
  88. /*
  89. Debug logs a message at debug level.
  90. */
  91. Debug(msg ...interface{})
  92. /*
  93. Info logs a message at info level.
  94. */
  95. Info(msg ...interface{})
  96. /*
  97. Warning logs a message at warning level.
  98. */
  99. Warning(msg ...interface{})
  100. /*
  101. Error logs a message at error level.
  102. */
  103. Error(msg ...interface{})
  104. /*
  105. Error logs a message at error level and a stacktrace.
  106. */
  107. LogStackTrace(loglevel Level, msg ...interface{})
  108. }
  109. /*
  110. GetLogger returns a logger of a certain scope. Use the empty string '' for the
  111. root scope.
  112. */
  113. func GetLogger(scope string) Logger {
  114. return &logger{scope}
  115. }
  116. /*
  117. ClearLogSinks removes all configured log sinks.
  118. */
  119. func ClearLogSinks() {
  120. logSinksLock.Lock()
  121. defer logSinksLock.Unlock()
  122. logSinks = make([][]*logSink, 0)
  123. }
  124. /*
  125. logger is the main Logger interface implementation.
  126. */
  127. type logger struct {
  128. scope string
  129. }
  130. /*
  131. AddLogSink adds a log sink to a logger. A log sink can be a file or console
  132. which satisfies the io.Writer interface.
  133. */
  134. func (l *logger) AddLogSink(loglevel Level, formatter Formatter, appender io.Writer) {
  135. addLogSink(loglevel, l.scope, formatter, appender)
  136. }
  137. /*
  138. Debug logs a message at debug level.
  139. */
  140. func (l *logger) Debug(msg ...interface{}) {
  141. publishLog(Debug, l.scope, msg...)
  142. }
  143. /*
  144. Info logs a message at info level.
  145. */
  146. func (l *logger) Info(msg ...interface{}) {
  147. publishLog(Info, l.scope, msg...)
  148. }
  149. /*
  150. Warning logs a message at warning level.
  151. */
  152. func (l *logger) Warning(msg ...interface{}) {
  153. publishLog(Warning, l.scope, msg...)
  154. }
  155. /*
  156. Error logs a message at error level.
  157. */
  158. func (l *logger) Error(msg ...interface{}) {
  159. publishLog(Error, l.scope, msg...)
  160. }
  161. /*
  162. Error logs a message at error level and a stacktrace.
  163. */
  164. func (l *logger) LogStackTrace(loglevel Level, msg ...interface{}) {
  165. msg = append(msg, fmt.Sprintln())
  166. msg = append(msg, string(debug.Stack()))
  167. publishLog(loglevel, l.scope, msg...)
  168. }
  169. // Singleton logger
  170. // ================
  171. /*
  172. logSink models a single log sink.
  173. */
  174. type logSink struct {
  175. io.Writer
  176. level Level
  177. scope string
  178. formatter Formatter
  179. }
  180. /*
  181. Implementation of sort interface for logSinks
  182. */
  183. type sinkSlice [][]*logSink
  184. func (p sinkSlice) Len() int { return len(p) }
  185. func (p sinkSlice) Less(i, j int) bool { return p[i][0].scope > p[j][0].scope }
  186. func (p sinkSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
  187. /*
  188. logSinks contains all registered log sinks.
  189. */
  190. var logSinks = make([][]*logSink, 0)
  191. var logSinksLock = sync.RWMutex{}
  192. /*
  193. addLogSink adds a new logging sink.
  194. */
  195. func addLogSink(level Level, scope string, formatter Formatter, sink io.Writer) {
  196. logSinksLock.Lock()
  197. defer logSinksLock.Unlock()
  198. // First see if the new sink can be appended to an existing list
  199. for i, scopeSinks := range logSinks {
  200. if scopeSinks[0].scope == scope {
  201. scopeSinks = append(scopeSinks, &logSink{sink, level, scope, formatter})
  202. logSinks[i] = scopeSinks
  203. return
  204. }
  205. }
  206. // Insert the new sink in the appropriate place
  207. logSinks = append(logSinks, []*logSink{{sink, level, scope, formatter}})
  208. sort.Sort(sinkSlice(logSinks))
  209. }
  210. /*
  211. publishLog publishes a log message.
  212. */
  213. func publishLog(loglevel Level, scope string, msg ...interface{}) {
  214. // Go through the sorted list of sinks
  215. for _, sinks := range logSinks {
  216. // Check if the log scope is within the message scope
  217. if strings.HasPrefix(scope, sinks[0].scope) {
  218. handled := false
  219. for _, sink := range sinks {
  220. // Check if the level is ok
  221. if logLevelPriority[sink.level] <= logLevelPriority[loglevel] {
  222. handled = true
  223. fmsg := sink.formatter.Format(loglevel, scope, msg...)
  224. if _, err := sink.Write([]byte(fmsg)); err != nil {
  225. // Something went wrong use the fallback logger
  226. fallbackLogger(fmt.Sprintf(
  227. "Cloud not publish log message: %v (message: %v)",
  228. err, fmsg))
  229. }
  230. }
  231. }
  232. if handled {
  233. return
  234. }
  235. }
  236. }
  237. // No handler for log message use the fallback logger
  238. fmsg := SimpleFormatter().Format(loglevel, scope, msg...)
  239. fallbackLogger(fmt.Sprintf("No log handler for log message: %v", fmsg))
  240. }