thatswhyyoualwaysleaveanote.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. // Copyright © 2016 Steve Francia <spf@spf13.com>.
  2. //
  3. // Use of this source code is governed by an MIT-style
  4. // license that can be found in the LICENSE file.
  5. package jwalterweatherman
  6. import (
  7. "fmt"
  8. "io"
  9. "io/ioutil"
  10. "log"
  11. "os"
  12. "sync/atomic"
  13. )
  14. // Level describes the chosen log level between
  15. // debug and critical.
  16. type Level int
  17. type NotePad struct {
  18. Handle io.Writer
  19. Level Level
  20. Prefix string
  21. Logger **log.Logger
  22. counter uint64
  23. }
  24. func (n *NotePad) incr() {
  25. atomic.AddUint64(&n.counter, 1)
  26. }
  27. func (n *NotePad) resetCounter() {
  28. atomic.StoreUint64(&n.counter, 0)
  29. }
  30. func (n *NotePad) getCount() uint64 {
  31. return atomic.LoadUint64(&n.counter)
  32. }
  33. type countingWriter struct {
  34. incrFunc func()
  35. }
  36. func (cw *countingWriter) Write(p []byte) (n int, err error) {
  37. cw.incrFunc()
  38. return 0, nil
  39. }
  40. // Feedback is special. It writes plainly to the output while
  41. // logging with the standard extra information (date, file, etc)
  42. // Only Println and Printf are currently provided for this
  43. type Feedback struct{}
  44. const (
  45. LevelTrace Level = iota
  46. LevelDebug
  47. LevelInfo
  48. LevelWarn
  49. LevelError
  50. LevelCritical
  51. LevelFatal
  52. DefaultLogThreshold = LevelWarn
  53. DefaultStdoutThreshold = LevelError
  54. )
  55. var (
  56. TRACE *log.Logger
  57. DEBUG *log.Logger
  58. INFO *log.Logger
  59. WARN *log.Logger
  60. ERROR *log.Logger
  61. CRITICAL *log.Logger
  62. FATAL *log.Logger
  63. LOG *log.Logger
  64. FEEDBACK Feedback
  65. LogHandle io.Writer = ioutil.Discard
  66. OutHandle io.Writer = os.Stdout
  67. BothHandle io.Writer = io.MultiWriter(LogHandle, OutHandle)
  68. NotePads []*NotePad = []*NotePad{trace, debug, info, warn, err, critical, fatal}
  69. trace *NotePad = &NotePad{Level: LevelTrace, Handle: os.Stdout, Logger: &TRACE, Prefix: "TRACE: "}
  70. debug *NotePad = &NotePad{Level: LevelDebug, Handle: os.Stdout, Logger: &DEBUG, Prefix: "DEBUG: "}
  71. info *NotePad = &NotePad{Level: LevelInfo, Handle: os.Stdout, Logger: &INFO, Prefix: "INFO: "}
  72. warn *NotePad = &NotePad{Level: LevelWarn, Handle: os.Stdout, Logger: &WARN, Prefix: "WARN: "}
  73. err *NotePad = &NotePad{Level: LevelError, Handle: os.Stdout, Logger: &ERROR, Prefix: "ERROR: "}
  74. critical *NotePad = &NotePad{Level: LevelCritical, Handle: os.Stdout, Logger: &CRITICAL, Prefix: "CRITICAL: "}
  75. fatal *NotePad = &NotePad{Level: LevelFatal, Handle: os.Stdout, Logger: &FATAL, Prefix: "FATAL: "}
  76. logThreshold Level = DefaultLogThreshold
  77. outputThreshold Level = DefaultStdoutThreshold
  78. )
  79. const (
  80. DATE = log.Ldate
  81. TIME = log.Ltime
  82. SFILE = log.Lshortfile
  83. LFILE = log.Llongfile
  84. MSEC = log.Lmicroseconds
  85. )
  86. var logFlags = DATE | TIME | SFILE
  87. func init() {
  88. SetStdoutThreshold(DefaultStdoutThreshold)
  89. }
  90. // initialize will setup the jWalterWeatherman standard approach of providing the user
  91. // some feedback and logging a potentially different amount based on independent log and output thresholds.
  92. // By default the output has a lower threshold than logged
  93. // Don't use if you have manually set the Handles of the different levels as it will overwrite them.
  94. func initialize() {
  95. BothHandle = io.MultiWriter(LogHandle, OutHandle)
  96. for _, n := range NotePads {
  97. if n.Level < outputThreshold && n.Level < logThreshold {
  98. n.Handle = ioutil.Discard
  99. } else if n.Level >= outputThreshold && n.Level >= logThreshold {
  100. n.Handle = BothHandle
  101. } else if n.Level >= outputThreshold && n.Level < logThreshold {
  102. n.Handle = OutHandle
  103. } else {
  104. n.Handle = LogHandle
  105. }
  106. }
  107. for _, n := range NotePads {
  108. n.Handle = io.MultiWriter(n.Handle, &countingWriter{n.incr})
  109. *n.Logger = log.New(n.Handle, n.Prefix, logFlags)
  110. }
  111. LOG = log.New(LogHandle,
  112. "LOG: ",
  113. logFlags)
  114. }
  115. // Set the log Flags (Available flag: DATE, TIME, SFILE, LFILE and MSEC)
  116. func SetLogFlag(flags int) {
  117. logFlags = flags
  118. initialize()
  119. }
  120. // Level returns the current global log threshold.
  121. func LogThreshold() Level {
  122. return logThreshold
  123. }
  124. // Level returns the current global output threshold.
  125. func StdoutThreshold() Level {
  126. return outputThreshold
  127. }
  128. // Ensures that the level provided is within the bounds of available levels
  129. func levelCheck(level Level) Level {
  130. switch {
  131. case level <= LevelTrace:
  132. return LevelTrace
  133. case level >= LevelFatal:
  134. return LevelFatal
  135. default:
  136. return level
  137. }
  138. }
  139. // Establishes a threshold where anything matching or above will be logged
  140. func SetLogThreshold(level Level) {
  141. logThreshold = levelCheck(level)
  142. initialize()
  143. }
  144. // Establishes a threshold where anything matching or above will be output
  145. func SetStdoutThreshold(level Level) {
  146. outputThreshold = levelCheck(level)
  147. initialize()
  148. }
  149. // Conveniently Sets the Log Handle to a io.writer created for the file behind the given filepath
  150. // Will only append to this file
  151. func SetLogFile(path string) {
  152. file, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
  153. if err != nil {
  154. CRITICAL.Println("Failed to open log file:", path, err)
  155. os.Exit(-1)
  156. }
  157. INFO.Println("Logging to", file.Name())
  158. LogHandle = file
  159. initialize()
  160. }
  161. // Conveniently Creates a temporary file and sets the Log Handle to a io.writer created for it
  162. func UseTempLogFile(prefix string) {
  163. file, err := ioutil.TempFile(os.TempDir(), prefix)
  164. if err != nil {
  165. CRITICAL.Println(err)
  166. }
  167. INFO.Println("Logging to", file.Name())
  168. LogHandle = file
  169. initialize()
  170. }
  171. // LogCountForLevel returns the number of log invocations for a given level.
  172. func LogCountForLevel(l Level) uint64 {
  173. for _, np := range NotePads {
  174. if np.Level == l {
  175. return np.getCount()
  176. }
  177. }
  178. return 0
  179. }
  180. // LogCountForLevelsGreaterThanorEqualTo returns the number of log invocations
  181. // greater than or equal to a given level threshold.
  182. func LogCountForLevelsGreaterThanorEqualTo(threshold Level) uint64 {
  183. var cnt uint64
  184. for _, np := range NotePads {
  185. if np.Level >= threshold {
  186. cnt += np.getCount()
  187. }
  188. }
  189. return cnt
  190. }
  191. // ResetLogCounters resets the invocation counters for all levels.
  192. func ResetLogCounters() {
  193. for _, np := range NotePads {
  194. np.resetCounter()
  195. }
  196. }
  197. // Disables logging for the entire JWW system
  198. func DiscardLogging() {
  199. LogHandle = ioutil.Discard
  200. initialize()
  201. }
  202. // Feedback is special. It writes plainly to the output while
  203. // logging with the standard extra information (date, file, etc)
  204. // Only Println and Printf are currently provided for this
  205. func (fb *Feedback) Println(v ...interface{}) {
  206. s := fmt.Sprintln(v...)
  207. fmt.Print(s)
  208. LOG.Output(2, s)
  209. }
  210. // Feedback is special. It writes plainly to the output while
  211. // logging with the standard extra information (date, file, etc)
  212. // Only Println and Printf are currently provided for this
  213. func (fb *Feedback) Printf(format string, v ...interface{}) {
  214. s := fmt.Sprintf(format, v...)
  215. fmt.Print(s)
  216. LOG.Output(2, s)
  217. }