entry.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. // Copyright (c) 2016 Uber Technologies, Inc.
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy
  4. // of this software and associated documentation files (the "Software"), to deal
  5. // in the Software without restriction, including without limitation the rights
  6. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. // copies of the Software, and to permit persons to whom the Software is
  8. // furnished to do so, subject to the following conditions:
  9. //
  10. // The above copyright notice and this permission notice shall be included in
  11. // all copies or substantial portions of the Software.
  12. //
  13. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. // THE SOFTWARE.
  20. package zapcore
  21. import (
  22. "fmt"
  23. "runtime"
  24. "strings"
  25. "sync"
  26. "time"
  27. "go.uber.org/zap/internal/bufferpool"
  28. "go.uber.org/zap/internal/exit"
  29. "go.uber.org/multierr"
  30. )
  31. var (
  32. _cePool = sync.Pool{New: func() interface{} {
  33. // Pre-allocate some space for cores.
  34. return &CheckedEntry{
  35. cores: make([]Core, 4),
  36. }
  37. }}
  38. )
  39. func getCheckedEntry() *CheckedEntry {
  40. ce := _cePool.Get().(*CheckedEntry)
  41. ce.reset()
  42. return ce
  43. }
  44. func putCheckedEntry(ce *CheckedEntry) {
  45. if ce == nil {
  46. return
  47. }
  48. _cePool.Put(ce)
  49. }
  50. // NewEntryCaller makes an EntryCaller from the return signature of
  51. // runtime.Caller.
  52. func NewEntryCaller(pc uintptr, file string, line int, ok bool) EntryCaller {
  53. if !ok {
  54. return EntryCaller{}
  55. }
  56. return EntryCaller{
  57. PC: pc,
  58. File: file,
  59. Line: line,
  60. Defined: true,
  61. }
  62. }
  63. // EntryCaller represents the caller of a logging function.
  64. type EntryCaller struct {
  65. Defined bool
  66. PC uintptr
  67. File string
  68. Line int
  69. Function string
  70. }
  71. // String returns the full path and line number of the caller.
  72. func (ec EntryCaller) String() string {
  73. return ec.FullPath()
  74. }
  75. // FullPath returns a /full/path/to/package/file:line description of the
  76. // caller.
  77. func (ec EntryCaller) FullPath() string {
  78. if !ec.Defined {
  79. return "undefined"
  80. }
  81. buf := bufferpool.Get()
  82. buf.AppendString(ec.File)
  83. buf.AppendByte(':')
  84. buf.AppendInt(int64(ec.Line))
  85. caller := buf.String()
  86. buf.Free()
  87. return caller
  88. }
  89. // TrimmedPath returns a package/file:line description of the caller,
  90. // preserving only the leaf directory name and file name.
  91. func (ec EntryCaller) TrimmedPath() string {
  92. if !ec.Defined {
  93. return "undefined"
  94. }
  95. // nb. To make sure we trim the path correctly on Windows too, we
  96. // counter-intuitively need to use '/' and *not* os.PathSeparator here,
  97. // because the path given originates from Go stdlib, specifically
  98. // runtime.Caller() which (as of Mar/17) returns forward slashes even on
  99. // Windows.
  100. //
  101. // See https://github.com/golang/go/issues/3335
  102. // and https://github.com/golang/go/issues/18151
  103. //
  104. // for discussion on the issue on Go side.
  105. //
  106. // Find the last separator.
  107. //
  108. idx := strings.LastIndexByte(ec.File, '/')
  109. if idx == -1 {
  110. return ec.FullPath()
  111. }
  112. // Find the penultimate separator.
  113. idx = strings.LastIndexByte(ec.File[:idx], '/')
  114. if idx == -1 {
  115. return ec.FullPath()
  116. }
  117. buf := bufferpool.Get()
  118. // Keep everything after the penultimate separator.
  119. buf.AppendString(ec.File[idx+1:])
  120. buf.AppendByte(':')
  121. buf.AppendInt(int64(ec.Line))
  122. caller := buf.String()
  123. buf.Free()
  124. return caller
  125. }
  126. // An Entry represents a complete log message. The entry's structured context
  127. // is already serialized, but the log level, time, message, and call site
  128. // information are available for inspection and modification. Any fields left
  129. // empty will be omitted when encoding.
  130. //
  131. // Entries are pooled, so any functions that accept them MUST be careful not to
  132. // retain references to them.
  133. type Entry struct {
  134. Level Level
  135. Time time.Time
  136. LoggerName string
  137. Message string
  138. Caller EntryCaller
  139. Stack string
  140. }
  141. // CheckWriteAction indicates what action to take after a log entry is
  142. // processed. Actions are ordered in increasing severity.
  143. type CheckWriteAction uint8
  144. const (
  145. // WriteThenNoop indicates that nothing special needs to be done. It's the
  146. // default behavior.
  147. WriteThenNoop CheckWriteAction = iota
  148. // WriteThenGoexit runs runtime.Goexit after Write.
  149. WriteThenGoexit
  150. // WriteThenPanic causes a panic after Write.
  151. WriteThenPanic
  152. // WriteThenFatal causes a fatal os.Exit after Write.
  153. WriteThenFatal
  154. )
  155. // CheckedEntry is an Entry together with a collection of Cores that have
  156. // already agreed to log it.
  157. //
  158. // CheckedEntry references should be created by calling AddCore or Should on a
  159. // nil *CheckedEntry. References are returned to a pool after Write, and MUST
  160. // NOT be retained after calling their Write method.
  161. type CheckedEntry struct {
  162. Entry
  163. ErrorOutput WriteSyncer
  164. dirty bool // best-effort detection of pool misuse
  165. should CheckWriteAction
  166. cores []Core
  167. }
  168. func (ce *CheckedEntry) reset() {
  169. ce.Entry = Entry{}
  170. ce.ErrorOutput = nil
  171. ce.dirty = false
  172. ce.should = WriteThenNoop
  173. for i := range ce.cores {
  174. // don't keep references to cores
  175. ce.cores[i] = nil
  176. }
  177. ce.cores = ce.cores[:0]
  178. }
  179. // Write writes the entry to the stored Cores, returns any errors, and returns
  180. // the CheckedEntry reference to a pool for immediate re-use. Finally, it
  181. // executes any required CheckWriteAction.
  182. func (ce *CheckedEntry) Write(fields ...Field) {
  183. if ce == nil {
  184. return
  185. }
  186. if ce.dirty {
  187. if ce.ErrorOutput != nil {
  188. // Make a best effort to detect unsafe re-use of this CheckedEntry.
  189. // If the entry is dirty, log an internal error; because the
  190. // CheckedEntry is being used after it was returned to the pool,
  191. // the message may be an amalgamation from multiple call sites.
  192. fmt.Fprintf(ce.ErrorOutput, "%v Unsafe CheckedEntry re-use near Entry %+v.\n", time.Now(), ce.Entry)
  193. ce.ErrorOutput.Sync()
  194. }
  195. return
  196. }
  197. ce.dirty = true
  198. var err error
  199. for i := range ce.cores {
  200. err = multierr.Append(err, ce.cores[i].Write(ce.Entry, fields))
  201. }
  202. if ce.ErrorOutput != nil {
  203. if err != nil {
  204. fmt.Fprintf(ce.ErrorOutput, "%v write error: %v\n", time.Now(), err)
  205. ce.ErrorOutput.Sync()
  206. }
  207. }
  208. should, msg := ce.should, ce.Message
  209. putCheckedEntry(ce)
  210. switch should {
  211. case WriteThenPanic:
  212. panic(msg)
  213. case WriteThenFatal:
  214. exit.Exit()
  215. case WriteThenGoexit:
  216. runtime.Goexit()
  217. }
  218. }
  219. // AddCore adds a Core that has agreed to log this CheckedEntry. It's intended to be
  220. // used by Core.Check implementations, and is safe to call on nil CheckedEntry
  221. // references.
  222. func (ce *CheckedEntry) AddCore(ent Entry, core Core) *CheckedEntry {
  223. if ce == nil {
  224. ce = getCheckedEntry()
  225. ce.Entry = ent
  226. }
  227. ce.cores = append(ce.cores, core)
  228. return ce
  229. }
  230. // Should sets this CheckedEntry's CheckWriteAction, which controls whether a
  231. // Core will panic or fatal after writing this log entry. Like AddCore, it's
  232. // safe to call on nil CheckedEntry references.
  233. func (ce *CheckedEntry) Should(ent Entry, should CheckWriteAction) *CheckedEntry {
  234. if ce == nil {
  235. ce = getCheckedEntry()
  236. ce.Entry = ent
  237. }
  238. ce.should = should
  239. return ce
  240. }