...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package promlog
18
19 import (
20 "fmt"
21 "os"
22 "sync"
23 "time"
24
25 "github.com/go-kit/log"
26 "github.com/go-kit/log/level"
27 )
28
29 var (
30
31
32
33 timestampFormat = log.TimestampFormat(
34 func() time.Time { return time.Now().UTC() },
35 "2006-01-02T15:04:05.000Z07:00",
36 )
37
38 LevelFlagOptions = []string{"debug", "info", "warn", "error"}
39 FormatFlagOptions = []string{"logfmt", "json"}
40 )
41
42
43
44 type AllowedLevel struct {
45 s string
46 o level.Option
47 }
48
49 func (l *AllowedLevel) UnmarshalYAML(unmarshal func(interface{}) error) error {
50 var s string
51 type plain string
52 if err := unmarshal((*plain)(&s)); err != nil {
53 return err
54 }
55 if s == "" {
56 return nil
57 }
58 lo := &AllowedLevel{}
59 if err := lo.Set(s); err != nil {
60 return err
61 }
62 *l = *lo
63 return nil
64 }
65
66 func (l *AllowedLevel) String() string {
67 return l.s
68 }
69
70
71 func (l *AllowedLevel) Set(s string) error {
72 switch s {
73 case "debug":
74 l.o = level.AllowDebug()
75 case "info":
76 l.o = level.AllowInfo()
77 case "warn":
78 l.o = level.AllowWarn()
79 case "error":
80 l.o = level.AllowError()
81 default:
82 return fmt.Errorf("unrecognized log level %q", s)
83 }
84 l.s = s
85 return nil
86 }
87
88
89 type AllowedFormat struct {
90 s string
91 }
92
93 func (f *AllowedFormat) String() string {
94 return f.s
95 }
96
97
98 func (f *AllowedFormat) Set(s string) error {
99 switch s {
100 case "logfmt", "json":
101 f.s = s
102 default:
103 return fmt.Errorf("unrecognized log format %q", s)
104 }
105 return nil
106 }
107
108
109 type Config struct {
110 Level *AllowedLevel
111 Format *AllowedFormat
112 }
113
114
115
116 func New(config *Config) log.Logger {
117 if config.Format != nil && config.Format.s == "json" {
118 return NewWithLogger(log.NewJSONLogger(log.NewSyncWriter(os.Stderr)), config)
119 }
120
121 return NewWithLogger(log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)), config)
122 }
123
124
125
126 func NewWithLogger(l log.Logger, config *Config) log.Logger {
127 if config.Level != nil {
128 l = log.With(l, "ts", timestampFormat, "caller", log.Caller(5))
129 l = level.NewFilter(l, config.Level.o)
130 } else {
131 l = log.With(l, "ts", timestampFormat, "caller", log.DefaultCaller)
132 }
133 return l
134 }
135
136
137
138
139 func NewDynamic(config *Config) *logger {
140 if config.Format != nil && config.Format.s == "json" {
141 return NewDynamicWithLogger(log.NewJSONLogger(log.NewSyncWriter(os.Stderr)), config)
142 }
143
144 return NewDynamicWithLogger(log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)), config)
145 }
146
147
148
149
150 func NewDynamicWithLogger(l log.Logger, config *Config) *logger {
151 lo := &logger{
152 base: l,
153 leveled: l,
154 }
155
156 if config.Level != nil {
157 lo.SetLevel(config.Level)
158 }
159
160 return lo
161 }
162
163 type logger struct {
164 base log.Logger
165 leveled log.Logger
166 currentLevel *AllowedLevel
167 mtx sync.Mutex
168 }
169
170
171 func (l *logger) Log(keyvals ...interface{}) error {
172 l.mtx.Lock()
173 defer l.mtx.Unlock()
174 return l.leveled.Log(keyvals...)
175 }
176
177
178 func (l *logger) SetLevel(lvl *AllowedLevel) {
179 l.mtx.Lock()
180 defer l.mtx.Unlock()
181 if lvl == nil {
182 l.leveled = log.With(l.base, "ts", timestampFormat, "caller", log.DefaultCaller)
183 l.currentLevel = nil
184 return
185 }
186
187 if l.currentLevel != nil && l.currentLevel.s != lvl.s {
188 _ = l.base.Log("msg", "Log level changed", "prev", l.currentLevel, "current", lvl)
189 }
190 l.currentLevel = lvl
191 l.leveled = level.NewFilter(log.With(l.base, "ts", timestampFormat, "caller", log.Caller(5)), lvl.o)
192 }
193
View as plain text