mirror of https://github.com/docker/docs.git
Merge pull request #8932 from duglin/UpdateLogrus
Update logrus to the latest so we can use ParseLevel() for PR #8335
This commit is contained in:
commit
38d3888194
|
@ -53,7 +53,7 @@ clone hg code.google.com/p/gosqlite 74691fb6f837
|
||||||
|
|
||||||
clone git github.com/docker/libtrust d273ef2565ca
|
clone git github.com/docker/libtrust d273ef2565ca
|
||||||
|
|
||||||
clone git github.com/Sirupsen/logrus v0.5.1
|
clone git github.com/Sirupsen/logrus v0.6.0
|
||||||
|
|
||||||
# get Go tip's archive/tar, for xattr support and improved performance
|
# get Go tip's archive/tar, for xattr support and improved performance
|
||||||
# TODO after Go 1.4 drops, bump our minimum supported version and drop this vendored dep
|
# TODO after Go 1.4 drops, bump our minimum supported version and drop this vendored dep
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
language: go
|
language: go
|
||||||
go:
|
go:
|
||||||
- 1.1
|
|
||||||
- 1.2
|
- 1.2
|
||||||
|
- 1.3
|
||||||
- tip
|
- tip
|
||||||
before_script:
|
install:
|
||||||
- go get github.com/stretchr/testify
|
- go get github.com/stretchr/testify
|
||||||
|
- go get github.com/stvp/go-udp-testing
|
||||||
|
- go get github.com/tobi/airbrake-go
|
||||||
|
|
|
@ -81,7 +81,7 @@ func init() {
|
||||||
|
|
||||||
// Use the Airbrake hook to report errors that have Error severity or above to
|
// Use the Airbrake hook to report errors that have Error severity or above to
|
||||||
// an exception tracker. You can create custom hooks, see the Hooks section.
|
// an exception tracker. You can create custom hooks, see the Hooks section.
|
||||||
log.AddHook(logrus_airbrake.AirbrakeHook)
|
log.AddHook(&logrus_airbrake.AirbrakeHook{})
|
||||||
|
|
||||||
// Output to stderr instead of stdout, could also be a file.
|
// Output to stderr instead of stdout, could also be a file.
|
||||||
log.SetOutput(os.Stderr)
|
log.SetOutput(os.Stderr)
|
||||||
|
@ -126,7 +126,7 @@ func main() {
|
||||||
// exported logger. See Godoc.
|
// exported logger. See Godoc.
|
||||||
log.Out = os.Stderr
|
log.Out = os.Stderr
|
||||||
|
|
||||||
log.WithFields(log.Fields{
|
log.WithFields(logrus.Fields{
|
||||||
"animal": "walrus",
|
"animal": "walrus",
|
||||||
"size": 10,
|
"size": 10,
|
||||||
}).Info("A group of walrus emerges from the ocean")
|
}).Info("A group of walrus emerges from the ocean")
|
||||||
|
@ -214,14 +214,20 @@ func init() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
* [`github.com/Sirupsen/logrus/hooks/airbrake`](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go).
|
* [`github.com/Sirupsen/logrus/hooks/airbrake`](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go)
|
||||||
Send errors to an exception tracking service compatible with the Airbrake API.
|
Send errors to an exception tracking service compatible with the Airbrake API.
|
||||||
Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes.
|
Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes.
|
||||||
|
|
||||||
* [`github.com/Sirupsen/logrus/hooks/syslog`](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go).
|
* [`github.com/Sirupsen/logrus/hooks/papertrail`](https://github.com/Sirupsen/logrus/blob/master/hooks/papertrail/papertrail.go)
|
||||||
|
Send errors to the Papertrail hosted logging service via UDP.
|
||||||
|
|
||||||
|
* [`github.com/Sirupsen/logrus/hooks/syslog`](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go)
|
||||||
Send errors to remote syslog server.
|
Send errors to remote syslog server.
|
||||||
Uses standard library `log/syslog` behind the scenes.
|
Uses standard library `log/syslog` behind the scenes.
|
||||||
|
|
||||||
|
* [`github.com/nubo/hiprus`](https://github.com/nubo/hiprus)
|
||||||
|
Send errors to a channel in hipchat.
|
||||||
|
|
||||||
#### Level logging
|
#### Level logging
|
||||||
|
|
||||||
Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.
|
Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// An entry is the final or intermediate Logrus logging entry. It containts all
|
// An entry is the final or intermediate Logrus logging entry. It contains all
|
||||||
// the fields passed with WithField{,s}. It's finally logged when Debug, Info,
|
// the fields passed with WithField{,s}. It's finally logged when Debug, Info,
|
||||||
// Warn, Error, Fatal or Panic is called on it. These objects can be reused and
|
// Warn, Error, Fatal or Panic is called on it. These objects can be reused and
|
||||||
// passed around as much as you wish to avoid field duplication.
|
// passed around as much as you wish to avoid field duplication.
|
||||||
|
@ -28,8 +28,6 @@ type Entry struct {
|
||||||
Message string
|
Message string
|
||||||
}
|
}
|
||||||
|
|
||||||
var baseTimestamp time.Time
|
|
||||||
|
|
||||||
func NewEntry(logger *Logger) *Entry {
|
func NewEntry(logger *Logger) *Entry {
|
||||||
return &Entry{
|
return &Entry{
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
|
@ -72,18 +70,22 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
|
||||||
return &Entry{Logger: entry.Logger, Data: data}
|
return &Entry{Logger: entry.Logger, Data: data}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (entry *Entry) log(level Level, msg string) string {
|
func (entry *Entry) log(level Level, msg string) {
|
||||||
entry.Time = time.Now()
|
entry.Time = time.Now()
|
||||||
entry.Level = level
|
entry.Level = level
|
||||||
entry.Message = msg
|
entry.Message = msg
|
||||||
|
|
||||||
if err := entry.Logger.Hooks.Fire(level, entry); err != nil {
|
if err := entry.Logger.Hooks.Fire(level, entry); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Failed to fire hook", err)
|
entry.Logger.mu.Lock()
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
||||||
|
entry.Logger.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
reader, err := entry.Reader()
|
reader, err := entry.Reader()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v", err)
|
entry.Logger.mu.Lock()
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
||||||
|
entry.Logger.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.Logger.mu.Lock()
|
entry.Logger.mu.Lock()
|
||||||
|
@ -91,10 +93,15 @@ func (entry *Entry) log(level Level, msg string) string {
|
||||||
|
|
||||||
_, err = io.Copy(entry.Logger.Out, reader)
|
_, err = io.Copy(entry.Logger.Out, reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Failed to write to log, %v", err)
|
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return reader.String()
|
// To avoid Entry#log() returning a value that only would make sense for
|
||||||
|
// panic() to use in Entry#Panic(), we avoid the allocation by checking
|
||||||
|
// directly here.
|
||||||
|
if level <= PanicLevel {
|
||||||
|
panic(reader.String())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (entry *Entry) Debug(args ...interface{}) {
|
func (entry *Entry) Debug(args ...interface{}) {
|
||||||
|
@ -134,8 +141,7 @@ func (entry *Entry) Fatal(args ...interface{}) {
|
||||||
|
|
||||||
func (entry *Entry) Panic(args ...interface{}) {
|
func (entry *Entry) Panic(args ...interface{}) {
|
||||||
if entry.Logger.Level >= PanicLevel {
|
if entry.Logger.Level >= PanicLevel {
|
||||||
msg := entry.log(PanicLevel, fmt.Sprint(args...))
|
entry.log(PanicLevel, fmt.Sprint(args...))
|
||||||
panic(msg)
|
|
||||||
}
|
}
|
||||||
panic(fmt.Sprint(args...))
|
panic(fmt.Sprint(args...))
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,7 +96,7 @@ func Fatal(args ...interface{}) {
|
||||||
std.Fatal(args...)
|
std.Fatal(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debugf logs a message at level Debugf on the standard logger.
|
// Debugf logs a message at level Debug on the standard logger.
|
||||||
func Debugf(format string, args ...interface{}) {
|
func Debugf(format string, args ...interface{}) {
|
||||||
std.Debugf(format, args...)
|
std.Debugf(format, args...)
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ func Errorf(format string, args ...interface{}) {
|
||||||
std.Errorf(format, args...)
|
std.Errorf(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Panicf logs a message at level Pancf on the standard logger.
|
// Panicf logs a message at level Panic on the standard logger.
|
||||||
func Panicf(format string, args ...interface{}) {
|
func Panicf(format string, args ...interface{}) {
|
||||||
std.Panicf(format, args...)
|
std.Panicf(format, args...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
package logrus
|
package logrus
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The Formatter interface is used to implement a custom Formatter. It takes an
|
// The Formatter interface is used to implement a custom Formatter. It takes an
|
||||||
// `Entry`. It exposes all the fields, including the default ones:
|
// `Entry`. It exposes all the fields, including the default ones:
|
||||||
//
|
//
|
||||||
|
@ -28,7 +24,7 @@ type Formatter interface {
|
||||||
//
|
//
|
||||||
// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
|
// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
|
||||||
//
|
//
|
||||||
// It's not exported because it's still using Data in an opionated way. It's to
|
// It's not exported because it's still using Data in an opinionated way. It's to
|
||||||
// avoid code duplication between the two default formatters.
|
// avoid code duplication between the two default formatters.
|
||||||
func prefixFieldClashes(entry *Entry) {
|
func prefixFieldClashes(entry *Entry) {
|
||||||
_, ok := entry.Data["time"]
|
_, ok := entry.Data["time"]
|
||||||
|
@ -36,19 +32,13 @@ func prefixFieldClashes(entry *Entry) {
|
||||||
entry.Data["fields.time"] = entry.Data["time"]
|
entry.Data["fields.time"] = entry.Data["time"]
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.Data["time"] = entry.Time.Format(time.RFC3339)
|
|
||||||
|
|
||||||
_, ok = entry.Data["msg"]
|
_, ok = entry.Data["msg"]
|
||||||
if ok {
|
if ok {
|
||||||
entry.Data["fields.msg"] = entry.Data["msg"]
|
entry.Data["fields.msg"] = entry.Data["msg"]
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.Data["msg"] = entry.Message
|
|
||||||
|
|
||||||
_, ok = entry.Data["level"]
|
_, ok = entry.Data["level"]
|
||||||
if ok {
|
if ok {
|
||||||
entry.Data["fields.level"] = entry.Data["level"]
|
entry.Data["fields.level"] = entry.Data["level"]
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.Data["level"] = entry.Level.String()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,16 @@ package logrus
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type JSONFormatter struct {
|
type JSONFormatter struct{}
|
||||||
}
|
|
||||||
|
|
||||||
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||||
prefixFieldClashes(entry)
|
prefixFieldClashes(entry)
|
||||||
|
entry.Data["time"] = entry.Time.Format(time.RFC3339)
|
||||||
|
entry.Data["msg"] = entry.Message
|
||||||
|
entry.Data["level"] = entry.Level.String()
|
||||||
|
|
||||||
serialized, err := json.Marshal(entry.Data)
|
serialized, err := json.Marshal(entry.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package logrus
|
package logrus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -30,6 +31,27 @@ func (level Level) String() string {
|
||||||
return "unknown"
|
return "unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseLevel takes a string level and returns the Logrus log level constant.
|
||||||
|
func ParseLevel(lvl string) (Level, error) {
|
||||||
|
switch lvl {
|
||||||
|
case "panic":
|
||||||
|
return PanicLevel, nil
|
||||||
|
case "fatal":
|
||||||
|
return FatalLevel, nil
|
||||||
|
case "error":
|
||||||
|
return ErrorLevel, nil
|
||||||
|
case "warn", "warning":
|
||||||
|
return WarnLevel, nil
|
||||||
|
case "info":
|
||||||
|
return InfoLevel, nil
|
||||||
|
case "debug":
|
||||||
|
return DebugLevel, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var l Level
|
||||||
|
return l, fmt.Errorf("not a valid logrus Level: %q", lvl)
|
||||||
|
}
|
||||||
|
|
||||||
// These are the different logging levels. You can set the logging level to log
|
// These are the different logging levels. You can set the logging level to log
|
||||||
// on your instance of logger, obtained with `logrus.New()`.
|
// on your instance of logger, obtained with `logrus.New()`.
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -3,6 +3,8 @@ package logrus
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -24,6 +26,31 @@ func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fi
|
||||||
assertions(fields)
|
assertions(fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) {
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
|
||||||
|
logger := New()
|
||||||
|
logger.Out = &buffer
|
||||||
|
logger.Formatter = &TextFormatter{
|
||||||
|
DisableColors: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
log(logger)
|
||||||
|
|
||||||
|
fields := make(map[string]string)
|
||||||
|
for _, kv := range strings.Split(buffer.String(), " ") {
|
||||||
|
if !strings.Contains(kv, "=") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
kvArr := strings.Split(kv, "=")
|
||||||
|
key := strings.TrimSpace(kvArr[0])
|
||||||
|
val, err := strconv.Unquote(kvArr[1])
|
||||||
|
assert.NoError(t, err)
|
||||||
|
fields[key] = val
|
||||||
|
}
|
||||||
|
assertions(fields)
|
||||||
|
}
|
||||||
|
|
||||||
func TestPrint(t *testing.T) {
|
func TestPrint(t *testing.T) {
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
LogAndAssertJSON(t, func(log *Logger) {
|
||||||
log.Print("test")
|
log.Print("test")
|
||||||
|
@ -163,6 +190,20 @@ func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDefaultFieldsAreNotPrefixed(t *testing.T) {
|
||||||
|
LogAndAssertText(t, func(log *Logger) {
|
||||||
|
ll := log.WithField("herp", "derp")
|
||||||
|
ll.Info("hello")
|
||||||
|
ll.Info("bye")
|
||||||
|
}, func(fields map[string]string) {
|
||||||
|
for _, fieldName := range []string{"fields.level", "fields.time", "fields.msg"} {
|
||||||
|
if _, ok := fields[fieldName]; ok {
|
||||||
|
t.Fatalf("should not have prefixed %q: %v", fieldName, fields)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestConvertLevelToString(t *testing.T) {
|
func TestConvertLevelToString(t *testing.T) {
|
||||||
assert.Equal(t, "debug", DebugLevel.String())
|
assert.Equal(t, "debug", DebugLevel.String())
|
||||||
assert.Equal(t, "info", InfoLevel.String())
|
assert.Equal(t, "info", InfoLevel.String())
|
||||||
|
@ -171,3 +212,36 @@ func TestConvertLevelToString(t *testing.T) {
|
||||||
assert.Equal(t, "fatal", FatalLevel.String())
|
assert.Equal(t, "fatal", FatalLevel.String())
|
||||||
assert.Equal(t, "panic", PanicLevel.String())
|
assert.Equal(t, "panic", PanicLevel.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseLevel(t *testing.T) {
|
||||||
|
l, err := ParseLevel("panic")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, PanicLevel, l)
|
||||||
|
|
||||||
|
l, err = ParseLevel("fatal")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, FatalLevel, l)
|
||||||
|
|
||||||
|
l, err = ParseLevel("error")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, ErrorLevel, l)
|
||||||
|
|
||||||
|
l, err = ParseLevel("warn")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, WarnLevel, l)
|
||||||
|
|
||||||
|
l, err = ParseLevel("warning")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, WarnLevel, l)
|
||||||
|
|
||||||
|
l, err = ParseLevel("info")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, InfoLevel, l)
|
||||||
|
|
||||||
|
l, err = ParseLevel("debug")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, DebugLevel, l)
|
||||||
|
|
||||||
|
l, err = ParseLevel("invalid")
|
||||||
|
assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error())
|
||||||
|
}
|
||||||
|
|
|
@ -16,8 +16,14 @@ const (
|
||||||
blue = 34
|
blue = 34
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
baseTimestamp time.Time
|
||||||
|
isTerminal bool
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
baseTimestamp = time.Now()
|
baseTimestamp = time.Now()
|
||||||
|
isTerminal = IsTerminal()
|
||||||
}
|
}
|
||||||
|
|
||||||
func miniTS() int {
|
func miniTS() int {
|
||||||
|
@ -31,45 +37,27 @@ type TextFormatter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||||
|
|
||||||
|
var keys []string
|
||||||
|
for k := range entry.Data {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
|
||||||
b := &bytes.Buffer{}
|
b := &bytes.Buffer{}
|
||||||
|
|
||||||
prefixFieldClashes(entry)
|
prefixFieldClashes(entry)
|
||||||
|
|
||||||
if (f.ForceColors || IsTerminal()) && !f.DisableColors {
|
isColored := (f.ForceColors || isTerminal) && !f.DisableColors
|
||||||
levelText := strings.ToUpper(entry.Data["level"].(string))[0:4]
|
|
||||||
|
|
||||||
levelColor := blue
|
if isColored {
|
||||||
|
printColored(b, entry, keys)
|
||||||
if entry.Data["level"] == "warning" {
|
|
||||||
levelColor = yellow
|
|
||||||
} else if entry.Data["level"] == "error" ||
|
|
||||||
entry.Data["level"] == "fatal" ||
|
|
||||||
entry.Data["level"] == "panic" {
|
|
||||||
levelColor = red
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Data["msg"])
|
|
||||||
|
|
||||||
keys := make([]string, 0)
|
|
||||||
for k, _ := range entry.Data {
|
|
||||||
if k != "level" && k != "time" && k != "msg" {
|
|
||||||
keys = append(keys, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Strings(keys)
|
|
||||||
for _, k := range keys {
|
|
||||||
v := entry.Data[k]
|
|
||||||
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%v", levelColor, k, v)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
f.AppendKeyValue(b, "time", entry.Data["time"].(string))
|
f.appendKeyValue(b, "time", entry.Time.Format(time.RFC3339))
|
||||||
f.AppendKeyValue(b, "level", entry.Data["level"].(string))
|
f.appendKeyValue(b, "level", entry.Level.String())
|
||||||
f.AppendKeyValue(b, "msg", entry.Data["msg"].(string))
|
f.appendKeyValue(b, "msg", entry.Message)
|
||||||
|
for _, key := range keys {
|
||||||
for key, value := range entry.Data {
|
f.appendKeyValue(b, key, entry.Data[key])
|
||||||
if key != "time" && key != "level" && key != "msg" {
|
|
||||||
f.AppendKeyValue(b, key, value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,10 +65,31 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||||
return b.Bytes(), nil
|
return b.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *TextFormatter) AppendKeyValue(b *bytes.Buffer, key, value interface{}) {
|
func printColored(b *bytes.Buffer, entry *Entry, keys []string) {
|
||||||
if _, ok := value.(string); ok {
|
var levelColor int
|
||||||
|
switch entry.Level {
|
||||||
|
case WarnLevel:
|
||||||
|
levelColor = yellow
|
||||||
|
case ErrorLevel, FatalLevel, PanicLevel:
|
||||||
|
levelColor = red
|
||||||
|
default:
|
||||||
|
levelColor = blue
|
||||||
|
}
|
||||||
|
|
||||||
|
levelText := strings.ToUpper(entry.Level.String())[0:4]
|
||||||
|
|
||||||
|
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
|
||||||
|
for _, k := range keys {
|
||||||
|
v := entry.Data[k]
|
||||||
|
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%v", levelColor, k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key, value interface{}) {
|
||||||
|
switch value.(type) {
|
||||||
|
case string, error:
|
||||||
fmt.Fprintf(b, "%v=%q ", key, value)
|
fmt.Fprintf(b, "%v=%q ", key, value)
|
||||||
} else {
|
default:
|
||||||
fmt.Fprintf(b, "%v=%v ", key, value)
|
fmt.Fprintf(b, "%v=%v ", key, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue