replace zap logger with logrus (#80)

This commit is contained in:
Alex Goodman 2020-08-01 11:58:10 -04:00 committed by GitHub
parent 861883c8d4
commit 11731fac40
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 274 additions and 271 deletions

View file

@ -4,22 +4,21 @@ import (
"fmt"
"os"
"github.com/anchore/stereoscope"
"github.com/wagoodman/go-partybus"
"github.com/anchore/grype/grype"
"github.com/anchore/grype/internal/config"
"github.com/anchore/grype/internal/format"
"github.com/anchore/grype/internal/logger"
"github.com/anchore/stereoscope"
"github.com/anchore/syft/syft"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"go.uber.org/zap"
"github.com/wagoodman/go-partybus"
"gopkg.in/yaml.v2"
)
var appConfig *config.Application
var log *zap.SugaredLogger
var log *logrus.Logger
var cliOnlyOpts config.CliOnlyOptions
var eventBus *partybus.Bus
var eventSubscription *partybus.Subscription
@ -70,7 +69,7 @@ func initAppConfig() {
}
func initLogging() {
cfg := logger.LogConfig{
cfg := logger.LogrusConfig{
EnableConsole: (appConfig.Log.FileLocation == "" || appConfig.CliOptions.Verbosity > 0) && !appConfig.Quiet,
EnableFile: appConfig.Log.FileLocation != "",
Level: appConfig.Log.LevelOpt,
@ -78,10 +77,18 @@ func initLogging() {
FileLocation: appConfig.Log.FileLocation,
}
logWrapper := logger.NewZapLogger(cfg)
logWrapper := logger.NewLogrusLogger(cfg)
log = logWrapper.Logger
grype.SetLogger(logWrapper)
syft.SetLogger(logWrapper)
// add a structured field to all loggers of dependencies
syft.SetLogger(&logger.LogrusNestedLogger{
Logger: log.WithField("from-lib", "syft"),
})
stereoscope.SetLogger(&logger.LogrusNestedLogger{
Logger: log.WithField("from-lib", "steroscope"),
})
}
func logAppConfig() {

View file

@ -27,23 +27,17 @@ func init() {
func runDbUpdateCmd(_ *cobra.Command, _ []string) int {
dbCurator := db.NewCurator(appConfig.Db.ToCuratorConfig())
updateAvailable, updateEntry, err := dbCurator.IsUpdateAvailable()
updated, err := dbCurator.Update()
if err != nil {
// TODO: should this be so fatal? we can certainly continue with a warning...
log.Errorf("unable to check for vulnerability database update: %+v", err)
log.Errorf("unable to update vulnerability database: %+v", err)
return 1
}
if updateAvailable {
err = dbCurator.UpdateTo(updateEntry)
if err != nil {
log.Errorf("unable to update vulnerability database: %+v", err)
return 1
}
} else {
fmt.Println("No vulnerability database update available")
if updated {
fmt.Println("Vulnerability database updated!")
return 0
}
fmt.Println("Vulnerability database updated!")
fmt.Println("No vulnerability database update available")
return 0
}

View file

@ -129,9 +129,6 @@ func startWorker(userInput string) <-chan error {
go func() {
defer wg.Done()
// TODO: move this log entry to syft
log.Info("Cataloging image")
catalog, _, theDistro, err = syft.Catalog(userInput, appConfig.ScopeOpt)
if err != nil {
errs <- fmt.Errorf("failed to catalog: %w", err)

5
go.mod
View file

@ -8,7 +8,7 @@ require (
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
github.com/anchore/grype-db v0.0.0-20200730184339-fc1f236ce8b2
github.com/anchore/stereoscope v0.0.0-20200706164556-7cf39d7f4639
github.com/anchore/syft v0.1.0-beta.2.0.20200730191658-271ba35c8520
github.com/anchore/syft v0.1.0-beta.2.0.20200801155638-78515da28521
github.com/dustin/go-humanize v1.0.0
github.com/facebookincubator/nvdtools v0.1.4-0.20200622182922-aed862a62ae6
github.com/go-test/deep v1.0.7
@ -19,13 +19,14 @@ require (
github.com/mitchellh/go-homedir v1.1.0
github.com/olekukonko/tablewriter v0.0.4
github.com/sergi/go-diff v1.1.0
github.com/sirupsen/logrus v1.6.0
github.com/spf13/afero v1.3.2
github.com/spf13/cobra v1.0.0
github.com/spf13/viper v1.7.0
github.com/wagoodman/go-partybus v0.0.0-20200526224238-eb215533f07d
github.com/wagoodman/go-progress v0.0.0-20200731105512-1020f39e6240
github.com/wagoodman/jotframe v0.0.0-20200730190914-3517092dd163
go.uber.org/zap v1.15.0
github.com/x-cray/logrus-prefixed-formatter v0.5.2
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9
gopkg.in/yaml.v2 v2.3.0
)

16
go.sum
View file

@ -121,8 +121,8 @@ github.com/anchore/stereoscope v0.0.0-20200520221116-025e07f1c93e h1:QBwtrM0MXi0
github.com/anchore/stereoscope v0.0.0-20200520221116-025e07f1c93e/go.mod h1:bkyLl5VITnrmgErv4S1vDfVz/TGAZ5il6161IQo7w2g=
github.com/anchore/stereoscope v0.0.0-20200706164556-7cf39d7f4639 h1:J1oytkj+aBuACNF2whtEiVxRXIZ8zwT+EiPTqm/FvwA=
github.com/anchore/stereoscope v0.0.0-20200706164556-7cf39d7f4639/go.mod h1:WntReQTI/I27FOQ87UgLVVzWgku6+ZsqfOTLxpIZFCs=
github.com/anchore/syft v0.1.0-beta.2.0.20200730191658-271ba35c8520 h1:PxH4oUP56qz0afZO2YGzHRCftLrYEVHUExqukmhTlLI=
github.com/anchore/syft v0.1.0-beta.2.0.20200730191658-271ba35c8520/go.mod h1:usYLBr5UXjUSVgki8Zy/wI7JC+5t5izMF4zaEEczr3g=
github.com/anchore/syft v0.1.0-beta.2.0.20200801155638-78515da28521 h1:a3s2ZqdtZIF5Hplk2otwSAzpuvKfVQsyTQBhXNFmK5A=
github.com/anchore/syft v0.1.0-beta.2.0.20200801155638-78515da28521/go.mod h1:0EPCQUJaV1ZmuCuxJl0jEJJs3QNx+VA0yGo6/BzetfQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
@ -461,6 +461,7 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
@ -543,6 +544,7 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
@ -553,6 +555,7 @@ github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
@ -566,6 +569,7 @@ github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb44
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
@ -607,12 +611,14 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg=
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
@ -777,14 +783,14 @@ github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59b
github.com/wagoodman/go-partybus v0.0.0-20200526224238-eb215533f07d h1:KOxOL6qpmqwoPloNwi+CEgc1ayjHNOFNrvoOmeDOjDg=
github.com/wagoodman/go-partybus v0.0.0-20200526224238-eb215533f07d/go.mod h1:JPirS5jde/CF5qIjcK4WX+eQmKXdPc6vcZkJ/P0hfPw=
github.com/wagoodman/go-progress v0.0.0-20200621122631-1a2120f0695a/go.mod h1:jLXFoL31zFaHKAAyZUh+sxiTDFe1L1ZHrcK2T1itVKA=
github.com/wagoodman/go-progress v0.0.0-20200621153512-2778c704bf22 h1:GYaiTP0ywrCjJ4qMxxCg+YKPSDMeFJg6i1X9X55LJCA=
github.com/wagoodman/go-progress v0.0.0-20200621153512-2778c704bf22/go.mod h1:jLXFoL31zFaHKAAyZUh+sxiTDFe1L1ZHrcK2T1itVKA=
github.com/wagoodman/go-progress v0.0.0-20200731105512-1020f39e6240 h1:r6BlIP7CVZtMlxUQhT40h1IE1TzEgKVqwmsVGuscvdk=
github.com/wagoodman/go-progress v0.0.0-20200731105512-1020f39e6240/go.mod h1:jLXFoL31zFaHKAAyZUh+sxiTDFe1L1ZHrcK2T1itVKA=
github.com/wagoodman/go-rpmdb v0.0.0-20200719223757-ce54a4b0607b h1:elYGLFZPymeTWJ6qA3tIzFet3LQ9D/Jl6HLWNyFjdQc=
github.com/wagoodman/go-rpmdb v0.0.0-20200719223757-ce54a4b0607b/go.mod h1:MjoIZzKmbYfcpbC6ARWMcHijAjtLBViDaHcayXKWQWI=
github.com/wagoodman/jotframe v0.0.0-20200730190914-3517092dd163 h1:qoZwR+bHbFFNirY4Yt7lqbOXnFAMnlFfR89w0TXwjrc=
github.com/wagoodman/jotframe v0.0.0-20200730190914-3517092dd163/go.mod h1:DzXZ1wfRedNhC3KQTick8Gf3CEPMFHsP5k4R/ldjKtw=
github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg=
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
github.com/xanzy/go-gitlab v0.32.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
@ -1162,6 +1168,7 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
@ -1173,6 +1180,7 @@ gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=

View file

@ -83,6 +83,52 @@ func (c *Curator) Delete() error {
return c.fs.RemoveAll(c.config.DbDir)
}
func (c *Curator) Update() (bool, error) {
// let consumers know of a monitorable event (download + import stages)
importProgress := &progress.Manual{
Total: 1,
}
stage := &progress.Stage{
Current: "checking for update",
}
downloadProgress := &progress.Manual{
Total: 1,
}
aggregateProgress := progress.NewAggregator(progress.DefaultStrategy, downloadProgress, importProgress)
bus.Publish(partybus.Event{
Type: event.UpdateVulnerabilityDatabase,
Value: progress.StagedProgressable(&struct {
progress.Stager
progress.Progressable
}{
Stager: progress.Stager(stage),
Progressable: progress.Progressable(aggregateProgress),
}),
})
defer downloadProgress.SetCompleted()
defer importProgress.SetCompleted()
updateAvailable, updateEntry, err := c.IsUpdateAvailable()
if err != nil {
// we want to continue if possible even if we can't check for an update
log.Infof("unable to check for vulnerability database update")
log.Debugf("check for vulnerability update failed: %+v", err)
}
if updateAvailable {
log.Infof("Downloading new vulnerability DB")
err = c.UpdateTo(updateEntry, downloadProgress, importProgress, stage)
if err != nil {
return false, fmt.Errorf("unable to update vulnerability database: %w", err)
}
log.Infof("Updated vulnerability DB to version=%d built=%q", updateEntry.Version, updateEntry.Built.String())
return true, nil
}
stage.Current = "no update available"
return false, nil
}
func (c *Curator) IsUpdateAvailable() (bool, *curation.ListingEntry, error) {
log.Debugf("checking for available database updates")
@ -153,31 +199,8 @@ func (c *Curator) ImportFrom(dbArchivePath string) error {
return c.fs.RemoveAll(tempDir)
}
func (c *Curator) UpdateTo(listing *curation.ListingEntry) error {
// let consumers know of a monitorable event (download + import stages)
importProgress := &progress.Manual{
Total: 1,
}
stage := &progress.Stage{
Current: "downloading",
}
downloadProgress := &progress.Manual{
Total: 1,
}
aggregateProgress := progress.NewAggregator(progress.DefaultStrategy, downloadProgress, importProgress)
bus.Publish(partybus.Event{
Type: event.UpdateVulnerabilityDatabase,
Source: path.Base(listing.URL.Path),
Value: progress.StagedProgressable(&struct {
progress.Stager
progress.Progressable
}{
Stager: progress.Stager(stage),
Progressable: progress.Progressable(aggregateProgress),
}),
})
func (c *Curator) UpdateTo(listing *curation.ListingEntry, downloadProgress, importProgress *progress.Manual, stage *progress.Stage) error {
stage.Current = "downloading"
// note: the temp directory is persisted upon download/validation/activation failure to allow for investigation
tempDir, err := c.download(listing, downloadProgress)
if err != nil {

View file

@ -49,22 +49,17 @@ func ParseAppUpdateAvailable(e partybus.Event) (string, error) {
return newVersion, nil
}
func ParseUpdateVulnerabilityDatabase(e partybus.Event) (string, progress.StagedProgressable, error) {
func ParseUpdateVulnerabilityDatabase(e partybus.Event) (progress.StagedProgressable, error) {
if err := checkEventType(e.Type, event.UpdateVulnerabilityDatabase); err != nil {
return "", nil, err
}
dbArchiveName, ok := e.Source.(string)
if !ok {
return "", nil, newPayloadErr(e.Type, "Source", e.Source)
return nil, err
}
prog, ok := e.Value.(progress.StagedProgressable)
if !ok {
return "", nil, newPayloadErr(e.Type, "Value", e.Value)
return nil, newPayloadErr(e.Type, "Value", e.Value)
}
return dbArchiveName, prog, nil
return prog, nil
}
func ParseVulnerabilityScanningStarted(e partybus.Event) (*matcher.Monitor, error) {

View file

@ -1,8 +1,6 @@
package grype
import (
"fmt"
"github.com/anchore/grype/internal/bus"
"github.com/wagoodman/go-partybus"
@ -21,7 +19,6 @@ import (
)
func FindVulnerabilities(provider vulnerability.Provider, userImageStr string, scopeOpt scope.Option) (result.Result, *pkg.Catalog, *scope.Scope, error) {
log.Info("Cataloging image")
catalog, theScope, theDistro, err := syft.Catalog(userImageStr, scopeOpt)
if err != nil {
return result.Result{}, nil, nil, err
@ -46,19 +43,9 @@ func LoadVulnerabilityDb(cfg db.Config, update bool) (vulnerability.Provider, er
dbCurator := db.NewCurator(cfg)
if update {
updateAvailable, updateEntry, err := dbCurator.IsUpdateAvailable()
_, err := dbCurator.Update()
if err != nil {
// we want to continue if possible even if we can't check for an update
log.Infof("unable to check for vulnerability database update")
log.Debugf("check for vulnerability update failed: %+v", err)
}
if updateAvailable {
log.Infof("Downloading new vulnerability DB")
err = dbCurator.UpdateTo(updateEntry)
if err != nil {
return nil, fmt.Errorf("unable to update vulnerability database: %w", err)
}
log.Infof("Updated vulnerability DB to version=%d built=%q", updateEntry.Version, updateEntry.Built.String())
return nil, err
}
}

View file

@ -39,7 +39,7 @@ func (pres *Presenter) Present(output io.Writer) error {
}
if len(rows) == 0 {
_, err := io.WriteString(output, "No vulnerabilities found")
_, err := io.WriteString(output, "No vulnerabilities found\n")
return err
}

View file

@ -1 +1 @@
No vulnerabilities found
No vulnerabilities found

View file

@ -5,6 +5,8 @@ import (
"path"
"strings"
"github.com/sirupsen/logrus"
"github.com/anchore/grype/grype/presenter"
"github.com/adrg/xdg"
@ -13,7 +15,6 @@ import (
"github.com/anchore/syft/syft/scope"
"github.com/mitchellh/go-homedir"
"github.com/spf13/viper"
"go.uber.org/zap/zapcore"
)
type CliOnlyOptions struct {
@ -37,7 +38,7 @@ type Application struct {
type Logging struct {
Structured bool `mapstructure:"structured"`
LevelOpt zapcore.Level
LevelOpt logrus.Level
Level string `mapstructure:"level"`
FileLocation string `mapstructure:"file"`
}
@ -116,27 +117,28 @@ func (cfg *Application) Build() error {
// TODO: this is bad: quiet option trumps all other logging options
// we should be able to quiet the console logging and leave file logging alone...
// ... this will be an enhancement for later
cfg.Log.LevelOpt = zapcore.PanicLevel
cfg.Log.LevelOpt = logrus.PanicLevel
} else {
if cfg.Log.Level != "" {
if cfg.CliOptions.Verbosity > 0 {
return fmt.Errorf("cannot explicitly set log level (cfg file or env var) and use -v flag together")
}
// set the log level explicitly
err := cfg.Log.LevelOpt.Set(cfg.Log.Level)
lvl, err := logrus.ParseLevel(strings.ToLower(cfg.Log.Level))
if err != nil {
return fmt.Errorf("bad log level value '%s': %+v", cfg.Log.Level, err)
return fmt.Errorf("bad log level configured (%q): %w", cfg.Log.Level, err)
}
// set the log level explicitly
cfg.Log.LevelOpt = lvl
} else {
// set the log level implicitly
switch v := cfg.CliOptions.Verbosity; {
case v == 1:
cfg.Log.LevelOpt = zapcore.InfoLevel
cfg.Log.LevelOpt = logrus.InfoLevel
case v >= 2:
cfg.Log.LevelOpt = zapcore.DebugLevel
cfg.Log.LevelOpt = logrus.DebugLevel
default:
cfg.Log.LevelOpt = zapcore.ErrorLevel
cfg.Log.LevelOpt = logrus.ErrorLevel
}
}
}

115
internal/logger/logrus.go Normal file
View file

@ -0,0 +1,115 @@
package logger
import (
"fmt"
"io"
"io/ioutil"
"os"
"github.com/sirupsen/logrus"
prefixed "github.com/x-cray/logrus-prefixed-formatter"
)
type LogrusConfig struct {
EnableConsole bool
EnableFile bool
Structured bool
Level logrus.Level
FileLocation string
}
type LogrusLogger struct {
Config LogrusConfig
Logger *logrus.Logger
}
type LogrusNestedLogger struct {
Logger *logrus.Entry
}
func NewLogrusLogger(cfg LogrusConfig) *LogrusLogger {
appLogger := logrus.New()
var output io.Writer
switch {
case cfg.EnableConsole && cfg.EnableFile:
logFile, err := os.OpenFile(cfg.FileLocation, os.O_WRONLY|os.O_CREATE, 0755)
if err != nil {
panic(fmt.Errorf("unable to setup log file: %w", err))
}
output = io.MultiWriter(os.Stderr, logFile)
case cfg.EnableConsole:
output = os.Stderr
case cfg.EnableFile:
logFile, err := os.OpenFile(cfg.FileLocation, os.O_WRONLY|os.O_CREATE, 0755)
if err != nil {
panic(fmt.Errorf("unable to setup log file: %w", err))
}
output = logFile
default:
output = ioutil.Discard
}
appLogger.SetOutput(output)
appLogger.SetLevel(cfg.Level)
if cfg.Structured {
appLogger.SetFormatter(&logrus.JSONFormatter{
TimestampFormat: "2006-01-02 15:04:05",
DisableTimestamp: false,
DisableHTMLEscape: false,
PrettyPrint: false,
})
} else {
appLogger.SetFormatter(&prefixed.TextFormatter{
TimestampFormat: "2006-01-02 15:04:05",
ForceColors: true,
ForceFormatting: true,
})
}
return &LogrusLogger{
Config: cfg,
Logger: appLogger,
}
}
func (l *LogrusLogger) Debugf(format string, args ...interface{}) {
l.Logger.Debugf(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) Info(args ...interface{}) {
l.Logger.Info(args...)
}
func (l *LogrusLogger) Errorf(format string, args ...interface{}) {
l.Logger.Errorf(format, args...)
}
func (l *LogrusNestedLogger) Debugf(format string, args ...interface{}) {
l.Logger.Debugf(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) Info(args ...interface{}) {
l.Logger.Info(args...)
}
func (l *LogrusNestedLogger) Errorf(format string, args ...interface{}) {
l.Logger.Errorf(format, args...)
}

View file

@ -1,141 +0,0 @@
package logger
import (
"os"
"github.com/anchore/grype/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
Structured bool
Level zapcore.Level
FileLocation string
}
type ZapLogger struct {
Config LogConfig
Logger *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 {
appLogger := ZapLogger{
Config: config,
}
cores := []zapcore.Core{}
if config.EnableConsole {
// note: the report should go to stdout, all logs should go to stderr
writer := zapcore.Lock(os.Stderr)
core := zapcore.NewCore(appLogger.getConsoleEncoder(config), writer, config.Level)
cores = append(cores, core)
}
if config.EnableFile {
writer := zapcore.AddSync(appLogger.logFileWriter(config.FileLocation))
core := zapcore.NewCore(appLogger.fileEncoder(config), writer, config.Level)
cores = append(cores, core)
}
combinedCore := zapcore.NewTee(cores...)
// AddCallerSkip skips 2 number of callers, this is important else the file that gets
// logged will always be the wrapped file (In our case logger.go)
appLogger.Logger = zap.New(
combinedCore,
zap.AddCallerSkip(2),
zap.AddCaller(),
).Sugar()
return &appLogger
}
func (l *ZapLogger) GetNamedLogger(name string) *ZapLogger {
return &ZapLogger{
Logger: l.Logger.Named(name),
}
}
func (l *ZapLogger) getConsoleEncoder(config LogConfig) zapcore.Encoder {
encoderConfig := zap.NewProductionEncoderConfig()
if config.Structured {
encoderConfig.EncodeName = zapcore.FullNameEncoder
encoderConfig.EncodeCaller = zapcore.FullCallerEncoder
return zapcore.NewJSONEncoder(encoderConfig)
}
encoderConfig.EncodeTime = nil
encoderConfig.EncodeCaller = nil
encoderConfig.EncodeLevel = l.consoleLevelEncoder
encoderConfig.EncodeName = l.nameEncoder
return zapcore.NewConsoleEncoder(encoderConfig)
}
func (l *ZapLogger) nameEncoder(loggerName string, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString("[" + loggerName + "]")
}
func (l *ZapLogger) consoleLevelEncoder(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
if level != zapcore.InfoLevel || l.Config.Level == zapcore.DebugLevel {
color, ok := levelToColor[level]
if !ok {
enc.AppendString("[" + level.CapitalString() + "]")
} else {
enc.AppendString("[" + color.Format(level.CapitalString()) + "]")
}
}
}
func (l *ZapLogger) fileEncoder(config LogConfig) zapcore.Encoder {
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
encoderConfig.EncodeName = zapcore.FullNameEncoder
encoderConfig.EncodeCaller = zapcore.FullCallerEncoder
if config.Structured {
return zapcore.NewJSONEncoder(encoderConfig)
}
return zapcore.NewConsoleEncoder(encoderConfig)
}
func (l *ZapLogger) logFileWriter(location string) zapcore.WriteSyncer {
file, _ := os.Create(location)
return zapcore.AddSync(file)
}
func (l *ZapLogger) Debugf(format string, args ...interface{}) {
l.Logger.Debugf(format, args...)
}
func (l *ZapLogger) Infof(format string, args ...interface{}) {
l.Logger.Infof(format, args...)
}
func (l *ZapLogger) Debug(args ...interface{}) {
l.Logger.Debug(args...)
}
func (l *ZapLogger) Info(args ...interface{}) {
l.Logger.Info(args...)
}
func (l *ZapLogger) Errorf(format string, args ...interface{}) {
l.Logger.Errorf(format, args...)
}

View file

@ -1,6 +1,7 @@
package etui
import (
"bytes"
"context"
"fmt"
"os"
@ -8,6 +9,7 @@ import (
grypeEvent "github.com/anchore/grype/grype/event"
"github.com/anchore/grype/internal/log"
"github.com/anchore/grype/internal/logger"
"github.com/anchore/grype/internal/ui/common"
grypeUI "github.com/anchore/grype/ui"
"github.com/wagoodman/go-partybus"
@ -35,6 +37,13 @@ func setupScreen(output *os.File) *frame.Frame {
func OutputToEphemeralTUI(workerErrs <-chan error, subscription *partybus.Subscription) error {
output := os.Stderr
// prep the logger to not clobber the screen from now on (logrus only)
logBuffer := bytes.NewBufferString("")
logWrapper, ok := log.Log.(*logger.LogrusLogger)
if ok {
logWrapper.Logger.SetOutput(logBuffer)
}
// hide cursor
_, _ = fmt.Fprint(output, "\x1b[?25l")
// show cursor
@ -49,6 +58,8 @@ func OutputToEphemeralTUI(workerErrs <-chan error, subscription *partybus.Subscr
if !isClosed {
fr.Close()
frame.Close()
// flush any errors to the screen before the report
fmt.Fprint(output, logBuffer.String())
}
}()
@ -85,9 +96,13 @@ eventLoop:
// finish before discontinuing dynamic content and showing the final report
wg.Wait()
fr.Close()
// TODO: there is a race condition within frame.Close() that sometimes leads to an extra blank line being output
frame.Close()
isClosed = true
// flush any errors to the screen before the report
fmt.Fprint(output, logBuffer.String())
if err := common.VulnerabilityScanningFinishedHandler(e); err != nil {
log.Errorf("unable to show %s event: %+v", e.Type, err)
}

View file

@ -37,8 +37,8 @@ func startProcess() (format.Simple, *common.Spinner) {
return formatter, &spinner
}
func DownloadingVulnerabilityDatabaseHandler(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error {
_, prog, err := grypeEventParsers.ParseUpdateVulnerabilityDatabase(event)
func (r *Handler) UpdateVulnerabilityDatabaseHandler(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error {
prog, err := grypeEventParsers.ParseUpdateVulnerabilityDatabase(event)
if err != nil {
return fmt.Errorf("bad FetchImage event: %w", err)
}
@ -50,45 +50,46 @@ func DownloadingVulnerabilityDatabaseHandler(ctx context.Context, fr *frame.Fram
wg.Add(1)
go func() {
defer line.Close()
defer wg.Done()
formatter, spinner := startProcess()
stream := progress.Stream(ctx, prog, 150*time.Millisecond)
title := tileFormat.Sprint("Updating Vulnerability DB...")
formatter, spinner := startProcess()
stream := progress.Stream(ctx, prog, 150*time.Millisecond)
title := tileFormat.Sprint("Vulnerability DB")
formatFn := func(p progress.Progress) {
progStr, err := formatter.Format(p)
spin := color.Magenta.Sprint(spinner.Next())
if err != nil {
_, _ = io.WriteString(line, fmt.Sprintf("Error: %+v", err))
} else {
var auxInfo string
switch prog.Stage() {
case "downloading":
auxInfo = auxInfoFormat.Sprintf("[%s / %s]", humanize.Bytes(uint64(prog.Current())), humanize.Bytes(uint64(prog.Size())))
default:
auxInfo = auxInfoFormat.Sprintf("[%s]", prog.Stage())
}
_, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate+"%s %s", spin, title, progStr, auxInfo))
formatFn := func(p progress.Progress) {
progStr, err := formatter.Format(p)
spin := color.Magenta.Sprint(spinner.Next())
if err != nil {
_, _ = io.WriteString(line, fmt.Sprintf("Error: %+v", err))
} else {
var auxInfo string
switch prog.Stage() {
case "downloading":
auxInfo = auxInfoFormat.Sprintf("[%s / %s]", humanize.Bytes(uint64(prog.Current())), humanize.Bytes(uint64(prog.Size())))
default:
progStr = ""
auxInfo = auxInfoFormat.Sprintf("[%s]", prog.Stage())
}
_, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate+"%s %s", spin, title, progStr, auxInfo))
}
}
go func() {
defer wg.Done()
formatFn(progress.Progress{})
for p := range stream {
formatFn(p)
}
spin := color.Green.Sprint(completedStatus)
title = tileFormat.Sprint("Updated Vulnerability DB")
_, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate, spin, title))
title = tileFormat.Sprint("Vulnerability DB")
auxInfo := auxInfoFormat.Sprintf("[%s]", prog.Stage())
_, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate+" %s", spin, title, auxInfo))
}()
return err
}
func VulnerabilityScanningStartedHandler(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error {
func (r *Handler) VulnerabilityScanningStartedHandler(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error {
monitor, err := grypeEventParsers.ParseVulnerabilityScanningStarted(event)
if err != nil {
return fmt.Errorf("bad %s event: %w", event.Type, err)
@ -101,18 +102,18 @@ func VulnerabilityScanningStartedHandler(ctx context.Context, fr *frame.Frame, e
wg.Add(1)
go func() {
defer line.Close()
defer wg.Done()
_, spinner := startProcess()
stream := progress.StreamMonitors(ctx, []progress.Monitorable{monitor.PackagesProcessed, monitor.VulnerabilitiesDiscovered}, 50*time.Millisecond)
title := tileFormat.Sprint("Scanning image...")
_, spinner := startProcess()
stream := progress.StreamMonitors(ctx, []progress.Monitorable{monitor.PackagesProcessed, monitor.VulnerabilitiesDiscovered}, 50*time.Millisecond)
title := tileFormat.Sprint("Scanning image...")
formatFn := func(val int64) {
spin := color.Magenta.Sprint(spinner.Next())
auxInfo := auxInfoFormat.Sprintf("[vulnerabilities %d]", val)
_, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate+"%s", spin, title, auxInfo))
}
formatFn := func(val int64) {
spin := color.Magenta.Sprint(spinner.Next())
auxInfo := auxInfoFormat.Sprintf("[vulnerabilities %d]", val)
_, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate+"%s", spin, title, auxInfo))
}
go func() {
defer wg.Done()
formatFn(0)
for p := range stream {

View file

@ -32,10 +32,9 @@ func (r *Handler) RespondsTo(event partybus.Event) bool {
func (r *Handler) Handle(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error {
switch event.Type {
case grypeEvent.VulnerabilityScanningStarted:
return VulnerabilityScanningStartedHandler(ctx, fr, event, wg)
return r.VulnerabilityScanningStartedHandler(ctx, fr, event, wg)
case grypeEvent.UpdateVulnerabilityDatabase:
return DownloadingVulnerabilityDatabaseHandler(ctx, fr, event, wg)
return r.UpdateVulnerabilityDatabaseHandler(ctx, fr, event, wg)
default:
return r.syftHandler.Handle(ctx, fr, event, wg)
}