int.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. package hclog
  2. import (
  3. "bufio"
  4. "encoding"
  5. "encoding/json"
  6. "fmt"
  7. "log"
  8. "os"
  9. "runtime"
  10. "strconv"
  11. "strings"
  12. "sync"
  13. "time"
  14. )
  15. var (
  16. _levelToBracket = map[Level]string{
  17. Debug: "[DEBUG]",
  18. Trace: "[TRACE]",
  19. Info: "[INFO ]",
  20. Warn: "[WARN ]",
  21. Error: "[ERROR]",
  22. }
  23. )
  24. // Given the options (nil for defaults), create a new Logger
  25. func New(opts *LoggerOptions) Logger {
  26. if opts == nil {
  27. opts = &LoggerOptions{}
  28. }
  29. output := opts.Output
  30. if output == nil {
  31. output = os.Stderr
  32. }
  33. level := opts.Level
  34. if level == NoLevel {
  35. level = DefaultLevel
  36. }
  37. mtx := opts.Mutex
  38. if mtx == nil {
  39. mtx = new(sync.Mutex)
  40. }
  41. ret := &intLogger{
  42. m: mtx,
  43. json: opts.JSONFormat,
  44. caller: opts.IncludeLocation,
  45. name: opts.Name,
  46. timeFormat: TimeFormat,
  47. w: bufio.NewWriter(output),
  48. level: level,
  49. }
  50. if opts.TimeFormat != "" {
  51. ret.timeFormat = opts.TimeFormat
  52. }
  53. return ret
  54. }
  55. // The internal logger implementation. Internal in that it is defined entirely
  56. // by this package.
  57. type intLogger struct {
  58. json bool
  59. caller bool
  60. name string
  61. timeFormat string
  62. // this is a pointer so that it's shared by any derived loggers, since
  63. // those derived loggers share the bufio.Writer as well.
  64. m *sync.Mutex
  65. w *bufio.Writer
  66. level Level
  67. implied []interface{}
  68. }
  69. // Make sure that intLogger is a Logger
  70. var _ Logger = &intLogger{}
  71. // The time format to use for logging. This is a version of RFC3339 that
  72. // contains millisecond precision
  73. const TimeFormat = "2006-01-02T15:04:05.000Z0700"
  74. // Log a message and a set of key/value pairs if the given level is at
  75. // or more severe that the threshold configured in the Logger.
  76. func (z *intLogger) Log(level Level, msg string, args ...interface{}) {
  77. if level < z.level {
  78. return
  79. }
  80. t := time.Now()
  81. z.m.Lock()
  82. defer z.m.Unlock()
  83. if z.json {
  84. z.logJson(t, level, msg, args...)
  85. } else {
  86. z.log(t, level, msg, args...)
  87. }
  88. z.w.Flush()
  89. }
  90. // Cleanup a path by returning the last 2 segments of the path only.
  91. func trimCallerPath(path string) string {
  92. // lovely borrowed from zap
  93. // nb. To make sure we trim the path correctly on Windows too, we
  94. // counter-intuitively need to use '/' and *not* os.PathSeparator here,
  95. // because the path given originates from Go stdlib, specifically
  96. // runtime.Caller() which (as of Mar/17) returns forward slashes even on
  97. // Windows.
  98. //
  99. // See https://github.com/golang/go/issues/3335
  100. // and https://github.com/golang/go/issues/18151
  101. //
  102. // for discussion on the issue on Go side.
  103. //
  104. // Find the last separator.
  105. //
  106. idx := strings.LastIndexByte(path, '/')
  107. if idx == -1 {
  108. return path
  109. }
  110. // Find the penultimate separator.
  111. idx = strings.LastIndexByte(path[:idx], '/')
  112. if idx == -1 {
  113. return path
  114. }
  115. return path[idx+1:]
  116. }
  117. // Non-JSON logging format function
  118. func (z *intLogger) log(t time.Time, level Level, msg string, args ...interface{}) {
  119. z.w.WriteString(t.Format(z.timeFormat))
  120. z.w.WriteByte(' ')
  121. s, ok := _levelToBracket[level]
  122. if ok {
  123. z.w.WriteString(s)
  124. } else {
  125. z.w.WriteString("[UNKN ]")
  126. }
  127. if z.caller {
  128. if _, file, line, ok := runtime.Caller(3); ok {
  129. z.w.WriteByte(' ')
  130. z.w.WriteString(trimCallerPath(file))
  131. z.w.WriteByte(':')
  132. z.w.WriteString(strconv.Itoa(line))
  133. z.w.WriteByte(':')
  134. }
  135. }
  136. z.w.WriteByte(' ')
  137. if z.name != "" {
  138. z.w.WriteString(z.name)
  139. z.w.WriteString(": ")
  140. }
  141. z.w.WriteString(msg)
  142. args = append(z.implied, args...)
  143. var stacktrace CapturedStacktrace
  144. if args != nil && len(args) > 0 {
  145. if len(args)%2 != 0 {
  146. cs, ok := args[len(args)-1].(CapturedStacktrace)
  147. if ok {
  148. args = args[:len(args)-1]
  149. stacktrace = cs
  150. } else {
  151. args = append(args, "<unknown>")
  152. }
  153. }
  154. z.w.WriteByte(':')
  155. FOR:
  156. for i := 0; i < len(args); i = i + 2 {
  157. var val string
  158. switch st := args[i+1].(type) {
  159. case string:
  160. val = st
  161. case int:
  162. val = strconv.FormatInt(int64(st), 10)
  163. case int64:
  164. val = strconv.FormatInt(int64(st), 10)
  165. case int32:
  166. val = strconv.FormatInt(int64(st), 10)
  167. case int16:
  168. val = strconv.FormatInt(int64(st), 10)
  169. case int8:
  170. val = strconv.FormatInt(int64(st), 10)
  171. case uint:
  172. val = strconv.FormatUint(uint64(st), 10)
  173. case uint64:
  174. val = strconv.FormatUint(uint64(st), 10)
  175. case uint32:
  176. val = strconv.FormatUint(uint64(st), 10)
  177. case uint16:
  178. val = strconv.FormatUint(uint64(st), 10)
  179. case uint8:
  180. val = strconv.FormatUint(uint64(st), 10)
  181. case CapturedStacktrace:
  182. stacktrace = st
  183. continue FOR
  184. case Format:
  185. val = fmt.Sprintf(st[0].(string), st[1:]...)
  186. default:
  187. val = fmt.Sprintf("%v", st)
  188. }
  189. z.w.WriteByte(' ')
  190. z.w.WriteString(args[i].(string))
  191. z.w.WriteByte('=')
  192. if strings.ContainsAny(val, " \t\n\r") {
  193. z.w.WriteByte('"')
  194. z.w.WriteString(val)
  195. z.w.WriteByte('"')
  196. } else {
  197. z.w.WriteString(val)
  198. }
  199. }
  200. }
  201. z.w.WriteString("\n")
  202. if stacktrace != "" {
  203. z.w.WriteString(string(stacktrace))
  204. }
  205. }
  206. // JSON logging function
  207. func (z *intLogger) logJson(t time.Time, level Level, msg string, args ...interface{}) {
  208. vals := map[string]interface{}{
  209. "@message": msg,
  210. "@timestamp": t.Format("2006-01-02T15:04:05.000000Z07:00"),
  211. }
  212. var levelStr string
  213. switch level {
  214. case Error:
  215. levelStr = "error"
  216. case Warn:
  217. levelStr = "warn"
  218. case Info:
  219. levelStr = "info"
  220. case Debug:
  221. levelStr = "debug"
  222. case Trace:
  223. levelStr = "trace"
  224. default:
  225. levelStr = "all"
  226. }
  227. vals["@level"] = levelStr
  228. if z.name != "" {
  229. vals["@module"] = z.name
  230. }
  231. if z.caller {
  232. if _, file, line, ok := runtime.Caller(3); ok {
  233. vals["@caller"] = fmt.Sprintf("%s:%d", file, line)
  234. }
  235. }
  236. args = append(z.implied, args...)
  237. if args != nil && len(args) > 0 {
  238. if len(args)%2 != 0 {
  239. cs, ok := args[len(args)-1].(CapturedStacktrace)
  240. if ok {
  241. args = args[:len(args)-1]
  242. vals["stacktrace"] = cs
  243. } else {
  244. args = append(args, "<unknown>")
  245. }
  246. }
  247. for i := 0; i < len(args); i = i + 2 {
  248. if _, ok := args[i].(string); !ok {
  249. // As this is the logging function not much we can do here
  250. // without injecting into logs...
  251. continue
  252. }
  253. val := args[i+1]
  254. switch sv := val.(type) {
  255. case error:
  256. // Check if val is of type error. If error type doesn't
  257. // implement json.Marshaler or encoding.TextMarshaler
  258. // then set val to err.Error() so that it gets marshaled
  259. switch sv.(type) {
  260. case json.Marshaler, encoding.TextMarshaler:
  261. default:
  262. val = sv.Error()
  263. }
  264. case Format:
  265. val = fmt.Sprintf(sv[0].(string), sv[1:]...)
  266. }
  267. vals[args[i].(string)] = val
  268. }
  269. }
  270. err := json.NewEncoder(z.w).Encode(vals)
  271. if err != nil {
  272. panic(err)
  273. }
  274. }
  275. // Emit the message and args at DEBUG level
  276. func (z *intLogger) Debug(msg string, args ...interface{}) {
  277. z.Log(Debug, msg, args...)
  278. }
  279. // Emit the message and args at TRACE level
  280. func (z *intLogger) Trace(msg string, args ...interface{}) {
  281. z.Log(Trace, msg, args...)
  282. }
  283. // Emit the message and args at INFO level
  284. func (z *intLogger) Info(msg string, args ...interface{}) {
  285. z.Log(Info, msg, args...)
  286. }
  287. // Emit the message and args at WARN level
  288. func (z *intLogger) Warn(msg string, args ...interface{}) {
  289. z.Log(Warn, msg, args...)
  290. }
  291. // Emit the message and args at ERROR level
  292. func (z *intLogger) Error(msg string, args ...interface{}) {
  293. z.Log(Error, msg, args...)
  294. }
  295. // Indicate that the logger would emit TRACE level logs
  296. func (z *intLogger) IsTrace() bool {
  297. return z.level == Trace
  298. }
  299. // Indicate that the logger would emit DEBUG level logs
  300. func (z *intLogger) IsDebug() bool {
  301. return z.level <= Debug
  302. }
  303. // Indicate that the logger would emit INFO level logs
  304. func (z *intLogger) IsInfo() bool {
  305. return z.level <= Info
  306. }
  307. // Indicate that the logger would emit WARN level logs
  308. func (z *intLogger) IsWarn() bool {
  309. return z.level <= Warn
  310. }
  311. // Indicate that the logger would emit ERROR level logs
  312. func (z *intLogger) IsError() bool {
  313. return z.level <= Error
  314. }
  315. // Return a sub-Logger for which every emitted log message will contain
  316. // the given key/value pairs. This is used to create a context specific
  317. // Logger.
  318. func (z *intLogger) With(args ...interface{}) Logger {
  319. var nz intLogger = *z
  320. nz.implied = make([]interface{}, 0, len(z.implied)+len(args))
  321. nz.implied = append(nz.implied, z.implied...)
  322. nz.implied = append(nz.implied, args...)
  323. return &nz
  324. }
  325. // Create a new sub-Logger that a name decending from the current name.
  326. // This is used to create a subsystem specific Logger.
  327. func (z *intLogger) Named(name string) Logger {
  328. var nz intLogger = *z
  329. if nz.name != "" {
  330. nz.name = nz.name + "." + name
  331. } else {
  332. nz.name = name
  333. }
  334. return &nz
  335. }
  336. // Create a new sub-Logger with an explicit name. This ignores the current
  337. // name. This is used to create a standalone logger that doesn't fall
  338. // within the normal hierarchy.
  339. func (z *intLogger) ResetNamed(name string) Logger {
  340. var nz intLogger = *z
  341. nz.name = name
  342. return &nz
  343. }
  344. // Create a *log.Logger that will send it's data through this Logger. This
  345. // allows packages that expect to be using the standard library log to actually
  346. // use this logger.
  347. func (z *intLogger) StandardLogger(opts *StandardLoggerOptions) *log.Logger {
  348. if opts == nil {
  349. opts = &StandardLoggerOptions{}
  350. }
  351. return log.New(&stdlogAdapter{z, opts.InferLevels}, "", 0)
  352. }