mirror of
https://github.com/anchore/syft
synced 2024-11-10 06:14:16 +00:00
add named logger + color formatting
This commit is contained in:
parent
09c7ca8f8f
commit
ae6feed8fc
8 changed files with 103 additions and 27 deletions
|
@ -42,5 +42,5 @@ func setCliOptions() {
|
|||
fmt.Printf("unable to bind flag '%s': %+v", flag, err)
|
||||
}
|
||||
|
||||
rootCmd.Flags().CountVarP(&cliOpts.Verbosity, "verbose", "v", "increase verbosity (-v, -vv, -vvv ...)")
|
||||
rootCmd.Flags().CountVarP(&cliOpts.Verbosity, "verbose", "v", "increase verbosity (-v = info, -vv = debug)")
|
||||
}
|
||||
|
|
12
cmd/init.go
12
cmd/init.go
|
@ -1,15 +1,16 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/anchore/imgbom/imgbom"
|
||||
"github.com/anchore/imgbom/internal/config"
|
||||
"github.com/anchore/imgbom/internal/format"
|
||||
"github.com/anchore/imgbom/internal/log"
|
||||
"github.com/anchore/imgbom/internal/logger"
|
||||
"github.com/spf13/viper"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var appConfig *config.Application
|
||||
|
@ -25,10 +26,10 @@ func initAppConfig() {
|
|||
|
||||
func initLogging() {
|
||||
config := logger.LogConfig{
|
||||
EnableConsole: appConfig.Log.FileLocation == "" && !appConfig.Quiet,
|
||||
EnableConsole: (appConfig.Log.FileLocation == "" || appConfig.CliOptions.Verbosity > 0) && !appConfig.Quiet,
|
||||
EnableFile: appConfig.Log.FileLocation != "",
|
||||
Level: appConfig.Log.LevelOpt,
|
||||
FormatAsJSON: appConfig.Log.FormatAsJSON,
|
||||
Structured: appConfig.Log.Structured,
|
||||
FileLocation: appConfig.Log.FileLocation,
|
||||
}
|
||||
|
||||
|
@ -36,10 +37,11 @@ func initLogging() {
|
|||
}
|
||||
|
||||
func logAppConfig() {
|
||||
appCfgStr, err := json.MarshalIndent(&appConfig, " ", " ")
|
||||
appCfgStr, err := yaml.Marshal(&appConfig)
|
||||
|
||||
if err != nil {
|
||||
log.Debugf("Could not display application config: %+v", err)
|
||||
} else {
|
||||
log.Debugf("Application config:\n%+v", string(appCfgStr))
|
||||
log.Debugf("Application config:\n%+v", format.Magenta.Format(string(appCfgStr)))
|
||||
}
|
||||
}
|
||||
|
|
1
go.mod
1
go.mod
|
@ -14,4 +14,5 @@ require (
|
|||
github.com/spf13/cobra v1.0.0
|
||||
github.com/spf13/viper v1.7.0
|
||||
go.uber.org/zap v1.15.0
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
)
|
||||
|
|
3
go.sum
3
go.sum
|
@ -131,6 +131,7 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
|
|||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
|
@ -280,8 +281,10 @@ github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN
|
|||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
|
|
|
@ -95,7 +95,6 @@ func (pres *Presenter) Present(output io.Writer, img *stereoscopeImg.Image, cata
|
|||
"id": src.ID(),
|
||||
"presenter": "json",
|
||||
}).Errorf("could not get metadata from catalog")
|
||||
|
||||
}
|
||||
|
||||
srcObj := source{
|
||||
|
|
|
@ -20,9 +20,9 @@ type CliOnlyOptions struct {
|
|||
}
|
||||
|
||||
type Application struct {
|
||||
configPath string
|
||||
ConfigPath string
|
||||
PresenterOpt presenter.Option
|
||||
Presenter string `mapstructure:"output"`
|
||||
Output string `mapstructure:"output"`
|
||||
ScopeOpt scope.Option
|
||||
Scope string `mapstructure:"scope"`
|
||||
Quiet bool `mapstructure:"quiet"`
|
||||
|
@ -31,7 +31,7 @@ type Application struct {
|
|||
}
|
||||
|
||||
type Logging struct {
|
||||
FormatAsJSON bool `mapstructure:"structured"`
|
||||
Structured bool `mapstructure:"structured"`
|
||||
LevelOpt zapcore.Level
|
||||
Level string `mapstructure:"level"`
|
||||
FileLocation string `mapstructure:"file"`
|
||||
|
@ -59,7 +59,7 @@ func LoadConfigFromFile(v *viper.Viper, cliOpts *CliOnlyOptions) (*Application,
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse config: %w", err)
|
||||
}
|
||||
config.configPath = v.ConfigFileUsed()
|
||||
config.ConfigPath = v.ConfigFileUsed()
|
||||
|
||||
err = config.Build()
|
||||
if err != nil {
|
||||
|
@ -71,9 +71,9 @@ func LoadConfigFromFile(v *viper.Viper, cliOpts *CliOnlyOptions) (*Application,
|
|||
|
||||
func (cfg *Application) Build() error {
|
||||
// set the presenter
|
||||
presenterOption := presenter.ParseOption(cfg.Presenter)
|
||||
presenterOption := presenter.ParseOption(cfg.Output)
|
||||
if presenterOption == presenter.UnknownPresenter {
|
||||
return fmt.Errorf("bad --output value '%s'", cfg.Presenter)
|
||||
return fmt.Errorf("bad --output value '%s'", cfg.Output)
|
||||
}
|
||||
cfg.PresenterOpt = presenterOption
|
||||
|
||||
|
@ -100,10 +100,10 @@ func (cfg *Application) Build() error {
|
|||
}
|
||||
} else {
|
||||
// set the log level implicitly
|
||||
switch cfg.CliOptions.Verbosity {
|
||||
case 1:
|
||||
switch v := cfg.CliOptions.Verbosity; {
|
||||
case v == 1:
|
||||
cfg.Log.LevelOpt = zapcore.InfoLevel
|
||||
case 2:
|
||||
case v >= 2:
|
||||
cfg.Log.LevelOpt = zapcore.DebugLevel
|
||||
default:
|
||||
cfg.Log.LevelOpt = zapcore.ErrorLevel
|
||||
|
@ -133,14 +133,21 @@ func readConfig(v *viper.Viper, configPath string) error {
|
|||
|
||||
// start searching for valid configs in order...
|
||||
|
||||
// 1. look for .<appname>/config.yaml (in the current directory)
|
||||
// 1. look for .<appname>.yaml (in the current directory)
|
||||
v.AddConfigPath(".")
|
||||
v.SetConfigName(internal.ApplicationName)
|
||||
if err := v.ReadInConfig(); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 2. look for .<appname>/config.yaml (in the current directory)
|
||||
v.AddConfigPath("." + internal.ApplicationName)
|
||||
v.SetConfigName("config")
|
||||
if err := v.ReadInConfig(); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 2. look for ~/.<appname>.yaml
|
||||
// 3. look for ~/.<appname>.yaml
|
||||
home, err := homedir.Dir()
|
||||
if err == nil {
|
||||
v.AddConfigPath(home)
|
||||
|
@ -150,7 +157,7 @@ func readConfig(v *viper.Viper, configPath string) error {
|
|||
}
|
||||
}
|
||||
|
||||
// 3. look for <appname>/config.yaml in xdg locations (starting with xdg home config dir, then moving upwards)
|
||||
// 4. look for <appname>/config.yaml in xdg locations (starting with xdg home config dir, then moving upwards)
|
||||
v.AddConfigPath(path.Join(xdg.ConfigHome, internal.ApplicationName))
|
||||
for _, dir := range xdg.ConfigDirs {
|
||||
v.AddConfigPath(path.Join(dir, internal.ApplicationName))
|
||||
|
|
21
internal/format/color.go
Normal file
21
internal/format/color.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package format
|
||||
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
DefaultColor Color = iota + 30
|
||||
Red
|
||||
Green
|
||||
Yellow
|
||||
Blue
|
||||
Magenta
|
||||
Cyan
|
||||
White
|
||||
)
|
||||
|
||||
type Color uint8
|
||||
|
||||
// TODO: not cross platform (windows...)
|
||||
func (c Color) Format(s string) string {
|
||||
return fmt.Sprintf("\x1b[%dm%s\x1b[0m", c, s)
|
||||
}
|
|
@ -4,14 +4,25 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/anchore/imgbom/imgbom/logger"
|
||||
"github.com/anchore/imgbom/internal/format"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
var levelToColor = map[zapcore.Level]format.Color{
|
||||
zapcore.DebugLevel: format.Magenta,
|
||||
zapcore.InfoLevel: format.Blue,
|
||||
zapcore.WarnLevel: format.Yellow,
|
||||
zapcore.ErrorLevel: format.Red,
|
||||
zapcore.DPanicLevel: format.Red,
|
||||
zapcore.PanicLevel: format.Red,
|
||||
zapcore.FatalLevel: format.Red,
|
||||
}
|
||||
|
||||
type LogConfig struct {
|
||||
EnableConsole bool
|
||||
EnableFile bool
|
||||
FormatAsJSON bool
|
||||
Structured bool
|
||||
Level zapcore.Level
|
||||
FileLocation string
|
||||
}
|
||||
|
@ -20,6 +31,12 @@ type ZapLogger struct {
|
|||
sugaredLogger *zap.SugaredLogger
|
||||
}
|
||||
|
||||
// TODO: Consider a human readable text encoder for better field handeling:
|
||||
// - https://github.com/uber-go/zap/issues/570
|
||||
// - https://github.com/uber-go/zap/pull/123
|
||||
// - TextEncoder w/ old interface: https://github.com/uber-go/zap/blob/6c2107996402d47d559199b78e1c44747fe732f9/text_encoder.go
|
||||
// - New interface example: https://github.com/uber-go/zap/blob/c2633d6de2d6e1170ad8f150660e3cf5310067c8/zapcore/json_encoder.go
|
||||
// - Register the encoder: https://github.com/uber-go/zap/blob/v1.15.0/encoder.go
|
||||
func NewZapLogger(config LogConfig) *ZapLogger {
|
||||
cores := []zapcore.Core{}
|
||||
|
||||
|
@ -31,8 +48,8 @@ func NewZapLogger(config LogConfig) *ZapLogger {
|
|||
}
|
||||
|
||||
if config.EnableFile {
|
||||
writer := zapcore.AddSync(getLogWriter(config.FileLocation))
|
||||
core := zapcore.NewCore(getFileEncoder(config), writer, config.Level)
|
||||
writer := zapcore.AddSync(logFileWriter(config.FileLocation))
|
||||
core := zapcore.NewCore(fileEncoder(config), writer, config.Level)
|
||||
cores = append(cores, core)
|
||||
}
|
||||
|
||||
|
@ -50,27 +67,53 @@ func NewZapLogger(config LogConfig) *ZapLogger {
|
|||
}
|
||||
}
|
||||
|
||||
func (l *ZapLogger) GetNamedLogger(name string) *ZapLogger {
|
||||
return &ZapLogger{
|
||||
sugaredLogger: l.sugaredLogger.Named(name),
|
||||
}
|
||||
}
|
||||
|
||||
func getConsoleEncoder(config LogConfig) zapcore.Encoder {
|
||||
encoderConfig := zap.NewProductionEncoderConfig()
|
||||
if config.FormatAsJSON {
|
||||
if config.Structured {
|
||||
encoderConfig.EncodeName = zapcore.FullNameEncoder
|
||||
encoderConfig.EncodeCaller = zapcore.FullCallerEncoder
|
||||
return zapcore.NewJSONEncoder(encoderConfig)
|
||||
}
|
||||
encoderConfig.EncodeTime = nil
|
||||
encoderConfig.EncodeCaller = nil
|
||||
encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
|
||||
encoderConfig.EncodeLevel = consoleLevelEncoder
|
||||
encoderConfig.EncodeName = nameEncoder
|
||||
return zapcore.NewConsoleEncoder(encoderConfig)
|
||||
}
|
||||
|
||||
func getFileEncoder(config LogConfig) zapcore.Encoder {
|
||||
func nameEncoder(loggerName string, enc zapcore.PrimitiveArrayEncoder) {
|
||||
enc.AppendString("[" + loggerName + "]")
|
||||
}
|
||||
|
||||
func consoleLevelEncoder(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
|
||||
if level != zapcore.InfoLevel {
|
||||
color, ok := levelToColor[level]
|
||||
if !ok {
|
||||
enc.AppendString("[" + level.CapitalString() + "]")
|
||||
} else {
|
||||
enc.AppendString("[" + color.Format(level.CapitalString()) + "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fileEncoder(config LogConfig) zapcore.Encoder {
|
||||
encoderConfig := zap.NewProductionEncoderConfig()
|
||||
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
|
||||
if config.FormatAsJSON {
|
||||
encoderConfig.EncodeName = zapcore.FullNameEncoder
|
||||
encoderConfig.EncodeCaller = zapcore.FullCallerEncoder
|
||||
if config.Structured {
|
||||
return zapcore.NewJSONEncoder(encoderConfig)
|
||||
}
|
||||
return zapcore.NewConsoleEncoder(encoderConfig)
|
||||
}
|
||||
|
||||
func getLogWriter(location string) zapcore.WriteSyncer {
|
||||
func logFileWriter(location string) zapcore.WriteSyncer {
|
||||
file, _ := os.Create(location)
|
||||
return zapcore.AddSync(file)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue