bump docker/docker, sirupsen/logrus v1.4.1

Full diff: https://github.com/sirupsen/logrus/compare/v1.3.0...v1.4.1

Fixes:

- Remove dependency on golang.org/x/crypto
- Fix wrong method calls Logger.Print and Logger.Warningln
- Update Entry.Logf to not do string formatting unless the log level is enabled
- Fix infinite recursion on unknown Level.String()
- Fix race condition in getCaller
- Fix Entry.WithContext method to return a copy of the initial entry

New:

- Add DeferExitHandler, similar to RegisterExitHandler but prepending the handler to the list of handlers (semantically like defer)
- Add CallerPrettyfier to JSONFormatter and `TextFormatter`
- Add Entry.WithContext() and Entry.Context, to set a context on entries to be used e.g. in hooks
- Enhance TextFormatter to not print caller information when they are empty

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2019-04-04 17:46:51 +02:00
parent c500b534e7
commit dfc81eda9c
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
14 changed files with 153 additions and 55 deletions

View File

@ -13,7 +13,7 @@ github.com/cpuguy83/go-md2man v1.0.8
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 # v1.1.0 github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 # v1.1.0
github.com/dgrijalva/jwt-go a2c85815a77d0f951e33ba4db5ae93629a1530af github.com/dgrijalva/jwt-go a2c85815a77d0f951e33ba4db5ae93629a1530af
github.com/docker/distribution 0d3efadf0154c2b8a4e7b6621fff9809655cc580 github.com/docker/distribution 0d3efadf0154c2b8a4e7b6621fff9809655cc580
github.com/docker/docker bcaa613d823afa461957e7ad17faade03d82656f github.com/docker/docker dbe4a30928d418e0570891a09703bcbc0e4997a1
github.com/docker/compose-on-kubernetes v0.4.21 github.com/docker/compose-on-kubernetes v0.4.21
github.com/docker/docker-credential-helpers 5241b46610f2491efdf9d1c85f1ddf5b02f6d962 github.com/docker/docker-credential-helpers 5241b46610f2491efdf9d1c85f1ddf5b02f6d962
# the docker/go package contains a customized version of canonical/json # the docker/go package contains a customized version of canonical/json
@ -67,7 +67,7 @@ github.com/prometheus/common 7600349dcfe1abd18d72d3a1770870d9800a7801
github.com/prometheus/procfs 7d6f385de8bea29190f15ba9931442a0eaef9af7 github.com/prometheus/procfs 7d6f385de8bea29190f15ba9931442a0eaef9af7
github.com/russross/blackfriday 1d6b8e9301e720b08a8938b8c25c018285885438 github.com/russross/blackfriday 1d6b8e9301e720b08a8938b8c25c018285885438
github.com/shurcooL/sanitized_anchor_name 10ef21a441db47d8b13ebcc5fd2310f636973c77 github.com/shurcooL/sanitized_anchor_name 10ef21a441db47d8b13ebcc5fd2310f636973c77
github.com/sirupsen/logrus v1.3.0 github.com/sirupsen/logrus 8bdbc7bcc01dcbb8ec23dc8a28e332258d25251f # v1.4.1
github.com/spf13/cobra v0.0.3 github.com/spf13/cobra v0.0.3
# temporary fork with https://github.com/spf13/pflag/pull/170 applied, which isn't merged yet upstream # temporary fork with https://github.com/spf13/pflag/pull/170 applied, which isn't merged yet upstream
github.com/spf13/pflag 4cb166e4f25ac4e8016a3595bbf7ea2e9aa85a2c https://github.com/thaJeztah/pflag.git github.com/spf13/pflag 4cb166e4f25ac4e8016a3595bbf7ea2e9aa85a2c https://github.com/thaJeztah/pflag.git

View File

