blob: ae2b861382b135b7037fb6935394c0233fdb0858 [file] [log] [blame]
Matteo Scandoloa4285862020-12-01 18:10:10 -08001// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
2//
3// Copyright 2013 Google Inc. All Rights Reserved.
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17// Package klog implements logging analogous to the Google-internal C++ INFO/ERROR/V setup.
18// It provides functions Info, Warning, Error, Fatal, plus formatting variants such as
19// Infof. It also provides V-style logging controlled by the -v and -vmodule=file=2 flags.
20//
21// Basic examples:
22//
23// klog.Info("Prepare to repel boarders")
24//
25// klog.Fatalf("Initialization failed: %s", err)
26//
27// See the documentation for the V function for an explanation of these examples:
28//
29// if klog.V(2) {
30// klog.Info("Starting transaction...")
31// }
32//
33// klog.V(2).Infoln("Processed", nItems, "elements")
34//
35// Log output is buffered and written periodically using Flush. Programs
36// should call Flush before exiting to guarantee all log output is written.
37//
38// By default, all log statements write to standard error.
39// This package provides several flags that modify this behavior.
40// As a result, flag.Parse must be called before any logging is done.
41//
42// -logtostderr=true
43// Logs are written to standard error instead of to files.
44// -alsologtostderr=false
45// Logs are written to standard error as well as to files.
46// -stderrthreshold=ERROR
47// Log events at or above this severity are logged to standard
48// error as well as to files.
49// -log_dir=""
50// Log files will be written to this directory instead of the
51// default temporary directory.
52//
53// Other flags provide aids to debugging.
54//
55// -log_backtrace_at=""
56// When set to a file and line number holding a logging statement,
57// such as
58// -log_backtrace_at=gopherflakes.go:234
59// a stack trace will be written to the Info log whenever execution
60// hits that statement. (Unlike with -vmodule, the ".go" must be
61// present.)
62// -v=0
63// Enable V-leveled logging at the specified level.
64// -vmodule=""
65// The syntax of the argument is a comma-separated list of pattern=N,
66// where pattern is a literal file name (minus the ".go" suffix) or
67// "glob" pattern and N is a V level. For instance,
68// -vmodule=gopher*=3
69// sets the V level to 3 in all Go files whose names begin "gopher".
70//
71package klog
72
73import (
74 "bufio"
75 "bytes"
76 "errors"
77 "flag"
78 "fmt"
79 "io"
80 stdLog "log"
81 "math"
82 "os"
83 "path/filepath"
84 "runtime"
85 "strconv"
86 "strings"
87 "sync"
88 "sync/atomic"
89 "time"
90
91 "github.com/go-logr/logr"
92)
93
94// severity identifies the sort of log: info, warning etc. It also implements
95// the flag.Value interface. The -stderrthreshold flag is of type severity and
96// should be modified only through the flag.Value interface. The values match
97// the corresponding constants in C++.
98type severity int32 // sync/atomic int32
99
100// These constants identify the log levels in order of increasing severity.
101// A message written to a high-severity log file is also written to each
102// lower-severity log file.
103const (
104 infoLog severity = iota
105 warningLog
106 errorLog
107 fatalLog
108 numSeverity = 4
109)
110
111const severityChar = "IWEF"
112
113var severityName = []string{
114 infoLog: "INFO",
115 warningLog: "WARNING",
116 errorLog: "ERROR",
117 fatalLog: "FATAL",
118}
119
120// get returns the value of the severity.
121func (s *severity) get() severity {
122 return severity(atomic.LoadInt32((*int32)(s)))
123}
124
125// set sets the value of the severity.
126func (s *severity) set(val severity) {
127 atomic.StoreInt32((*int32)(s), int32(val))
128}
129
130// String is part of the flag.Value interface.
131func (s *severity) String() string {
132 return strconv.FormatInt(int64(*s), 10)
133}
134
135// Get is part of the flag.Getter interface.
136func (s *severity) Get() interface{} {
137 return *s
138}
139
140// Set is part of the flag.Value interface.
141func (s *severity) Set(value string) error {
142 var threshold severity
143 // Is it a known name?
144 if v, ok := severityByName(value); ok {
145 threshold = v
146 } else {
147 v, err := strconv.ParseInt(value, 10, 32)
148 if err != nil {
149 return err
150 }
151 threshold = severity(v)
152 }
153 logging.stderrThreshold.set(threshold)
154 return nil
155}
156
157func severityByName(s string) (severity, bool) {
158 s = strings.ToUpper(s)
159 for i, name := range severityName {
160 if name == s {
161 return severity(i), true
162 }
163 }
164 return 0, false
165}
166
167// OutputStats tracks the number of output lines and bytes written.
168type OutputStats struct {
169 lines int64
170 bytes int64
171}
172
173// Lines returns the number of lines written.
174func (s *OutputStats) Lines() int64 {
175 return atomic.LoadInt64(&s.lines)
176}
177
178// Bytes returns the number of bytes written.
179func (s *OutputStats) Bytes() int64 {
180 return atomic.LoadInt64(&s.bytes)
181}
182
183// Stats tracks the number of lines of output and number of bytes
184// per severity level. Values must be read with atomic.LoadInt64.
185var Stats struct {
186 Info, Warning, Error OutputStats
187}
188
189var severityStats = [numSeverity]*OutputStats{
190 infoLog: &Stats.Info,
191 warningLog: &Stats.Warning,
192 errorLog: &Stats.Error,
193}
194
195// Level is exported because it appears in the arguments to V and is
196// the type of the v flag, which can be set programmatically.
197// It's a distinct type because we want to discriminate it from logType.
198// Variables of type level are only changed under logging.mu.
199// The -v flag is read only with atomic ops, so the state of the logging
200// module is consistent.
201
202// Level is treated as a sync/atomic int32.
203
204// Level specifies a level of verbosity for V logs. *Level implements
205// flag.Value; the -v flag is of type Level and should be modified
206// only through the flag.Value interface.
207type Level int32
208
209// get returns the value of the Level.
210func (l *Level) get() Level {
211 return Level(atomic.LoadInt32((*int32)(l)))
212}
213
214// set sets the value of the Level.
215func (l *Level) set(val Level) {
216 atomic.StoreInt32((*int32)(l), int32(val))
217}
218
219// String is part of the flag.Value interface.
220func (l *Level) String() string {
221 return strconv.FormatInt(int64(*l), 10)
222}
223
224// Get is part of the flag.Getter interface.
225func (l *Level) Get() interface{} {
226 return *l
227}
228
229// Set is part of the flag.Value interface.
230func (l *Level) Set(value string) error {
231 v, err := strconv.ParseInt(value, 10, 32)
232 if err != nil {
233 return err
234 }
235 logging.mu.Lock()
236 defer logging.mu.Unlock()
237 logging.setVState(Level(v), logging.vmodule.filter, false)
238 return nil
239}
240
241// moduleSpec represents the setting of the -vmodule flag.
242type moduleSpec struct {
243 filter []modulePat
244}
245
246// modulePat contains a filter for the -vmodule flag.
247// It holds a verbosity level and a file pattern to match.
248type modulePat struct {
249 pattern string
250 literal bool // The pattern is a literal string
251 level Level
252}
253
254// match reports whether the file matches the pattern. It uses a string
255// comparison if the pattern contains no metacharacters.
256func (m *modulePat) match(file string) bool {
257 if m.literal {
258 return file == m.pattern
259 }
260 match, _ := filepath.Match(m.pattern, file)
261 return match
262}
263
264func (m *moduleSpec) String() string {
265 // Lock because the type is not atomic. TODO: clean this up.
266 logging.mu.Lock()
267 defer logging.mu.Unlock()
268 var b bytes.Buffer
269 for i, f := range m.filter {
270 if i > 0 {
271 b.WriteRune(',')
272 }
273 fmt.Fprintf(&b, "%s=%d", f.pattern, f.level)
274 }
275 return b.String()
276}
277
278// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the
279// struct is not exported.
280func (m *moduleSpec) Get() interface{} {
281 return nil
282}
283
284var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of filename=N")
285
286// Syntax: -vmodule=recordio=2,file=1,gfs*=3
287func (m *moduleSpec) Set(value string) error {
288 var filter []modulePat
289 for _, pat := range strings.Split(value, ",") {
290 if len(pat) == 0 {
291 // Empty strings such as from a trailing comma can be ignored.
292 continue
293 }
294 patLev := strings.Split(pat, "=")
295 if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 {
296 return errVmoduleSyntax
297 }
298 pattern := patLev[0]
299 v, err := strconv.ParseInt(patLev[1], 10, 32)
300 if err != nil {
301 return errors.New("syntax error: expect comma-separated list of filename=N")
302 }
303 if v < 0 {
304 return errors.New("negative value for vmodule level")
305 }
306 if v == 0 {
307 continue // Ignore. It's harmless but no point in paying the overhead.
308 }
309 // TODO: check syntax of filter?
310 filter = append(filter, modulePat{pattern, isLiteral(pattern), Level(v)})
311 }
312 logging.mu.Lock()
313 defer logging.mu.Unlock()
314 logging.setVState(logging.verbosity, filter, true)
315 return nil
316}
317
318// isLiteral reports whether the pattern is a literal string, that is, has no metacharacters
319// that require filepath.Match to be called to match the pattern.
320func isLiteral(pattern string) bool {
321 return !strings.ContainsAny(pattern, `\*?[]`)
322}
323
324// traceLocation represents the setting of the -log_backtrace_at flag.
325type traceLocation struct {
326 file string
327 line int
328}
329
330// isSet reports whether the trace location has been specified.
331// logging.mu is held.
332func (t *traceLocation) isSet() bool {
333 return t.line > 0
334}
335
336// match reports whether the specified file and line matches the trace location.
337// The argument file name is the full path, not the basename specified in the flag.
338// logging.mu is held.
339func (t *traceLocation) match(file string, line int) bool {
340 if t.line != line {
341 return false
342 }
343 if i := strings.LastIndex(file, "/"); i >= 0 {
344 file = file[i+1:]
345 }
346 return t.file == file
347}
348
349func (t *traceLocation) String() string {
350 // Lock because the type is not atomic. TODO: clean this up.
351 logging.mu.Lock()
352 defer logging.mu.Unlock()
353 return fmt.Sprintf("%s:%d", t.file, t.line)
354}
355
356// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the
357// struct is not exported
358func (t *traceLocation) Get() interface{} {
359 return nil
360}
361
362var errTraceSyntax = errors.New("syntax error: expect file.go:234")
363
364// Syntax: -log_backtrace_at=gopherflakes.go:234
365// Note that unlike vmodule the file extension is included here.
366func (t *traceLocation) Set(value string) error {
367 if value == "" {
368 // Unset.
369 logging.mu.Lock()
370 defer logging.mu.Unlock()
371 t.line = 0
372 t.file = ""
373 return nil
374 }
375 fields := strings.Split(value, ":")
376 if len(fields) != 2 {
377 return errTraceSyntax
378 }
379 file, line := fields[0], fields[1]
380 if !strings.Contains(file, ".") {
381 return errTraceSyntax
382 }
383 v, err := strconv.Atoi(line)
384 if err != nil {
385 return errTraceSyntax
386 }
387 if v <= 0 {
388 return errors.New("negative or zero value for level")
389 }
390 logging.mu.Lock()
391 defer logging.mu.Unlock()
392 t.line = v
393 t.file = file
394 return nil
395}
396
397// flushSyncWriter is the interface satisfied by logging destinations.
398type flushSyncWriter interface {
399 Flush() error
400 Sync() error
401 io.Writer
402}
403
404// init sets up the defaults and runs flushDaemon.
405func init() {
406 logging.stderrThreshold = errorLog // Default stderrThreshold is ERROR.
407 logging.setVState(0, nil, false)
408 logging.logDir = ""
409 logging.logFile = ""
410 logging.logFileMaxSizeMB = 1800
411 logging.toStderr = true
412 logging.alsoToStderr = false
413 logging.skipHeaders = false
414 logging.addDirHeader = false
415 logging.skipLogHeaders = false
416 go logging.flushDaemon()
417}
418
419// InitFlags is for explicitly initializing the flags.
420func InitFlags(flagset *flag.FlagSet) {
421 if flagset == nil {
422 flagset = flag.CommandLine
423 }
424
425 flagset.StringVar(&logging.logDir, "log_dir", logging.logDir, "If non-empty, write log files in this directory")
426 flagset.StringVar(&logging.logFile, "log_file", logging.logFile, "If non-empty, use this log file")
427 flagset.Uint64Var(&logging.logFileMaxSizeMB, "log_file_max_size", logging.logFileMaxSizeMB,
428 "Defines the maximum size a log file can grow to. Unit is megabytes. "+
429 "If the value is 0, the maximum file size is unlimited.")
430 flagset.BoolVar(&logging.toStderr, "logtostderr", logging.toStderr, "log to standard error instead of files")
431 flagset.BoolVar(&logging.alsoToStderr, "alsologtostderr", logging.alsoToStderr, "log to standard error as well as files")
432 flagset.Var(&logging.verbosity, "v", "number for the log level verbosity")
433 flagset.BoolVar(&logging.addDirHeader, "add_dir_header", logging.addDirHeader, "If true, adds the file directory to the header of the log messages")
434 flagset.BoolVar(&logging.skipHeaders, "skip_headers", logging.skipHeaders, "If true, avoid header prefixes in the log messages")
435 flagset.BoolVar(&logging.skipLogHeaders, "skip_log_headers", logging.skipLogHeaders, "If true, avoid headers when opening log files")
436 flagset.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr")
437 flagset.Var(&logging.vmodule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging")
438 flagset.Var(&logging.traceLocation, "log_backtrace_at", "when logging hits line file:N, emit a stack trace")
439}
440
441// Flush flushes all pending log I/O.
442func Flush() {
443 logging.lockAndFlushAll()
444}
445
446// loggingT collects all the global state of the logging setup.
447type loggingT struct {
448 // Boolean flags. Not handled atomically because the flag.Value interface
449 // does not let us avoid the =true, and that shorthand is necessary for
450 // compatibility. TODO: does this matter enough to fix? Seems unlikely.
451 toStderr bool // The -logtostderr flag.
452 alsoToStderr bool // The -alsologtostderr flag.
453
454 // Level flag. Handled atomically.
455 stderrThreshold severity // The -stderrthreshold flag.
456
457 // freeList is a list of byte buffers, maintained under freeListMu.
458 freeList *buffer
459 // freeListMu maintains the free list. It is separate from the main mutex
460 // so buffers can be grabbed and printed to without holding the main lock,
461 // for better parallelization.
462 freeListMu sync.Mutex
463
464 // mu protects the remaining elements of this structure and is
465 // used to synchronize logging.
466 mu sync.Mutex
467 // file holds writer for each of the log types.
468 file [numSeverity]flushSyncWriter
469 // pcs is used in V to avoid an allocation when computing the caller's PC.
470 pcs [1]uintptr
471 // vmap is a cache of the V Level for each V() call site, identified by PC.
472 // It is wiped whenever the vmodule flag changes state.
473 vmap map[uintptr]Level
474 // filterLength stores the length of the vmodule filter chain. If greater
475 // than zero, it means vmodule is enabled. It may be read safely
476 // using sync.LoadInt32, but is only modified under mu.
477 filterLength int32
478 // traceLocation is the state of the -log_backtrace_at flag.
479 traceLocation traceLocation
480 // These flags are modified only under lock, although verbosity may be fetched
481 // safely using atomic.LoadInt32.
482 vmodule moduleSpec // The state of the -vmodule flag.
483 verbosity Level // V logging level, the value of the -v flag/
484
485 // If non-empty, overrides the choice of directory in which to write logs.
486 // See createLogDirs for the full list of possible destinations.
487 logDir string
488
489 // If non-empty, specifies the path of the file to write logs. mutually exclusive
490 // with the log_dir option.
491 logFile string
492
493 // When logFile is specified, this limiter makes sure the logFile won't exceeds a certain size. When exceeds, the
494 // logFile will be cleaned up. If this value is 0, no size limitation will be applied to logFile.
495 logFileMaxSizeMB uint64
496
497 // If true, do not add the prefix headers, useful when used with SetOutput
498 skipHeaders bool
499
500 // If true, do not add the headers to log files
501 skipLogHeaders bool
502
503 // If true, add the file directory to the header
504 addDirHeader bool
505
506 // If set, all output will be redirected unconditionally to the provided logr.Logger
507 logr logr.Logger
508}
509
510// buffer holds a byte Buffer for reuse. The zero value is ready for use.
511type buffer struct {
512 bytes.Buffer
513 tmp [64]byte // temporary byte array for creating headers.
514 next *buffer
515}
516
517var logging loggingT
518
519// setVState sets a consistent state for V logging.
520// l.mu is held.
521func (l *loggingT) setVState(verbosity Level, filter []modulePat, setFilter bool) {
522 // Turn verbosity off so V will not fire while we are in transition.
523 l.verbosity.set(0)
524 // Ditto for filter length.
525 atomic.StoreInt32(&l.filterLength, 0)
526
527 // Set the new filters and wipe the pc->Level map if the filter has changed.
528 if setFilter {
529 l.vmodule.filter = filter
530 l.vmap = make(map[uintptr]Level)
531 }
532
533 // Things are consistent now, so enable filtering and verbosity.
534 // They are enabled in order opposite to that in V.
535 atomic.StoreInt32(&l.filterLength, int32(len(filter)))
536 l.verbosity.set(verbosity)
537}
538
539// getBuffer returns a new, ready-to-use buffer.
540func (l *loggingT) getBuffer() *buffer {
541 l.freeListMu.Lock()
542 b := l.freeList
543 if b != nil {
544 l.freeList = b.next
545 }
546 l.freeListMu.Unlock()
547 if b == nil {
548 b = new(buffer)
549 } else {
550 b.next = nil
551 b.Reset()
552 }
553 return b
554}
555
556// putBuffer returns a buffer to the free list.
557func (l *loggingT) putBuffer(b *buffer) {
558 if b.Len() >= 256 {
559 // Let big buffers die a natural death.
560 return
561 }
562 l.freeListMu.Lock()
563 b.next = l.freeList
564 l.freeList = b
565 l.freeListMu.Unlock()
566}
567
568var timeNow = time.Now // Stubbed out for testing.
569
570/*
571header formats a log header as defined by the C++ implementation.
572It returns a buffer containing the formatted header and the user's file and line number.
573The depth specifies how many stack frames above lives the source line to be identified in the log message.
574
575Log lines have this form:
576 Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg...
577where the fields are defined as follows:
578 L A single character, representing the log level (eg 'I' for INFO)
579 mm The month (zero padded; ie May is '05')
580 dd The day (zero padded)
581 hh:mm:ss.uuuuuu Time in hours, minutes and fractional seconds
582 threadid The space-padded thread ID as returned by GetTID()
583 file The file name
584 line The line number
585 msg The user-supplied message
586*/
587func (l *loggingT) header(s severity, depth int) (*buffer, string, int) {
588 _, file, line, ok := runtime.Caller(3 + depth)
589 if !ok {
590 file = "???"
591 line = 1
592 } else {
593 if slash := strings.LastIndex(file, "/"); slash >= 0 {
594 path := file
595 file = path[slash+1:]
596 if l.addDirHeader {
597 if dirsep := strings.LastIndex(path[:slash], "/"); dirsep >= 0 {
598 file = path[dirsep+1:]
599 }
600 }
601 }
602 }
603 return l.formatHeader(s, file, line), file, line
604}
605
606// formatHeader formats a log header using the provided file name and line number.
607func (l *loggingT) formatHeader(s severity, file string, line int) *buffer {
608 now := timeNow()
609 if line < 0 {
610 line = 0 // not a real line number, but acceptable to someDigits
611 }
612 if s > fatalLog {
613 s = infoLog // for safety.
614 }
615 buf := l.getBuffer()
616 if l.skipHeaders {
617 return buf
618 }
619
620 // Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand.
621 // It's worth about 3X. Fprintf is hard.
622 _, month, day := now.Date()
623 hour, minute, second := now.Clock()
624 // Lmmdd hh:mm:ss.uuuuuu threadid file:line]
625 buf.tmp[0] = severityChar[s]
626 buf.twoDigits(1, int(month))
627 buf.twoDigits(3, day)
628 buf.tmp[5] = ' '
629 buf.twoDigits(6, hour)
630 buf.tmp[8] = ':'
631 buf.twoDigits(9, minute)
632 buf.tmp[11] = ':'
633 buf.twoDigits(12, second)
634 buf.tmp[14] = '.'
635 buf.nDigits(6, 15, now.Nanosecond()/1000, '0')
636 buf.tmp[21] = ' '
637 buf.nDigits(7, 22, pid, ' ') // TODO: should be TID
638 buf.tmp[29] = ' '
639 buf.Write(buf.tmp[:30])
640 buf.WriteString(file)
641 buf.tmp[0] = ':'
642 n := buf.someDigits(1, line)
643 buf.tmp[n+1] = ']'
644 buf.tmp[n+2] = ' '
645 buf.Write(buf.tmp[:n+3])
646 return buf
647}
648
649// Some custom tiny helper functions to print the log header efficiently.
650
651const digits = "0123456789"
652
653// twoDigits formats a zero-prefixed two-digit integer at buf.tmp[i].
654func (buf *buffer) twoDigits(i, d int) {
655 buf.tmp[i+1] = digits[d%10]
656 d /= 10
657 buf.tmp[i] = digits[d%10]
658}
659
660// nDigits formats an n-digit integer at buf.tmp[i],
661// padding with pad on the left.
662// It assumes d >= 0.
663func (buf *buffer) nDigits(n, i, d int, pad byte) {
664 j := n - 1
665 for ; j >= 0 && d > 0; j-- {
666 buf.tmp[i+j] = digits[d%10]
667 d /= 10
668 }
669 for ; j >= 0; j-- {
670 buf.tmp[i+j] = pad
671 }
672}
673
674// someDigits formats a zero-prefixed variable-width integer at buf.tmp[i].
675func (buf *buffer) someDigits(i, d int) int {
676 // Print into the top, then copy down. We know there's space for at least
677 // a 10-digit number.
678 j := len(buf.tmp)
679 for {
680 j--
681 buf.tmp[j] = digits[d%10]
682 d /= 10
683 if d == 0 {
684 break
685 }
686 }
687 return copy(buf.tmp[i:], buf.tmp[j:])
688}
689
690func (l *loggingT) println(s severity, logr logr.Logger, args ...interface{}) {
691 buf, file, line := l.header(s, 0)
692 // if logr is set, we clear the generated header as we rely on the backing
693 // logr implementation to print headers
694 if logr != nil {
695 l.putBuffer(buf)
696 buf = l.getBuffer()
697 }
698 fmt.Fprintln(buf, args...)
699 l.output(s, logr, buf, file, line, false)
700}
701
702func (l *loggingT) print(s severity, logr logr.Logger, args ...interface{}) {
703 l.printDepth(s, logr, 1, args...)
704}
705
706func (l *loggingT) printDepth(s severity, logr logr.Logger, depth int, args ...interface{}) {
707 buf, file, line := l.header(s, depth)
708 // if logr is set, we clear the generated header as we rely on the backing
709 // logr implementation to print headers
710 if logr != nil {
711 l.putBuffer(buf)
712 buf = l.getBuffer()
713 }
714 fmt.Fprint(buf, args...)
715 if buf.Bytes()[buf.Len()-1] != '\n' {
716 buf.WriteByte('\n')
717 }
718 l.output(s, logr, buf, file, line, false)
719}
720
721func (l *loggingT) printf(s severity, logr logr.Logger, format string, args ...interface{}) {
722 buf, file, line := l.header(s, 0)
723 // if logr is set, we clear the generated header as we rely on the backing
724 // logr implementation to print headers
725 if logr != nil {
726 l.putBuffer(buf)
727 buf = l.getBuffer()
728 }
729 fmt.Fprintf(buf, format, args...)
730 if buf.Bytes()[buf.Len()-1] != '\n' {
731 buf.WriteByte('\n')
732 }
733 l.output(s, logr, buf, file, line, false)
734}
735
736// printWithFileLine behaves like print but uses the provided file and line number. If
737// alsoLogToStderr is true, the log message always appears on standard error; it
738// will also appear in the log file unless --logtostderr is set.
739func (l *loggingT) printWithFileLine(s severity, logr logr.Logger, file string, line int, alsoToStderr bool, args ...interface{}) {
740 buf := l.formatHeader(s, file, line)
741 // if logr is set, we clear the generated header as we rely on the backing
742 // logr implementation to print headers
743 if logr != nil {
744 l.putBuffer(buf)
745 buf = l.getBuffer()
746 }
747 fmt.Fprint(buf, args...)
748 if buf.Bytes()[buf.Len()-1] != '\n' {
749 buf.WriteByte('\n')
750 }
751 l.output(s, logr, buf, file, line, alsoToStderr)
752}
753
754// if loggr is specified, will call loggr.Error, otherwise output with logging module.
755func (l *loggingT) errorS(err error, loggr logr.Logger, msg string, keysAndValues ...interface{}) {
756 if loggr != nil {
757 loggr.Error(err, msg, keysAndValues)
758 return
759 }
760 l.printS(err, msg, keysAndValues...)
761}
762
763// if loggr is specified, will call loggr.Info, otherwise output with logging module.
764func (l *loggingT) infoS(loggr logr.Logger, msg string, keysAndValues ...interface{}) {
765 if loggr != nil {
766 loggr.Info(msg, keysAndValues)
767 return
768 }
769 l.printS(nil, msg, keysAndValues...)
770}
771
772// printS is called from infoS and errorS if loggr is not specified.
773// if err arguments is specified, will output to errorLog severity
774func (l *loggingT) printS(err error, msg string, keysAndValues ...interface{}) {
775 b := &bytes.Buffer{}
776 b.WriteString(fmt.Sprintf("%q", msg))
777 if err != nil {
778 b.WriteByte(' ')
779 b.WriteString(fmt.Sprintf("err=%q", err.Error()))
780 }
781 kvListFormat(b, keysAndValues...)
782 var s severity
783 if err == nil {
784 s = infoLog
785 } else {
786 s = errorLog
787 }
788 l.printDepth(s, logging.logr, 2, b)
789}
790
791const missingValue = "(MISSING)"
792
793func kvListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
794 for i := 0; i < len(keysAndValues); i += 2 {
795 var v interface{}
796 k := keysAndValues[i]
797 if i+1 < len(keysAndValues) {
798 v = keysAndValues[i+1]
799 } else {
800 v = missingValue
801 }
802 b.WriteByte(' ')
803
804 switch v.(type) {
805 case string, error:
806 b.WriteString(fmt.Sprintf("%s=%q", k, v))
807 default:
808 if _, ok := v.(fmt.Stringer); ok {
809 b.WriteString(fmt.Sprintf("%s=%q", k, v))
810 } else {
811 b.WriteString(fmt.Sprintf("%s=%+v", k, v))
812 }
813 }
814 }
815}
816
817// redirectBuffer is used to set an alternate destination for the logs
818type redirectBuffer struct {
819 w io.Writer
820}
821
822func (rb *redirectBuffer) Sync() error {
823 return nil
824}
825
826func (rb *redirectBuffer) Flush() error {
827 return nil
828}
829
830func (rb *redirectBuffer) Write(bytes []byte) (n int, err error) {
831 return rb.w.Write(bytes)
832}
833
834// SetLogger will set the backing logr implementation for klog.
835// If set, all log lines will be suppressed from the regular Output, and
836// redirected to the logr implementation.
837// All log lines include the 'severity', 'file' and 'line' values attached as
838// structured logging values.
839// Use as:
840// ...
841// klog.SetLogger(zapr.NewLogger(zapLog))
842func SetLogger(logr logr.Logger) {
843 logging.logr = logr
844}
845
846// SetOutput sets the output destination for all severities
847func SetOutput(w io.Writer) {
848 logging.mu.Lock()
849 defer logging.mu.Unlock()
850 for s := fatalLog; s >= infoLog; s-- {
851 rb := &redirectBuffer{
852 w: w,
853 }
854 logging.file[s] = rb
855 }
856}
857
858// SetOutputBySeverity sets the output destination for specific severity
859func SetOutputBySeverity(name string, w io.Writer) {
860 logging.mu.Lock()
861 defer logging.mu.Unlock()
862 sev, ok := severityByName(name)
863 if !ok {
864 panic(fmt.Sprintf("SetOutputBySeverity(%q): unrecognized severity name", name))
865 }
866 rb := &redirectBuffer{
867 w: w,
868 }
869 logging.file[sev] = rb
870}
871
872// LogToStderr sets whether to log exclusively to stderr, bypassing outputs
873func LogToStderr(stderr bool) {
874 logging.mu.Lock()
875 defer logging.mu.Unlock()
876
877 logging.toStderr = stderr
878}
879
880// output writes the data to the log files and releases the buffer.
881func (l *loggingT) output(s severity, log logr.Logger, buf *buffer, file string, line int, alsoToStderr bool) {
882 l.mu.Lock()
883 if l.traceLocation.isSet() {
884 if l.traceLocation.match(file, line) {
885 buf.Write(stacks(false))
886 }
887 }
888 data := buf.Bytes()
889 if log != nil {
890 // TODO: set 'severity' and caller information as structured log info
891 // keysAndValues := []interface{}{"severity", severityName[s], "file", file, "line", line}
892 if s == errorLog {
893 l.logr.Error(nil, string(data))
894 } else {
895 log.Info(string(data))
896 }
897 } else if l.toStderr {
898 os.Stderr.Write(data)
899 } else {
900 if alsoToStderr || l.alsoToStderr || s >= l.stderrThreshold.get() {
901 os.Stderr.Write(data)
902 }
903
904 if logging.logFile != "" {
905 // Since we are using a single log file, all of the items in l.file array
906 // will point to the same file, so just use one of them to write data.
907 if l.file[infoLog] == nil {
908 if err := l.createFiles(infoLog); err != nil {
909 os.Stderr.Write(data) // Make sure the message appears somewhere.
910 l.exit(err)
911 }
912 }
913 l.file[infoLog].Write(data)
914 } else {
915 if l.file[s] == nil {
916 if err := l.createFiles(s); err != nil {
917 os.Stderr.Write(data) // Make sure the message appears somewhere.
918 l.exit(err)
919 }
920 }
921
922 switch s {
923 case fatalLog:
924 l.file[fatalLog].Write(data)
925 fallthrough
926 case errorLog:
927 l.file[errorLog].Write(data)
928 fallthrough
929 case warningLog:
930 l.file[warningLog].Write(data)
931 fallthrough
932 case infoLog:
933 l.file[infoLog].Write(data)
934 }
935 }
936 }
937 if s == fatalLog {
938 // If we got here via Exit rather than Fatal, print no stacks.
939 if atomic.LoadUint32(&fatalNoStacks) > 0 {
940 l.mu.Unlock()
941 timeoutFlush(10 * time.Second)
942 os.Exit(1)
943 }
944 // Dump all goroutine stacks before exiting.
945 trace := stacks(true)
946 // Write the stack trace for all goroutines to the stderr.
947 if l.toStderr || l.alsoToStderr || s >= l.stderrThreshold.get() || alsoToStderr {
948 os.Stderr.Write(trace)
949 }
950 // Write the stack trace for all goroutines to the files.
951 logExitFunc = func(error) {} // If we get a write error, we'll still exit below.
952 for log := fatalLog; log >= infoLog; log-- {
953 if f := l.file[log]; f != nil { // Can be nil if -logtostderr is set.
954 f.Write(trace)
955 }
956 }
957 l.mu.Unlock()
958 timeoutFlush(10 * time.Second)
959 os.Exit(255) // C++ uses -1, which is silly because it's anded with 255 anyway.
960 }
961 l.putBuffer(buf)
962 l.mu.Unlock()
963 if stats := severityStats[s]; stats != nil {
964 atomic.AddInt64(&stats.lines, 1)
965 atomic.AddInt64(&stats.bytes, int64(len(data)))
966 }
967}
968
969// timeoutFlush calls Flush and returns when it completes or after timeout
970// elapses, whichever happens first. This is needed because the hooks invoked
971// by Flush may deadlock when klog.Fatal is called from a hook that holds
972// a lock.
973func timeoutFlush(timeout time.Duration) {
974 done := make(chan bool, 1)
975 go func() {
976 Flush() // calls logging.lockAndFlushAll()
977 done <- true
978 }()
979 select {
980 case <-done:
981 case <-time.After(timeout):
982 fmt.Fprintln(os.Stderr, "klog: Flush took longer than", timeout)
983 }
984}
985
986// stacks is a wrapper for runtime.Stack that attempts to recover the data for all goroutines.
987func stacks(all bool) []byte {
988 // We don't know how big the traces are, so grow a few times if they don't fit. Start large, though.
989 n := 10000
990 if all {
991 n = 100000
992 }
993 var trace []byte
994 for i := 0; i < 5; i++ {
995 trace = make([]byte, n)
996 nbytes := runtime.Stack(trace, all)
997 if nbytes < len(trace) {
998 return trace[:nbytes]
999 }
1000 n *= 2
1001 }
1002 return trace
1003}
1004
1005// logExitFunc provides a simple mechanism to override the default behavior
1006// of exiting on error. Used in testing and to guarantee we reach a required exit
1007// for fatal logs. Instead, exit could be a function rather than a method but that
1008// would make its use clumsier.
1009var logExitFunc func(error)
1010
1011// exit is called if there is trouble creating or writing log files.
1012// It flushes the logs and exits the program; there's no point in hanging around.
1013// l.mu is held.
1014func (l *loggingT) exit(err error) {
1015 fmt.Fprintf(os.Stderr, "log: exiting because of error: %s\n", err)
1016 // If logExitFunc is set, we do that instead of exiting.
1017 if logExitFunc != nil {
1018 logExitFunc(err)
1019 return
1020 }
1021 l.flushAll()
1022 os.Exit(2)
1023}
1024
1025// syncBuffer joins a bufio.Writer to its underlying file, providing access to the
1026// file's Sync method and providing a wrapper for the Write method that provides log
1027// file rotation. There are conflicting methods, so the file cannot be embedded.
1028// l.mu is held for all its methods.
1029type syncBuffer struct {
1030 logger *loggingT
1031 *bufio.Writer
1032 file *os.File
1033 sev severity
1034 nbytes uint64 // The number of bytes written to this file
1035 maxbytes uint64 // The max number of bytes this syncBuffer.file can hold before cleaning up.
1036}
1037
1038func (sb *syncBuffer) Sync() error {
1039 return sb.file.Sync()
1040}
1041
1042// CalculateMaxSize returns the real max size in bytes after considering the default max size and the flag options.
1043func CalculateMaxSize() uint64 {
1044 if logging.logFile != "" {
1045 if logging.logFileMaxSizeMB == 0 {
1046 // If logFileMaxSizeMB is zero, we don't have limitations on the log size.
1047 return math.MaxUint64
1048 }
1049 // Flag logFileMaxSizeMB is in MB for user convenience.
1050 return logging.logFileMaxSizeMB * 1024 * 1024
1051 }
1052 // If "log_file" flag is not specified, the target file (sb.file) will be cleaned up when reaches a fixed size.
1053 return MaxSize
1054}
1055
1056func (sb *syncBuffer) Write(p []byte) (n int, err error) {
1057 if sb.nbytes+uint64(len(p)) >= sb.maxbytes {
1058 if err := sb.rotateFile(time.Now(), false); err != nil {
1059 sb.logger.exit(err)
1060 }
1061 }
1062 n, err = sb.Writer.Write(p)
1063 sb.nbytes += uint64(n)
1064 if err != nil {
1065 sb.logger.exit(err)
1066 }
1067 return
1068}
1069
1070// rotateFile closes the syncBuffer's file and starts a new one.
1071// The startup argument indicates whether this is the initial startup of klog.
1072// If startup is true, existing files are opened for appending instead of truncated.
1073func (sb *syncBuffer) rotateFile(now time.Time, startup bool) error {
1074 if sb.file != nil {
1075 sb.Flush()
1076 sb.file.Close()
1077 }
1078 var err error
1079 sb.file, _, err = create(severityName[sb.sev], now, startup)
1080 sb.nbytes = 0
1081 if err != nil {
1082 return err
1083 }
1084
1085 sb.Writer = bufio.NewWriterSize(sb.file, bufferSize)
1086
1087 if sb.logger.skipLogHeaders {
1088 return nil
1089 }
1090
1091 // Write header.
1092 var buf bytes.Buffer
1093 fmt.Fprintf(&buf, "Log file created at: %s\n", now.Format("2006/01/02 15:04:05"))
1094 fmt.Fprintf(&buf, "Running on machine: %s\n", host)
1095 fmt.Fprintf(&buf, "Binary: Built with %s %s for %s/%s\n", runtime.Compiler, runtime.Version(), runtime.GOOS, runtime.GOARCH)
1096 fmt.Fprintf(&buf, "Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg\n")
1097 n, err := sb.file.Write(buf.Bytes())
1098 sb.nbytes += uint64(n)
1099 return err
1100}
1101
1102// bufferSize sizes the buffer associated with each log file. It's large
1103// so that log records can accumulate without the logging thread blocking
1104// on disk I/O. The flushDaemon will block instead.
1105const bufferSize = 256 * 1024
1106
1107// createFiles creates all the log files for severity from sev down to infoLog.
1108// l.mu is held.
1109func (l *loggingT) createFiles(sev severity) error {
1110 now := time.Now()
1111 // Files are created in decreasing severity order, so as soon as we find one
1112 // has already been created, we can stop.
1113 for s := sev; s >= infoLog && l.file[s] == nil; s-- {
1114 sb := &syncBuffer{
1115 logger: l,
1116 sev: s,
1117 maxbytes: CalculateMaxSize(),
1118 }
1119 if err := sb.rotateFile(now, true); err != nil {
1120 return err
1121 }
1122 l.file[s] = sb
1123 }
1124 return nil
1125}
1126
1127const flushInterval = 5 * time.Second
1128
1129// flushDaemon periodically flushes the log file buffers.
1130func (l *loggingT) flushDaemon() {
1131 for range time.NewTicker(flushInterval).C {
1132 l.lockAndFlushAll()
1133 }
1134}
1135
1136// lockAndFlushAll is like flushAll but locks l.mu first.
1137func (l *loggingT) lockAndFlushAll() {
1138 l.mu.Lock()
1139 l.flushAll()
1140 l.mu.Unlock()
1141}
1142
1143// flushAll flushes all the logs and attempts to "sync" their data to disk.
1144// l.mu is held.
1145func (l *loggingT) flushAll() {
1146 // Flush from fatal down, in case there's trouble flushing.
1147 for s := fatalLog; s >= infoLog; s-- {
1148 file := l.file[s]
1149 if file != nil {
1150 file.Flush() // ignore error
1151 file.Sync() // ignore error
1152 }
1153 }
1154}
1155
1156// CopyStandardLogTo arranges for messages written to the Go "log" package's
1157// default logs to also appear in the Google logs for the named and lower
1158// severities. Subsequent changes to the standard log's default output location
1159// or format may break this behavior.
1160//
1161// Valid names are "INFO", "WARNING", "ERROR", and "FATAL". If the name is not
1162// recognized, CopyStandardLogTo panics.
1163func CopyStandardLogTo(name string) {
1164 sev, ok := severityByName(name)
1165 if !ok {
1166 panic(fmt.Sprintf("log.CopyStandardLogTo(%q): unrecognized severity name", name))
1167 }
1168 // Set a log format that captures the user's file and line:
1169 // d.go:23: message
1170 stdLog.SetFlags(stdLog.Lshortfile)
1171 stdLog.SetOutput(logBridge(sev))
1172}
1173
1174// logBridge provides the Write method that enables CopyStandardLogTo to connect
1175// Go's standard logs to the logs provided by this package.
1176type logBridge severity
1177
1178// Write parses the standard logging line and passes its components to the
1179// logger for severity(lb).
1180func (lb logBridge) Write(b []byte) (n int, err error) {
1181 var (
1182 file = "???"
1183 line = 1
1184 text string
1185 )
1186 // Split "d.go:23: message" into "d.go", "23", and "message".
1187 if parts := bytes.SplitN(b, []byte{':'}, 3); len(parts) != 3 || len(parts[0]) < 1 || len(parts[2]) < 1 {
1188 text = fmt.Sprintf("bad log format: %s", b)
1189 } else {
1190 file = string(parts[0])
1191 text = string(parts[2][1:]) // skip leading space
1192 line, err = strconv.Atoi(string(parts[1]))
1193 if err != nil {
1194 text = fmt.Sprintf("bad line number: %s", b)
1195 line = 1
1196 }
1197 }
1198 // printWithFileLine with alsoToStderr=true, so standard log messages
1199 // always appear on standard error.
1200 logging.printWithFileLine(severity(lb), logging.logr, file, line, true, text)
1201 return len(b), nil
1202}
1203
1204// setV computes and remembers the V level for a given PC
1205// when vmodule is enabled.
1206// File pattern matching takes the basename of the file, stripped
1207// of its .go suffix, and uses filepath.Match, which is a little more
1208// general than the *? matching used in C++.
1209// l.mu is held.
1210func (l *loggingT) setV(pc uintptr) Level {
1211 fn := runtime.FuncForPC(pc)
1212 file, _ := fn.FileLine(pc)
1213 // The file is something like /a/b/c/d.go. We want just the d.
1214 if strings.HasSuffix(file, ".go") {
1215 file = file[:len(file)-3]
1216 }
1217 if slash := strings.LastIndex(file, "/"); slash >= 0 {
1218 file = file[slash+1:]
1219 }
1220 for _, filter := range l.vmodule.filter {
1221 if filter.match(file) {
1222 l.vmap[pc] = filter.level
1223 return filter.level
1224 }
1225 }
1226 l.vmap[pc] = 0
1227 return 0
1228}
1229
1230// Verbose is a boolean type that implements Infof (like Printf) etc.
1231// See the documentation of V for more information.
1232type Verbose struct {
1233 enabled bool
1234 logr logr.Logger
1235}
1236
1237func newVerbose(level Level, b bool) Verbose {
1238 if logging.logr == nil {
1239 return Verbose{b, nil}
1240 }
1241 return Verbose{b, logging.logr.V(int(level))}
1242}
1243
1244// V reports whether verbosity at the call site is at least the requested level.
1245// The returned value is a struct of type Verbose, which implements Info, Infoln
1246// and Infof. These methods will write to the Info log if called.
1247// Thus, one may write either
1248// if glog.V(2).Enabled() { klog.Info("log this") }
1249// or
1250// klog.V(2).Info("log this")
1251// The second form is shorter but the first is cheaper if logging is off because it does
1252// not evaluate its arguments.
1253//
1254// Whether an individual call to V generates a log record depends on the setting of
1255// the -v and -vmodule flags; both are off by default. The V call will log if its level
1256// is less than or equal to the value of the -v flag, or alternatively if its level is
1257// less than or equal to the value of the -vmodule pattern matching the source file
1258// containing the call.
1259func V(level Level) Verbose {
1260 // This function tries hard to be cheap unless there's work to do.
1261 // The fast path is two atomic loads and compares.
1262
1263 // Here is a cheap but safe test to see if V logging is enabled globally.
1264 if logging.verbosity.get() >= level {
1265 return newVerbose(level, true)
1266 }
1267
1268 // It's off globally but it vmodule may still be set.
1269 // Here is another cheap but safe test to see if vmodule is enabled.
1270 if atomic.LoadInt32(&logging.filterLength) > 0 {
1271 // Now we need a proper lock to use the logging structure. The pcs field
1272 // is shared so we must lock before accessing it. This is fairly expensive,
1273 // but if V logging is enabled we're slow anyway.
1274 logging.mu.Lock()
1275 defer logging.mu.Unlock()
1276 if runtime.Callers(2, logging.pcs[:]) == 0 {
1277 return newVerbose(level, false)
1278 }
1279 v, ok := logging.vmap[logging.pcs[0]]
1280 if !ok {
1281 v = logging.setV(logging.pcs[0])
1282 }
1283 return newVerbose(level, v >= level)
1284 }
1285 return newVerbose(level, false)
1286}
1287
1288// Enabled will return true if this log level is enabled, guarded by the value
1289// of v.
1290// See the documentation of V for usage.
1291func (v Verbose) Enabled() bool {
1292 return v.enabled
1293}
1294
1295// Info is equivalent to the global Info function, guarded by the value of v.
1296// See the documentation of V for usage.
1297func (v Verbose) Info(args ...interface{}) {
1298 if v.enabled {
1299 logging.print(infoLog, v.logr, args...)
1300 }
1301}
1302
1303// Infoln is equivalent to the global Infoln function, guarded by the value of v.
1304// See the documentation of V for usage.
1305func (v Verbose) Infoln(args ...interface{}) {
1306 if v.enabled {
1307 logging.println(infoLog, v.logr, args...)
1308 }
1309}
1310
1311// Infof is equivalent to the global Infof function, guarded by the value of v.
1312// See the documentation of V for usage.
1313func (v Verbose) Infof(format string, args ...interface{}) {
1314 if v.enabled {
1315 logging.printf(infoLog, v.logr, format, args...)
1316 }
1317}
1318
1319// InfoS is equivalent to the global InfoS function, guarded by the value of v.
1320// See the documentation of V for usage.
1321func (v Verbose) InfoS(msg string, keysAndValues ...interface{}) {
1322 if v.enabled {
1323 logging.infoS(v.logr, msg, keysAndValues...)
1324 }
1325}
1326
1327// Error is equivalent to the global Error function, guarded by the value of v.
1328// See the documentation of V for usage.
1329func (v Verbose) Error(err error, msg string, args ...interface{}) {
1330 if v.enabled {
1331 logging.errorS(err, v.logr, msg, args...)
1332 }
1333}
1334
1335// Info logs to the INFO log.
1336// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
1337func Info(args ...interface{}) {
1338 logging.print(infoLog, logging.logr, args...)
1339}
1340
1341// InfoDepth acts as Info but uses depth to determine which call frame to log.
1342// InfoDepth(0, "msg") is the same as Info("msg").
1343func InfoDepth(depth int, args ...interface{}) {
1344 logging.printDepth(infoLog, logging.logr, depth, args...)
1345}
1346
1347// Infoln logs to the INFO log.
1348// Arguments are handled in the manner of fmt.Println; a newline is always appended.
1349func Infoln(args ...interface{}) {
1350 logging.println(infoLog, logging.logr, args...)
1351}
1352
1353// Infof logs to the INFO log.
1354// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
1355func Infof(format string, args ...interface{}) {
1356 logging.printf(infoLog, logging.logr, format, args...)
1357}
1358
1359// InfoS structured logs to the INFO log.
1360// The msg argument used to add constant description to the log line.
1361// The key/value pairs would be join by "=" ; a newline is always appended.
1362//
1363// Basic examples:
1364// >> klog.InfoS("Pod status updated", "pod", "kubedns", "status", "ready")
1365// output:
1366// >> I1025 00:15:15.525108 1 controller_utils.go:116] "Pod status updated" pod="kubedns" status="ready"
1367func InfoS(msg string, keysAndValues ...interface{}) {
1368 logging.infoS(logging.logr, msg, keysAndValues...)
1369}
1370
1371// Warning logs to the WARNING and INFO logs.
1372// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
1373func Warning(args ...interface{}) {
1374 logging.print(warningLog, logging.logr, args...)
1375}
1376
1377// WarningDepth acts as Warning but uses depth to determine which call frame to log.
1378// WarningDepth(0, "msg") is the same as Warning("msg").
1379func WarningDepth(depth int, args ...interface{}) {
1380 logging.printDepth(warningLog, logging.logr, depth, args...)
1381}
1382
1383// Warningln logs to the WARNING and INFO logs.
1384// Arguments are handled in the manner of fmt.Println; a newline is always appended.
1385func Warningln(args ...interface{}) {
1386 logging.println(warningLog, logging.logr, args...)
1387}
1388
1389// Warningf logs to the WARNING and INFO logs.
1390// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
1391func Warningf(format string, args ...interface{}) {
1392 logging.printf(warningLog, logging.logr, format, args...)
1393}
1394
1395// Error logs to the ERROR, WARNING, and INFO logs.
1396// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
1397func Error(args ...interface{}) {
1398 logging.print(errorLog, logging.logr, args...)
1399}
1400
1401// ErrorDepth acts as Error but uses depth to determine which call frame to log.
1402// ErrorDepth(0, "msg") is the same as Error("msg").
1403func ErrorDepth(depth int, args ...interface{}) {
1404 logging.printDepth(errorLog, logging.logr, depth, args...)
1405}
1406
1407// Errorln logs to the ERROR, WARNING, and INFO logs.
1408// Arguments are handled in the manner of fmt.Println; a newline is always appended.
1409func Errorln(args ...interface{}) {
1410 logging.println(errorLog, logging.logr, args...)
1411}
1412
1413// Errorf logs to the ERROR, WARNING, and INFO logs.
1414// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
1415func Errorf(format string, args ...interface{}) {
1416 logging.printf(errorLog, logging.logr, format, args...)
1417}
1418
1419// ErrorS structured logs to the ERROR, WARNING, and INFO logs.
1420// the err argument used as "err" field of log line.
1421// The msg argument used to add constant description to the log line.
1422// The key/value pairs would be join by "=" ; a newline is always appended.
1423//
1424// Basic examples:
1425// >> klog.ErrorS(err, "Failed to update pod status")
1426// output:
1427// >> E1025 00:15:15.525108 1 controller_utils.go:114] "Failed to update pod status" err="timeout"
1428func ErrorS(err error, msg string, keysAndValues ...interface{}) {
1429 logging.errorS(err, logging.logr, msg, keysAndValues...)
1430}
1431
1432// Fatal logs to the FATAL, ERROR, WARNING, and INFO logs,
1433// including a stack trace of all running goroutines, then calls os.Exit(255).
1434// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
1435func Fatal(args ...interface{}) {
1436 logging.print(fatalLog, logging.logr, args...)
1437}
1438
1439// FatalDepth acts as Fatal but uses depth to determine which call frame to log.
1440// FatalDepth(0, "msg") is the same as Fatal("msg").
1441func FatalDepth(depth int, args ...interface{}) {
1442 logging.printDepth(fatalLog, logging.logr, depth, args...)
1443}
1444
1445// Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs,
1446// including a stack trace of all running goroutines, then calls os.Exit(255).
1447// Arguments are handled in the manner of fmt.Println; a newline is always appended.
1448func Fatalln(args ...interface{}) {
1449 logging.println(fatalLog, logging.logr, args...)
1450}
1451
1452// Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs,
1453// including a stack trace of all running goroutines, then calls os.Exit(255).
1454// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
1455func Fatalf(format string, args ...interface{}) {
1456 logging.printf(fatalLog, logging.logr, format, args...)
1457}
1458
1459// fatalNoStacks is non-zero if we are to exit without dumping goroutine stacks.
1460// It allows Exit and relatives to use the Fatal logs.
1461var fatalNoStacks uint32
1462
1463// Exit logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
1464// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
1465func Exit(args ...interface{}) {
1466 atomic.StoreUint32(&fatalNoStacks, 1)
1467 logging.print(fatalLog, logging.logr, args...)
1468}
1469
1470// ExitDepth acts as Exit but uses depth to determine which call frame to log.
1471// ExitDepth(0, "msg") is the same as Exit("msg").
1472func ExitDepth(depth int, args ...interface{}) {
1473 atomic.StoreUint32(&fatalNoStacks, 1)
1474 logging.printDepth(fatalLog, logging.logr, depth, args...)
1475}
1476
1477// Exitln logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
1478func Exitln(args ...interface{}) {
1479 atomic.StoreUint32(&fatalNoStacks, 1)
1480 logging.println(fatalLog, logging.logr, args...)
1481}
1482
1483// Exitf logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
1484// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
1485func Exitf(format string, args ...interface{}) {
1486 atomic.StoreUint32(&fatalNoStacks, 1)
1487 logging.printf(fatalLog, logging.logr, format, args...)
1488}
1489
1490// ObjectRef references a kubernetes object
1491type ObjectRef struct {
1492 Name string `json:"name"`
1493 Namespace string `json:"namespace,omitempty"`
1494}
1495
1496func (ref ObjectRef) String() string {
1497 if ref.Namespace != "" {
1498 return fmt.Sprintf("%s/%s", ref.Namespace, ref.Name)
1499 }
1500 return ref.Name
1501}
1502
1503// KMetadata is a subset of the kubernetes k8s.io/apimachinery/pkg/apis/meta/v1.Object interface
1504// this interface may expand in the future, but will always be a subset of the
1505// kubernetes k8s.io/apimachinery/pkg/apis/meta/v1.Object interface
1506type KMetadata interface {
1507 GetName() string
1508 GetNamespace() string
1509}
1510
1511// KObj returns ObjectRef from ObjectMeta
1512func KObj(obj KMetadata) ObjectRef {
1513 return ObjectRef{
1514 Name: obj.GetName(),
1515 Namespace: obj.GetNamespace(),
1516 }
1517}
1518
1519// KRef returns ObjectRef from name and namespace
1520func KRef(namespace, name string) ObjectRef {
1521 return ObjectRef{
1522 Name: name,
1523 Namespace: namespace,
1524 }
1525}