You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

178 lines
4.0 KiB

  1. package logrus
  2. import (
  3. "bytes"
  4. "fmt"
  5. "sort"
  6. "strings"
  7. "sync"
  8. "time"
  9. )
  10. const (
  11. nocolor = 0
  12. red = 31
  13. green = 32
  14. yellow = 33
  15. blue = 36
  16. gray = 37
  17. )
  18. var (
  19. baseTimestamp time.Time
  20. )
  21. func init() {
  22. baseTimestamp = time.Now()
  23. }
  24. // TextFormatter formats logs into text
  25. type TextFormatter struct {
  26. // Set to true to bypass checking for a TTY before outputting colors.
  27. ForceColors bool
  28. // Force disabling colors.
  29. DisableColors bool
  30. // Disable timestamp logging. useful when output is redirected to logging
  31. // system that already adds timestamps.
  32. DisableTimestamp bool
  33. // Enable logging the full timestamp when a TTY is attached instead of just
  34. // the time passed since beginning of execution.
  35. FullTimestamp bool
  36. // TimestampFormat to use for display when a full timestamp is printed
  37. TimestampFormat string
  38. // The fields are sorted by default for a consistent output. For applications
  39. // that log extremely frequently and don't use the JSON formatter this may not
  40. // be desired.
  41. DisableSorting bool
  42. // QuoteEmptyFields will wrap empty fields in quotes if true
  43. QuoteEmptyFields bool
  44. // Whether the logger's out is to a terminal
  45. isTerminal bool
  46. sync.Once
  47. }
  48. func (f *TextFormatter) init(entry *Entry) {
  49. if entry.Logger != nil {
  50. f.isTerminal = checkIfTerminal(entry.Logger.Out)
  51. }
  52. }
  53. // Format renders a single log entry
  54. func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
  55. var b *bytes.Buffer
  56. keys := make([]string, 0, len(entry.Data))
  57. for k := range entry.Data {
  58. keys = append(keys, k)
  59. }
  60. if !f.DisableSorting {
  61. sort.Strings(keys)
  62. }
  63. if entry.Buffer != nil {
  64. b = entry.Buffer
  65. } else {
  66. b = &bytes.Buffer{}
  67. }
  68. prefixFieldClashes(entry.Data)
  69. f.Do(func() { f.init(entry) })
  70. isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors
  71. timestampFormat := f.TimestampFormat
  72. if timestampFormat == "" {
  73. timestampFormat = defaultTimestampFormat
  74. }
  75. if isColored {
  76. f.printColored(b, entry, keys, timestampFormat)
  77. } else {
  78. if !f.DisableTimestamp {
  79. f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat))
  80. }
  81. f.appendKeyValue(b, "level", entry.Level.String())
  82. if entry.Message != "" {
  83. f.appendKeyValue(b, "msg", entry.Message)
  84. }
  85. for _, key := range keys {
  86. f.appendKeyValue(b, key, entry.Data[key])
  87. }
  88. }
  89. b.WriteByte('\n')
  90. return b.Bytes(), nil
  91. }
  92. func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) {
  93. var levelColor int
  94. switch entry.Level {
  95. case DebugLevel:
  96. levelColor = gray
  97. case WarnLevel:
  98. levelColor = yellow
  99. case ErrorLevel, FatalLevel, PanicLevel:
  100. levelColor = red
  101. default:
  102. levelColor = blue
  103. }
  104. levelText := strings.ToUpper(entry.Level.String())[0:4]
  105. if f.DisableTimestamp {
  106. fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m %-44s ", levelColor, levelText, entry.Message)
  107. } else if !f.FullTimestamp {
  108. fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), entry.Message)
  109. } else {
  110. fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message)
  111. }
  112. for _, k := range keys {
  113. v := entry.Data[k]
  114. fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
  115. f.appendValue(b, v)
  116. }
  117. }
  118. func (f *TextFormatter) needsQuoting(text string) bool {
  119. if f.QuoteEmptyFields && len(text) == 0 {
  120. return true
  121. }
  122. for _, ch := range text {
  123. if !((ch >= 'a' && ch <= 'z') ||
  124. (ch >= 'A' && ch <= 'Z') ||
  125. (ch >= '0' && ch <= '9') ||
  126. ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '@' || ch == '^' || ch == '+') {
  127. return true
  128. }
  129. }
  130. return false
  131. }
  132. func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
  133. if b.Len() > 0 {
  134. b.WriteByte(' ')
  135. }
  136. b.WriteString(key)
  137. b.WriteByte('=')
  138. f.appendValue(b, value)
  139. }
  140. func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
  141. stringVal, ok := value.(string)
  142. if !ok {
  143. stringVal = fmt.Sprint(value)
  144. }
  145. if !f.needsQuoting(stringVal) {
  146. b.WriteString(stringVal)
  147. } else {
  148. b.WriteString(fmt.Sprintf("%q", stringVal))
  149. }
  150. }