qmp-testing-suite/golang-producer-consumer/vendor/gitlab-app.eng.qops.net/reporting-framework/ripsaw-logger-golang/logger.go

183 lines
3.6 KiB
Go
Executable File

package ripsawlogger
import (
"fmt"
"io/ioutil"
"os"
"time"
)
const (
LOG_INFO = "info"
LOG_WARN = "warn"
LOG_ERROR = "error"
LOG_AUDIT = "audit"
LOG_ACCESS = "access"
)
var (
levels = []string{LOG_INFO, LOG_WARN, LOG_ERROR, LOG_AUDIT, LOG_ACCESS}
defaultLogger Logger = new(multiLogger) // a list of loggers to send each log entry to
defaults = true // whether to append default data to logs
hostname, _ = os.Hostname()
)
// A LogEntry represents a single line of data destined for a log file and is a simple
// JSON-like map[string]interface{}. May include any arbitrary data.
type LogEntry struct {
Time time.Time
Level string
Contents map[string]interface{}
}
// The Logger interface is implemented by logging targets, which direct the log data to
// an appopriate place.
type Logger interface {
Log(data LogEntry)
Flush()
}
type multiLogger []Logger
func (ml multiLogger) Log(data LogEntry) {
// wrap in default properties
if defaults == true {
data.Contents["_hostname"] = hostname
data.Contents["_language"] = "golang"
}
for _, logger := range ml {
logger.Log(data)
}
}
func (ml multiLogger) Flush() {
for _, logger := range ml {
logger.Flush()
}
}
type filteredLogger struct {
logger Logger
levels []string
}
func NewFilteredLogger(log Logger, levels ...string) filteredLogger {
return filteredLogger{
logger: log,
levels: levels,
}
}
func (l filteredLogger) Log(data LogEntry) {
for _, v := range l.levels {
if data.Level == v {
l.logger.Log(data)
}
}
}
func (l filteredLogger) Flush() {
// nothing
}
func NewStandardLogger() Logger {
return multiLogger([]Logger{
NewFilteredLogger(NewWriterLogger(os.Stdout), LOG_INFO, LOG_WARN, LOG_ERROR),
NewFilteredLogger(NewWriterLogger(ioutil.Discard), LOG_AUDIT, LOG_ACCESS),
})
// return NewWriterLogger(map[string]io.Writer{
// LOG_INFO: os.Stdout,
// LOG_WARN: os.Stdout,
// LOG_ERROR: os.Stderr,
// LOG_AUDIT: ioutil.Discard,
// LOG_ACCESS: ioutil.Discard,
// })
}
func Configure(loggers []Logger) {
logger := multiLogger(loggers)
defaultLogger = &logger
}
func EnableDefaults(def bool) {
defaults = def
}
func Info(args ...interface{}) {
log(newLogEntry(LOG_INFO, args...))
}
func Warn(args ...interface{}) {
log(newLogEntry(LOG_WARN, args...))
}
func Error(args ...interface{}) {
log(newLogEntry(LOG_ERROR, args...))
}
func Audit(args ...interface{}) {
log(newLogEntry(LOG_AUDIT, args...))
}
func Flush() {
defaultLogger.Flush()
}
func getDefaultLogger() Logger {
return defaultLogger
}
func log(entry LogEntry) {
defaultLogger.Log(entry)
}
// formats generic interface{} arguments into a map[string]interface{} that
// can be cleanly converted to JSON and logged.
func newLogEntry(level string, args ...interface{}) LogEntry {
baseObject := make(map[string]interface{}, 0)
var logTime time.Time
if len(args) > 0 {
var message string
// if the first argument is a time.Time object, use it as the message time
if time, ok := args[0].(time.Time); ok {
logTime = time
args = args[1:]
}
// if the final argument is a map, use that
if base, ok := args[len(args)-1].(map[string]interface{}); ok {
baseObject = base
args = args[:len(args)-1]
}
// if we still have any, find a way to messagize them
if len(args) > 0 {
if str, ok := args[0].(string); ok && str != "" {
message = fmt.Sprintf(str, args[1:]...)
} else {
message = fmt.Sprint(args...)
}
}
if message != "" {
baseObject["message"] = message
}
}
return LogEntry{
Time: logTime,
Level: level,
Contents: baseObject,
}
}