| // Copyright 2020 The Monogon Project Authors. | 
 | // | 
 | // SPDX-License-Identifier: Apache-2.0 | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //     http://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | package logtree | 
 |  | 
 | import ( | 
 | 	"fmt" | 
 |  | 
 | 	cpb "source.monogon.dev/metropolis/proto/common" | 
 | ) | 
 |  | 
 | // LeveledLogger is a generic interface for glog-style logging. There are four | 
 | // hardcoded log severities, in increasing order: INFO, WARNING, ERROR, FATAL. | 
 | // Logging at a certain severity level logs not only to consumers expecting data at | 
 | // that severity level, but also all lower severity levels. For example, an ERROR | 
 | // log will also be passed to consumers looking at INFO or WARNING logs. | 
 | type LeveledLogger interface { | 
 | 	// Info logs at the INFO severity. Arguments are handled in the manner of | 
 | 	// fmt.Print, a terminating newline is added if missing. | 
 | 	Info(args ...interface{}) | 
 | 	// Infof logs at the INFO severity. Arguments are handled in the manner of | 
 | 	// fmt.Printf, a terminating newline is added if missing. | 
 | 	Infof(format string, args ...interface{}) | 
 |  | 
 | 	// Warning logs at the WARNING severity. Arguments are handled in the manner of | 
 | 	// fmt.Print, a terminating newline is added if missing. | 
 | 	Warning(args ...interface{}) | 
 | 	// Warningf logs at the WARNING severity. Arguments are handled in the manner of | 
 | 	// fmt.Printf, a terminating newline is added if missing. | 
 | 	Warningf(format string, args ...interface{}) | 
 |  | 
 | 	// Error logs at the ERROR severity. Arguments are handled in the manner of | 
 | 	// fmt.Print, a terminating newline is added if missing. | 
 | 	Error(args ...interface{}) | 
 | 	// Errorf logs at the ERROR severity. Arguments are handled in the manner of | 
 | 	// fmt.Printf, a terminating newline is added if missing. | 
 | 	Errorf(format string, args ...interface{}) | 
 |  | 
 | 	// Fatal logs at the FATAL severity and aborts the current program. Arguments are | 
 | 	// handled in the manner of fmt.Print, a terminating newline is added if missing. | 
 | 	Fatal(args ...interface{}) | 
 | 	// Fatalf logs at the FATAL severity and aborts the current program. Arguments are | 
 | 	// handled in the manner of fmt.Printf, a terminating newline is added if missing. | 
 | 	Fatalf(format string, args ...interface{}) | 
 |  | 
 | 	// V returns a VerboseLeveledLogger at a given verbosity level. These verbosity | 
 | 	// levels can be dynamically set and unset on a package-granular level by consumers | 
 | 	// of the LeveledLogger logs. The returned value represents whether logging at the | 
 | 	// given verbosity level was active at that time, and as such should not be a long- | 
 | 	// lived object in programs. This construct is further refered to as 'V-logs'. | 
 | 	V(level VerbosityLevel) VerboseLeveledLogger | 
 |  | 
 | 	// WithAddedStackDepth returns the same LeveledLogger, but adjusted with an | 
 | 	// additional 'extra stack depth' which will be used to skip a given number of | 
 | 	// stack/call frames when determining the location where the error originated. | 
 | 	// For example, WithStackDepth(1) will return a logger that will skip one | 
 | 	// stack/call frame. Then, with function foo() calling function helper() which | 
 | 	// in turns call l.Infof(), the log line will be emitted with the call site of | 
 | 	// helper() within foo(), instead of the default behaviour of logging the | 
 | 	// call site of Infof() within helper(). | 
 | 	// | 
 | 	// This is useful for functions which somehow wrap loggers in helper functions, | 
 | 	// for example to expose a slightly different API. | 
 | 	WithAddedStackDepth(depth int) LeveledLogger | 
 | } | 
 |  | 
 | // VerbosityLevel is a verbosity level defined for V-logs. This can be changed | 
 | // programmatically per Go package. When logging at a given VerbosityLevel V, the | 
 | // current level must be equal or higher to V for the logs to be recorded. | 
 | // Conversely, enabling a V-logging at a VerbosityLevel V also enables all logging | 
 | // at lower levels [Int32Min .. (V-1)]. | 
 | type VerbosityLevel int32 | 
 |  | 
 | type VerboseLeveledLogger interface { | 
 | 	// Enabled returns if this level was enabled. If not enabled, all logging into this | 
 | 	// logger will be discarded immediately. Thus, Enabled() can be used to check the | 
 | 	// verbosity level before performing any logging: | 
 | 	//    if l.V(3).Enabled() { l.Info("V3 is enabled") } | 
 | 	// or, in simple cases, the convenience function .Info can be used: | 
 | 	//    l.V(3).Info("V3 is enabled") | 
 | 	// The second form is shorter and more convenient, but more expensive, as its | 
 | 	// arguments are always evaluated. | 
 | 	Enabled() bool | 
 | 	// Info is the equivalent of a LeveledLogger's Info call, guarded by whether this | 
 | 	// VerboseLeveledLogger is enabled. | 
 | 	Info(args ...interface{}) | 
 | 	// Infof is the equivalent of a LeveledLogger's Infof call, guarded by whether this | 
 | 	// VerboseLeveledLogger is enabled. | 
 | 	Infof(format string, args ...interface{}) | 
 | } | 
 |  | 
 | // Severity is one of the severities as described in LeveledLogger. | 
 | type Severity string | 
 |  | 
 | const ( | 
 | 	INFO    Severity = "I" | 
 | 	WARNING Severity = "W" | 
 | 	ERROR   Severity = "E" | 
 | 	FATAL   Severity = "F" | 
 | ) | 
 |  | 
 | var ( | 
 | 	// SeverityAtLeast maps a given severity to a list of severities that at that | 
 | 	// severity or higher. In other words, SeverityAtLeast[X] returns a list of | 
 | 	// severities that might be seen in a log at severity X. | 
 | 	SeverityAtLeast = map[Severity][]Severity{ | 
 | 		INFO:    {INFO, WARNING, ERROR, FATAL}, | 
 | 		WARNING: {WARNING, ERROR, FATAL}, | 
 | 		ERROR:   {ERROR, FATAL}, | 
 | 		FATAL:   {FATAL}, | 
 | 	} | 
 | ) | 
 |  | 
 | func (s Severity) AtLeast(other Severity) bool { | 
 | 	for _, el := range SeverityAtLeast[other] { | 
 | 		if el == s { | 
 | 			return true | 
 | 		} | 
 | 	} | 
 | 	return false | 
 | } | 
 |  | 
 | // Valid returns whether true if this severity is one of the known levels | 
 | // (INFO, WARNING, ERROR or FATAL), false otherwise. | 
 | func (s Severity) Valid() bool { | 
 | 	switch s { | 
 | 	case INFO, WARNING, ERROR, FATAL: | 
 | 		return true | 
 | 	default: | 
 | 		return false | 
 | 	} | 
 | } | 
 |  | 
 | func SeverityFromProto(s cpb.LeveledLogSeverity) (Severity, error) { | 
 | 	switch s { | 
 | 	case cpb.LeveledLogSeverity_INFO: | 
 | 		return INFO, nil | 
 | 	case cpb.LeveledLogSeverity_WARNING: | 
 | 		return WARNING, nil | 
 | 	case cpb.LeveledLogSeverity_ERROR: | 
 | 		return ERROR, nil | 
 | 	case cpb.LeveledLogSeverity_FATAL: | 
 | 		return FATAL, nil | 
 | 	default: | 
 | 		return "", fmt.Errorf("unknown severity value %d", s) | 
 | 	} | 
 | } | 
 |  | 
 | func (s Severity) ToProto() cpb.LeveledLogSeverity { | 
 | 	switch s { | 
 | 	case INFO: | 
 | 		return cpb.LeveledLogSeverity_INFO | 
 | 	case WARNING: | 
 | 		return cpb.LeveledLogSeverity_WARNING | 
 | 	case ERROR: | 
 | 		return cpb.LeveledLogSeverity_ERROR | 
 | 	case FATAL: | 
 | 		return cpb.LeveledLogSeverity_FATAL | 
 | 	default: | 
 | 		return cpb.LeveledLogSeverity_INVALID | 
 | 	} | 
 | } |