1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Update logrus to v0.6.0 so we can use ParseLevel() for PR #8335

Signed-off-by: Doug Davis <dug@us.ibm.com>
This commit is contained in:
Doug Davis 2014-11-03 11:31:37 -08:00
parent 025363848b
commit a12d897394
10 changed files with 181 additions and 69 deletions

View file

@ -53,7 +53,7 @@ clone hg code.google.com/p/gosqlite 74691fb6f837
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
# TODO after Go 1.4 drops, bump our minimum supported version and drop this vendored dep

View file

@ -1,7 +1,9 @@
language: go
go:
- 1.1
- 1.2
- 1.3
- tip
before_script:
install:
- go get github.com/stretchr/testify
- go get github.com/stvp/go-udp-testing
- go get github.com/tobi/airbrake-go

View file

@ -81,7 +81,7 @@ func init() {
// 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.
log.AddHook(logrus_airbrake.AirbrakeHook)
log.AddHook(&logrus_airbrake.AirbrakeHook{})
// Output to stderr instead of stdout, could also be a file.
log.SetOutput(os.Stderr)
@ -126,7 +126,7 @@ func main() {
// exported logger. See Godoc.
log.Out = os.Stderr
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"animal": "walrus",
"size": 10,
}).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.
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.
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
Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.

View file

@ -8,7 +8,7 @@ import (
"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,
// 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.
@ -28,8 +28,6 @@ type Entry struct {
Message string
}
var baseTimestamp time.Time
func NewEntry(logger *Logger) *Entry {
return &Entry{
Logger: logger,
@ -72,18 +70,22 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
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.Level = level
entry.Message = msg
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()
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()
@ -91,10 +93,15 @@ func (entry *Entry) log(level Level, msg string) string {
_, err = io.Copy(entry.Logger.Out, reader)
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{}) {
@ -134,8 +141,7 @@ func (entry *Entry) Fatal(args ...interface{}) {
func (entry *Entry) Panic(args ...interface{}) {
if entry.Logger.Level >= PanicLevel {
msg := entry.log(PanicLevel, fmt.Sprint(args...))
panic(msg)
entry.log(PanicLevel, fmt.Sprint(args...))
}
panic(fmt.Sprint(args...))
}

View file

@ -96,7 +96,7 @@ func Fatal(args ...interface{}) {
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{}) {
std.Debugf(format, args...)
}
@ -126,7 +126,7 @@ func Errorf(format string, args ...interface{}) {
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{}) {
std.Panicf(format, args...)
}

View file

@ -1,9 +1,5 @@
package logrus
import (
"time"
)
// The Formatter interface is used to implement a custom Formatter. It takes an
// `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": "..."}
//
// 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.
func prefixFieldClashes(entry *Entry) {
_, ok := entry.Data["time"]
@ -36,19 +32,13 @@ func prefixFieldClashes(entry *Entry) {
entry.Data["fields.time"] = entry.Data["time"]
}
entry.Data["time"] = entry.Time.Format(time.RFC3339)
_, ok = entry.Data["msg"]
if ok {
entry.Data["fields.msg"] = entry.Data["msg"]
}
entry.Data["msg"] = entry.Message
_, ok = entry.Data["level"]
if ok {
entry.Data["fields.level"] = entry.Data["level"]
}
entry.Data["level"] = entry.Level.String()
}

View file

@ -3,13 +3,16 @@ package logrus
import (
"encoding/json"
"fmt"
"time"
)
type JSONFormatter struct {
}
type JSONFormatter struct{}
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
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)
if err != nil {

View file

@ -1,6 +1,7 @@
package logrus
import (
"fmt"
"log"
)
@ -30,6 +31,27 @@ func (level Level) String() string {
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
// on your instance of logger, obtained with `logrus.New()`.
const (

View file

@ -3,6 +3,8 @@ package logrus
import (
"bytes"
"encoding/json"
"strconv"
"strings"
"testing"
"github.com/stretchr/testify/assert"
@ -24,6 +26,31 @@ func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fi
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) {
LogAndAssertJSON(t, func(log *Logger) {
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) {
assert.Equal(t, "debug", DebugLevel.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, "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())
}

View file

@ -16,8 +16,14 @@ const (
blue = 34
)
var (
baseTimestamp time.Time
isTerminal bool
)
func init() {
baseTimestamp = time.Now()
isTerminal = IsTerminal()
}
func miniTS() int {
@ -31,45 +37,27 @@ type TextFormatter struct {
}
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{}
prefixFieldClashes(entry)
if (f.ForceColors || IsTerminal()) && !f.DisableColors {
levelText := strings.ToUpper(entry.Data["level"].(string))[0:4]
isColored := (f.ForceColors || isTerminal) && !f.DisableColors
levelColor := blue
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)
}
if isColored {
printColored(b, entry, keys)
} else {
f.AppendKeyValue(b, "time", entry.Data["time"].(string))
f.AppendKeyValue(b, "level", entry.Data["level"].(string))
f.AppendKeyValue(b, "msg", entry.Data["msg"].(string))
for key, value := range entry.Data {
if key != "time" && key != "level" && key != "msg" {
f.AppendKeyValue(b, key, value)
}
f.appendKeyValue(b, "time", entry.Time.Format(time.RFC3339))
f.appendKeyValue(b, "level", entry.Level.String())
f.appendKeyValue(b, "msg", entry.Message)
for _, key := range keys {
f.appendKeyValue(b, key, entry.Data[key])
}
}
@ -77,10 +65,31 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
return b.Bytes(), nil
}
func (f *TextFormatter) AppendKeyValue(b *bytes.Buffer, key, value interface{}) {
if _, ok := value.(string); ok {
func printColored(b *bytes.Buffer, entry *Entry, keys []string) {
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)
} else {
default:
fmt.Fprintf(b, "%v=%v ", key, value)
}
}