Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

logger.go 6.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. // Copyright (c) 2017 Daniel Oaks <daniel@danieloaks.net>
  2. // released under the MIT license
  3. package logger
  4. import (
  5. "bufio"
  6. "fmt"
  7. "os"
  8. "time"
  9. "strings"
  10. "sync"
  11. colorable "github.com/mattn/go-colorable"
  12. "github.com/mgutz/ansi"
  13. )
  14. // Level represents the level to log messages at.
  15. type Level int
  16. const (
  17. // LogDebug represents debug messages.
  18. LogDebug Level = iota
  19. // LogInfo represents informational messages.
  20. LogInfo
  21. // LogWarning represents warnings.
  22. LogWarning
  23. // LogError represents errors.
  24. LogError
  25. )
  26. var (
  27. LogLevelNames = map[string]Level{
  28. "debug": LogDebug,
  29. "info": LogInfo,
  30. "warn": LogWarning,
  31. "warning": LogWarning,
  32. "warnings": LogWarning,
  33. "error": LogError,
  34. "errors": LogError,
  35. }
  36. LogLevelDisplayNames = map[Level]string{
  37. LogDebug: "debug",
  38. LogInfo: "info",
  39. LogWarning: "warning",
  40. LogError: "error",
  41. }
  42. )
  43. // Manager is the main interface used to log debug/info/error messages.
  44. type Manager struct {
  45. loggers []singleLogger
  46. stderrWriteLock sync.Mutex
  47. fileWriteLock sync.Mutex
  48. DumpingRawInOut bool
  49. }
  50. // Config represents the configuration of a single logger.
  51. type Config struct {
  52. // logging methods
  53. MethodStderr bool
  54. MethodFile bool
  55. Filename string
  56. // logging level
  57. Level Level
  58. // logging types
  59. Types []string
  60. ExcludedTypes []string
  61. }
  62. // NewManager returns a new log manager.
  63. func NewManager(config ...Config) (*Manager, error) {
  64. var logger Manager
  65. for _, logConfig := range config {
  66. typeMap := make(map[string]bool)
  67. for _, name := range logConfig.Types {
  68. typeMap[name] = true
  69. }
  70. excludedTypeMap := make(map[string]bool)
  71. for _, name := range logConfig.ExcludedTypes {
  72. excludedTypeMap[name] = true
  73. }
  74. sLogger := singleLogger{
  75. MethodSTDERR: logConfig.MethodStderr,
  76. MethodFile: fileMethod{
  77. Enabled: logConfig.MethodFile,
  78. Filename: logConfig.Filename,
  79. },
  80. Level: logConfig.Level,
  81. Types: typeMap,
  82. ExcludedTypes: excludedTypeMap,
  83. stderrWriteLock: &logger.stderrWriteLock,
  84. fileWriteLock: &logger.fileWriteLock,
  85. }
  86. if typeMap["userinput"] || typeMap["useroutput"] || (typeMap["*"] && !(excludedTypeMap["userinput"] && excludedTypeMap["useroutput"])) {
  87. logger.DumpingRawInOut = true
  88. }
  89. if sLogger.MethodFile.Enabled {
  90. file, err := os.OpenFile(sLogger.MethodFile.Filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
  91. if err != nil {
  92. return nil, fmt.Errorf("Could not open log file %s [%s]", sLogger.MethodFile.Filename, err.Error())
  93. }
  94. writer := bufio.NewWriter(file)
  95. sLogger.MethodFile.File = file
  96. sLogger.MethodFile.Writer = writer
  97. }
  98. logger.loggers = append(logger.loggers, sLogger)
  99. }
  100. return &logger, nil
  101. }
  102. // Log logs the given message with the given details.
  103. func (logger *Manager) Log(level Level, logType string, messageParts ...string) {
  104. for _, singleLogger := range logger.loggers {
  105. singleLogger.Log(level, logType, messageParts...)
  106. }
  107. }
  108. // Debug logs the given message as a debug message.
  109. func (logger *Manager) Debug(logType string, messageParts ...string) {
  110. for _, singleLogger := range logger.loggers {
  111. singleLogger.Log(LogDebug, logType, messageParts...)
  112. }
  113. }
  114. // Info logs the given message as an info message.
  115. func (logger *Manager) Info(logType string, messageParts ...string) {
  116. for _, singleLogger := range logger.loggers {
  117. singleLogger.Log(LogInfo, logType, messageParts...)
  118. }
  119. }
  120. // Warning logs the given message as a warning message.
  121. func (logger *Manager) Warning(logType string, messageParts ...string) {
  122. for _, singleLogger := range logger.loggers {
  123. singleLogger.Log(LogWarning, logType, messageParts...)
  124. }
  125. }
  126. // Error logs the given message as an error message.
  127. func (logger *Manager) Error(logType string, messageParts ...string) {
  128. for _, singleLogger := range logger.loggers {
  129. singleLogger.Log(LogError, logType, messageParts...)
  130. }
  131. }
  132. // Fatal logs the given message as an error message, then exits.
  133. func (logger *Manager) Fatal(logType string, messageParts ...string) {
  134. logger.Error(logType, messageParts...)
  135. logger.Error("FATAL", "Fatal error encountered, application exiting")
  136. os.Exit(1)
  137. }
  138. type fileMethod struct {
  139. Enabled bool
  140. Filename string
  141. File *os.File
  142. Writer *bufio.Writer
  143. }
  144. // singleLogger represents a single logger instance.
  145. type singleLogger struct {
  146. stderrWriteLock *sync.Mutex
  147. fileWriteLock *sync.Mutex
  148. MethodSTDERR bool
  149. MethodFile fileMethod
  150. Level Level
  151. Types map[string]bool
  152. ExcludedTypes map[string]bool
  153. }
  154. // Log logs the given message with the given details.
  155. func (logger *singleLogger) Log(level Level, logType string, messageParts ...string) {
  156. // no logging enabled
  157. if !(logger.MethodSTDERR || logger.MethodFile.Enabled) {
  158. return
  159. }
  160. // ensure we're logging to the given level
  161. if level < logger.Level {
  162. return
  163. }
  164. // ensure we're capturing this logType
  165. logTypeCleaned := strings.ToLower(strings.TrimSpace(logType))
  166. capturing := (logger.Types["*"] || logger.Types[logTypeCleaned]) && !logger.ExcludedTypes["*"] && !logger.ExcludedTypes[logTypeCleaned]
  167. if !capturing {
  168. return
  169. }
  170. // assemble full line
  171. timeGrey := ansi.ColorFunc("243")
  172. grey := ansi.ColorFunc("8")
  173. alert := ansi.ColorFunc("232+b:red")
  174. warn := ansi.ColorFunc("black:214")
  175. info := ansi.ColorFunc("117")
  176. debug := ansi.ColorFunc("78")
  177. section := ansi.ColorFunc("229")
  178. levelDisplay := LogLevelDisplayNames[level]
  179. if level == LogError {
  180. levelDisplay = alert(levelDisplay)
  181. } else if level == LogWarning {
  182. levelDisplay = warn(levelDisplay)
  183. } else if level == LogInfo {
  184. levelDisplay = info(levelDisplay)
  185. } else if level == LogDebug {
  186. levelDisplay = debug(levelDisplay)
  187. }
  188. sep := grey(":")
  189. fullStringFormatted := fmt.Sprintf("%s %s %s %s %s %s ", timeGrey(time.Now().UTC().Format("2006-01-02T15:04:05Z")), sep, levelDisplay, sep, section(logType), sep)
  190. fullStringRaw := fmt.Sprintf("%s : %s : %s : ", time.Now().UTC().Format("2006-01-02T15:04:05Z"), LogLevelDisplayNames[level], logType)
  191. for i, p := range messageParts {
  192. fullStringFormatted += p
  193. fullStringRaw += p
  194. if i != len(messageParts)-1 {
  195. fullStringFormatted += " " + sep + " "
  196. fullStringRaw += " : "
  197. }
  198. }
  199. // output
  200. if logger.MethodSTDERR {
  201. logger.stderrWriteLock.Lock()
  202. fmt.Fprintln(colorable.NewColorableStderr(), fullStringFormatted)
  203. logger.stderrWriteLock.Unlock()
  204. }
  205. if logger.MethodFile.Enabled {
  206. logger.fileWriteLock.Lock()
  207. logger.MethodFile.Writer.WriteString(fullStringRaw + "\n")
  208. logger.fileWriteLock.Unlock()
  209. }
  210. }