format.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. package log
  2. import (
  3. "context"
  4. "strconv"
  5. "strings"
  6. "time"
  7. F "github.com/sagernet/sing/common/format"
  8. "github.com/logrusorgru/aurora"
  9. )
  10. type Formatter struct {
  11. BaseTime time.Time
  12. DisableColors bool
  13. DisableTimestamp bool
  14. FullTimestamp bool
  15. TimestampFormat string
  16. DisableLineBreak bool
  17. }
  18. func (f Formatter) Format(ctx context.Context, level Level, tag string, message string, timestamp time.Time) string {
  19. levelString := strings.ToUpper(FormatLevel(level))
  20. if !f.DisableColors {
  21. switch level {
  22. case LevelDebug, LevelTrace:
  23. levelString = aurora.White(levelString).String()
  24. case LevelInfo:
  25. levelString = aurora.Cyan(levelString).String()
  26. case LevelWarn:
  27. levelString = aurora.Yellow(levelString).String()
  28. case LevelError, LevelFatal, LevelPanic:
  29. levelString = aurora.Red(levelString).String()
  30. }
  31. }
  32. if tag != "" {
  33. message = tag + ": " + message
  34. }
  35. var id ID
  36. var hasId bool
  37. if ctx != nil {
  38. id, hasId = IDFromContext(ctx)
  39. }
  40. if hasId {
  41. activeDuration := FormatDuration(time.Since(id.CreatedAt))
  42. if !f.DisableColors {
  43. var color aurora.Color
  44. color = aurora.Color(uint8(id.ID))
  45. color %= 215
  46. row := uint(color / 36)
  47. column := uint(color % 36)
  48. var r, g, b float32
  49. r = float32(row * 51)
  50. g = float32(column / 6 * 51)
  51. b = float32((column % 6) * 51)
  52. luma := 0.2126*r + 0.7152*g + 0.0722*b
  53. if luma < 60 {
  54. row = 5 - row
  55. column = 35 - column
  56. color = aurora.Color(row*36 + column)
  57. }
  58. color += 16
  59. color = color << 16
  60. color |= 1 << 14
  61. message = F.ToString("[", aurora.Colorize(id.ID, color).String(), " ", activeDuration, "] ", message)
  62. } else {
  63. message = F.ToString("[", id.ID, " ", activeDuration, "] ", message)
  64. }
  65. }
  66. switch {
  67. case f.DisableTimestamp:
  68. message = levelString + " " + message
  69. case f.FullTimestamp:
  70. message = timestamp.Format(f.TimestampFormat) + " " + levelString + " " + message
  71. default:
  72. message = levelString + "[" + xd(int(timestamp.Sub(f.BaseTime)/time.Second), 4) + "] " + message
  73. }
  74. if f.DisableLineBreak {
  75. if message[len(message)-1] == '\n' {
  76. message = message[:len(message)-1]
  77. }
  78. } else {
  79. if message[len(message)-1] != '\n' {
  80. message += "\n"
  81. }
  82. }
  83. return message
  84. }
  85. func (f Formatter) FormatWithSimple(ctx context.Context, level Level, tag string, message string, timestamp time.Time) (string, string) {
  86. levelString := strings.ToUpper(FormatLevel(level))
  87. if !f.DisableColors {
  88. switch level {
  89. case LevelDebug, LevelTrace:
  90. levelString = aurora.White(levelString).String()
  91. case LevelInfo:
  92. levelString = aurora.Cyan(levelString).String()
  93. case LevelWarn:
  94. levelString = aurora.Yellow(levelString).String()
  95. case LevelError, LevelFatal, LevelPanic:
  96. levelString = aurora.Red(levelString).String()
  97. }
  98. }
  99. if tag != "" {
  100. message = tag + ": " + message
  101. }
  102. messageSimple := message
  103. var id ID
  104. var hasId bool
  105. if ctx != nil {
  106. id, hasId = IDFromContext(ctx)
  107. }
  108. if hasId {
  109. activeDuration := FormatDuration(time.Since(id.CreatedAt))
  110. if !f.DisableColors {
  111. var color aurora.Color
  112. color = aurora.Color(uint8(id.ID))
  113. color %= 215
  114. row := uint(color / 36)
  115. column := uint(color % 36)
  116. var r, g, b float32
  117. r = float32(row * 51)
  118. g = float32(column / 6 * 51)
  119. b = float32((column % 6) * 51)
  120. luma := 0.2126*r + 0.7152*g + 0.0722*b
  121. if luma < 60 {
  122. row = 5 - row
  123. column = 35 - column
  124. color = aurora.Color(row*36 + column)
  125. }
  126. color += 16
  127. color = color << 16
  128. color |= 1 << 14
  129. message = F.ToString("[", aurora.Colorize(id.ID, color).String(), " ", activeDuration, "] ", message)
  130. } else {
  131. message = F.ToString("[", id.ID, " ", activeDuration, "] ", message)
  132. }
  133. messageSimple = F.ToString("[", id.ID, " ", activeDuration, "] ", messageSimple)
  134. }
  135. switch {
  136. case f.DisableTimestamp:
  137. message = levelString + " " + message
  138. case f.FullTimestamp:
  139. message = timestamp.Format(f.TimestampFormat) + " " + levelString + " " + message
  140. default:
  141. message = levelString + "[" + xd(int(timestamp.Sub(f.BaseTime)/time.Second), 4) + "] " + message
  142. }
  143. if message[len(message)-1] != '\n' {
  144. message += "\n"
  145. }
  146. return message, messageSimple
  147. }
  148. func xd(value int, x int) string {
  149. message := strconv.Itoa(value)
  150. for len(message) < x {
  151. message = "0" + message
  152. }
  153. return message
  154. }
  155. func FormatDuration(duration time.Duration) string {
  156. if duration < time.Second {
  157. return F.ToString(duration.Milliseconds(), "ms")
  158. } else if duration < time.Minute {
  159. return F.ToString(int64(duration.Seconds()), ".", int64(duration.Seconds()*100)%100, "s")
  160. } else {
  161. return F.ToString(int64(duration.Minutes()), "m", int64(duration.Seconds())%60, "s")
  162. }
  163. }