@ -11,7 +11,7 @@ github.com/Microsoft/opengcs a10967154e143a36014584a6f664344e3bb0aa64
github.com/konsorten/go-windows-terminal-sequences v1.0.1 github.com/konsorten/go-windows-terminal-sequences v1.0.1
github.com/kr/pty 5cf931ef8f github.com/kr/pty 5cf931ef8f
github.com/mattn/go-shellwords v1.0.3 github.com/mattn/go-shellwords v1.0.3
github.com/sirupsen/logrus v1.3.0 github.com/sirupsen/logrus 8bdbc7bcc01dcbb8ec23dc8a28e332258d25251f # v1.4.1
github.com/tchap/go-patricia v2.2.6 github.com/tchap/go-patricia v2.2.6
github.com/vdemeester/shakers 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3 github.com/vdemeester/shakers 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3
golang.org/x/net a680a1efc54dd51c040b3b5ce4939ea3cf2ea0d1 golang.org/x/net a680a1efc54dd51c040b3b5ce4939ea3cf2ea0d1
@ -161,4 +161,4 @@ github.com/morikuni/aec 39771216ff4c63d11f5e604076f9c45e8be1067b
# metrics # metrics
github.com/docker/go-metrics d466d4f6fd960e01820085bd7e1a24426ee7ef18 github.com/docker/go-metrics d466d4f6fd960e01820085bd7e1a24426ee7ef18
github.com/opencontainers/selinux 9e2c5215628a2567782777efb2049f385484f918 # v1.2 github.com/opencontainers/selinux 0bb7b9fa9ba5c1120e9d22caed4961fca4228408 # v1.2.1

View File

