1
19
20 package log
21
22 import (
23 "errors"
24 goflag "flag"
25 "fmt"
26 "io"
27 "os"
28 "path/filepath"
29 "runtime"
30 "strconv"
31 "strings"
32 "sync"
33 "time"
34
35 klog "github.com/go-kit/kit/log"
36 "github.com/golang/glog"
37 flag "github.com/spf13/pflag"
38 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
39 k8sruntime "k8s.io/apimachinery/pkg/runtime"
40 )
41
42 const (
43 libvirtTimestampFormat = "2006-01-02 15:04:05.999-0700"
44 logTimestampFormat = "2006-01-02T15:04:05.000000Z"
45 )
46
47 type LogLevel int32
48
49 const (
50 INFO LogLevel = iota
51 WARNING
52 ERROR
53 FATAL
54 )
55
56 var LogLevelNames = map[LogLevel]string{
57 INFO: "info",
58 WARNING: "warning",
59 ERROR: "error",
60 FATAL: "fatal",
61 }
62
63 var lock sync.Mutex
64
65 type LoggableObject interface {
66 metav1.ObjectMetaAccessor
67 k8sruntime.Object
68 }
69
70 type FilteredVerbosityLogger struct {
71 filteredLogger FilteredLogger
72 }
73
74 type FilteredLogger struct {
75 logger klog.Logger
76 component string
77 filterLevel LogLevel
78 currentLogLevel LogLevel
79 verbosityLevel int
80 currentVerbosityLevel int
81 err error
82 }
83
84 var Log = DefaultLogger()
85
86 func InitializeLogging(comp string) {
87 defaultComponent = comp
88 Log = DefaultLogger()
89 glog.CopyStandardLogTo(LogLevelNames[INFO])
90 goflag.CommandLine.Set("component", comp)
91 goflag.CommandLine.Set("logtostderr", "true")
92 }
93
94 func getDefaultVerbosity() int {
95 if verbosityFlag := flag.Lookup("v"); verbosityFlag != nil {
96 defaultVerbosity, _ := strconv.Atoi(verbosityFlag.Value.String())
97 return defaultVerbosity
98 } else {
99
100
101 return 2
102 }
103 }
104
105
106 func MakeLogger(logger klog.Logger) *FilteredLogger {
107 defaultLogLevel := INFO
108
109 defaultVerbosity = getDefaultVerbosity()
110
111 defaultCurrentVerbosity := 2
112
113 return &FilteredLogger{
114 logger: logger,
115 component: defaultComponent,
116 filterLevel: defaultLogLevel,
117 currentLogLevel: defaultLogLevel,
118 verbosityLevel: defaultVerbosity,
119 currentVerbosityLevel: defaultCurrentVerbosity,
120 }
121 }
122
123 type NullLogger struct{}
124
125 func (n NullLogger) Log(params ...interface{}) error { return nil }
126
127 var loggers = make(map[string]*FilteredLogger)
128 var defaultComponent = ""
129 var defaultVerbosity = 0
130
131 func createLogger(component string) {
132 lock.Lock()
133 defer lock.Unlock()
134 _, ok := loggers[component]
135 if ok == false {
136 logger := klog.NewJSONLogger(os.Stderr)
137 log := MakeLogger(logger)
138 log.component = component
139 loggers[component] = log
140 }
141 }
142
143 func Logger(component string) *FilteredLogger {
144 _, ok := loggers[component]
145 if ok == false {
146 createLogger(component)
147 }
148 return loggers[component]
149 }
150
151 func DefaultLogger() *FilteredLogger {
152 return Logger(defaultComponent)
153 }
154
155
156
157 func (l *FilteredLogger) SetIOWriter(w io.Writer) {
158 l.logger = klog.NewJSONLogger(w)
159 goflag.CommandLine.Set("logtostderr", "false")
160 }
161
162 func (l *FilteredLogger) SetLogger(logger klog.Logger) *FilteredLogger {
163 l.logger = logger
164 return l
165 }
166
167 type LogError struct {
168 message string
169 }
170
171 func (e LogError) Error() string {
172 return e.message
173 }
174
175 func (l FilteredLogger) msg(msg interface{}) {
176 l.log(3, "msg", msg)
177 }
178
179 func (l FilteredLogger) msgf(msg string, args ...interface{}) {
180 l.log(3, "msg", fmt.Sprintf(msg, args...))
181 }
182
183 func (l FilteredLogger) Log(params ...interface{}) error {
184 return l.log(2, params...)
185 }
186
187 func (l FilteredLogger) log(skipFrames int, params ...interface{}) error {
188
189
190
191 if l.currentLogLevel >= WARNING || (l.filterLevel == INFO &&
192 (l.currentLogLevel == l.filterLevel) &&
193 (l.currentVerbosityLevel <= l.verbosityLevel)) {
194 now := time.Now().UTC()
195 _, fileName, lineNumber, _ := runtime.Caller(skipFrames)
196 logParams := make([]interface{}, 0, 8)
197
198 logParams = append(logParams,
199 "level", LogLevelNames[l.currentLogLevel],
200 "timestamp", now.Format(logTimestampFormat),
201 "pos", fmt.Sprintf("%s:%d", filepath.Base(fileName), lineNumber),
202 "component", l.component,
203 )
204 if l.err != nil {
205 l.logger = klog.With(l.logger, "reason", l.err)
206 }
207 return klog.WithPrefix(l.logger, logParams...).Log(params...)
208 }
209 return nil
210 }
211
212 func (l FilteredVerbosityLogger) Log(params ...interface{}) error {
213 return l.filteredLogger.log(2, params...)
214 }
215
216 func (l FilteredVerbosityLogger) V(level int) *FilteredVerbosityLogger {
217 if level >= 0 {
218 l.filteredLogger.currentVerbosityLevel = level
219 }
220 return &l
221 }
222
223 func (l FilteredVerbosityLogger) Info(msg string) {
224 l.filteredLogger.Level(INFO).log(2, "msg", msg)
225 }
226
227 func (l FilteredVerbosityLogger) Infof(msg string, args ...interface{}) {
228 l.filteredLogger.log(2, "msg", fmt.Sprintf(msg, args...))
229 }
230
231 func (l FilteredVerbosityLogger) Object(obj LoggableObject) *FilteredVerbosityLogger {
232 l.filteredLogger.Object(obj)
233 return &l
234 }
235
236 func (l FilteredVerbosityLogger) Reason(err error) *FilteredVerbosityLogger {
237 l.filteredLogger.err = err
238 return &l
239 }
240
241 func (l FilteredVerbosityLogger) Verbosity(level int) bool {
242 return l.filteredLogger.Verbosity(level)
243 }
244
245 func (l FilteredLogger) Object(obj LoggableObject) *FilteredLogger {
246
247 name := obj.GetObjectMeta().GetName()
248 namespace := obj.GetObjectMeta().GetNamespace()
249 uid := obj.GetObjectMeta().GetUID()
250 kind := obj.GetObjectKind().GroupVersionKind().Kind
251
252 logParams := make([]interface{}, 0)
253 if namespace != "" {
254 logParams = append(logParams, "namespace", namespace)
255 }
256 logParams = append(logParams, "name", name)
257 logParams = append(logParams, "kind", kind)
258 logParams = append(logParams, "uid", uid)
259
260 l.with(logParams...)
261 return &l
262 }
263
264 func (l FilteredLogger) With(obj ...interface{}) *FilteredLogger {
265 l.logger = klog.With(l.logger, obj...)
266 return &l
267 }
268
269 func (l *FilteredLogger) with(obj ...interface{}) *FilteredLogger {
270 l.logger = klog.With(l.logger, obj...)
271 return l
272 }
273
274 func (l *FilteredLogger) SetLogLevel(filterLevel LogLevel) error {
275 if (filterLevel >= INFO) && (filterLevel <= FATAL) {
276 l.filterLevel = filterLevel
277 return nil
278 }
279 return fmt.Errorf("Log level %d does not exist", filterLevel)
280 }
281
282 func (l *FilteredLogger) SetVerbosityLevel(level int) error {
283 if level >= 0 {
284 l.verbosityLevel = level
285 } else {
286 return errors.New("Verbosity setting must not be negative")
287 }
288 return nil
289 }
290
291
292
293 func (l FilteredLogger) V(level int) *FilteredVerbosityLogger {
294 if level >= 0 {
295 l.currentVerbosityLevel = level
296 }
297 return &FilteredVerbosityLogger{
298 filteredLogger: l,
299 }
300 }
301
302 func (l FilteredLogger) Verbosity(level int) bool {
303 return l.currentVerbosityLevel >= level
304 }
305
306 func (l FilteredLogger) Reason(err error) *FilteredLogger {
307 l.err = err
308 return &l
309 }
310
311 func (l FilteredLogger) Level(level LogLevel) *FilteredLogger {
312 l.currentLogLevel = level
313 return &l
314 }
315
316 func (l FilteredLogger) Info(msg string) {
317 l.Level(INFO).msg(msg)
318 }
319
320 func (l FilteredLogger) Infof(msg string, args ...interface{}) {
321 l.Level(INFO).msgf(msg, args...)
322 }
323
324 func (l FilteredLogger) Warning(msg string) {
325 l.Level(WARNING).msg(msg)
326 }
327
328 func (l FilteredLogger) Warningf(msg string, args ...interface{}) {
329 l.Level(WARNING).msgf(msg, args...)
330 }
331
332 func (l FilteredLogger) Error(msg string) {
333 l.Level(ERROR).msg(msg)
334 }
335
336 func (l FilteredLogger) Errorf(msg string, args ...interface{}) {
337 l.Level(ERROR).msgf(msg, args...)
338 }
339
340 func (l FilteredLogger) Critical(msg string) {
341 l.Level(FATAL).msg(msg)
342 panic(msg)
343 }
344
345 func (l FilteredLogger) Criticalf(msg string, args ...interface{}) {
346 l.Level(FATAL).msgf(msg, args...)
347 }
348
349 func LogLibvirtLogLine(logger *FilteredLogger, line string) {
350
351 if len(strings.TrimSpace(line)) == 0 {
352 return
353 }
354
355 fragments := strings.SplitN(line, ": ", 5)
356 if len(fragments) < 4 {
357 now := time.Now()
358 logger.logger.Log(
359 "level", "info",
360 "timestamp", now.Format(logTimestampFormat),
361 "component", logger.component,
362 "subcomponent", "libvirt",
363 "msg", line,
364 )
365 return
366 }
367 severity := strings.ToLower(strings.TrimSpace(fragments[2]))
368
369 if severity == "debug" {
370 severity = "info"
371 }
372
373 t, err := time.Parse(libvirtTimestampFormat, strings.TrimSpace(fragments[0]))
374 if err != nil {
375 fmt.Println(err)
376 return
377 }
378 thread := strings.TrimSpace(fragments[1])
379 pos := strings.TrimSpace(fragments[3])
380 msg := strings.TrimSpace(fragments[4])
381
382
383
384
385 if strings.Contains(msg, "unable to execute QEMU agent command") {
386 if logger.verbosityLevel < 4 {
387 return
388 }
389
390 severity = LogLevelNames[WARNING]
391 }
392
393
394 isPos := false
395 if split := strings.Split(pos, ":"); len(split) == 2 {
396 if _, err := strconv.Atoi(split[1]); err == nil {
397 isPos = true
398 }
399 }
400
401 if !isPos {
402 msg = strings.TrimSpace(fragments[3] + ": " + fragments[4])
403 logger.logger.Log(
404 "level", severity,
405 "timestamp", t.Format(logTimestampFormat),
406 "component", logger.component,
407 "subcomponent", "libvirt",
408 "thread", thread,
409 "msg", msg,
410 )
411 } else {
412 logger.logger.Log(
413 "level", severity,
414 "timestamp", t.Format(logTimestampFormat),
415 "pos", pos,
416 "component", logger.component,
417 "subcomponent", "libvirt",
418 "thread", thread,
419 "msg", msg,
420 )
421 }
422 }
423
424 var qemuLogLines = ""
425
426 func LogQemuLogLine(logger *FilteredLogger, line string) {
427
428 if len(strings.TrimSpace(line)) == 0 {
429 return
430 }
431
432
433 if strings.HasSuffix(line, "\\") {
434 qemuLogLines += line
435 return
436 }
437
438 if len(qemuLogLines) > 0 {
439 line = qemuLogLines + line
440 qemuLogLines = ""
441 }
442
443 now := time.Now()
444 logger.logger.Log(
445 "level", "info",
446 "timestamp", now.Format(logTimestampFormat),
447 "component", logger.component,
448 "subcomponent", "qemu",
449 "msg", line,
450 )
451 }
452
View as plain text