forked from viamrobotics/rdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlogger_registry.go
163 lines (145 loc) · 4.25 KB
/
logger_registry.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package logging
import (
"fmt"
"regexp"
"sync"
"sync/atomic"
)
// Registry is a registry of loggers. It is stored on a logger, and holds a map
// of known subloggers (`loggers`) and a slice of configuration objects
// (`logConfig`).
type Registry struct {
mu sync.RWMutex
loggers map[string]Logger
logConfig []LoggerPatternConfig
// DeduplicateLogs controls whether to deduplicate logs. Slightly odd to store this on
// the registry but preferable to having a global atomic.
DeduplicateLogs atomic.Bool
}
func newRegistry() *Registry {
return &Registry{
loggers: make(map[string]Logger),
}
}
func (lr *Registry) registerLogger(name string, logger Logger) {
lr.mu.Lock()
defer lr.mu.Unlock()
lr.loggers[name] = logger
}
func (lr *Registry) loggerNamed(name string) (logger Logger, ok bool) {
lr.mu.RLock()
defer lr.mu.RUnlock()
logger, ok = lr.loggers[name]
return
}
func (lr *Registry) updateLoggerLevel(name string, level Level) error {
lr.mu.RLock()
defer lr.mu.RUnlock()
logger, ok := lr.loggers[name]
if !ok {
return fmt.Errorf("logger named %s not recognized", name)
}
logger.SetLevel(level)
return nil
}
// Update updates the logger registry with the passed in `logConfig`. Invalid patterns
// are warn-logged through the warnLogger.
func (lr *Registry) Update(logConfig []LoggerPatternConfig, warnLogger Logger) error {
lr.mu.Lock()
lr.logConfig = logConfig
lr.mu.Unlock()
appliedConfigs := make(map[string]Level)
for _, lpc := range logConfig {
if !validatePattern(lpc.Pattern) {
warnLogger.Warnw("failed to validate a pattern", "pattern", lpc.Pattern)
continue
}
r, err := regexp.Compile(buildRegexFromPattern(lpc.Pattern))
if err != nil {
return err
}
for _, name := range lr.getRegisteredLoggerNames() {
if r.MatchString(name) {
level, err := LevelFromString(lpc.Level)
if err != nil {
return err
}
appliedConfigs[name] = level
}
}
}
for _, name := range lr.getRegisteredLoggerNames() {
level, ok := appliedConfigs[name]
if !ok {
// If no config was applied; return logger to level of passed in
// warnLogger. Idea being that if _no_ config applies to logger
// anymore, warnLogger should be the logger from entrypoint and
// therefore the highest in the tree of loggers.
level = warnLogger.GetLevel()
}
err := lr.updateLoggerLevel(name, level)
if err != nil {
return err
}
}
return nil
}
func (lr *Registry) getRegisteredLoggerNames() []string {
lr.mu.RLock()
defer lr.mu.RUnlock()
registeredNames := make([]string, 0, len(lr.loggers))
for name := range lr.loggers {
registeredNames = append(registeredNames, name)
}
return registeredNames
}
// GetCurrentConfig gets the current config.
func (lr *Registry) GetCurrentConfig() []LoggerPatternConfig {
lr.mu.RLock()
defer lr.mu.RUnlock()
return lr.logConfig
}
// AddAppenderToAll adds the specified appender to all loggers in the registry.
func (lr *Registry) AddAppenderToAll(appender Appender) {
lr.mu.RLock()
defer lr.mu.RUnlock()
for _, logger := range lr.loggers {
logger.AddAppender(appender)
}
}
// getOrRegister will either:
// - return an existing logger for the input logger `name` or
// - register the input `logger` for the given logger `name` and configure it based on the
// existing patterns.
//
// Such that if concurrent callers try registering the same logger, the "winner"s logger will be
// registered and all losers will return the winning logger.
//
// It is expected in racing scenarios that all callers are trying to register behavioral equivalent
// `logger` objects.
func (lr *Registry) getOrRegister(name string, logger Logger) Logger {
lr.mu.Lock()
defer lr.mu.Unlock()
if existingLogger, ok := lr.loggers[name]; ok {
return existingLogger
}
lr.loggers[name] = logger
for _, lpc := range lr.logConfig {
r, err := regexp.Compile(buildRegexFromPattern(lpc.Pattern))
if err != nil {
// Can ignore error here; invalid pattern will already have been
// warn-logged as part of config reading.
continue
}
if r.MatchString(name) {
level, err := LevelFromString(lpc.Level)
if err != nil {
// Can ignore error here; invalid level will already have been
// warn-logged as part of config reading.
continue
}
logger.SetLevel(level)
}
}
return logger
}