@ -365,6 +365,7 @@ Third party logging formatters:
* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events. * [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout. * [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦. * [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
* [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure.
You can define your formatter by implementing the `Formatter` interface, You can define your formatter by implementing the `Formatter` interface,
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a

View File

@ -51,9 +51,9 @@ func Exit(code int) {
os.Exit(code) os.Exit(code)
} }
// RegisterExitHandler adds a Logrus Exit handler, call logrus.Exit to invoke // RegisterExitHandler appends a Logrus Exit handler to the list of handlers,
// all handlers. The handlers will also be invoked when any Fatal log entry is // call logrus.Exit to invoke all handlers. The handlers will also be invoked when
// made. // any Fatal log entry is made.
// //
// This method is useful when a caller wishes to use logrus to log a fatal // This method is useful when a caller wishes to use logrus to log a fatal
// message but also needs to gracefully shutdown. An example usecase could be // message but also needs to gracefully shutdown. An example usecase could be
@ -62,3 +62,15 @@ func Exit(code int) {
func RegisterExitHandler(handler func()) { func RegisterExitHandler(handler func()) {
handlers = append(handlers, handler) handlers = append(handlers, handler)
} }
// DeferExitHandler prepends a Logrus Exit handler to the list of handlers,
// call logrus.Exit to invoke all handlers. The handlers will also be invoked when
// any Fatal log entry is made.
//
// This method is useful when a caller wishes to use logrus to log a fatal
// message but also needs to gracefully shutdown. An example usecase could be
// closing database connections, or sending a alert that the application is
// closing.
func DeferExitHandler(handler func()) {
handlers = append([]func(){handler}, handlers...)
}

View File

@ -2,6 +2,7 @@ package logrus
import ( import (
"bytes" "bytes"
"context"
"fmt" "fmt"
"os" "os"
"reflect" "reflect"
@ -69,6 +70,9 @@ type Entry struct {
// When formatter is called in entry.log(), a Buffer may be set to entry // When formatter is called in entry.log(), a Buffer may be set to entry
Buffer *bytes.Buffer Buffer *bytes.Buffer
// Contains the context set by the user. Useful for hook processing etc.
Context context.Context
// err may contain a field formatting error // err may contain a field formatting error
err string err string
} }
@ -97,6 +101,11 @@ func (entry *Entry) WithError(err error) *Entry {
return entry.WithField(ErrorKey, err) return entry.WithField(ErrorKey, err)
} }
// Add a context to the Entry.
func (entry *Entry) WithContext(ctx context.Context) *Entry {
return &Entry{Logger: entry.Logger, Data: entry.Data, Time: entry.Time, err: entry.err, Context: ctx}
}
// Add a single field to the Entry. // Add a single field to the Entry.
func (entry *Entry) WithField(key string, value interface{}) *Entry { func (entry *Entry) WithField(key string, value interface{}) *Entry {
return entry.WithFields(Fields{key: value}) return entry.WithFields(Fields{key: value})
@ -130,12 +139,12 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
data[k] = v data[k] = v
} }
} }
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr} return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context}
} }
// Overrides the time of the Entry. // Overrides the time of the Entry.
func (entry *Entry) WithTime(t time.Time) *Entry { func (entry *Entry) WithTime(t time.Time) *Entry {
return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t, err: entry.err} return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t, err: entry.err, Context: entry.Context}
} }
// getPackageName reduces a fully qualified function name to the package name // getPackageName reduces a fully qualified function name to the package name
@ -156,20 +165,23 @@ func getPackageName(f string) string {
// getCaller retrieves the name of the first non-logrus calling function // getCaller retrieves the name of the first non-logrus calling function
func getCaller() *runtime.Frame { func getCaller() *runtime.Frame {
// cache this package's fully-qualified name
callerInitOnce.Do(func() {
pcs := make([]uintptr, 2)
_ = runtime.Callers(0, pcs)
logrusPackage = getPackageName(runtime.FuncForPC(pcs[1]).Name())
// now that we have the cache, we can skip a minimum count of known-logrus functions
// XXX this is dubious, the number of frames may vary
minimumCallerDepth = knownLogrusFrames
})
// Restrict the lookback frames to avoid runaway lookups // Restrict the lookback frames to avoid runaway lookups
pcs := make([]uintptr, maximumCallerDepth) pcs := make([]uintptr, maximumCallerDepth)
depth := runtime.Callers(minimumCallerDepth, pcs) depth := runtime.Callers(minimumCallerDepth, pcs)
frames := runtime.CallersFrames(pcs[:depth]) frames := runtime.CallersFrames(pcs[:depth])
// cache this package's fully-qualified name
callerInitOnce.Do(func() {
logrusPackage = getPackageName(runtime.FuncForPC(pcs[0]).Name())
// now that we have the cache, we can skip a minimum count of known-logrus functions
// XXX this is dubious, the number of frames may vary store an entry in a logger interface
minimumCallerDepth = knownLogrusFrames
})
for f, again := frames.Next(); again; f, again = frames.Next() { for f, again := frames.Next(); again; f, again = frames.Next() {
pkg := getPackageName(f.Function) pkg := getPackageName(f.Function)
@ -298,7 +310,9 @@ func (entry *Entry) Panic(args ...interface{}) {
// Entry Printf family functions // Entry Printf family functions
func (entry *Entry) Logf(level Level, format string, args ...interface{}) { func (entry *Entry) Logf(level Level, format string, args ...interface{}) {
entry.Log(level, fmt.Sprintf(format, args...)) if entry.Logger.IsLevelEnabled(level) {
entry.Log(level, fmt.Sprintf(format, args...))
}
} }
func (entry *Entry) Tracef(format string, args ...interface{}) { func (entry *Entry) Tracef(format string, args ...interface{}) {

View File

@ -1,6 +1,7 @@
package logrus package logrus
import ( import (
"context"
"io" "io"
"time" "time"
) )
@ -55,6 +56,11 @@ func WithError(err error) *Entry {
return std.WithField(ErrorKey, err) return std.WithField(ErrorKey, err)
} }
// WithContext creates an entry from the standard logger and adds a context to it.
func WithContext(ctx context.Context) *Entry {
return std.WithContext(ctx)
}
// WithField creates an entry from the standard logger and adds a field to // WithField creates an entry from the standard logger and adds a field to
// it. If you want multiple fields, use `WithFields`. // it. If you want multiple fields, use `WithFields`.
// //

View File

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"runtime"
) )
type fieldKey string type fieldKey string
@ -42,6 +43,12 @@ type JSONFormatter struct {
// } // }
FieldMap FieldMap FieldMap FieldMap
// CallerPrettyfier can be set by the user to modify the content
// of the function and file keys in the json data when ReportCaller is
// activated. If any of the returned value is the empty string the
// corresponding key will be removed from json fields.
CallerPrettyfier func(*runtime.Frame) (function string, file string)
// PrettyPrint will indent all json logs // PrettyPrint will indent all json logs
PrettyPrint bool PrettyPrint bool
} }
@ -82,8 +89,17 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message
data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String() data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String()
if entry.HasCaller() { if entry.HasCaller() {
data[f.FieldMap.resolve(FieldKeyFunc)] = entry.Caller.Function funcVal := entry.Caller.Function
data[f.FieldMap.resolve(FieldKeyFile)] = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
if f.CallerPrettyfier != nil {
funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
}
if funcVal != "" {
data[f.FieldMap.resolve(FieldKeyFunc)] = funcVal
}
if fileVal != "" {
data[f.FieldMap.resolve(FieldKeyFile)] = fileVal
}
} }
var b *bytes.Buffer var b *bytes.Buffer
@ -98,7 +114,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
encoder.SetIndent("", " ") encoder.SetIndent("", " ")
} }
if err := encoder.Encode(data); err != nil { if err := encoder.Encode(data); err != nil {
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) return nil, fmt.Errorf("failed to marshal fields to JSON, %v", err)
} }
return b.Bytes(), nil return b.Bytes(), nil

View File

@ -1,6 +1,7 @@
package logrus package logrus
import ( import (
"context"
"io" "io"
"os" "os"
"sync" "sync"
@ -124,6 +125,13 @@ func (logger *Logger) WithError(err error) *Entry {
return entry.WithError(err) return entry.WithError(err)
} }
// Add a context to the log entry.
func (logger *Logger) WithContext(ctx context.Context) *Entry {
entry := logger.newEntry()
defer logger.releaseEntry(entry)
return entry.WithContext(ctx)
}
// Overrides the time of the log entry. // Overrides the time of the log entry.
func (logger *Logger) WithTime(t time.Time) *Entry { func (logger *Logger) WithTime(t time.Time) *Entry {
entry := logger.newEntry() entry := logger.newEntry()
@ -200,7 +208,7 @@ func (logger *Logger) Info(args ...interface{}) {
func (logger *Logger) Print(args ...interface{}) { func (logger *Logger) Print(args ...interface{}) {
entry := logger.newEntry() entry := logger.newEntry()
entry.Info(args...) entry.Print(args...)
logger.releaseEntry(entry) logger.releaseEntry(entry)
} }
@ -256,7 +264,7 @@ func (logger *Logger) Warnln(args ...interface{}) {
} }
func (logger *Logger) Warningln(args ...interface{}) { func (logger *Logger) Warningln(args ...interface{}) {
logger.Warn(args...) logger.Warnln(args...)
} }
func (logger *Logger) Errorln(args ...interface{}) { func (logger *Logger) Errorln(args ...interface{}) {

View File

@ -74,7 +74,7 @@ func (level Level) MarshalText() ([]byte, error) {
return []byte("panic"), nil return []byte("panic"), nil
} }
return nil, fmt.Errorf("not a valid lorus level %q", level) return nil, fmt.Errorf("not a valid logrus level %d", level)
} }
// A constant exposing all logging levels // A constant exposing all logging levels

View File

@ -1,9 +0,0 @@
// +build !appengine,!js,!windows,aix
package logrus
import "io"
func checkIfTerminal(w io.Writer) bool {
return false
}

View File

@ -0,0 +1,13 @@
// +build darwin dragonfly freebsd netbsd openbsd
package logrus
import "golang.org/x/sys/unix"
const ioctlReadTermios = unix.TIOCGETA
func isTerminal(fd int) bool {
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
return err == nil
}

View File

@ -1,18 +1,16 @@
// +build !appengine,!js,!windows,!aix // +build !appengine,!js,!windows
package logrus package logrus
import ( import (
"io" "io"
"os" "os"
"golang.org/x/crypto/ssh/terminal"
) )
func checkIfTerminal(w io.Writer) bool { func checkIfTerminal(w io.Writer) bool {
switch v := w.(type) { switch v := w.(type) {
case *os.File: case *os.File:
return terminal.IsTerminal(int(v.Fd())) return isTerminal(int(v.Fd()))
default: default:
return false return false
} }

View File

@ -0,0 +1,13 @@
// +build linux aix
package logrus
import "golang.org/x/sys/unix"
const ioctlReadTermios = unix.TCGETS
func isTerminal(fd int) bool {
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
return err == nil
}

View File

@ -12,18 +12,13 @@ import (
) )
const ( const (
nocolor = 0 red = 31
red = 31 yellow = 33
green = 32 blue = 36
yellow = 33 gray = 37
blue = 36
gray = 37
) )
var ( var baseTimestamp time.Time
baseTimestamp time.Time
emptyFieldMap FieldMap
)
func init() { func init() {
baseTimestamp = time.Now() baseTimestamp = time.Now()
@ -77,6 +72,12 @@ type TextFormatter struct {
// FieldKeyMsg: "@message"}} // FieldKeyMsg: "@message"}}
FieldMap FieldMap FieldMap FieldMap
// CallerPrettyfier can be set by the user to modify the content
// of the function and file keys in the data when ReportCaller is
// activated. If any of the returned value is the empty string the
// corresponding key will be removed from fields.
CallerPrettyfier func(*runtime.Frame) (function string, file string)
terminalInitOnce sync.Once terminalInitOnce sync.Once
} }
@ -118,6 +119,8 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
keys = append(keys, k) keys = append(keys, k)
} }
var funcVal, fileVal string
fixedKeys := make([]string, 0, 4+len(data)) fixedKeys := make([]string, 0, 4+len(data))
if !f.DisableTimestamp { if !f.DisableTimestamp {
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime)) fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime))
@ -130,8 +133,19 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLogrusError)) fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLogrusError))
} }
if entry.HasCaller() { if entry.HasCaller() {
fixedKeys = append(fixedKeys, if f.CallerPrettyfier != nil {
f.FieldMap.resolve(FieldKeyFunc), f.FieldMap.resolve(FieldKeyFile)) funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
} else {
funcVal = entry.Caller.Function
fileVal = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
}
if funcVal != "" {
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFunc))
}
if fileVal != "" {
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFile))
}
} }
if !f.DisableSorting { if !f.DisableSorting {
@ -166,6 +180,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
if f.isColored() { if f.isColored() {
f.printColored(b, entry, keys, data, timestampFormat) f.printColored(b, entry, keys, data, timestampFormat)
} else { } else {
for _, key := range fixedKeys { for _, key := range fixedKeys {
var value interface{} var value interface{}
switch { switch {
@ -178,9 +193,9 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
case key == f.FieldMap.resolve(FieldKeyLogrusError): case key == f.FieldMap.resolve(FieldKeyLogrusError):
value = entry.err value = entry.err
case key == f.FieldMap.resolve(FieldKeyFunc) && entry.HasCaller(): case key == f.FieldMap.resolve(FieldKeyFunc) && entry.HasCaller():
value = entry.Caller.Function value = funcVal
case key == f.FieldMap.resolve(FieldKeyFile) && entry.HasCaller(): case key == f.FieldMap.resolve(FieldKeyFile) && entry.HasCaller():
value = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) value = fileVal
default: default:
value = data[key] value = data[key]
} }
@ -215,10 +230,21 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
entry.Message = strings.TrimSuffix(entry.Message, "\n") entry.Message = strings.TrimSuffix(entry.Message, "\n")
caller := "" caller := ""
if entry.HasCaller() { if entry.HasCaller() {
caller = fmt.Sprintf("%s:%d %s()", funcVal := fmt.Sprintf("%s()", entry.Caller.Function)
entry.Caller.File, entry.Caller.Line, entry.Caller.Function) fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
if f.CallerPrettyfier != nil {
funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
}
if fileVal == "" {
caller = funcVal
} else if funcVal == "" {
caller = fileVal
} else {
caller = fileVal + " " + funcVal
}
} }
if f.DisableTimestamp { if f.DisableTimestamp {