leveler.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. // Copyright (C) 2025 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package slogutil
  7. import (
  8. "log/slog"
  9. "maps"
  10. "sync"
  11. )
  12. // A levelTracker keeps track of log level per package. This enables the
  13. // traditional STTRACE variable to set certain packages to debug level, but
  14. // also allows setting packages to other levels such as WARN to silence
  15. // INFO-level messages.
  16. //
  17. // The STTRACE environment variable is one way of controlling this, where
  18. // mentioning a package makes it DEBUG level:
  19. // STTRACE="model,protocol" # model and protocol are at DEBUG level
  20. // however you can also give specific levels after a colon:
  21. // STTRACE="model:WARNING,protocol:DEBUG"
  22. func PackageDescrs() map[string]string {
  23. return globalLevels.Descrs()
  24. }
  25. func PackageLevels() map[string]slog.Level {
  26. return globalLevels.Levels()
  27. }
  28. func SetPackageLevel(pkg string, level slog.Level) {
  29. globalLevels.Set(pkg, level)
  30. }
  31. func SetDefaultLevel(level slog.Level) {
  32. globalLevels.SetDefault(level)
  33. }
  34. type levelTracker struct {
  35. mut sync.RWMutex
  36. defLevel slog.Level
  37. descrs map[string]string // package name to description
  38. levels map[string]slog.Level // package name to level
  39. }
  40. func (t *levelTracker) Get(pkg string) slog.Level {
  41. t.mut.RLock()
  42. defer t.mut.RUnlock()
  43. if level, ok := t.levels[pkg]; ok {
  44. return level
  45. }
  46. return t.defLevel
  47. }
  48. func (t *levelTracker) Set(pkg string, level slog.Level) {
  49. t.mut.Lock()
  50. changed := t.levels[pkg] != level
  51. t.levels[pkg] = level
  52. t.mut.Unlock()
  53. if changed {
  54. slog.Info("Changed package log level", "package", pkg, "level", level)
  55. }
  56. }
  57. func (t *levelTracker) SetDefault(level slog.Level) {
  58. t.mut.Lock()
  59. changed := t.defLevel != level
  60. t.defLevel = level
  61. t.mut.Unlock()
  62. if changed {
  63. slog.Info("Changed default log level", "level", level)
  64. }
  65. }
  66. func (t *levelTracker) SetDescr(pkg, descr string) {
  67. t.mut.Lock()
  68. t.descrs[pkg] = descr
  69. t.mut.Unlock()
  70. }
  71. func (t *levelTracker) Descrs() map[string]string {
  72. t.mut.RLock()
  73. defer t.mut.RUnlock()
  74. m := make(map[string]string, len(t.descrs))
  75. maps.Copy(m, t.descrs)
  76. return m
  77. }
  78. func (t *levelTracker) Levels() map[string]slog.Level {
  79. t.mut.RLock()
  80. defer t.mut.RUnlock()
  81. m := make(map[string]slog.Level, len(t.descrs))
  82. for pkg := range t.descrs {
  83. if level, ok := t.levels[pkg]; ok {
  84. m[pkg] = level
  85. } else {
  86. m[pkg] = t.defLevel
  87. }
  88. }
  89. return m
  90. }