diff --git a/cmd/git-sync/main.go b/cmd/git-sync/main.go index 7a167c9..0dffae0 100644 --- a/cmd/git-sync/main.go +++ b/cmd/git-sync/main.go @@ -37,10 +37,9 @@ import ( "strings" "time" + "github.com/go-logr/glogr" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/thockin/glogr" - "github.com/thockin/logr" ) var flRepo = flag.String("repo", envString("GIT_SYNC_REPO", ""), @@ -105,7 +104,7 @@ var flHTTPMetrics = flag.Bool("http-metrics", envBool("GIT_SYNC_HTTP_METRICS", t var flHTTPprof = flag.Bool("http-pprof", envBool("GIT_SYNC_HTTP_PPROF", false), "enable the pprof debug endpoints on git-sync's HTTP endpoint") -var log = newLoggerOrDie() +var log = glogr.New() // Total pull/error, summary on pull duration var ( @@ -126,15 +125,6 @@ func init() { prometheus.MustRegister(syncCount) } -func newLoggerOrDie() logr.Logger { - g, err := glogr.New() - if err != nil { - fmt.Fprintf(os.Stderr, "failind to initialize logging: %v\n", err) - os.Exit(1) - } - return g -} - func envString(key, def string) string { if env := os.Getenv(key); env != "" { return env @@ -158,7 +148,7 @@ func envInt(key string, def int) int { if env := os.Getenv(key); env != "" { val, err := strconv.Atoi(env) if err != nil { - log.Errorf("invalid value for %q: using default: %v", key, def) + log.Error(err, "invalid env value, using default", "key", key, "val", os.Getenv(key), "default", def) return def } return val @@ -170,7 +160,7 @@ func envFloat(key string, def float64) float64 { if env := os.Getenv(key); env != "" { val, err := strconv.ParseFloat(env, 64) if err != nil { - log.Errorf("invalid value for %q: using default: %v", key, def) + log.Error(err, "invalid env value, using default", "key", key, "val", os.Getenv(key), "default", def) return def } return val @@ -182,7 +172,7 @@ func envDuration(key string, def time.Duration) time.Duration { if env := os.Getenv(key); env != "" { val, err := time.ParseDuration(env) if err != nil { - log.Errorf("invalid value for %q: using default: %v", key, def) + log.Error(err, "invalid env value, using default", "key", key, "val", os.Getenv(key), "default", def) return def } return val @@ -267,7 +257,7 @@ func main() { } // From here on, output goes through logging. - log.V(0).Infof("starting up: %q", os.Args) + log.V(0).Info("starting up", "args", os.Args) // Startup webhooks goroutine webhookTriggerChan := make(chan struct{}, 1) @@ -291,13 +281,13 @@ func main() { syncDuration.WithLabelValues("error").Observe(time.Now().Sub(start).Seconds()) syncCount.WithLabelValues("error").Inc() if initialSync || (*flMaxSyncFailures != -1 && failCount >= *flMaxSyncFailures) { - log.Errorf("error syncing repo: %v", err) + log.Error(err, "failed to sync repo, aborting") os.Exit(1) } failCount++ - log.Errorf("unexpected error syncing repo: %v", err) - log.V(0).Infof("waiting %v before retrying", waitTime(*flWait)) + log.Error(err, "unexpected error syncing repo, will retry") + log.V(0).Info("waiting before retrying", "waitTime", waitTime(*flWait)) cancel() time.Sleep(waitTime(*flWait)) continue @@ -319,17 +309,17 @@ func main() { os.Exit(0) } if isHash, err := revIsHash(ctx, *flRev, *flRoot); err != nil { - log.Errorf("can't tell if rev %s is a git hash, exiting", *flRev) + log.Error(err, "can't tell if rev is a git hash, exiting", "rev", *flRev) os.Exit(1) } else if isHash { - log.V(0).Infof("rev %s appears to be a git hash, no further sync needed", *flRev) + log.V(0).Info("rev appears to be a git hash, no further sync needed", "rev", *flRev) sleepForever() } initialSync = false } failCount = 0 - log.V(1).Infof("next sync in %v", waitTime(*flWait)) + log.V(1).Info("next sync", "wait_time", waitTime(*flWait)) cancel() time.Sleep(waitTime(*flWait)) } @@ -361,7 +351,8 @@ func sleepForever() { // updateSymlink atomically swaps the symlink to point at the specified directory and cleans up the previous worktree. func updateSymlink(ctx context.Context, gitRoot, link, newDir string) error { // Get currently-linked repo directory (to be removed), unless it doesn't exist - currentDir, err := filepath.EvalSymlinks(path.Join(gitRoot, link)) + fullpath := path.Join(gitRoot, link) + currentDir, err := filepath.EvalSymlinks(fullpath) if err != nil && !os.IsNotExist(err) { return fmt.Errorf("error accessing symlink: %v", err) } @@ -373,15 +364,16 @@ func updateSymlink(ctx context.Context, gitRoot, link, newDir string) error { return fmt.Errorf("error converting to relative path: %v", err) } - if _, err := runCommand(ctx, gitRoot, "ln", "-snf", newDirRelative, "tmp-link"); err != nil { + const tmplink = "tmp-link" + if _, err := runCommand(ctx, gitRoot, "ln", "-snf", newDirRelative, tmplink); err != nil { return fmt.Errorf("error creating symlink: %v", err) } - log.V(1).Infof("created symlink %s -> %s", "tmp-link", newDirRelative) + log.V(1).Info("created tmp symlink", "root", gitRoot, "dst", newDirRelative, "src", tmplink) - if _, err := runCommand(ctx, gitRoot, "mv", "-T", "tmp-link", link); err != nil { + if _, err := runCommand(ctx, gitRoot, "mv", "-T", tmplink, link); err != nil { return fmt.Errorf("error replacing symlink: %v", err) } - log.V(1).Infof("renamed symlink %s to %s", "tmp-link", link) + log.V(1).Info("renamed symlink", "root", gitRoot, "old_name", tmplink, "new_name", link) // Clean up previous worktree if len(currentDir) > 0 { @@ -389,14 +381,14 @@ func updateSymlink(ctx context.Context, gitRoot, link, newDir string) error { return fmt.Errorf("error removing directory: %v", err) } - log.V(1).Infof("removed %s", currentDir) + log.V(1).Info("removed previous worktree", "path", currentDir) _, err := runCommand(ctx, gitRoot, *flGitCmd, "worktree", "prune") if err != nil { return err } - log.V(1).Infof("pruned old worktrees") + log.V(1).Info("pruned old worktrees") } return nil @@ -404,7 +396,7 @@ func updateSymlink(ctx context.Context, gitRoot, link, newDir string) error { // addWorktreeAndSwap creates a new worktree and calls updateSymlink to swap the symlink to point to the new worktree func addWorktreeAndSwap(ctx context.Context, gitRoot, dest, branch, rev string, depth int, hash string) error { - log.V(0).Infof("syncing to %s (%s)", rev, hash) + log.V(0).Info("syncing git", "rev", rev, "hash", hash) args := []string{"fetch", "--tags"} if depth != 0 { @@ -428,7 +420,7 @@ func addWorktreeAndSwap(ctx context.Context, gitRoot, dest, branch, rev string, if err != nil { return err } - log.V(0).Infof("added worktree %s for origin/%s", worktreePath, branch) + log.V(0).Info("added worktree", "path", worktreePath, "branch", fmt.Sprintf("origin/%s", branch)) // The .git file in the worktree directory holds a reference to // /git/.git/worktrees/. Replace it with a reference @@ -448,7 +440,7 @@ func addWorktreeAndSwap(ctx context.Context, gitRoot, dest, branch, rev string, if err != nil { return err } - log.V(0).Infof("reset worktree %s to %s", worktreePath, hash) + log.V(0).Info("reset worktree to hash", "path", worktreePath, "hash", hash) if *flChmod != 0 { // set file permissions @@ -471,7 +463,7 @@ func cloneRepo(ctx context.Context, repo, branch, rev string, depth int, gitRoot if err != nil { if strings.Contains(err.Error(), "already exists and is not an empty directory") { // Maybe a previous run crashed? Git won't use this dir. - log.V(0).Infof("%s exists and is not empty (previous crash?), cleaning up", gitRoot) + log.V(0).Info("git root exists and is not empty (previous crash?), cleaning up", "path", gitRoot) err := os.RemoveAll(gitRoot) if err != nil { return err @@ -484,7 +476,7 @@ func cloneRepo(ctx context.Context, repo, branch, rev string, depth int, gitRoot return err } } - log.V(0).Infof("cloned %s", repo) + log.V(0).Info("cloned repo", "origin", repo) return nil } @@ -533,15 +525,13 @@ func syncRepo(ctx context.Context, repo, branch, rev string, depth int, gitRoot, if err != nil { return false, err } - log.V(2).Infof("local hash: %s", local) - log.V(2).Infof("remote hash: %s", remote) - if local != remote { - log.V(0).Infof("update required") - hash = remote - } else { - log.V(1).Infof("no update required") + log.V(2).Info("git state", "local", local, "remote", remote) + if local == remote { + log.V(1).Info("no update required") return false, nil } + log.V(0).Info("update required") + hash = remote } return true, addWorktreeAndSwap(ctx, gitRoot, dest, branch, rev, depth, hash) @@ -594,7 +584,7 @@ func cmdForLog(command string, args ...string) string { } func runCommand(ctx context.Context, cwd, command string, args ...string) (string, error) { - log.V(5).Infof("run(%q): %s", cwd, cmdForLog(command, args...)) + log.V(5).Info("running command", "cwd", cwd, "cmd", cmdForLog(command, args...)) cmd := exec.CommandContext(ctx, command, args...) if cwd != "" { @@ -603,6 +593,7 @@ func runCommand(ctx context.Context, cwd, command string, args ...string) (strin output, err := cmd.CombinedOutput() if ctx.Err() == context.DeadlineExceeded { return "", fmt.Errorf("command timed out: %v: %q", err, string(output)) + } if err != nil { return "", fmt.Errorf("error running command: %v: %q", err, string(output)) @@ -612,7 +603,7 @@ func runCommand(ctx context.Context, cwd, command string, args ...string) (strin } func setupGitAuth(username, password, gitURL string) error { - log.V(1).Infof("setting up the git credential cache") + log.V(1).Info("setting up git credential cache") cmd := exec.Command(*flGitCmd, "config", "--global", "credential.helper", "cache") output, err := cmd.CombinedOutput() if err != nil { @@ -636,20 +627,20 @@ func setupGitAuth(username, password, gitURL string) error { } func setupGitSSH(setupKnownHosts bool) error { - log.V(1).Infof("setting up git SSH credentials") + log.V(1).Info("setting up git SSH credentials") var pathToSSHSecret = *flSSHKeyFile var pathToSSHKnownHosts = *flSSHKnownHostsFile _, err := os.Stat(pathToSSHSecret) if err != nil { - return fmt.Errorf("error: could not find SSH key Secret: %v", err) + return fmt.Errorf("error: could not access SSH key Secret: %v", err) } if setupKnownHosts { _, err := os.Stat(pathToSSHKnownHosts) if err != nil { - return fmt.Errorf("error: could not find SSH known_hosts file: %v", err) + return fmt.Errorf("error: could not access SSH known_hosts file: %v", err) } err = os.Setenv("GIT_SSH_COMMAND", fmt.Sprintf("ssh -q -o UserKnownHostsFile=%s -i %s", pathToSSHKnownHosts, pathToSSHSecret)) @@ -666,13 +657,13 @@ func setupGitSSH(setupKnownHosts bool) error { } func setupGitCookieFile() error { - log.V(1).Infof("configuring git cookie file") + log.V(1).Info("configuring git cookie file") var pathToCookieFile = "/etc/git-secret/cookie_file" _, err := os.Stat(pathToCookieFile) if err != nil { - return fmt.Errorf("error: could not find git cookie file: %v", err) + return fmt.Errorf("error: could not access git cookie file: %v", err) } cmd := exec.Command(*flGitCmd, "config", "--global", "http.cookiefile", pathToCookieFile) diff --git a/cmd/git-sync/webhook.go b/cmd/git-sync/webhook.go index 77ccb1c..7d634f5 100644 --- a/cmd/git-sync/webhook.go +++ b/cmd/git-sync/webhook.go @@ -54,10 +54,10 @@ func (w *Webhook) run(ch chan struct{}) { for { if err := w.Do(); err != nil { - log.Errorf("error calling webhook %v: %v", w.URL, err) + log.Error(err, "error calling webhook", "url", w.URL) time.Sleep(w.Backoff) } else { - log.V(0).Infof("calling webhook %v was: OK\n", w.URL) + log.V(0).Info("success calling webhook", "url", w.URL) break } } diff --git a/go.mod b/go.mod index 073dd8f..9b29ddf 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,8 @@ module k8s.io/git-sync require ( + github.com/go-logr/glogr v0.1.0 github.com/go-logr/logr v0.1.0 // indirect github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect github.com/prometheus/client_golang v0.9.2 - github.com/thockin/glogr v0.0.0-20160825042232-7a7f3ced4f9f - github.com/thockin/logr v0.0.0-20160822044224-103d90809f34 ) diff --git a/go.sum b/go.sum index 4b5cc78..b9f0527 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,7 @@ --github.com/thockin/glogr v0.0.0-20160825042232-7a7f3ced4f9f h1:UQ/VqTtxN8EoIShWVbNR9b0/vyxKLwhV6ZGcIjZ5KB4= --github.com/thockin/glogr v0.0.0-20160825042232-7a7f3ced4f9f/go.mod h1:HRvPbLXOCiSU8hpsTlLSX4Fw6l+yF2uziElIkThrlsI= --github.com/thockin/logr v0.0.0-20160822044224-103d90809f34 h1:iVWjMSZyeDCd1D2XQkUjsAWPfvA3M4aPGGsEmxaXY1s= --github.com/thockin/logr v0.0.0-20160822044224-103d90809f34/go.mod h1:GEqn6NzwaoUO+OS+z1oR6Ka9NXmRDe3NUF5KZUAoT+I= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/go-logr/glogr v0.1.0 h1:5W02LkUIi+DaBwtWKYGxoX9gqVMo6i9ehwkhorjcP74= +github.com/go-logr/glogr v0.1.0/go.mod h1:GDQ2+z9PAAX7+qBhL3FzAL2Nf8dxyliu0ppgJIX7YhU= github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= @@ -20,9 +18,6 @@ github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jO github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/thockin/glogr v0.0.0-20160825042232-7a7f3ced4f9f h1:UQ/VqTtxN8EoIShWVbNR9b0/vyxKLwhV6ZGcIjZ5KB4= -github.com/thockin/glogr v0.0.0-20160825042232-7a7f3ced4f9f/go.mod h1:HRvPbLXOCiSU8hpsTlLSX4Fw6l+yF2uziElIkThrlsI= -github.com/thockin/logr v0.0.0-20160822044224-103d90809f34 h1:iVWjMSZyeDCd1D2XQkUjsAWPfvA3M4aPGGsEmxaXY1s= -github.com/thockin/logr v0.0.0-20160822044224-103d90809f34/go.mod h1:GEqn6NzwaoUO+OS+z1oR6Ka9NXmRDe3NUF5KZUAoT+I= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/vendor/github.com/thockin/glogr/LICENSE b/vendor/github.com/go-logr/glogr/LICENSE similarity index 100% rename from vendor/github.com/thockin/glogr/LICENSE rename to vendor/github.com/go-logr/glogr/LICENSE diff --git a/vendor/github.com/thockin/glogr/README.md b/vendor/github.com/go-logr/glogr/README.md similarity index 100% rename from vendor/github.com/thockin/glogr/README.md rename to vendor/github.com/go-logr/glogr/README.md diff --git a/vendor/github.com/go-logr/glogr/glogr.go b/vendor/github.com/go-logr/glogr/glogr.go new file mode 100644 index 0000000..f5ad488 --- /dev/null +++ b/vendor/github.com/go-logr/glogr/glogr.go @@ -0,0 +1,152 @@ +// Package glogr implements github.com/thockin/logr.Logger in terms of +// github.com/golang/glog. +package glogr + +import ( + "bytes" + "encoding/json" + "fmt" + "runtime" + "sort" + + "github.com/go-logr/logr" + "github.com/golang/glog" +) + +// New returns a logr.Logger which is implemented by glog. +func New() logr.Logger { + return glogger{ + level: 0, + prefix: "", + values: nil, + } +} + +type glogger struct { + level int + prefix string + values []interface{} +} + +func (l glogger) clone() glogger { + return glogger{ + level: l.level, + prefix: l.prefix, + values: copySlice(l.values), + } +} + +func copySlice(in []interface{}) []interface{} { + out := make([]interface{}, len(in)) + copy(out, in) + return out +} + +// Magic string for intermediate frames that we should ignore. +const autogeneratedFrameName = "" + +// Discover how many frames we need to climb to find the caller. This approach +// was suggested by Ian Lance Taylor of the Go team, so it *should* be safe +// enough (famous last words). +func framesToCaller() int { + // 1 is the immediate caller. 3 should be too many. + for i := 1; i < 3; i++ { + _, file, _, _ := runtime.Caller(i + 1) // +1 for this function's frame + if file != autogeneratedFrameName { + return i + } + } + return 1 // something went wrong, this is safe +} + +type kvPair struct { + key string + val interface{} +} + +func flatten(kvList ...interface{}) string { + keys := make([]string, 0, len(kvList)) + vals := make(map[string]interface{}, len(kvList)) + for i := 0; i < len(kvList); i += 2 { + k, ok := kvList[i].(string) + if !ok { + panic(fmt.Sprintf("key is not a string: %s", pretty(kvList[i]))) + } + var v interface{} + if i+1 < len(kvList) { + v = kvList[i+1] + } + keys = append(keys, k) + vals[k] = v + } + sort.Strings(keys) + buf := bytes.Buffer{} + for i, k := range keys { + v := vals[k] + if i > 0 { + buf.WriteRune(' ') + } + buf.WriteString(pretty(k)) + buf.WriteString("=") + buf.WriteString(pretty(v)) + } + return buf.String() +} + +func pretty(value interface{}) string { + jb, _ := json.Marshal(value) + return string(jb) +} + +func (l glogger) Info(msg string, kvList ...interface{}) { + if l.Enabled() { + lvlStr := flatten("level", l.level) + msgStr := flatten("msg", msg) + fixedStr := flatten(l.values...) + userStr := flatten(kvList...) + glog.InfoDepth(framesToCaller(), l.prefix, " ", lvlStr, " ", msgStr, " ", fixedStr, " ", userStr) + } +} + +func (l glogger) Enabled() bool { + return bool(glog.V(glog.Level(l.level))) +} + +func (l glogger) Error(err error, msg string, kvList ...interface{}) { + msgStr := flatten("msg", msg) + var loggableErr interface{} + if err != nil { + loggableErr = err.Error() + } + errStr := flatten("error", loggableErr) + fixedStr := flatten(l.values...) + userStr := flatten(kvList...) + glog.ErrorDepth(framesToCaller(), l.prefix, " ", msgStr, " ", errStr, " ", fixedStr, " ", userStr) +} + +func (l glogger) V(level int) logr.InfoLogger { + new := l.clone() + new.level = level + return new +} + +// WithName returns a new logr.Logger with the specified name appended. glogr +// uses '/' characters to separate name elements. Callers should not pass '/' +// in the provided name string, but this library does not actually enforce that. +func (l glogger) WithName(name string) logr.Logger { + new := l.clone() + if len(l.prefix) > 0 { + new.prefix = l.prefix + "/" + } + new.prefix += name + return new +} + +func (l glogger) WithValues(kvList ...interface{}) logr.Logger { + new := l.clone() + new.values = append(new.values, kvList...) + return new +} + +var _ logr.Logger = glogger{} +var _ logr.InfoLogger = glogger{} diff --git a/vendor/github.com/thockin/logr/LICENSE b/vendor/github.com/go-logr/logr/LICENSE similarity index 100% rename from vendor/github.com/thockin/logr/LICENSE rename to vendor/github.com/go-logr/logr/LICENSE diff --git a/vendor/github.com/thockin/logr/README.md b/vendor/github.com/go-logr/logr/README.md similarity index 100% rename from vendor/github.com/thockin/logr/README.md rename to vendor/github.com/go-logr/logr/README.md diff --git a/vendor/github.com/go-logr/logr/logr.go b/vendor/github.com/go-logr/logr/logr.go new file mode 100644 index 0000000..ad72e78 --- /dev/null +++ b/vendor/github.com/go-logr/logr/logr.go @@ -0,0 +1,151 @@ +// Package logr defines abstract interfaces for logging. Packages can depend on +// these interfaces and callers can implement logging in whatever way is +// appropriate. +// +// This design derives from Dave Cheney's blog: +// http://dave.cheney.net/2015/11/05/lets-talk-about-logging +// +// This is a BETA grade API. Until there is a significant 2nd implementation, +// I don't really know how it will change. +// +// The logging specifically makes it non-trivial to use format strings, to encourage +// attaching structured information instead of unstructured format strings. +// +// Usage +// +// Logging is done using a Logger. Loggers can have name prefixes and named values +// attached, so that all log messages logged with that Logger have some base context +// associated. +// +// The term "key" is used to refer to the name associated with a particular value, to +// disambiguate it from the general Logger name. +// +// For instance, suppose we're trying to reconcile the state of an object, and we want +// to log that we've made some decision. +// +// With the traditional log package, we might write +// log.Printf( +// "decided to set field foo to value %q for object %s/%s", +// targetValue, object.Namespace, object.Name) +// +// With logr's structured logging, we'd write +// // elsewhere in the file, set up the logger to log with the prefix of "reconcilers", +// // and the named value target-type=Foo, for extra context. +// log := mainLogger.WithName("reconcilers").WithValues("target-type", "Foo") +// +// // later on... +// log.Info("setting field foo on object", "value", targetValue, "object", object) +// +// Depending on our logging implementation, we could then make logging decisions based on field values +// (like only logging such events for objects in a certain namespace), or copy the structured +// information into a structured log store. +// +// For logging errors, Logger has a method called Error. Suppose we wanted to log an +// error while reconciling. With the traditional log package, we might write +// log.Errorf("unable to reconcile object %s/%s: %v", object.Namespace, object.Name, err) +// +// With logr, we'd instead write +// // assuming the above setup for log +// log.Error(err, "unable to reconcile object", "object", object) +// +// This functions similarly to: +// log.Info("unable to reconcile object", "error", err, "object", object) +// +// However, it ensures that a standard key for the error value ("error") is used across all +// error logging. Furthermore, certain implementations may choose to attach additional +// information (such as stack traces) on calls to Error, so it's preferred to use Error +// to log errors. +// +// Parts of a log line +// +// Each log message from a Logger has four types of context: +// logger name, log verbosity, log message, and the named values. +// +// The Logger name constists of a series of name "segments" added by successive calls to WithName. +// These name segments will be joined in some way by the underlying implementation. It is strongly +// reccomended that name segements contain simple identifiers (letters, digits, and hyphen), and do +// not contain characters that could muddle the log output or confuse the joining operation (e.g. +// whitespace, commas, periods, slashes, brackets, quotes, etc). +// +// Log verbosity represents how little a log matters. Level zero, the default, matters most. +// Increasing levels matter less and less. Try to avoid lots of different verbosity levels, +// and instead provide useful keys, logger names, and log messages for users to filter on. +// It's illegal to pass a log level below zero. +// +// The log message consists of a constant message attached to the the log line. This +// should generally be a simple description of what's occuring, and should never be a format string. +// +// Variable information can then be attached using named values (key/value pairs). Keys are arbitrary +// strings, while values may be any Go value. +// +// Key Naming Conventions +// +// While users are generally free to use key names of their choice, it's generally best to avoid +// using the following keys, as they're frequently used by implementations: +// +// - `"error"`: the underlying error value in the `Error` method. +// - `"stacktrace"`: the stack trace associated with a particular log line or error +// (often from the `Error` message). +// - `"caller"`: the calling information (file/line) of a particular log line. +// - `"msg"`: the log message. +// - `"level"`: the log level. +// - `"ts"`: the timestamp for a log line. +// +// Implementations are encouraged to make use of these keys to represent the above +// concepts, when neccessary (for example, in a pure-JSON output form, it would be +// necessary to represent at least message and timestamp as ordinary named values). +package logr + +// TODO: consider adding back in format strings if they're really needed +// TODO: consider other bits of zap/zapcore functionality like ObjectMarshaller (for arbitrary objects) +// TODO: consider other bits of glog functionality like Flush, InfoDepth, OutputStats + +// InfoLogger represents the ability to log non-error messages, at a particular verbosity. +type InfoLogger interface { + // Info logs a non-error message with the given key/value pairs as context. + // + // The msg argument should be used to add some constant description to + // the log line. The key/value pairs can then be used to add additional + // variable information. The key/value pairs should alternate string + // keys and arbitrary values. + Info(msg string, keysAndValues ...interface{}) + + // Enabled tests whether this InfoLogger is enabled. For example, + // commandline flags might be used to set the logging verbosity and disable + // some info logs. + Enabled() bool +} + +// Logger represents the ability to log messages, both errors and not. +type Logger interface { + // All Loggers implement InfoLogger. Calling InfoLogger methods directly on + // a Logger value is equivalent to calling them on a V(0) InfoLogger. For + // example, logger.Info() produces the same result as logger.V(0).Info. + InfoLogger + + // Error logs an error, with the given message and key/value pairs as context. + // It functions similarly to calling Info with the "error" named value, but may + // have unique behavior, and should be preferred for logging errors (see the + // package documentations for more information). + // + // The msg field should be used to add context to any underlying error, + // while the err field should be used to attach the actual error that + // triggered this log line, if present. + Error(err error, msg string, keysAndValues ...interface{}) + + // V returns an InfoLogger value for a specific verbosity level. A higher + // verbosity level means a log message is less important. It's illegal to + // pass a log level less than zero. + V(level int) InfoLogger + + // WithValues adds some key-value pairs of context to a logger. + // See Info for documentation on how key/value pairs work. + WithValues(keysAndValues ...interface{}) Logger + + // WithName adds a new element to the logger's name. + // Successive calls with WithName continue to append + // suffixes to the logger's name. It's strongly reccomended + // that name segments contain only letters, digits, and hyphens + // (see the package documentation for more information). + WithName(name string) Logger +} diff --git a/vendor/github.com/thockin/glogr/glogr.go b/vendor/github.com/thockin/glogr/glogr.go deleted file mode 100644 index d2c8534..0000000 --- a/vendor/github.com/thockin/glogr/glogr.go +++ /dev/null @@ -1,86 +0,0 @@ -// Package glogr implements github.com/thockin/logr.Logger in terms of -// github.com/golang/glog. -package glogr - -import ( - "fmt" - "runtime" - - "github.com/golang/glog" - "github.com/thockin/logr" -) - -// New returns a logr.Logger which is implemented by glog. -func New() (logr.Logger, error) { - return glogger{ - level: 0, - prefix: "", - }, nil -} - -type glogger struct { - level int - prefix string -} - -func prepend(prefix interface{}, args []interface{}) []interface{} { - return append([]interface{}{prefix}, args...) -} - -// Magic string for intermediate frames that we should ignore. -const autogeneratedFrameName = "" - -// Discover how many frames we need to climb to find the caller. This approach -// was suggested by Ian Lance Taylor of the Go team, so it *should* be safe -// enough (famous last words). -func framesToCaller() int { - // 1 is the immediate caller. 3 should be too many. - for i := 1; i < 3; i++ { - _, file, _, _ := runtime.Caller(i + 1) // +1 for this function's frame - if file != autogeneratedFrameName { - return i - } - } - return 1 // something went wrong, this is safe -} - -func (l glogger) Info(args ...interface{}) { - if l.Enabled() { - glog.InfoDepth(framesToCaller(), prepend(l.prefix, args)...) - } -} - -func (l glogger) Infof(format string, args ...interface{}) { - if l.Enabled() { - glog.InfoDepth(framesToCaller(), fmt.Sprintf("%s"+format, prepend(l.prefix, args)...)) - } -} - -func (l glogger) Enabled() bool { - return bool(glog.V(glog.Level(l.level))) -} - -func (l glogger) Error(args ...interface{}) { - glog.ErrorDepth(framesToCaller(), prepend(l.prefix, args)...) -} - -func (l glogger) Errorf(format string, args ...interface{}) { - glog.ErrorDepth(framesToCaller(), fmt.Sprintf("%s"+format, prepend(l.prefix, args)...)) -} - -func (l glogger) V(level int) logr.InfoLogger { - return glogger{ - level: level, - prefix: l.prefix, - } -} - -func (l glogger) NewWithPrefix(prefix string) logr.Logger { - return glogger{ - level: l.level, - prefix: prefix, - } -} - -var _ logr.Logger = glogger{} -var _ logr.InfoLogger = glogger{} diff --git a/vendor/github.com/thockin/logr/logr.go b/vendor/github.com/thockin/logr/logr.go deleted file mode 100644 index cd065be..0000000 --- a/vendor/github.com/thockin/logr/logr.go +++ /dev/null @@ -1,48 +0,0 @@ -// Package logr defines abstract interfaces for logging. Packages can depend on -// these interfaces and callers can implement logging in whatever way is -// appropriate. -// -// This design derives from Dave Cheney's blog: -// http://dave.cheney.net/2015/11/05/lets-talk-about-logging -// -// This is a BETA grade API. Until there is a significant 2nd implementation, -// I don't really know how it will change. -package logr - -// TODO: consider structured logging, a la uber-go/zap -// TODO: consider other bits of glog functionality like Flush, InfoDepth, OutputStats - -// InfoLogger represents the ability to log non-error messages. -type InfoLogger interface { - // Info logs a non-error message. This is behaviorally akin to fmt.Print. - Info(args ...interface{}) - - // Infof logs a formatted non-error message. - Infof(format string, args ...interface{}) - - // Enabled test whether this InfoLogger is enabled. For example, - // commandline flags might be used to set the logging verbosity and disable - // some info logs. - Enabled() bool -} - -// Logger represents the ability to log messages, both errors and not. -type Logger interface { - // All Loggers implement InfoLogger. Calling InfoLogger methods directly on - // a Logger value is equivalent to calling them on a V(0) InfoLogger. For - // example, logger.Info() produces the same result as logger.V(0).Info. - InfoLogger - - // Error logs a error message. This is behaviorally akin to fmt.Print. - Error(args ...interface{}) - - // Errorf logs a formatted error message. - Errorf(format string, args ...interface{}) - - // V returns an InfoLogger value for a specific verbosity level. A higher - // verbosity level means a log message is less important. - V(level int) InfoLogger - - // NewWithPrefix returns a Logger which prefixes all messages. - NewWithPrefix(prefix string) Logger -} diff --git a/vendor/modules.txt b/vendor/modules.txt index b4f3957..af11c83 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,5 +1,9 @@ # github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 github.com/beorn7/perks/quantile +# github.com/go-logr/glogr v0.1.0 +github.com/go-logr/glogr +# github.com/go-logr/logr v0.1.0 +github.com/go-logr/logr # github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b github.com/golang/glog # github.com/golang/protobuf v1.2.0 @@ -21,7 +25,3 @@ github.com/prometheus/procfs github.com/prometheus/procfs/nfs github.com/prometheus/procfs/xfs github.com/prometheus/procfs/internal/util -# github.com/thockin/glogr v0.0.0-20160825042232-7a7f3ced4f9f -github.com/thockin/glogr -# github.com/thockin/logr v0.0.0-20160822044224-103d90809f34 -github.com/thockin/logr