d2dbb8c963
* cli: initial work on debug; server-status target * debug: add metrics capture target (#7376) * check against DR secondary * debug: add compression * refactor check into preflight func * debug: set short test time on tests, fix exit code bug * debug: use temp dir for output on tests * debug: use mholt/archiver for compression * first pass on adding pprof * use logger for output * refactor polling target capture logic * debug: poll and collect replication status * debug: poll and collect host-info; rename output files and collection refactor * fix comments * add archive test; fix bugs found * rename flag name to singular target * add target output test; scaffold other tests cases * debug/test: add pprof and index file tests * debug/test: add min timing check tests * debug: fix index gen race and collection goroutine race * debug: extend archive tests, handle race between program exit and polling goroutines * update docstring * debug: correctly add to pollingWg * debug: add config target support * debug: don't wait on interrupt shutdown; add file exists unit tests * move pprof bits into its goroutine * debug: skip empty metrics and some pprof file creation if permission denied, add matching unit test * address comments and feedback * Vault debug using run.Group (#7658) * debug: switch to use oklog/run.Group * debug: use context to cancel requests and interrupt rungroups. * debug: trigger the first interval properly * debug: metrics collection should use metrics interval * debug: add missing continue on metrics error * debug: remove the use of buffered chan to trigger first interval * debug: don't shadow BaseCommand's client, properly block on interval capture failures * debug: actually use c.cachedClient everywhere * go mod vendor * debug: run all pprof in goroutines; bump pprof timings in tests to reduce flakiness * debug: update help text
458 lines
13 KiB
Go
458 lines
13 KiB
Go
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Package xlog provides a simple logging package that allows to disable
|
|
// certain message categories. It defines a type, Logger, with multiple
|
|
// methods for formatting output. The package has also a predefined
|
|
// 'standard' Logger accessible through helper function Print[f|ln],
|
|
// Fatal[f|ln], Panic[f|ln], Warn[f|ln], Print[f|ln] and Debug[f|ln]
|
|
// that are easier to use then creating a Logger manually. That logger
|
|
// writes to standard error and prints the date and time of each logged
|
|
// message, which can be configured using the function SetFlags.
|
|
//
|
|
// The Fatal functions call os.Exit(1) after the message is output
|
|
// unless not suppressed by the flags. The Panic functions call panic
|
|
// after the writing the log message unless suppressed.
|
|
package xlog
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"runtime"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// The flags define what information is prefixed to each log entry
|
|
// generated by the Logger. The Lno* versions allow the suppression of
|
|
// specific output. The bits are or'ed together to control what will be
|
|
// printed. There is no control over the order of the items printed and
|
|
// the format. The full format is:
|
|
//
|
|
// 2009-01-23 01:23:23.123123 /a/b/c/d.go:23: message
|
|
//
|
|
const (
|
|
Ldate = 1 << iota // the date: 2009-01-23
|
|
Ltime // the time: 01:23:23
|
|
Lmicroseconds // microsecond resolution: 01:23:23.123123
|
|
Llongfile // full file name and line number: /a/b/c/d.go:23
|
|
Lshortfile // final file name element and line number: d.go:23
|
|
Lnopanic // suppresses output from Panic[f|ln] but not the panic call
|
|
Lnofatal // suppresses output from Fatal[f|ln] but not the exit
|
|
Lnowarn // suppresses output from Warn[f|ln]
|
|
Lnoprint // suppresses output from Print[f|ln]
|
|
Lnodebug // suppresses output from Debug[f|ln]
|
|
// initial values for the standard logger
|
|
Lstdflags = Ldate | Ltime | Lnodebug
|
|
)
|
|
|
|
// A Logger represents an active logging object that generates lines of
|
|
// output to an io.Writer. Each logging operation if not suppressed
|
|
// makes a single call to the Writer's Write method. A Logger can be
|
|
// used simultaneously from multiple goroutines; it guarantees to
|
|
// serialize access to the Writer.
|
|
type Logger struct {
|
|
mu sync.Mutex // ensures atomic writes; and protects the following
|
|
// fields
|
|
prefix string // prefix to write at beginning of each line
|
|
flag int // properties
|
|
out io.Writer // destination for output
|
|
buf []byte // for accumulating text to write
|
|
}
|
|
|
|
// New creates a new Logger. The out argument sets the destination to
|
|
// which the log output will be written. The prefix appears at the
|
|
// beginning of each log line. The flag argument defines the logging
|
|
// properties.
|
|
func New(out io.Writer, prefix string, flag int) *Logger {
|
|
return &Logger{out: out, prefix: prefix, flag: flag}
|
|
}
|
|
|
|
// std is the standard logger used by the package scope functions.
|
|
var std = New(os.Stderr, "", Lstdflags)
|
|
|
|
// itoa converts the integer to ASCII. A negative widths will avoid
|
|
// zero-padding. The function supports only non-negative integers.
|
|
func itoa(buf *[]byte, i int, wid int) {
|
|
var u = uint(i)
|
|
if u == 0 && wid <= 1 {
|
|
*buf = append(*buf, '0')
|
|
return
|
|
}
|
|
var b [32]byte
|
|
bp := len(b)
|
|
for ; u > 0 || wid > 0; u /= 10 {
|
|
bp--
|
|
wid--
|
|
b[bp] = byte(u%10) + '0'
|
|
}
|
|
*buf = append(*buf, b[bp:]...)
|
|
}
|
|
|
|
// formatHeader puts the header into the buf field of the buffer.
|
|
func (l *Logger) formatHeader(t time.Time, file string, line int) {
|
|
l.buf = append(l.buf, l.prefix...)
|
|
if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 {
|
|
if l.flag&Ldate != 0 {
|
|
year, month, day := t.Date()
|
|
itoa(&l.buf, year, 4)
|
|
l.buf = append(l.buf, '-')
|
|
itoa(&l.buf, int(month), 2)
|
|
l.buf = append(l.buf, '-')
|
|
itoa(&l.buf, day, 2)
|
|
l.buf = append(l.buf, ' ')
|
|
}
|
|
if l.flag&(Ltime|Lmicroseconds) != 0 {
|
|
hour, min, sec := t.Clock()
|
|
itoa(&l.buf, hour, 2)
|
|
l.buf = append(l.buf, ':')
|
|
itoa(&l.buf, min, 2)
|
|
l.buf = append(l.buf, ':')
|
|
itoa(&l.buf, sec, 2)
|
|
if l.flag&Lmicroseconds != 0 {
|
|
l.buf = append(l.buf, '.')
|
|
itoa(&l.buf, t.Nanosecond()/1e3, 6)
|
|
}
|
|
l.buf = append(l.buf, ' ')
|
|
}
|
|
}
|
|
if l.flag&(Lshortfile|Llongfile) != 0 {
|
|
if l.flag&Lshortfile != 0 {
|
|
short := file
|
|
for i := len(file) - 1; i > 0; i-- {
|
|
if file[i] == '/' {
|
|
short = file[i+1:]
|
|
break
|
|
}
|
|
}
|
|
file = short
|
|
}
|
|
l.buf = append(l.buf, file...)
|
|
l.buf = append(l.buf, ':')
|
|
itoa(&l.buf, line, -1)
|
|
l.buf = append(l.buf, ": "...)
|
|
}
|
|
}
|
|
|
|
func (l *Logger) output(calldepth int, now time.Time, s string) error {
|
|
var file string
|
|
var line int
|
|
if l.flag&(Lshortfile|Llongfile) != 0 {
|
|
l.mu.Unlock()
|
|
var ok bool
|
|
_, file, line, ok = runtime.Caller(calldepth)
|
|
if !ok {
|
|
file = "???"
|
|
line = 0
|
|
}
|
|
l.mu.Lock()
|
|
}
|
|
l.buf = l.buf[:0]
|
|
l.formatHeader(now, file, line)
|
|
l.buf = append(l.buf, s...)
|
|
if len(s) == 0 || s[len(s)-1] != '\n' {
|
|
l.buf = append(l.buf, '\n')
|
|
}
|
|
_, err := l.out.Write(l.buf)
|
|
return err
|
|
}
|
|
|
|
// Output writes the string s with the header controlled by the flags to
|
|
// the l.out writer. A newline will be appended if s doesn't end in a
|
|
// newline. Calldepth is used to recover the PC, although all current
|
|
// calls of Output use the call depth 2. Access to the function is serialized.
|
|
func (l *Logger) Output(calldepth, noflag int, v ...interface{}) error {
|
|
now := time.Now()
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
if l.flag&noflag != 0 {
|
|
return nil
|
|
}
|
|
s := fmt.Sprint(v...)
|
|
return l.output(calldepth+1, now, s)
|
|
}
|
|
|
|
// Outputf works like output but formats the output like Printf.
|
|
func (l *Logger) Outputf(calldepth int, noflag int, format string, v ...interface{}) error {
|
|
now := time.Now()
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
if l.flag&noflag != 0 {
|
|
return nil
|
|
}
|
|
s := fmt.Sprintf(format, v...)
|
|
return l.output(calldepth+1, now, s)
|
|
}
|
|
|
|
// Outputln works like output but formats the output like Println.
|
|
func (l *Logger) Outputln(calldepth int, noflag int, v ...interface{}) error {
|
|
now := time.Now()
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
if l.flag&noflag != 0 {
|
|
return nil
|
|
}
|
|
s := fmt.Sprintln(v...)
|
|
return l.output(calldepth+1, now, s)
|
|
}
|
|
|
|
// Panic prints the message like Print and calls panic. The printing
|
|
// might be suppressed by the flag Lnopanic.
|
|
func (l *Logger) Panic(v ...interface{}) {
|
|
l.Output(2, Lnopanic, v...)
|
|
s := fmt.Sprint(v...)
|
|
panic(s)
|
|
}
|
|
|
|
// Panic prints the message like Print and calls panic. The printing
|
|
// might be suppressed by the flag Lnopanic.
|
|
func Panic(v ...interface{}) {
|
|
std.Output(2, Lnopanic, v...)
|
|
s := fmt.Sprint(v...)
|
|
panic(s)
|
|
}
|
|
|
|
// Panicf prints the message like Printf and calls panic. The printing
|
|
// might be suppressed by the flag Lnopanic.
|
|
func (l *Logger) Panicf(format string, v ...interface{}) {
|
|
l.Outputf(2, Lnopanic, format, v...)
|
|
s := fmt.Sprintf(format, v...)
|
|
panic(s)
|
|
}
|
|
|
|
// Panicf prints the message like Printf and calls panic. The printing
|
|
// might be suppressed by the flag Lnopanic.
|
|
func Panicf(format string, v ...interface{}) {
|
|
std.Outputf(2, Lnopanic, format, v...)
|
|
s := fmt.Sprintf(format, v...)
|
|
panic(s)
|
|
}
|
|
|
|
// Panicln prints the message like Println and calls panic. The printing
|
|
// might be suppressed by the flag Lnopanic.
|
|
func (l *Logger) Panicln(v ...interface{}) {
|
|
l.Outputln(2, Lnopanic, v...)
|
|
s := fmt.Sprintln(v...)
|
|
panic(s)
|
|
}
|
|
|
|
// Panicln prints the message like Println and calls panic. The printing
|
|
// might be suppressed by the flag Lnopanic.
|
|
func Panicln(v ...interface{}) {
|
|
std.Outputln(2, Lnopanic, v...)
|
|
s := fmt.Sprintln(v...)
|
|
panic(s)
|
|
}
|
|
|
|
// Fatal prints the message like Print and calls os.Exit(1). The
|
|
// printing might be suppressed by the flag Lnofatal.
|
|
func (l *Logger) Fatal(v ...interface{}) {
|
|
l.Output(2, Lnofatal, v...)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Fatal prints the message like Print and calls os.Exit(1). The
|
|
// printing might be suppressed by the flag Lnofatal.
|
|
func Fatal(v ...interface{}) {
|
|
std.Output(2, Lnofatal, v...)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Fatalf prints the message like Printf and calls os.Exit(1). The
|
|
// printing might be suppressed by the flag Lnofatal.
|
|
func (l *Logger) Fatalf(format string, v ...interface{}) {
|
|
l.Outputf(2, Lnofatal, format, v...)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Fatalf prints the message like Printf and calls os.Exit(1). The
|
|
// printing might be suppressed by the flag Lnofatal.
|
|
func Fatalf(format string, v ...interface{}) {
|
|
std.Outputf(2, Lnofatal, format, v...)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Fatalln prints the message like Println and calls os.Exit(1). The
|
|
// printing might be suppressed by the flag Lnofatal.
|
|
func (l *Logger) Fatalln(format string, v ...interface{}) {
|
|
l.Outputln(2, Lnofatal, v...)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Fatalln prints the message like Println and calls os.Exit(1). The
|
|
// printing might be suppressed by the flag Lnofatal.
|
|
func Fatalln(format string, v ...interface{}) {
|
|
std.Outputln(2, Lnofatal, v...)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Warn prints the message like Print. The printing might be suppressed
|
|
// by the flag Lnowarn.
|
|
func (l *Logger) Warn(v ...interface{}) {
|
|
l.Output(2, Lnowarn, v...)
|
|
}
|
|
|
|
// Warn prints the message like Print. The printing might be suppressed
|
|
// by the flag Lnowarn.
|
|
func Warn(v ...interface{}) {
|
|
std.Output(2, Lnowarn, v...)
|
|
}
|
|
|
|
// Warnf prints the message like Printf. The printing might be suppressed
|
|
// by the flag Lnowarn.
|
|
func (l *Logger) Warnf(format string, v ...interface{}) {
|
|
l.Outputf(2, Lnowarn, format, v...)
|
|
}
|
|
|
|
// Warnf prints the message like Printf. The printing might be suppressed
|
|
// by the flag Lnowarn.
|
|
func Warnf(format string, v ...interface{}) {
|
|
std.Outputf(2, Lnowarn, format, v...)
|
|
}
|
|
|
|
// Warnln prints the message like Println. The printing might be suppressed
|
|
// by the flag Lnowarn.
|
|
func (l *Logger) Warnln(v ...interface{}) {
|
|
l.Outputln(2, Lnowarn, v...)
|
|
}
|
|
|
|
// Warnln prints the message like Println. The printing might be suppressed
|
|
// by the flag Lnowarn.
|
|
func Warnln(v ...interface{}) {
|
|
std.Outputln(2, Lnowarn, v...)
|
|
}
|
|
|
|
// Print prints the message like fmt.Print. The printing might be suppressed
|
|
// by the flag Lnoprint.
|
|
func (l *Logger) Print(v ...interface{}) {
|
|
l.Output(2, Lnoprint, v...)
|
|
}
|
|
|
|
// Print prints the message like fmt.Print. The printing might be suppressed
|
|
// by the flag Lnoprint.
|
|
func Print(v ...interface{}) {
|
|
std.Output(2, Lnoprint, v...)
|
|
}
|
|
|
|
// Printf prints the message like fmt.Printf. The printing might be suppressed
|
|
// by the flag Lnoprint.
|
|
func (l *Logger) Printf(format string, v ...interface{}) {
|
|
l.Outputf(2, Lnoprint, format, v...)
|
|
}
|
|
|
|
// Printf prints the message like fmt.Printf. The printing might be suppressed
|
|
// by the flag Lnoprint.
|
|
func Printf(format string, v ...interface{}) {
|
|
std.Outputf(2, Lnoprint, format, v...)
|
|
}
|
|
|
|
// Println prints the message like fmt.Println. The printing might be
|
|
// suppressed by the flag Lnoprint.
|
|
func (l *Logger) Println(v ...interface{}) {
|
|
l.Outputln(2, Lnoprint, v...)
|
|
}
|
|
|
|
// Println prints the message like fmt.Println. The printing might be
|
|
// suppressed by the flag Lnoprint.
|
|
func Println(v ...interface{}) {
|
|
std.Outputln(2, Lnoprint, v...)
|
|
}
|
|
|
|
// Debug prints the message like Print. The printing might be suppressed
|
|
// by the flag Lnodebug.
|
|
func (l *Logger) Debug(v ...interface{}) {
|
|
l.Output(2, Lnodebug, v...)
|
|
}
|
|
|
|
// Debug prints the message like Print. The printing might be suppressed
|
|
// by the flag Lnodebug.
|
|
func Debug(v ...interface{}) {
|
|
std.Output(2, Lnodebug, v...)
|
|
}
|
|
|
|
// Debugf prints the message like Printf. The printing might be suppressed
|
|
// by the flag Lnodebug.
|
|
func (l *Logger) Debugf(format string, v ...interface{}) {
|
|
l.Outputf(2, Lnodebug, format, v...)
|
|
}
|
|
|
|
// Debugf prints the message like Printf. The printing might be suppressed
|
|
// by the flag Lnodebug.
|
|
func Debugf(format string, v ...interface{}) {
|
|
std.Outputf(2, Lnodebug, format, v...)
|
|
}
|
|
|
|
// Debugln prints the message like Println. The printing might be suppressed
|
|
// by the flag Lnodebug.
|
|
func (l *Logger) Debugln(v ...interface{}) {
|
|
l.Outputln(2, Lnodebug, v...)
|
|
}
|
|
|
|
// Debugln prints the message like Println. The printing might be suppressed
|
|
// by the flag Lnodebug.
|
|
func Debugln(v ...interface{}) {
|
|
std.Outputln(2, Lnodebug, v...)
|
|
}
|
|
|
|
// Flags returns the current flags used by the logger.
|
|
func (l *Logger) Flags() int {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
return l.flag
|
|
}
|
|
|
|
// Flags returns the current flags used by the standard logger.
|
|
func Flags() int {
|
|
return std.Flags()
|
|
}
|
|
|
|
// SetFlags sets the flags of the logger.
|
|
func (l *Logger) SetFlags(flag int) {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
l.flag = flag
|
|
}
|
|
|
|
// SetFlags sets the flags for the standard logger.
|
|
func SetFlags(flag int) {
|
|
std.SetFlags(flag)
|
|
}
|
|
|
|
// Prefix returns the prefix used by the logger.
|
|
func (l *Logger) Prefix() string {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
return l.prefix
|
|
}
|
|
|
|
// Prefix returns the prefix used by the standard logger of the package.
|
|
func Prefix() string {
|
|
return std.Prefix()
|
|
}
|
|
|
|
// SetPrefix sets the prefix for the logger.
|
|
func (l *Logger) SetPrefix(prefix string) {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
l.prefix = prefix
|
|
}
|
|
|
|
// SetPrefix sets the prefix of the standard logger of the package.
|
|
func SetPrefix(prefix string) {
|
|
std.SetPrefix(prefix)
|
|
}
|
|
|
|
// SetOutput sets the output of the logger.
|
|
func (l *Logger) SetOutput(w io.Writer) {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
l.out = w
|
|
}
|
|
|
|
// SetOutput sets the output for the standard logger of the package.
|
|
func SetOutput(w io.Writer) {
|
|
std.SetOutput(w)
|
|
}
|