logger.go 4.6 KB

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