-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathregistry.go
133 lines (113 loc) · 3.19 KB
/
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
package log
import (
"sort"
"sync"
"github.com/dyweb/gommon/util/runtimeutil"
)
// registry.go is used for maintain relationship between loggers across packages and projects
// it also contains util func for traverse registry and logger
var globalRegistryGroup = newRegistryGroup()
type registryGroup struct {
// we didn't use RWMutex because when walking a group, the main purpose is to modify loggers
// inside registries, so two walking should not happens in parallel
mu sync.Mutex
registries map[string]*Registry
}
func newRegistryGroup() *registryGroup {
return ®istryGroup{
registries: make(map[string]*Registry),
}
}
func (rg *registryGroup) add(reg *Registry) {
rg.mu.Lock()
defer rg.mu.Unlock()
id := reg.identity
if id == "" {
panic("log registry identity is empty")
}
oReg, ok := rg.registries[id]
if ok {
if oReg == reg {
return
} else {
panic("log registry is already registered for " + id)
}
}
rg.registries[id] = reg
}
// Registry contains default and tracked loggers, it is per package
type Registry struct {
mu sync.Mutex
loggers []*Logger
// identity is a string for package
identity string
}
// NewRegistry create a log registry with a default logger for a package.
// It registers itself in globalRegistryGroup so it can be updated later using WalkRegistries
func NewRegistry() *Registry {
frame := runtimeutil.GetCallerFrame(1)
pkg, _ := runtimeutil.SplitPackageFunc(frame.Function)
reg := Registry{
identity: pkg,
loggers: []*Logger{newPackageLoggerWithSkip(1)},
}
globalRegistryGroup.add(®)
return ®
}
func (r *Registry) Identity() string {
return r.identity
}
// Logger returns the default logger in registry
func (r *Registry) Logger() *Logger {
if len(r.loggers) < 1 {
panic("no default logger found in registry")
}
return r.loggers[0]
}
// NewLogger creates a logger based on default logger and register it in registry.
// It should be used sparingly, if you need to add more fields as context for a func,
// you should make copy from default logger using methods like TODO: WithFields?
// to avoid register them in registry
func (r *Registry) NewLogger() *Logger {
// no lock is added because addLogger also acquire lock
id := newIdentityFromCaller(1)
l := copyOrCreateLogger(r.Logger(), &id)
r.addLogger(l)
return l
}
// addLogger registers a logger into registry. It's a nop if the logger is already there
func (r *Registry) addLogger(l *Logger) {
r.mu.Lock()
defer r.mu.Unlock()
for _, ol := range r.loggers {
if ol == l {
return
}
}
r.loggers = append(r.loggers, l)
}
// WalkRegistry walks registry in globalRegistryGroup in sorted order of id (package path)
func WalkRegistry(cb func(r *Registry)) {
group := globalRegistryGroup
group.mu.Lock()
defer group.mu.Unlock()
// visit in the order of sorted id
var ids []string
for id := range group.registries {
ids = append(ids, id)
}
sort.Strings(ids)
for _, id := range ids {
cb(group.registries[id])
}
}
// WalkLogger calls WalkRegistry and within each registry, walk in insert order of loggers
func WalkLogger(cb func(l *Logger)) {
WalkRegistry(func(r *Registry) {
r.mu.Lock()
defer r.mu.Unlock()
for _, l := range r.loggers {
cb(l)
}
})
}