From 30f86961b88b7a590157f28fc6cb8f22f16dfa06 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Mon, 26 Apr 2021 17:23:08 -0500 Subject: [PATCH] clairctl: move to zlog This change uses zlog inside clairctl instead of using a command-only logging facility. It makes info prints the default output level, with the existing `D` flag upping the output to "debug" and the new `q` flag dropping the output to "warn". Signed-off-by: Hank Donnay --- cmd/clairctl/client.go | 67 +++++++++++++++++++++++++++++++--------- cmd/clairctl/debug.go | 8 ----- cmd/clairctl/main.go | 42 ++++++++++++++++++------- cmd/clairctl/manifest.go | 25 +++++++++++---- cmd/clairctl/report.go | 36 +++++++++++++++------ 5 files changed, 128 insertions(+), 50 deletions(-) delete mode 100644 cmd/clairctl/debug.go diff --git a/cmd/clairctl/client.go b/cmd/clairctl/client.go index 8d0898a063..978d805d23 100644 --- a/cmd/clairctl/client.go +++ b/cmd/clairctl/client.go @@ -13,6 +13,7 @@ import ( "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote/transport" "github.com/quay/claircore" + "github.com/quay/zlog" "github.com/tomnomnom/linkheader" "github.com/quay/clair/v4/httptransport" @@ -84,7 +85,10 @@ func (c *Client) getValidator(path string) string { } func (c *Client) setValidator(path, v string) { - debug.Printf("setting validator %q → %q", path, v) + zlog.Debug(context.Background()). + Str("path", path). + Str("validator", v). + Msg("setting validator") c.mu.Lock() defer c.mu.Unlock() c.validator[path] = v @@ -99,7 +103,9 @@ func (c *Client) IndexReport(ctx context.Context, id claircore.Digest, m *clairc ) fp, err := c.host.Parse(path.Join(c.host.RequestURI(), httptransport.IndexReportAPIPath, id.String())) if err != nil { - debug.Printf("unable to construct index_report url: %v", err) + zlog.Debug(ctx). + Err(err). + Msg("unable to construct index_report url") return err } req = c.request(ctx, fp, http.MethodGet) @@ -109,13 +115,22 @@ func (c *Client) IndexReport(ctx context.Context, id claircore.Digest, m *clairc res.Body.Close() } if err != nil { - debug.Printf("request failed for url %q: %v", req.URL.String(), err) + zlog.Debug(ctx). + Err(err). + Stringer("url", req.URL). + Msg("request failed") return err } - debug.Printf("%s %s: %s", res.Request.Method, res.Request.URL.Path, res.Status) + zlog.Debug(ctx). + Str("method", res.Request.Method). + Str("path", res.Request.URL.Path). + Str("status", res.Status). + Send() switch res.StatusCode { case http.StatusOK, http.StatusNotFound: - debug.Printf("need to post manifest %v", id) + zlog.Debug(ctx). + Stringer("manifest", id). + Msg("need to post manifest") case http.StatusNotModified: return nil default: @@ -123,12 +138,16 @@ func (c *Client) IndexReport(ctx context.Context, id claircore.Digest, m *clairc } if m == nil { - debug.Printf("don't have needed manifest %v", id) + zlog.Debug(ctx). + Stringer("manifest", id). + Msg("don't have needed manifest") return errNeedManifest } ru, err := c.host.Parse(path.Join(c.host.RequestURI(), httptransport.IndexAPIPath)) if err != nil { - debug.Printf("unable to construct index_report url: %v", err) + zlog.Debug(ctx). + Err(err). + Msg("unable to construct index_report url") return err } @@ -136,11 +155,18 @@ func (c *Client) IndexReport(ctx context.Context, id claircore.Digest, m *clairc req.Body = codec.JSONReader(m) res, err = c.client.Do(req) if err != nil { - debug.Printf("request failed for url %q: %v", req.URL.String(), err) + zlog.Debug(ctx). + Err(err). + Stringer("url", req.URL). + Msg("request failed") return err } defer res.Body.Close() - debug.Printf("%s %s: %s", res.Request.Method, res.Request.URL.Path, res.Status) + zlog.Debug(ctx). + Str("method", res.Request.Method). + Str("path", res.Request.URL.Path). + Str("status", res.Status). + Send() switch res.StatusCode { case http.StatusOK: case http.StatusCreated: @@ -152,7 +178,9 @@ func (c *Client) IndexReport(ctx context.Context, id claircore.Digest, m *clairc dec := codec.GetDecoder(res.Body) defer codec.PutDecoder(dec) if err := dec.Decode(&report); err != nil { - debug.Printf("unable to decode json payload: %v", err) + zlog.Debug(ctx). + Err(err). + Msg("unable to decode json payload") return err } if !report.Success && report.Err != "" { @@ -179,17 +207,26 @@ func (c *Client) VulnerabilityReport(ctx context.Context, id claircore.Digest) ( ) u, err := c.host.Parse(path.Join(c.host.RequestURI(), httptransport.VulnerabilityReportPath, id.String())) if err != nil { - debug.Printf("unable to construct vulnerability_report url: %v", err) + zlog.Debug(ctx). + Err(err). + Msg("unable to construct vulnerability_report url") return nil, err } req = c.request(ctx, u, http.MethodGet) res, err = c.client.Do(req) if err != nil { - debug.Printf("request failed for url %q: %v", req.URL.String(), err) + zlog.Debug(ctx). + Err(err). + Stringer("url", req.URL). + Msg("request failed") return nil, err } defer res.Body.Close() - debug.Printf("%s %s: %s", res.Request.Method, res.Request.URL.Path, res.Status) + zlog.Debug(ctx). + Str("method", res.Request.Method). + Str("path", res.Request.URL.Path). + Str("status", res.Status). + Send() switch res.StatusCode { case http.StatusOK: case http.StatusNotModified: @@ -202,7 +239,9 @@ func (c *Client) VulnerabilityReport(ctx context.Context, id claircore.Digest) ( dec := codec.GetDecoder(res.Body) defer codec.PutDecoder(dec) if err := dec.Decode(&report); err != nil { - debug.Printf("unable to decode json payload: %v", err) + zlog.Debug(ctx). + Err(err). + Msg("unable to decode json payload") return nil, err } diff --git a/cmd/clairctl/debug.go b/cmd/clairctl/debug.go deleted file mode 100644 index d98cd62309..0000000000 --- a/cmd/clairctl/debug.go +++ /dev/null @@ -1,8 +0,0 @@ -package main - -import ( - "io/ioutil" - "log" -) - -var debug = log.New(ioutil.Discard, "debug: ", log.LstdFlags) diff --git a/cmd/clairctl/main.go b/cmd/clairctl/main.go index 4091b048bc..d3d455eb95 100644 --- a/cmd/clairctl/main.go +++ b/cmd/clairctl/main.go @@ -2,16 +2,24 @@ package main import ( "context" - "log" "os" + "time" _ "github.com/quay/claircore/updater/defaults" + "github.com/quay/zlog" + "github.com/rs/zerolog" "github.com/urfave/cli/v2" "gopkg.in/square/go-jose.v2/jwt" ) var ( - flagDebug bool + logout = zerolog.New(&zerolog.ConsoleWriter{ + Out: os.Stderr, + TimeFormat: time.RFC3339, + }).Level(zerolog.InfoLevel). + With(). + Timestamp(). + Logger() commonClaim = jwt.Claims{} ) @@ -24,14 +32,18 @@ func main() { app := &cli.App{ Name: "clairctl", - Version: "0.1.0", + Version: "0.2.0", Usage: "interact with a clair API", Description: "A command-line tool for clair v4.", EnableBashCompletion: true, Before: func(c *cli.Context) error { + if c.IsSet("q") { + logout = logout.Level(zerolog.WarnLevel) + } if c.IsSet("D") { - debug.SetOutput(os.Stderr) + logout = logout.Level(zerolog.DebugLevel) } + zlog.Set(&logout) commonClaim.Issuer = c.String("issuer") return nil }, @@ -46,6 +58,10 @@ func main() { Name: "D", Usage: "print debugging logs", }, + &cli.BoolFlag{ + Name: "q", + Usage: "quieter log output", + }, &cli.PathFlag{ Name: "config", Aliases: []string{"c"}, @@ -61,14 +77,16 @@ func main() { Value: "clairctl", }, }, + ExitErrHandler: func(c *cli.Context, err error) { + if err != nil { + exit = 1 + if err, ok := err.(cli.ExitCoder); ok { + exit = err.ExitCode() + } + logout.Error().Err(err).Send() + } + }, } - log.SetFlags(log.Flags()) - if err := app.RunContext(ctx, os.Args); err != nil { - exit = 1 - if err, ok := err.(cli.ExitCoder); ok { - exit = err.ExitCode() - } - log.Println(err) - } + app.RunContext(ctx, os.Args) } diff --git a/cmd/clairctl/manifest.go b/cmd/clairctl/manifest.go index 6306181c4e..5cec9a8917 100644 --- a/cmd/clairctl/manifest.go +++ b/cmd/clairctl/manifest.go @@ -12,6 +12,7 @@ import ( "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/quay/claircore" + "github.com/quay/zlog" "github.com/urfave/cli/v2" "golang.org/x/sync/errgroup" @@ -26,7 +27,8 @@ var ManifestCmd = &cli.Command{ } func manifestAction(c *cli.Context) error { - debug.Println("manifest") + ctx := c.Context + zlog.Debug(ctx).Msg("manifest") args := c.Args() if args.Len() == 0 { return errors.New("missing needed arguments") @@ -46,14 +48,19 @@ func manifestAction(c *cli.Context) error { for i := 0; i < args.Len(); i++ { name := args.Get(i) - debug.Printf("%s: fetching", name) + zlog.Debug(ctx).Str("name", name).Msg("fetching") eg.Go(func() error { m, err := Inspect(ctx, name) if err != nil { - debug.Printf("%s: err: %v", name, err) + zlog.Debug(ctx). + Str("name", name). + Err(err). + Send() return err } - debug.Printf("%s: ok", name) + zlog.Debug(ctx). + Str("name", name). + Msg("ok") result <- m return nil }) @@ -93,13 +100,19 @@ func Inspect(ctx context.Context, r string) (*claircore.Manifest, error) { return nil, err } out := claircore.Manifest{Hash: ccd} - debug.Printf("%s: found manifest %v", r, ccd) + zlog.Debug(ctx). + Str("ref", r). + Stringer("digest", ccd). + Msg("found manifest") ls, err := img.Layers() if err != nil { return nil, err } - debug.Printf("%s: found %d layers", r, len(ls)) + zlog.Debug(ctx). + Str("ref", r). + Int("count", len(ls)). + Msg("found layers") repo := ref.Context() rURL := url.URL{ diff --git a/cmd/clairctl/report.go b/cmd/clairctl/report.go index c0bb1a0a88..3df297c43c 100644 --- a/cmd/clairctl/report.go +++ b/cmd/clairctl/report.go @@ -1,6 +1,7 @@ package main import ( + "context" "encoding/xml" "errors" "fmt" @@ -11,6 +12,7 @@ import ( "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/quay/claircore" + "github.com/quay/zlog" "github.com/urfave/cli/v2" "golang.org/x/sync/errgroup" @@ -62,23 +64,23 @@ func (o *outFmt) String() string { return o.fmt } -func (o *outFmt) Formatter(w io.WriteCloser) Formatter { +func (o *outFmt) Formatter(ctx context.Context, w io.WriteCloser) Formatter { switch o.fmt { case "", "text": - debug.Println("using text output") + zlog.Debug(ctx).Msg("using text output") r, err := newTextFormatter(w) if err != nil { panic(err) } return r case "json": - debug.Println("using json output") + zlog.Debug(ctx).Msg("using json output") return &jsonFormatter{ enc: codec.GetEncoder(w), c: w, } case "xml": - debug.Println("using xml output") + zlog.Debug(ctx).Msg("using xml output") return &xmlFormatter{ enc: xml.NewEncoder(w), c: w, @@ -137,7 +139,7 @@ func reportAction(c *cli.Context) error { go func() { defer close(done) out := c.Generic("out").(*outFmt) - f := out.Formatter(os.Stdout) + f := out.Formatter(ctx, os.Stdout) defer f.Close() for r := range result { if err := f.Format(r); err != nil { @@ -148,14 +150,22 @@ func reportAction(c *cli.Context) error { for i := 0; i < args.Len(); i++ { ref := args.Get(i) - debug.Printf("%s: fetching", ref) + zlog.Debug(ctx). + Str("ref", ref). + Msg("fetching") eg.Go(func() error { d, err := resolveRef(ref) if err != nil { - debug.Printf("%s: error: %v", ref, err) + zlog.Debug(ctx). + Str("ref", ref). + Err(err). + Send() return err } - debug.Printf("%s: manifest: %v", ref, d) + zlog.Debug(ctx). + Str("ref", ref). + Stringer("digest", d). + Msg("found manifest") // This bit is tricky: // @@ -171,12 +181,18 @@ func reportAction(c *cli.Context) error { case errors.Is(err, errNeedManifest): m, err = Inspect(ctx, ref) if err != nil { - debug.Printf("%s: manifest error: %v", ref, err) + zlog.Debug(ctx). + Str("ref", ref). + Err(err). + Msg("manifest error") return err } goto Again default: - debug.Printf("%s: index error: %v", ref, err) + zlog.Debug(ctx). + Str("ref", ref). + Err(err). + Msg("index error") return err }