mirror of https://github.com/docker/cli.git
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:
parent
c500b534e7
commit
dfc81eda9c
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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...)
|
||||||
|
}
|
||||||
|
|
|
@ -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{}) {
|
||||||
|
|
|
@ -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`.
|
||||||
//
|
//
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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{}) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
// +build !appengine,!js,!windows,aix
|
|
||||||
|
|
||||||
package logrus
|
|
||||||
|
|
||||||
import "io"
|
|
||||||
|
|
||||||
func checkIfTerminal(w io.Writer) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue