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