Fix #13708
Signed-off-by: Alexander Morozov <lk4d4@docker.com>
| ... | ... |
@@ -43,7 +43,7 @@ clone() {
|
| 43 | 43 |
} |
| 44 | 44 |
|
| 45 | 45 |
# the following lines are in sorted order, FYI |
| 46 |
-clone git github.com/Sirupsen/logrus v0.7.3 # logrus is a common dependency among multiple deps |
|
| 46 |
+clone git github.com/Sirupsen/logrus v0.8.2 # logrus is a common dependency among multiple deps |
|
| 47 | 47 |
clone git github.com/docker/libtrust 230dfd18c232 |
| 48 | 48 |
clone git github.com/go-check/check 64131543e7896d5bcc6bd5a76287eb75ea96c673 |
| 49 | 49 |
clone git github.com/gorilla/context 14f550f51a |
| ... | ... |
@@ -1,3 +1,17 @@ |
| 1 |
+# 0.8.2 |
|
| 2 |
+ |
|
| 3 |
+logrus: fix more Fatal family functions |
|
| 4 |
+ |
|
| 5 |
+# 0.8.1 |
|
| 6 |
+ |
|
| 7 |
+logrus: fix not exiting on `Fatalf` and `Fatalln` |
|
| 8 |
+ |
|
| 9 |
+# 0.8.0 |
|
| 10 |
+ |
|
| 11 |
+logrus: defaults to stderr instead of stdout |
|
| 12 |
+hooks/sentry: add special field for `*http.Request` |
|
| 13 |
+formatter/text: ignore Windows for colors |
|
| 14 |
+ |
|
| 1 | 15 |
# 0.7.3 |
| 2 | 16 |
|
| 3 | 17 |
formatter/\*: allow configuration of timestamp layout |
| ... | ... |
@@ -32,7 +32,7 @@ ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"} |
| 32 | 32 |
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"} |
| 33 | 33 |
``` |
| 34 | 34 |
|
| 35 |
-With the default `log.Formatter = new(logrus.TextFormatter)` when a TTY is not |
|
| 35 |
+With the default `log.Formatter = new(&log.TextFormatter{})` when a TTY is not
|
|
| 36 | 36 |
attached, the output is compatible with the |
| 37 | 37 |
[logfmt](http://godoc.org/github.com/kr/logfmt) format: |
| 38 | 38 |
|
| ... | ... |
@@ -211,6 +211,7 @@ func init() {
|
| 211 | 211 |
| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. | |
| 212 | 212 |
| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` | |
| 213 | 213 |
| [Graylog](https://github.com/gemnasium/logrus-hooks/tree/master/graylog) | Hook for logging to [Graylog](http://graylog2.org/) | |
| 214 |
+| [Raygun](https://github.com/squirkle/logrus-raygun-hook) | Hook for logging to [Raygun.io](http://raygun.io/) | |
|
| 214 | 215 |
|
| 215 | 216 |
#### Level logging |
| 216 | 217 |
|
| ... | ... |
@@ -269,7 +270,7 @@ init() {
|
| 269 | 269 |
log.SetFormatter(logrus.JSONFormatter) |
| 270 | 270 |
} else {
|
| 271 | 271 |
// The TextFormatter is default, you don't actually have to do this. |
| 272 |
- log.SetFormatter(logrus.TextFormatter) |
|
| 272 |
+ log.SetFormatter(&log.TextFormatter{})
|
|
| 273 | 273 |
} |
| 274 | 274 |
} |
| 275 | 275 |
``` |
| ... | ... |
@@ -188,6 +188,7 @@ func (entry *Entry) Fatalf(format string, args ...interface{}) {
|
| 188 | 188 |
if entry.Logger.Level >= FatalLevel {
|
| 189 | 189 |
entry.Fatal(fmt.Sprintf(format, args...)) |
| 190 | 190 |
} |
| 191 |
+ os.Exit(1) |
|
| 191 | 192 |
} |
| 192 | 193 |
|
| 193 | 194 |
func (entry *Entry) Panicf(format string, args ...interface{}) {
|
| ... | ... |
@@ -234,6 +235,7 @@ func (entry *Entry) Fatalln(args ...interface{}) {
|
| 234 | 234 |
if entry.Logger.Level >= FatalLevel {
|
| 235 | 235 |
entry.Fatal(entry.sprintlnn(args...)) |
| 236 | 236 |
} |
| 237 |
+ os.Exit(1) |
|
| 237 | 238 |
} |
| 238 | 239 |
|
| 239 | 240 |
func (entry *Entry) Panicln(args ...interface{}) {
|
| ... | ... |
@@ -34,12 +34,13 @@ func main() {
|
| 34 | 34 |
## Special fields |
| 35 | 35 |
|
| 36 | 36 |
Some logrus fields have a special meaning in this hook, |
| 37 |
-these are server_name and logger. |
|
| 37 |
+these are `server_name`, `logger` and `http_request`. |
|
| 38 | 38 |
When logs are sent to sentry these fields are treated differently. |
| 39 |
-- server_name (also known as hostname) is the name of the server which |
|
| 39 |
+- `server_name` (also known as hostname) is the name of the server which |
|
| 40 | 40 |
is logging the event (hostname.example.com) |
| 41 |
-- logger is the part of the application which is logging the event. |
|
| 41 |
+- `logger` is the part of the application which is logging the event. |
|
| 42 | 42 |
In go this usually means setting it to the name of the package. |
| 43 |
+- `http_request` is the in-coming request(*http.Request). The detailed request data are sent to Sentry. |
|
| 43 | 44 |
|
| 44 | 45 |
## Timeout |
| 45 | 46 |
|
| ... | ... |
@@ -3,6 +3,7 @@ package logrus_sentry |
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
"time" |
| 6 |
+ "net/http" |
|
| 6 | 7 |
|
| 7 | 8 |
"github.com/Sirupsen/logrus" |
| 8 | 9 |
"github.com/getsentry/raven-go" |
| ... | ... |
@@ -36,6 +37,22 @@ func getAndDel(d logrus.Fields, key string) (string, bool) {
|
| 36 | 36 |
return val, true |
| 37 | 37 |
} |
| 38 | 38 |
|
| 39 |
+func getAndDelRequest(d logrus.Fields, key string) (*http.Request, bool) {
|
|
| 40 |
+ var ( |
|
| 41 |
+ ok bool |
|
| 42 |
+ v interface{}
|
|
| 43 |
+ req *http.Request |
|
| 44 |
+ ) |
|
| 45 |
+ if v, ok = d[key]; !ok {
|
|
| 46 |
+ return nil, false |
|
| 47 |
+ } |
|
| 48 |
+ if req, ok = v.(*http.Request); !ok || req == nil {
|
|
| 49 |
+ return nil, false |
|
| 50 |
+ } |
|
| 51 |
+ delete(d, key) |
|
| 52 |
+ return req, true |
|
| 53 |
+} |
|
| 54 |
+ |
|
| 39 | 55 |
// SentryHook delivers logs to a sentry server. |
| 40 | 56 |
type SentryHook struct {
|
| 41 | 57 |
// Timeout sets the time to wait for a delivery error from the sentry server. |
| ... | ... |
@@ -61,7 +78,7 @@ func NewSentryHook(DSN string, levels []logrus.Level) (*SentryHook, error) {
|
| 61 | 61 |
// Called when an event should be sent to sentry |
| 62 | 62 |
// Special fields that sentry uses to give more information to the server |
| 63 | 63 |
// are extracted from entry.Data (if they are found) |
| 64 |
-// These fields are: logger and server_name |
|
| 64 |
+// These fields are: logger, server_name and http_request |
|
| 65 | 65 |
func (hook *SentryHook) Fire(entry *logrus.Entry) error {
|
| 66 | 66 |
packet := &raven.Packet{
|
| 67 | 67 |
Message: entry.Message, |
| ... | ... |
@@ -78,6 +95,9 @@ func (hook *SentryHook) Fire(entry *logrus.Entry) error {
|
| 78 | 78 |
if serverName, ok := getAndDel(d, "server_name"); ok {
|
| 79 | 79 |
packet.ServerName = serverName |
| 80 | 80 |
} |
| 81 |
+ if req, ok := getAndDelRequest(d, "http_request"); ok {
|
|
| 82 |
+ packet.Interfaces = append(packet.Interfaces, raven.NewHttp(req)) |
|
| 83 |
+ } |
|
| 81 | 84 |
packet.Extra = map[string]interface{}(d)
|
| 82 | 85 |
|
| 83 | 86 |
_, errCh := hook.client.Capture(packet, nil) |
| ... | ... |
@@ -61,9 +61,12 @@ func TestSpecialFields(t *testing.T) {
|
| 61 | 61 |
t.Fatal(err.Error()) |
| 62 | 62 |
} |
| 63 | 63 |
logger.Hooks.Add(hook) |
| 64 |
+ |
|
| 65 |
+ req, _ := http.NewRequest("GET", "url", nil)
|
|
| 64 | 66 |
logger.WithFields(logrus.Fields{
|
| 65 |
- "server_name": server_name, |
|
| 66 |
- "logger": logger_name, |
|
| 67 |
+ "server_name": server_name, |
|
| 68 |
+ "logger": logger_name, |
|
| 69 |
+ "http_request": req, |
|
| 67 | 70 |
}).Error(message) |
| 68 | 71 |
|
| 69 | 72 |
packet := <-pch |
| ... | ... |
@@ -24,11 +24,12 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
| 24 | 24 |
} |
| 25 | 25 |
prefixFieldClashes(data) |
| 26 | 26 |
|
| 27 |
- if f.TimestampFormat == "" {
|
|
| 28 |
- f.TimestampFormat = DefaultTimestampFormat |
|
| 27 |
+ timestampFormat := f.TimestampFormat |
|
| 28 |
+ if timestampFormat == "" {
|
|
| 29 |
+ timestampFormat = DefaultTimestampFormat |
|
| 29 | 30 |
} |
| 30 | 31 |
|
| 31 |
- data["time"] = entry.Time.Format(f.TimestampFormat) |
|
| 32 |
+ data["time"] = entry.Time.Format(timestampFormat) |
|
| 32 | 33 |
data["msg"] = entry.Message |
| 33 | 34 |
data["level"] = entry.Level.String() |
| 34 | 35 |
|
| ... | ... |
@@ -44,7 +44,7 @@ type Logger struct {
|
| 44 | 44 |
// It's recommended to make this a global instance called `log`. |
| 45 | 45 |
func New() *Logger {
|
| 46 | 46 |
return &Logger{
|
| 47 |
- Out: os.Stdout, |
|
| 47 |
+ Out: os.Stderr, |
|
| 48 | 48 |
Formatter: new(TextFormatter), |
| 49 | 49 |
Hooks: make(levelHooks), |
| 50 | 50 |
Level: InfoLevel, |
| ... | ... |
@@ -102,6 +102,7 @@ func (logger *Logger) Fatalf(format string, args ...interface{}) {
|
| 102 | 102 |
if logger.Level >= FatalLevel {
|
| 103 | 103 |
NewEntry(logger).Fatalf(format, args...) |
| 104 | 104 |
} |
| 105 |
+ os.Exit(1) |
|
| 105 | 106 |
} |
| 106 | 107 |
|
| 107 | 108 |
func (logger *Logger) Panicf(format string, args ...interface{}) {
|
| ... | ... |
@@ -148,6 +149,7 @@ func (logger *Logger) Fatal(args ...interface{}) {
|
| 148 | 148 |
if logger.Level >= FatalLevel {
|
| 149 | 149 |
NewEntry(logger).Fatal(args...) |
| 150 | 150 |
} |
| 151 |
+ os.Exit(1) |
|
| 151 | 152 |
} |
| 152 | 153 |
|
| 153 | 154 |
func (logger *Logger) Panic(args ...interface{}) {
|
| ... | ... |
@@ -194,6 +196,7 @@ func (logger *Logger) Fatalln(args ...interface{}) {
|
| 194 | 194 |
if logger.Level >= FatalLevel {
|
| 195 | 195 |
NewEntry(logger).Fatalln(args...) |
| 196 | 196 |
} |
| 197 |
+ os.Exit(1) |
|
| 197 | 198 |
} |
| 198 | 199 |
|
| 199 | 200 |
func (logger *Logger) Panicln(args ...interface{}) {
|
| ... | ... |
@@ -191,7 +191,7 @@ func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) {
|
| 191 | 191 |
log.WithField("level", 1).Info("test")
|
| 192 | 192 |
}, func(fields Fields) {
|
| 193 | 193 |
assert.Equal(t, fields["level"], "info") |
| 194 |
- assert.Equal(t, fields["fields.level"], 1) |
|
| 194 |
+ assert.Equal(t, fields["fields.level"], 1.0) // JSON has floats only |
|
| 195 | 195 |
}) |
| 196 | 196 |
} |
| 197 | 197 |
|
| ... | ... |
@@ -3,6 +3,7 @@ package logrus |
| 3 | 3 |
import ( |
| 4 | 4 |
"bytes" |
| 5 | 5 |
"fmt" |
| 6 |
+ "runtime" |
|
| 6 | 7 |
"sort" |
| 7 | 8 |
"strings" |
| 8 | 9 |
"time" |
| ... | ... |
@@ -69,7 +70,8 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
| 69 | 69 |
|
| 70 | 70 |
prefixFieldClashes(entry.Data) |
| 71 | 71 |
|
| 72 |
- isColored := (f.ForceColors || isTerminal) && !f.DisableColors |
|
| 72 |
+ isColorTerminal := isTerminal && (runtime.GOOS != "windows") |
|
| 73 |
+ isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors |
|
| 73 | 74 |
|
| 74 | 75 |
if f.TimestampFormat == "" {
|
| 75 | 76 |
f.TimestampFormat = DefaultTimestampFormat |