logger.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. package log
  2. import (
  3. "fmt"
  4. "os"
  5. "time"
  6. "github.com/go-stack/stack"
  7. )
  8. const timeKey = "t"
  9. const lvlKey = "lvl"
  10. const msgKey = "msg"
  11. const errorKey = "LOG15_ERROR"
  12. const skipLevel = 2
  13. type Lvl int
  14. const (
  15. LvlCrit Lvl = iota
  16. LvlError
  17. LvlWarn
  18. LvlInfo
  19. LvlDebug
  20. LvlTrace
  21. )
  22. // AlignedString returns a 5-character string containing the name of a Lvl.
  23. func (l Lvl) AlignedString() string {
  24. switch l {
  25. case LvlTrace:
  26. return "TRACE"
  27. case LvlDebug:
  28. return "DEBUG"
  29. case LvlInfo:
  30. return "INFO "
  31. case LvlWarn:
  32. return "WARN "
  33. case LvlError:
  34. return "ERROR"
  35. case LvlCrit:
  36. return "CRIT "
  37. default:
  38. panic("bad level")
  39. }
  40. }
  41. // Strings returns the name of a Lvl.
  42. func (l Lvl) String() string {
  43. switch l {
  44. case LvlTrace:
  45. return "trce"
  46. case LvlDebug:
  47. return "dbug"
  48. case LvlInfo:
  49. return "info"
  50. case LvlWarn:
  51. return "warn"
  52. case LvlError:
  53. return "eror"
  54. case LvlCrit:
  55. return "crit"
  56. default:
  57. panic("bad level")
  58. }
  59. }
  60. // LvlFromString returns the appropriate Lvl from a string name.
  61. // Useful for parsing command line args and configuration files.
  62. func LvlFromString(lvlString string) (Lvl, error) {
  63. switch lvlString {
  64. case "trace", "trce":
  65. return LvlTrace, nil
  66. case "debug", "dbug":
  67. return LvlDebug, nil
  68. case "info":
  69. return LvlInfo, nil
  70. case "warn":
  71. return LvlWarn, nil
  72. case "error", "eror":
  73. return LvlError, nil
  74. case "crit":
  75. return LvlCrit, nil
  76. default:
  77. return LvlDebug, fmt.Errorf("Unknown level: %v", lvlString)
  78. }
  79. }
  80. // A Record is what a Logger asks its handler to write
  81. type Record struct {
  82. Time time.Time
  83. Lvl Lvl
  84. Msg string
  85. Ctx []interface{}
  86. Call stack.Call
  87. KeyNames RecordKeyNames
  88. }
  89. // RecordKeyNames gets stored in a Record when the write function is executed.
  90. type RecordKeyNames struct {
  91. Time string
  92. Msg string
  93. Lvl string
  94. }
  95. // A Logger writes key/value pairs to a Handler
  96. type Logger interface {
  97. // New returns a new Logger that has this logger's context plus the given context
  98. New(ctx ...interface{}) Logger
  99. // GetHandler gets the handler associated with the logger.
  100. GetHandler() Handler
  101. // SetHandler updates the logger to write records to the specified handler.
  102. SetHandler(h Handler)
  103. // Log a message at the given level with context key/value pairs
  104. Trace(msg string, ctx ...interface{})
  105. Debug(msg string, ctx ...interface{})
  106. Info(msg string, ctx ...interface{})
  107. Warn(msg string, ctx ...interface{})
  108. Error(msg string, ctx ...interface{})
  109. Crit(msg string, ctx ...interface{})
  110. }
  111. type logger struct {
  112. ctx []interface{}
  113. h *swapHandler
  114. }
  115. func (l *logger) write(msg string, lvl Lvl, ctx []interface{}, skip int) {
  116. l.h.Log(&Record{
  117. Time: time.Now(),
  118. Lvl: lvl,
  119. Msg: msg,
  120. Ctx: newContext(l.ctx, ctx),
  121. Call: stack.Caller(skip),
  122. KeyNames: RecordKeyNames{
  123. Time: timeKey,
  124. Msg: msgKey,
  125. Lvl: lvlKey,
  126. },
  127. })
  128. }
  129. func (l *logger) New(ctx ...interface{}) Logger {
  130. child := &logger{newContext(l.ctx, ctx), new(swapHandler)}
  131. child.SetHandler(l.h)
  132. return child
  133. }
  134. func newContext(prefix []interface{}, suffix []interface{}) []interface{} {
  135. normalizedSuffix := normalize(suffix)
  136. newCtx := make([]interface{}, len(prefix)+len(normalizedSuffix))
  137. n := copy(newCtx, prefix)
  138. copy(newCtx[n:], normalizedSuffix)
  139. return newCtx
  140. }
  141. func (l *logger) Trace(msg string, ctx ...interface{}) {
  142. l.write(msg, LvlTrace, ctx, skipLevel)
  143. }
  144. func (l *logger) Debug(msg string, ctx ...interface{}) {
  145. l.write(msg, LvlDebug, ctx, skipLevel)
  146. }
  147. func (l *logger) Info(msg string, ctx ...interface{}) {
  148. l.write(msg, LvlInfo, ctx, skipLevel)
  149. }
  150. func (l *logger) Warn(msg string, ctx ...interface{}) {
  151. l.write(msg, LvlWarn, ctx, skipLevel)
  152. }
  153. func (l *logger) Error(msg string, ctx ...interface{}) {
  154. l.write(msg, LvlError, ctx, skipLevel)
  155. }
  156. func (l *logger) Crit(msg string, ctx ...interface{}) {
  157. l.write(msg, LvlCrit, ctx, skipLevel)
  158. os.Exit(1)
  159. }
  160. func (l *logger) GetHandler() Handler {
  161. return l.h.Get()
  162. }
  163. func (l *logger) SetHandler(h Handler) {
  164. l.h.Swap(h)
  165. }
  166. func normalize(ctx []interface{}) []interface{} {
  167. // if the caller passed a Ctx object, then expand it
  168. if len(ctx) == 1 {
  169. if ctxMap, ok := ctx[0].(Ctx); ok {
  170. ctx = ctxMap.toArray()
  171. }
  172. }
  173. // ctx needs to be even because it's a series of key/value pairs
  174. // no one wants to check for errors on logging functions,
  175. // so instead of erroring on bad input, we'll just make sure
  176. // that things are the right length and users can fix bugs
  177. // when they see the output looks wrong
  178. if len(ctx)%2 != 0 {
  179. ctx = append(ctx, nil, errorKey, "Normalized odd number of arguments by adding nil")
  180. }
  181. return ctx
  182. }
  183. // Lazy allows you to defer calculation of a logged value that is expensive
  184. // to compute until it is certain that it must be evaluated with the given filters.
  185. //
  186. // Lazy may also be used in conjunction with a Logger's New() function
  187. // to generate a child logger which always reports the current value of changing
  188. // state.
  189. //
  190. // You may wrap any function which takes no arguments to Lazy. It may return any
  191. // number of values of any type.
  192. type Lazy struct {
  193. Fn interface{}
  194. }
  195. // Ctx is a map of key/value pairs to pass as context to a log function
  196. // Use this only if you really need greater safety around the arguments you pass
  197. // to the logging functions.
  198. type Ctx map[string]interface{}
  199. func (c Ctx) toArray() []interface{} {
  200. arr := make([]interface{}, len(c)*2)
  201. i := 0
  202. for k, v := range c {
  203. arr[i] = k
  204. arr[i+1] = v
  205. i += 2
  206. }
  207. return arr
  208. }