Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
Alex Goodman
2020-09-08 08:37:40 -04:00
parent c348d97fe0
commit 206fd8b7d3
19 changed files with 498 additions and 54 deletions
+78
View File
@@ -0,0 +1,78 @@
package logger
import (
"bytes"
"strings"
"github.com/hashicorp/go-hclog"
)
// Source: https://github.com/hashicorp/go-hclog/blob/master/stdlog.go (MIT License)
// Provides a io.Writer to shim the data out of *log.Logger
// and back into our Logger. This is basically the only way to
// build upon *log.Logger.
type HCLogStdlogAdapter struct {
log hclog.Logger
inferLevels bool
forceLevel hclog.Level
}
// Take the data, infer the levels if configured, and send it through
// a regular Logger.
func (s *HCLogStdlogAdapter) Write(data []byte) (int, error) {
str := string(bytes.TrimRight(data, " \t\n"))
if s.forceLevel != hclog.NoLevel {
// Use pickLevel to strip log levels included in the line since we are
// forcing the level
_, str := s.pickLevel(str)
// Log at the forced level
s.dispatch(str, s.forceLevel)
} else if s.inferLevels {
level, str := s.pickLevel(str)
s.dispatch(str, level)
} else {
s.log.Info(str)
}
return len(data), nil
}
func (s *HCLogStdlogAdapter) dispatch(str string, level hclog.Level) {
switch level {
case hclog.Trace:
s.log.Trace(str)
case hclog.Debug:
s.log.Debug(str)
case hclog.Info:
s.log.Info(str)
case hclog.Warn:
s.log.Warn(str)
case hclog.Error:
s.log.Error(str)
default:
s.log.Info(str)
}
}
// Detect, based on conventions, what log level this is.
func (s *HCLogStdlogAdapter) pickLevel(str string) (hclog.Level, string) {
switch {
case strings.HasPrefix(str, "[DEBUG]"):
return hclog.Debug, strings.TrimSpace(str[7:])
case strings.HasPrefix(str, "[TRACE]"):
return hclog.Trace, strings.TrimSpace(str[7:])
case strings.HasPrefix(str, "[INFO]"):
return hclog.Info, strings.TrimSpace(str[6:])
case strings.HasPrefix(str, "[WARN]"):
return hclog.Warn, strings.TrimSpace(str[7:])
case strings.HasPrefix(str, "[ERROR]"):
return hclog.Error, strings.TrimSpace(str[7:])
case strings.HasPrefix(str, "[ERR]"):
return hclog.Error, strings.TrimSpace(str[5:])
default:
return hclog.Info, str
}
}
+148
View File
@@ -0,0 +1,148 @@
package logger
import (
"io"
"log"
"github.com/anchore/syft/syft/logger"
"github.com/hashicorp/go-hclog"
)
// integrity check
var _ hclog.Logger = &LoggerHCLogAdapter{}
// LoggerHCLogAdapter is used to (partially) adapt a syft logger.Logger object to the hclog.Logger interface for syft plugins.
// Note: this does not implement all functionality required by the hclog.Logger.
type LoggerHCLogAdapter struct {
Logger logger.Logger
}
func NewLoggerHCLogAdapter(logger logger.Logger) *LoggerHCLogAdapter {
return &LoggerHCLogAdapter{
Logger: logger,
}
}
// Args are alternating key, val pairs
// keys must be strings
// vals can be any type, but display is implementation specific
// Emit a message and key/value pairs at a provided log level
func (l *LoggerHCLogAdapter) Log(level hclog.Level, msg string, args ...interface{}) {
l.Logger.Info(append([]interface{}{msg}, args...)...)
}
// Emit a message and key/value pairs at the TRACE level
func (l *LoggerHCLogAdapter) Trace(msg string, args ...interface{}) {
l.Logger.Debug(append([]interface{}{msg}, args...)...)
}
// Emit a message and key/value pairs at the DEBUG level
func (l *LoggerHCLogAdapter) Debug(msg string, args ...interface{}) {
l.Logger.Debug(append([]interface{}{msg}, args...)...)
}
// Emit a message and key/value pairs at the INFO level
func (l *LoggerHCLogAdapter) Info(msg string, args ...interface{}) {
l.Logger.Info(append([]interface{}{msg}, args...)...)
}
// Emit a message and key/value pairs at the WARN level
func (l *LoggerHCLogAdapter) Warn(msg string, args ...interface{}) {
l.Logger.Error(append([]interface{}{msg}, args...)...)
}
// Emit a message and key/value pairs at the ERROR level
func (l *LoggerHCLogAdapter) Error(msg string, args ...interface{}) {
l.Logger.Error(append([]interface{}{msg}, args...)...)
}
// Indicate if TRACE logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LoggerHCLogAdapter) IsTrace() bool {
// TODO: not implemented
return true
}
// Indicate if DEBUG logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LoggerHCLogAdapter) IsDebug() bool {
// TODO: not implemented
return true
}
// Indicate if INFO logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LoggerHCLogAdapter) IsInfo() bool {
// TODO: not implemented
return true
}
// Indicate if WARN logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LoggerHCLogAdapter) IsWarn() bool {
// TODO: not implemented
return true
}
// Indicate if ERROR logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LoggerHCLogAdapter) IsError() bool {
// TODO: not implemented
return true
}
// ImpliedArgs returns With key/value pairs
func (l *LoggerHCLogAdapter) ImpliedArgs() []interface{} {
// TODO: not implemented
return nil
}
// Creates a sublogger that will always have the given key/value pairs
func (l *LoggerHCLogAdapter) With(args ...interface{}) hclog.Logger {
// TODO: not implemented
return l
}
// Returns the Name of the logger
func (l *LoggerHCLogAdapter) Name() string {
// TODO: not implemented
return ""
}
// Create a logger that will prepend the name string on the front of all messages.
// If the logger already has a name, the new value will be appended to the current
// name. That way, a major subsystem can use this to decorate all it's own logs
// without losing context.
func (l *LoggerHCLogAdapter) Named(name string) hclog.Logger {
// TODO: not implemented
return l
}
// Create a logger that will prepend the name string on the front of all messages.
// This sets the name of the logger to the value directly, unlike Named which honor
// the current name as well.
func (l *LoggerHCLogAdapter) ResetNamed(name string) hclog.Logger {
// TODO: not implemented
return l
}
// Updates the level. This should affect all sub-loggers as well. If an
// implementation cannot update the level on the fly, it should no-op.
func (l *LoggerHCLogAdapter) SetLevel(level hclog.Level) {
// TODO: not implemented
}
// Return a value that conforms to the stdlib log.Logger interface
func (l *LoggerHCLogAdapter) StandardLogger(opts *hclog.StandardLoggerOptions) *log.Logger {
return log.New(l.StandardWriter(opts), "", log.LstdFlags)
}
// Return a value that conforms to io.Writer, which can be passed into log.SetOutput()
func (l *LoggerHCLogAdapter) StandardWriter(opts *hclog.StandardLoggerOptions) io.Writer {
return &HCLogStdlogAdapter{
log: l,
inferLevels: opts.InferLevels,
forceLevel: opts.ForceLevel,
}
}
+25 -12
View File
@@ -6,10 +6,15 @@ import (
"io/ioutil"
"os"
"github.com/anchore/syft/syft/logger"
"github.com/sirupsen/logrus"
prefixed "github.com/x-cray/logrus-prefixed-formatter"
)
// integrity check
var _ logger.Logger = &LogrusLogger{}
var _ logger.Logger = &LogrusNestedLogger{}
type LogrusConfig struct {
EnableConsole bool
EnableFile bool
@@ -76,42 +81,50 @@ func NewLogrusLogger(cfg LogrusConfig) *LogrusLogger {
}
}
func (l *LogrusLogger) Debugf(format string, args ...interface{}) {
l.Logger.Debugf(format, args...)
func (l *LogrusLogger) Errorf(format string, args ...interface{}) {
l.Logger.Errorf(format, args...)
}
func (l *LogrusLogger) Infof(format string, args ...interface{}) {
l.Logger.Infof(format, args...)
}
func (l *LogrusLogger) Debug(args ...interface{}) {
l.Logger.Debug(args...)
func (l *LogrusLogger) Debugf(format string, args ...interface{}) {
l.Logger.Debugf(format, args...)
}
func (l *LogrusLogger) Error(args ...interface{}) {
l.Logger.Error(args...)
}
func (l *LogrusLogger) Info(args ...interface{}) {
l.Logger.Info(args...)
}
func (l *LogrusLogger) Errorf(format string, args ...interface{}) {
l.Logger.Errorf(format, args...)
func (l *LogrusLogger) Debug(args ...interface{}) {
l.Logger.Debug(args...)
}
func (l *LogrusNestedLogger) Debugf(format string, args ...interface{}) {
l.Logger.Debugf(format, args...)
func (l *LogrusNestedLogger) Errorf(format string, args ...interface{}) {
l.Logger.Errorf(format, args...)
}
func (l *LogrusNestedLogger) Infof(format string, args ...interface{}) {
l.Logger.Infof(format, args...)
}
func (l *LogrusNestedLogger) Debug(args ...interface{}) {
l.Logger.Debug(args...)
func (l *LogrusNestedLogger) Debugf(format string, args ...interface{}) {
l.Logger.Debugf(format, args...)
}
func (l *LogrusNestedLogger) Error(args ...interface{}) {
l.Logger.Error(args...)
}
func (l *LogrusNestedLogger) Info(args ...interface{}) {
l.Logger.Info(args...)
}
func (l *LogrusNestedLogger) Errorf(format string, args ...interface{}) {
l.Logger.Errorf(format, args...)
func (l *LogrusNestedLogger) Debug(args ...interface{}) {
l.Logger.Debug(args...)
}
+181
View File
@@ -0,0 +1,181 @@
package logger
import (
"fmt"
"io"
"log"
"github.com/hashicorp/go-hclog"
"github.com/sirupsen/logrus"
)
// integrity check
var _ hclog.Logger = &LogrusHCLogAdapter{}
var levels = map[hclog.Level]logrus.Level{
hclog.NoLevel: logrus.PanicLevel,
hclog.Error: logrus.ErrorLevel,
hclog.Warn: logrus.WarnLevel,
hclog.Info: logrus.InfoLevel,
hclog.Debug: logrus.DebugLevel,
hclog.Trace: logrus.TraceLevel,
}
// LogrusHCLogAdapter is used to adapt a logrus logger object to the hclog.Logger interface for syft plugins.
type LogrusHCLogAdapter struct {
logger *logrus.Logger
entry *logrus.Entry
}
func NewLogrusHCLogAdapter(logger *logrus.Logger, fields map[string]interface{}) *LogrusHCLogAdapter {
if fields != nil {
return &LogrusHCLogAdapter{
logger: logger,
entry: logger.WithFields(fields),
}
}
return &LogrusHCLogAdapter{
logger: logger,
}
}
func (l *LogrusHCLogAdapter) log(level hclog.Level, msg string, args ...interface{}) {
if l.entry != nil {
l.entry.WithFields(makeArgFields(args...)).Log(levels[level], msg)
return
}
l.logger.WithFields(makeArgFields(args...)).Log(levels[level], msg)
}
// Args are alternating key, val pairs
// keys must be strings
// vals can be any type, but display is implementation specific
// Emit a message and key/value pairs at a provided log level
func (l *LogrusHCLogAdapter) Log(level hclog.Level, msg string, args ...interface{}) {
l.log(level, msg, args...)
}
// Emit a message and key/value pairs at the TRACE level
func (l *LogrusHCLogAdapter) Trace(msg string, args ...interface{}) {
l.log(hclog.Trace, msg, args...)
}
// Emit a message and key/value pairs at the DEBUG level
func (l *LogrusHCLogAdapter) Debug(msg string, args ...interface{}) {
l.log(hclog.Debug, msg, args...)
}
// Emit a message and key/value pairs at the INFO level
func (l *LogrusHCLogAdapter) Info(msg string, args ...interface{}) {
l.log(hclog.Info, msg, args...)
}
// Emit a message and key/value pairs at the WARN level
func (l *LogrusHCLogAdapter) Warn(msg string, args ...interface{}) {
l.log(hclog.Warn, msg, args...)
}
// Emit a message and key/value pairs at the ERROR level
func (l *LogrusHCLogAdapter) Error(msg string, args ...interface{}) {
l.log(hclog.Error, msg, args...)
}
// Indicate if TRACE logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LogrusHCLogAdapter) IsTrace() bool {
return l.logger.Level <= logrus.TraceLevel
}
// Indicate if DEBUG logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LogrusHCLogAdapter) IsDebug() bool {
return l.logger.Level <= logrus.DebugLevel
}
// Indicate if INFO logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LogrusHCLogAdapter) IsInfo() bool {
return l.logger.Level <= logrus.InfoLevel
}
// Indicate if WARN logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LogrusHCLogAdapter) IsWarn() bool {
return l.logger.Level <= logrus.WarnLevel
}
// Indicate if ERROR logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LogrusHCLogAdapter) IsError() bool {
return l.logger.Level <= logrus.ErrorLevel
}
// ImpliedArgs returns With key/value pairs
func (l *LogrusHCLogAdapter) ImpliedArgs() []interface{} {
// TODO: not implemented
return nil
}
// Creates a sublogger that will always have the given key/value pairs
func (l *LogrusHCLogAdapter) With(args ...interface{}) hclog.Logger {
l.entry = l.logger.WithFields(makeArgFields(args...))
return l
}
// Returns the Name of the logger
func (l *LogrusHCLogAdapter) Name() string {
// TODO: not implemented
return ""
}
// Create a logger that will prepend the name string on the front of all messages.
// If the logger already has a name, the new value will be appended to the current
// name. That way, a major subsystem can use this to decorate all it's own logs
// without losing context.
func (l *LogrusHCLogAdapter) Named(name string) hclog.Logger {
return NewLogrusHCLogAdapter(l.logger, logrus.Fields{"name": name})
}
// Create a logger that will prepend the name string on the front of all messages.
// This sets the name of the logger to the value directly, unlike Named which honor
// the current name as well.
func (l *LogrusHCLogAdapter) ResetNamed(name string) hclog.Logger {
return NewLogrusHCLogAdapter(l.logger, logrus.Fields{"name": name})
}
// Updates the level. This should affect all sub-loggers as well. If an
// implementation cannot update the level on the fly, it should no-op.
func (l *LogrusHCLogAdapter) SetLevel(level hclog.Level) {
l.logger.SetLevel(levels[level])
}
// Return a value that conforms to the stdlib log.Logger interface
func (l *LogrusHCLogAdapter) StandardLogger(opts *hclog.StandardLoggerOptions) *log.Logger {
return log.New(l.StandardWriter(opts), "", log.LstdFlags)
}
// Return a value that conforms to io.Writer, which can be passed into log.SetOutput()
func (l *LogrusHCLogAdapter) StandardWriter(opts *hclog.StandardLoggerOptions) io.Writer {
return &HCLogStdlogAdapter{
log: l,
inferLevels: opts.InferLevels,
forceLevel: opts.ForceLevel,
}
}
func makeArgFields(args ...interface{}) logrus.Fields {
if len(args)%2 != 0 {
panic(fmt.Errorf("odd number of logger key-value pairs (%d): %+v", len(args), args))
}
var key string
var fields = make(logrus.Fields)
for i, arg := range args {
if i%2 == 0 {
key = arg.(string)
} else {
fields[key] = arg
}
}
return fields
}