cmd/tailscaled, logpolicy, logtail: support log levels
Log levels can now be specified with "[v1] " or "[v2] " substrings that are then stripped and filtered at the final logger. This follows our existing "[unexpected]" etc convention and doesn't require a wholesale reworking of our logging at the moment. cmd/tailscaled then gets a new --verbose=N flag to take a log level that controls what gets logged to stderr (and thus systemd, syslog, etc). Logtail is unaffected by --verbose. This commit doesn't add annotations to any existing log prints. That is in the next commit. Updates #924 Updates #282 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>pull/1054/head
parent
d97ee12179
commit
57dd247376
|
@ -64,6 +64,7 @@ var args struct {
|
||||||
port uint16
|
port uint16
|
||||||
statepath string
|
statepath string
|
||||||
socketpath string
|
socketpath string
|
||||||
|
verbose int
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -76,6 +77,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
printVersion := false
|
printVersion := false
|
||||||
|
flag.IntVar(&args.verbose, "verbose", 0, "log verbosity level; 0 is default, 1 or higher are increasingly verbose")
|
||||||
flag.BoolVar(&args.cleanup, "cleanup", false, "clean up system state and exit")
|
flag.BoolVar(&args.cleanup, "cleanup", false, "clean up system state and exit")
|
||||||
flag.BoolVar(&args.fake, "fake", false, "use userspace fake tunnel+routing instead of kernel TUN interface")
|
flag.BoolVar(&args.fake, "fake", false, "use userspace fake tunnel+routing instead of kernel TUN interface")
|
||||||
flag.StringVar(&args.debug, "debug", "", "listen address ([ip]:port) of optional debug server")
|
flag.StringVar(&args.debug, "debug", "", "listen address ([ip]:port) of optional debug server")
|
||||||
|
@ -118,6 +120,7 @@ func run() error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
pol := logpolicy.New("tailnode.log.tailscale.io")
|
pol := logpolicy.New("tailnode.log.tailscale.io")
|
||||||
|
pol.SetVerbosityLevel(args.verbose)
|
||||||
defer func() {
|
defer func() {
|
||||||
// Finish uploading logs after closing everything else.
|
// Finish uploading logs after closing everything else.
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||||
|
|
|
@ -413,6 +413,15 @@ func New(collection string) *Policy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetVerbosityLevel controls the verbosity level that should be
|
||||||
|
// written to stderr. 0 is the default (not verbose). Levels 1 or higher
|
||||||
|
// are increasingly verbose.
|
||||||
|
//
|
||||||
|
// It should not be changed concurrently with log writes.
|
||||||
|
func (p *Policy) SetVerbosityLevel(level int) {
|
||||||
|
p.Logtail.SetVerbosityLevel(level)
|
||||||
|
}
|
||||||
|
|
||||||
// Close immediately shuts down the logger.
|
// Close immediately shuts down the logger.
|
||||||
func (p *Policy) Close() {
|
func (p *Policy) Close() {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
|
@ -39,6 +39,7 @@ type Config struct {
|
||||||
LowMemory bool // if true, logtail minimizes memory use
|
LowMemory bool // if true, logtail minimizes memory use
|
||||||
TimeNow func() time.Time // if set, subsitutes uses of time.Now
|
TimeNow func() time.Time // if set, subsitutes uses of time.Now
|
||||||
Stderr io.Writer // if set, logs are sent here instead of os.Stderr
|
Stderr io.Writer // if set, logs are sent here instead of os.Stderr
|
||||||
|
StderrLevel int // max verbosity level to write to stderr; 0 means the non-verbose messages only
|
||||||
Buffer Buffer // temp storage, if nil a MemoryBuffer
|
Buffer Buffer // temp storage, if nil a MemoryBuffer
|
||||||
NewZstdEncoder func() Encoder // if set, used to compress logs for transmission
|
NewZstdEncoder func() Encoder // if set, used to compress logs for transmission
|
||||||
|
|
||||||
|
@ -69,6 +70,7 @@ func NewLogger(cfg Config, logf tslogger.Logf) *Logger {
|
||||||
}
|
}
|
||||||
l := &Logger{
|
l := &Logger{
|
||||||
stderr: cfg.Stderr,
|
stderr: cfg.Stderr,
|
||||||
|
stderrLevel: cfg.StderrLevel,
|
||||||
httpc: cfg.HTTPC,
|
httpc: cfg.HTTPC,
|
||||||
url: cfg.BaseURL + "/c/" + cfg.Collection + "/" + cfg.PrivateID.String(),
|
url: cfg.BaseURL + "/c/" + cfg.Collection + "/" + cfg.PrivateID.String(),
|
||||||
lowMem: cfg.LowMemory,
|
lowMem: cfg.LowMemory,
|
||||||
|
@ -99,6 +101,7 @@ func NewLogger(cfg Config, logf tslogger.Logf) *Logger {
|
||||||
// logging facilities and uploading to a log server.
|
// logging facilities and uploading to a log server.
|
||||||
type Logger struct {
|
type Logger struct {
|
||||||
stderr io.Writer
|
stderr io.Writer
|
||||||
|
stderrLevel int
|
||||||
httpc *http.Client
|
httpc *http.Client
|
||||||
url string
|
url string
|
||||||
lowMem bool
|
lowMem bool
|
||||||
|
@ -116,6 +119,15 @@ type Logger struct {
|
||||||
shutdownDone chan struct{} // closd when shutdown complete
|
shutdownDone chan struct{} // closd when shutdown complete
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetVerbosityLevel controls the verbosity level that should be
|
||||||
|
// written to stderr. 0 is the default (not verbose). Levels 1 or higher
|
||||||
|
// are increasingly verbose.
|
||||||
|
//
|
||||||
|
// It should not be changed concurrently with log writes.
|
||||||
|
func (l *Logger) SetVerbosityLevel(level int) {
|
||||||
|
l.stderrLevel = level
|
||||||
|
}
|
||||||
|
|
||||||
// Shutdown gracefully shuts down the logger while completing any
|
// Shutdown gracefully shuts down the logger while completing any
|
||||||
// remaining uploads.
|
// remaining uploads.
|
||||||
//
|
//
|
||||||
|
@ -457,7 +469,8 @@ func (l *Logger) Write(buf []byte) (int, error) {
|
||||||
if len(buf) == 0 {
|
if len(buf) == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
if l.stderr != nil && l.stderr != ioutil.Discard {
|
level, buf := parseAndRemoveLogLevel(buf)
|
||||||
|
if l.stderr != nil && l.stderr != ioutil.Discard && level <= l.stderrLevel {
|
||||||
if buf[len(buf)-1] == '\n' {
|
if buf[len(buf)-1] == '\n' {
|
||||||
l.stderr.Write(buf)
|
l.stderr.Write(buf)
|
||||||
} else {
|
} else {
|
||||||
|
@ -471,3 +484,23 @@ func (l *Logger) Write(buf []byte) (int, error) {
|
||||||
_, err := l.send(b)
|
_, err := l.send(b)
|
||||||
return len(buf), err
|
return len(buf), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
openBracketV = []byte("[v")
|
||||||
|
v1 = []byte("[v1] ")
|
||||||
|
v2 = []byte("[v2] ")
|
||||||
|
)
|
||||||
|
|
||||||
|
// level 0 is normal (or unknown) level; 1+ are increasingly verbose
|
||||||
|
func parseAndRemoveLogLevel(buf []byte) (level int, cleanBuf []byte) {
|
||||||
|
if len(buf) == 0 || buf[0] == '{' || !bytes.Contains(buf, openBracketV) {
|
||||||
|
return 0, buf
|
||||||
|
}
|
||||||
|
if bytes.Contains(buf, v1) {
|
||||||
|
return 1, bytes.ReplaceAll(buf, v1, nil)
|
||||||
|
}
|
||||||
|
if bytes.Contains(buf, v2) {
|
||||||
|
return 2, bytes.ReplaceAll(buf, v2, nil)
|
||||||
|
}
|
||||||
|
return 0, buf
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue