Wideband autonomous SDR analysis engine forked from sdr-visual-suite
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

174 行
3.9KB

  1. package logging
  2. import (
  3. "errors"
  4. "io"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "sync"
  9. "time"
  10. "github.com/rs/zerolog"
  11. )
  12. type Config struct {
  13. Level string `yaml:"level" json:"level"`
  14. Categories []string `yaml:"categories" json:"categories"`
  15. RateLimitMs int `yaml:"rate_limit_ms" json:"rate_limit_ms"`
  16. Stdout bool `yaml:"stdout" json:"stdout"`
  17. StdoutColor bool `yaml:"stdout_color" json:"stdout_color"`
  18. File string `yaml:"file" json:"file"`
  19. FileLevel string `yaml:"file_level" json:"file_level"`
  20. TimeFormat string `yaml:"time_format" json:"time_format"`
  21. DisableTime bool `yaml:"disable_time" json:"disable_time"`
  22. }
  23. type rateLimiter struct {
  24. mu sync.Mutex
  25. last map[string]time.Time
  26. limit time.Duration
  27. }
  28. func (r *rateLimiter) allow(key string) bool {
  29. if r.limit <= 0 {
  30. return true
  31. }
  32. now := time.Now()
  33. r.mu.Lock()
  34. defer r.mu.Unlock()
  35. if t, ok := r.last[key]; ok {
  36. if now.Sub(t) < r.limit {
  37. return false
  38. }
  39. }
  40. r.last[key] = now
  41. return true
  42. }
  43. var (
  44. logger zerolog.Logger
  45. fileLogger zerolog.Logger
  46. cfg Config
  47. cats map[string]bool
  48. rlim = &rateLimiter{last: map[string]time.Time{}}
  49. fileHandle *os.File
  50. )
  51. func Init(c Config) error {
  52. cfg = c
  53. if cfg.TimeFormat == "" {
  54. cfg.TimeFormat = "15:04:05"
  55. }
  56. cats = map[string]bool{}
  57. for _, c := range cfg.Categories {
  58. cats[strings.ToLower(strings.TrimSpace(c))] = true
  59. }
  60. rl := time.Duration(cfg.RateLimitMs) * time.Millisecond
  61. rlim.limit = rl
  62. level := parseLevel(cfg.Level)
  63. writers := make([]io.Writer, 0, 2)
  64. if cfg.Stdout {
  65. cw := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: cfg.TimeFormat, NoColor: !cfg.StdoutColor}
  66. if cfg.DisableTime {
  67. cw.PartsExclude = append(cw.PartsExclude, zerolog.TimestampFieldName)
  68. }
  69. writers = append(writers, cw)
  70. }
  71. if cfg.File != "" {
  72. dir := filepath.Dir(cfg.File)
  73. if dir != "." && dir != "" {
  74. if err := os.MkdirAll(dir, 0o755); err != nil {
  75. return err
  76. }
  77. }
  78. fh, err := os.OpenFile(cfg.File, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644)
  79. if err != nil {
  80. return err
  81. }
  82. fileHandle = fh
  83. writers = append(writers, fh)
  84. fileLevel := parseLevel(cfg.FileLevel)
  85. fileLogger = zerolog.New(fh).Level(fileLevel).With().Timestamp().Logger()
  86. }
  87. if len(writers) == 0 {
  88. return errors.New("logging: no outputs enabled")
  89. }
  90. mw := io.MultiWriter(writers...)
  91. logger = zerolog.New(mw).Level(level).With().Timestamp().Logger()
  92. return nil
  93. }
  94. func Close() {
  95. if fileHandle != nil {
  96. _ = fileHandle.Close()
  97. fileHandle = nil
  98. }
  99. }
  100. func EnabledCategory(cat string) bool {
  101. if len(cats) == 0 {
  102. return true
  103. }
  104. _, ok := cats[strings.ToLower(cat)]
  105. return ok
  106. }
  107. func logf(level zerolog.Level, cat, msg string, kv ...any) {
  108. if !EnabledCategory(cat) {
  109. return
  110. }
  111. key := cat + ":" + level.String()
  112. if !rlim.allow(key) {
  113. return
  114. }
  115. if level < logger.GetLevel() {
  116. return
  117. }
  118. l := logger.With().Str("cat", cat).Logger()
  119. e := (&l).WithLevel(level)
  120. for i := 0; i+1 < len(kv); i += 2 {
  121. k, ok := kv[i].(string)
  122. if !ok {
  123. continue
  124. }
  125. switch v := kv[i+1].(type) {
  126. case string:
  127. e = e.Str(k, v)
  128. case int:
  129. e = e.Int(k, v)
  130. case int64:
  131. e = e.Int64(k, v)
  132. case float64:
  133. e = e.Float64(k, v)
  134. case bool:
  135. e = e.Bool(k, v)
  136. default:
  137. e = e.Interface(k, v)
  138. }
  139. }
  140. e.Msg(msg)
  141. }
  142. func Debug(cat, msg string, kv ...any) { logf(zerolog.DebugLevel, cat, msg, kv...) }
  143. func Info(cat, msg string, kv ...any) { logf(zerolog.InfoLevel, cat, msg, kv...) }
  144. func Warn(cat, msg string, kv ...any) { logf(zerolog.WarnLevel, cat, msg, kv...) }
  145. func Error(cat, msg string, kv ...any) { logf(zerolog.ErrorLevel, cat, msg, kv...) }
  146. func parseLevel(raw string) zerolog.Level {
  147. s := strings.ToLower(strings.TrimSpace(raw))
  148. switch s {
  149. case "debug":
  150. return zerolog.DebugLevel
  151. case "info", "informal":
  152. return zerolog.InfoLevel
  153. case "warn", "warning":
  154. return zerolog.WarnLevel
  155. case "error":
  156. return zerolog.ErrorLevel
  157. default:
  158. return zerolog.InfoLevel
  159. }
  160. }