Browse code

vendor: update logrus to 0.7.1

Changes relevant for Docker since 0.6.6 are (most other changes are hooks and
options for formatters):

* Debugging color output changed to gray.
* Don't quote the number 9 when it's by it self (i.e. `omg=9` instead of
`omg="8"`, this was the case for all other numbers)
* Performance is better when running a high logging level with lots of low-level
logging.
* Minor internal refactoring and more tests.

Signed-off-by: Simon Eskildsen <sirup@sirupsen.com>

Simon Eskildsen authored on 2015/03/22 11:06:50
Showing 14 changed files
... ...
@@ -53,7 +53,7 @@ clone hg code.google.com/p/gosqlite 74691fb6f837
53 53
 
54 54
 clone git github.com/docker/libtrust 230dfd18c232
55 55
 
56
-clone git github.com/Sirupsen/logrus v0.6.6
56
+clone git github.com/Sirupsen/logrus v0.7.1
57 57
 
58 58
 clone git github.com/go-fsnotify/fsnotify v1.0.4
59 59
 
... ...
@@ -82,7 +82,7 @@ func init() {
82 82
 
83 83
   // Use the Airbrake hook to report errors that have Error severity or above to
84 84
   // an exception tracker. You can create custom hooks, see the Hooks section.
85
-  log.AddHook(&logrus_airbrake.AirbrakeHook{})
85
+  log.AddHook(airbrake.NewHook("https://example.com", "xyz", "development"))
86 86
 
87 87
   // Output to stderr instead of stdout, could also be a file.
88 88
   log.SetOutput(os.Stderr)
... ...
@@ -164,43 +164,8 @@ You can add hooks for logging levels. For example to send errors to an exception
164 164
 tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
165 165
 multiple places simultaneously, e.g. syslog.
166 166
 
167
-```go
168
-// Not the real implementation of the Airbrake hook. Just a simple sample.
169
-import (
170
-  log "github.com/Sirupsen/logrus"
171
-)
172
-
173
-func init() {
174
-  log.AddHook(new(AirbrakeHook))
175
-}
176
-
177
-type AirbrakeHook struct{}
178
-
179
-// `Fire()` takes the entry that the hook is fired for. `entry.Data[]` contains
180
-// the fields for the entry. See the Fields section of the README.
181
-func (hook *AirbrakeHook) Fire(entry *logrus.Entry) error {
182
-  err := airbrake.Notify(entry.Data["error"].(error))
183
-  if err != nil {
184
-    log.WithFields(log.Fields{
185
-      "source":   "airbrake",
186
-      "endpoint": airbrake.Endpoint,
187
-    }).Info("Failed to send error to Airbrake")
188
-  }
189
-
190
-  return nil
191
-}
192
-
193
-// `Levels()` returns a slice of `Levels` the hook is fired for.
194
-func (hook *AirbrakeHook) Levels() []log.Level {
195
-  return []log.Level{
196
-    log.ErrorLevel,
197
-    log.FatalLevel,
198
-    log.PanicLevel,
199
-  }
200
-}
201
-```
202
-
203
-Logrus comes with built-in hooks. Add those, or your custom hook, in `init`:
167
+Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
168
+`init`:
204 169
 
205 170
 ```go
206 171
 import (
... ...
@@ -211,7 +176,7 @@ import (
211 211
 )
212 212
 
213 213
 func init() {
214
-  log.AddHook(new(logrus_airbrake.AirbrakeHook))
214
+  log.AddHook(airbrake.NewHook("https://example.com", "xyz", "development"))
215 215
 
216 216
   hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
217 217
   if err != nil {
... ...
@@ -233,6 +198,9 @@ func init() {
233 233
   Send errors to remote syslog server.
234 234
   Uses standard library `log/syslog` behind the scenes.
235 235
 
236
+* [`github.com/Sirupsen/logrus/hooks/bugsnag`](https://github.com/Sirupsen/logrus/blob/master/hooks/bugsnag/bugsnag.go)
237
+  Send errors to the Bugsnag exception tracking service.
238
+
236 239
 * [`github.com/nubo/hiprus`](https://github.com/nubo/hiprus)
237 240
   Send errors to a channel in hipchat.
238 241
 
... ...
@@ -321,6 +289,11 @@ The built-in logging formatters are:
321 321
     field to `true`.  To force no colored output even if there is a TTY  set the
322 322
     `DisableColors` field to `true`
323 323
 * `logrus.JSONFormatter`. Logs fields as JSON.
324
+* `logrus_logstash.LogstashFormatter`. Logs fields as Logstash Events (http://logstash.net).
325
+
326
+    ```go
327
+      logrus.SetFormatter(&logrus_logstash.LogstashFormatter{Type: “application_name"})
328
+    ```
324 329
 
325 330
 Third party logging formatters:
326 331
 
... ...
@@ -3,21 +3,16 @@ package main
3 3
 import (
4 4
 	"github.com/Sirupsen/logrus"
5 5
 	"github.com/Sirupsen/logrus/hooks/airbrake"
6
-	"github.com/tobi/airbrake-go"
7 6
 )
8 7
 
9 8
 var log = logrus.New()
10 9
 
11 10
 func init() {
12 11
 	log.Formatter = new(logrus.TextFormatter) // default
13
-	log.Hooks.Add(new(logrus_airbrake.AirbrakeHook))
12
+	log.Hooks.Add(airbrake.NewHook("https://example.com", "xyz", "development"))
14 13
 }
15 14
 
16 15
 func main() {
17
-	airbrake.Endpoint = "https://exceptions.whatever.com/notifier_api/v2/notices.xml"
18
-	airbrake.ApiKey = "whatever"
19
-	airbrake.Environment = "production"
20
-
21 16
 	log.WithFields(logrus.Fields{
22 17
 		"animal": "walrus",
23 18
 		"size":   10,
24 19
new file mode 100644
... ...
@@ -0,0 +1,48 @@
0
+package logstash
1
+
2
+import (
3
+	"encoding/json"
4
+	"fmt"
5
+	"github.com/Sirupsen/logrus"
6
+	"time"
7
+)
8
+
9
+// Formatter generates json in logstash format.
10
+// Logstash site: http://logstash.net/
11
+type LogstashFormatter struct {
12
+	Type string // if not empty use for logstash type field.
13
+}
14
+
15
+func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) {
16
+	entry.Data["@version"] = 1
17
+	entry.Data["@timestamp"] = entry.Time.Format(time.RFC3339)
18
+
19
+	// set message field
20
+	v, ok := entry.Data["message"]
21
+	if ok {
22
+		entry.Data["fields.message"] = v
23
+	}
24
+	entry.Data["message"] = entry.Message
25
+
26
+	// set level field
27
+	v, ok = entry.Data["level"]
28
+	if ok {
29
+		entry.Data["fields.level"] = v
30
+	}
31
+	entry.Data["level"] = entry.Level.String()
32
+
33
+	// set type field
34
+	if f.Type != "" {
35
+		v, ok = entry.Data["type"]
36
+		if ok {
37
+			entry.Data["fields.type"] = v
38
+		}
39
+		entry.Data["type"] = f.Type
40
+	}
41
+
42
+	serialized, err := json.Marshal(entry.Data)
43
+	if err != nil {
44
+		return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
45
+	}
46
+	return append(serialized, '\n'), nil
47
+}
0 48
new file mode 100644
... ...
@@ -0,0 +1,52 @@
0
+package logstash
1
+
2
+import (
3
+	"bytes"
4
+	"encoding/json"
5
+	"github.com/Sirupsen/logrus"
6
+	"github.com/stretchr/testify/assert"
7
+	"testing"
8
+)
9
+
10
+func TestLogstashFormatter(t *testing.T) {
11
+	assert := assert.New(t)
12
+
13
+	lf := LogstashFormatter{Type: "abc"}
14
+
15
+	fields := logrus.Fields{
16
+		"message": "def",
17
+		"level":   "ijk",
18
+		"type":    "lmn",
19
+		"one":     1,
20
+		"pi":      3.14,
21
+		"bool":    true,
22
+	}
23
+
24
+	entry := logrus.WithFields(fields)
25
+	entry.Message = "msg"
26
+	entry.Level = logrus.InfoLevel
27
+
28
+	b, _ := lf.Format(entry)
29
+
30
+	var data map[string]interface{}
31
+	dec := json.NewDecoder(bytes.NewReader(b))
32
+	dec.UseNumber()
33
+	dec.Decode(&data)
34
+
35
+	// base fields
36
+	assert.Equal(json.Number("1"), data["@version"])
37
+	assert.NotEmpty(data["@timestamp"])
38
+	assert.Equal("abc", data["type"])
39
+	assert.Equal("msg", data["message"])
40
+	assert.Equal("info", data["level"])
41
+
42
+	// substituted fields
43
+	assert.Equal("def", data["fields.message"])
44
+	assert.Equal("ijk", data["fields.level"])
45
+	assert.Equal("lmn", data["fields.type"])
46
+
47
+	// formats
48
+	assert.Equal(json.Number("1"), data["one"])
49
+	assert.Equal(json.Number("3.14"), data["pi"])
50
+	assert.Equal(true, data["bool"])
51
+}
... ...
@@ -1,51 +1,51 @@
1
-package logrus_airbrake
1
+package airbrake
2 2
 
3 3
 import (
4
+	"errors"
5
+	"fmt"
6
+
4 7
 	"github.com/Sirupsen/logrus"
5 8
 	"github.com/tobi/airbrake-go"
6 9
 )
7 10
 
8 11
 // AirbrakeHook to send exceptions to an exception-tracking service compatible
9
-// with the Airbrake API. You must set:
10
-// * airbrake.Endpoint
11
-// * airbrake.ApiKey
12
-// * airbrake.Environment
13
-//
14
-// Before using this hook, to send an error. Entries that trigger an Error,
15
-// Fatal or Panic should now include an "error" field to send to Airbrake.
16
-type AirbrakeHook struct{}
17
-
18
-func (hook *AirbrakeHook) Fire(entry *logrus.Entry) error {
19
-	if entry.Data["error"] == nil {
20
-		entry.Logger.WithFields(logrus.Fields{
21
-			"source":   "airbrake",
22
-			"endpoint": airbrake.Endpoint,
23
-		}).Warn("Exceptions sent to Airbrake must have an 'error' key with the error")
24
-		return nil
12
+// with the Airbrake API.
13
+type airbrakeHook struct {
14
+	APIKey      string
15
+	Endpoint    string
16
+	Environment string
17
+}
18
+
19
+func NewHook(endpoint, apiKey, env string) *airbrakeHook {
20
+	return &airbrakeHook{
21
+		APIKey:      apiKey,
22
+		Endpoint:    endpoint,
23
+		Environment: env,
25 24
 	}
25
+}
26
+
27
+func (hook *airbrakeHook) Fire(entry *logrus.Entry) error {
28
+	airbrake.ApiKey = hook.APIKey
29
+	airbrake.Endpoint = hook.Endpoint
30
+	airbrake.Environment = hook.Environment
26 31
 
32
+	var notifyErr error
27 33
 	err, ok := entry.Data["error"].(error)
28
-	if !ok {
29
-		entry.Logger.WithFields(logrus.Fields{
30
-			"source":   "airbrake",
31
-			"endpoint": airbrake.Endpoint,
32
-		}).Warn("Exceptions sent to Airbrake must have an `error` key of type `error`")
33
-		return nil
34
+	if ok {
35
+		notifyErr = err
36
+	} else {
37
+		notifyErr = errors.New(entry.Message)
34 38
 	}
35 39
 
36
-	airErr := airbrake.Notify(err)
40
+	airErr := airbrake.Notify(notifyErr)
37 41
 	if airErr != nil {
38
-		entry.Logger.WithFields(logrus.Fields{
39
-			"source":   "airbrake",
40
-			"endpoint": airbrake.Endpoint,
41
-			"error":    airErr,
42
-		}).Warn("Failed to send error to Airbrake")
42
+		return fmt.Errorf("Failed to send error to Airbrake: %s", airErr)
43 43
 	}
44 44
 
45 45
 	return nil
46 46
 }
47 47
 
48
-func (hook *AirbrakeHook) Levels() []logrus.Level {
48
+func (hook *airbrakeHook) Levels() []logrus.Level {
49 49
 	return []logrus.Level{
50 50
 		logrus.ErrorLevel,
51 51
 		logrus.FatalLevel,
52 52
new file mode 100644
... ...
@@ -0,0 +1,133 @@
0
+package airbrake
1
+
2
+import (
3
+	"encoding/xml"
4
+	"net/http"
5
+	"net/http/httptest"
6
+	"testing"
7
+	"time"
8
+
9
+	"github.com/Sirupsen/logrus"
10
+)
11
+
12
+type notice struct {
13
+	Error NoticeError `xml:"error"`
14
+}
15
+type NoticeError struct {
16
+	Class   string `xml:"class"`
17
+	Message string `xml:"message"`
18
+}
19
+
20
+type customErr struct {
21
+	msg string
22
+}
23
+
24
+func (e *customErr) Error() string {
25
+	return e.msg
26
+}
27
+
28
+const (
29
+	testAPIKey    = "abcxyz"
30
+	testEnv       = "development"
31
+	expectedClass = "*airbrake.customErr"
32
+	expectedMsg   = "foo"
33
+	unintendedMsg = "Airbrake will not see this string"
34
+)
35
+
36
+var (
37
+	noticeError = make(chan NoticeError, 1)
38
+)
39
+
40
+// TestLogEntryMessageReceived checks if invoking Logrus' log.Error
41
+// method causes an XML payload containing the log entry message is received
42
+// by a HTTP server emulating an Airbrake-compatible endpoint.
43
+func TestLogEntryMessageReceived(t *testing.T) {
44
+	log := logrus.New()
45
+	ts := startAirbrakeServer(t)
46
+	defer ts.Close()
47
+
48
+	hook := NewHook(ts.URL, testAPIKey, "production")
49
+	log.Hooks.Add(hook)
50
+
51
+	log.Error(expectedMsg)
52
+
53
+	select {
54
+	case received := <-noticeError:
55
+		if received.Message != expectedMsg {
56
+			t.Errorf("Unexpected message received: %s", received.Message)
57
+		}
58
+	case <-time.After(time.Second):
59
+		t.Error("Timed out; no notice received by Airbrake API")
60
+	}
61
+}
62
+
63
+// TestLogEntryMessageReceived confirms that, when passing an error type using
64
+// logrus.Fields, a HTTP server emulating an Airbrake endpoint receives the
65
+// error message returned by the Error() method on the error interface
66
+// rather than the logrus.Entry.Message string.
67
+func TestLogEntryWithErrorReceived(t *testing.T) {
68
+	log := logrus.New()
69
+	ts := startAirbrakeServer(t)
70
+	defer ts.Close()
71
+
72
+	hook := NewHook(ts.URL, testAPIKey, "production")
73
+	log.Hooks.Add(hook)
74
+
75
+	log.WithFields(logrus.Fields{
76
+		"error": &customErr{expectedMsg},
77
+	}).Error(unintendedMsg)
78
+
79
+	select {
80
+	case received := <-noticeError:
81
+		if received.Message != expectedMsg {
82
+			t.Errorf("Unexpected message received: %s", received.Message)
83
+		}
84
+		if received.Class != expectedClass {
85
+			t.Errorf("Unexpected error class: %s", received.Class)
86
+		}
87
+	case <-time.After(time.Second):
88
+		t.Error("Timed out; no notice received by Airbrake API")
89
+	}
90
+}
91
+
92
+// TestLogEntryWithNonErrorTypeNotReceived confirms that, when passing a
93
+// non-error type using logrus.Fields, a HTTP server emulating an Airbrake
94
+// endpoint receives the logrus.Entry.Message string.
95
+//
96
+// Only error types are supported when setting the 'error' field using
97
+// logrus.WithFields().
98
+func TestLogEntryWithNonErrorTypeNotReceived(t *testing.T) {
99
+	log := logrus.New()
100
+	ts := startAirbrakeServer(t)
101
+	defer ts.Close()
102
+
103
+	hook := NewHook(ts.URL, testAPIKey, "production")
104
+	log.Hooks.Add(hook)
105
+
106
+	log.WithFields(logrus.Fields{
107
+		"error": expectedMsg,
108
+	}).Error(unintendedMsg)
109
+
110
+	select {
111
+	case received := <-noticeError:
112
+		if received.Message != unintendedMsg {
113
+			t.Errorf("Unexpected message received: %s", received.Message)
114
+		}
115
+	case <-time.After(time.Second):
116
+		t.Error("Timed out; no notice received by Airbrake API")
117
+	}
118
+}
119
+
120
+func startAirbrakeServer(t *testing.T) *httptest.Server {
121
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
122
+		var notice notice
123
+		if err := xml.NewDecoder(r.Body).Decode(&notice); err != nil {
124
+			t.Error(err)
125
+		}
126
+		r.Body.Close()
127
+
128
+		noticeError <- notice.Error
129
+	}))
130
+
131
+	return ts
132
+}
0 133
new file mode 100644
... ...
@@ -0,0 +1,68 @@
0
+package logrus_bugsnag
1
+
2
+import (
3
+	"errors"
4
+
5
+	"github.com/Sirupsen/logrus"
6
+	"github.com/bugsnag/bugsnag-go"
7
+)
8
+
9
+type bugsnagHook struct{}
10
+
11
+// ErrBugsnagUnconfigured is returned if NewBugsnagHook is called before
12
+// bugsnag.Configure. Bugsnag must be configured before the hook.
13
+var ErrBugsnagUnconfigured = errors.New("bugsnag must be configured before installing this logrus hook")
14
+
15
+// ErrBugsnagSendFailed indicates that the hook failed to submit an error to
16
+// bugsnag. The error was successfully generated, but `bugsnag.Notify()`
17
+// failed.
18
+type ErrBugsnagSendFailed struct {
19
+	err error
20
+}
21
+
22
+func (e ErrBugsnagSendFailed) Error() string {
23
+	return "failed to send error to Bugsnag: " + e.err.Error()
24
+}
25
+
26
+// NewBugsnagHook initializes a logrus hook which sends exceptions to an
27
+// exception-tracking service compatible with the Bugsnag API. Before using
28
+// this hook, you must call bugsnag.Configure(). The returned object should be
29
+// registered with a log via `AddHook()`
30
+//
31
+// Entries that trigger an Error, Fatal or Panic should now include an "error"
32
+// field to send to Bugsnag.
33
+func NewBugsnagHook() (*bugsnagHook, error) {
34
+	if bugsnag.Config.APIKey == "" {
35
+		return nil, ErrBugsnagUnconfigured
36
+	}
37
+	return &bugsnagHook{}, nil
38
+}
39
+
40
+// Fire forwards an error to Bugsnag. Given a logrus.Entry, it extracts the
41
+// "error" field (or the Message if the error isn't present) and sends it off.
42
+func (hook *bugsnagHook) Fire(entry *logrus.Entry) error {
43
+	var notifyErr error
44
+	err, ok := entry.Data["error"].(error)
45
+	if ok {
46
+		notifyErr = err
47
+	} else {
48
+		notifyErr = errors.New(entry.Message)
49
+	}
50
+
51
+	bugsnagErr := bugsnag.Notify(notifyErr)
52
+	if bugsnagErr != nil {
53
+		return ErrBugsnagSendFailed{bugsnagErr}
54
+	}
55
+
56
+	return nil
57
+}
58
+
59
+// Levels enumerates the log levels on which the error should be forwarded to
60
+// bugsnag: everything at or above the "Error" level.
61
+func (hook *bugsnagHook) Levels() []logrus.Level {
62
+	return []logrus.Level{
63
+		logrus.ErrorLevel,
64
+		logrus.FatalLevel,
65
+		logrus.PanicLevel,
66
+	}
67
+}
0 68
new file mode 100644
... ...
@@ -0,0 +1,64 @@
0
+package logrus_bugsnag
1
+
2
+import (
3
+	"encoding/json"
4
+	"errors"
5
+	"io/ioutil"
6
+	"net/http"
7
+	"net/http/httptest"
8
+	"testing"
9
+	"time"
10
+
11
+	"github.com/Sirupsen/logrus"
12
+	"github.com/bugsnag/bugsnag-go"
13
+)
14
+
15
+type notice struct {
16
+	Events []struct {
17
+		Exceptions []struct {
18
+			Message string `json:"message"`
19
+		} `json:"exceptions"`
20
+	} `json:"events"`
21
+}
22
+
23
+func TestNoticeReceived(t *testing.T) {
24
+	msg := make(chan string, 1)
25
+	expectedMsg := "foo"
26
+
27
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
28
+		var notice notice
29
+		data, _ := ioutil.ReadAll(r.Body)
30
+		if err := json.Unmarshal(data, &notice); err != nil {
31
+			t.Error(err)
32
+		}
33
+		_ = r.Body.Close()
34
+
35
+		msg <- notice.Events[0].Exceptions[0].Message
36
+	}))
37
+	defer ts.Close()
38
+
39
+	hook := &bugsnagHook{}
40
+
41
+	bugsnag.Configure(bugsnag.Configuration{
42
+		Endpoint:     ts.URL,
43
+		ReleaseStage: "production",
44
+		APIKey:       "12345678901234567890123456789012",
45
+		Synchronous:  true,
46
+	})
47
+
48
+	log := logrus.New()
49
+	log.Hooks.Add(hook)
50
+
51
+	log.WithFields(logrus.Fields{
52
+		"error": errors.New(expectedMsg),
53
+	}).Error("Bugsnag will not see this string")
54
+
55
+	select {
56
+	case received := <-msg:
57
+		if received != expectedMsg {
58
+			t.Errorf("Unexpected message received: %s", received)
59
+		}
60
+	case <-time.After(time.Second):
61
+		t.Error("Timed out; no notice received by Bugsnag API")
62
+	}
63
+}
... ...
@@ -11,11 +11,12 @@ type JSONFormatter struct{}
11 11
 func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
12 12
 	data := make(Fields, len(entry.Data)+3)
13 13
 	for k, v := range entry.Data {
14
-		// Otherwise errors are ignored by `encoding/json`
15
-		// https://github.com/Sirupsen/logrus/issues/137
16
-		if err, ok := v.(error); ok {
17
-			data[k] = err.Error()
18
-		} else {
14
+		switch v := v.(type) {
15
+		case error:
16
+			// Otherwise errors are ignored by `encoding/json`
17
+			// https://github.com/Sirupsen/logrus/issues/137
18
+			data[k] = v.Error()
19
+		default:
19 20
 			data[k] = v
20 21
 		}
21 22
 	}
... ...
@@ -65,11 +65,15 @@ func (logger *Logger) WithFields(fields Fields) *Entry {
65 65
 }
66 66
 
67 67
 func (logger *Logger) Debugf(format string, args ...interface{}) {
68
-	NewEntry(logger).Debugf(format, args...)
68
+	if logger.Level >= DebugLevel {
69
+		NewEntry(logger).Debugf(format, args...)
70
+	}
69 71
 }
70 72
 
71 73
 func (logger *Logger) Infof(format string, args ...interface{}) {
72
-	NewEntry(logger).Infof(format, args...)
74
+	if logger.Level >= InfoLevel {
75
+		NewEntry(logger).Infof(format, args...)
76
+	}
73 77
 }
74 78
 
75 79
 func (logger *Logger) Printf(format string, args ...interface{}) {
... ...
@@ -77,31 +81,45 @@ func (logger *Logger) Printf(format string, args ...interface{}) {
77 77
 }
78 78
 
79 79
 func (logger *Logger) Warnf(format string, args ...interface{}) {
80
-	NewEntry(logger).Warnf(format, args...)
80
+	if logger.Level >= WarnLevel {
81
+		NewEntry(logger).Warnf(format, args...)
82
+	}
81 83
 }
82 84
 
83 85
 func (logger *Logger) Warningf(format string, args ...interface{}) {
84
-	NewEntry(logger).Warnf(format, args...)
86
+	if logger.Level >= WarnLevel {
87
+		NewEntry(logger).Warnf(format, args...)
88
+	}
85 89
 }
86 90
 
87 91
 func (logger *Logger) Errorf(format string, args ...interface{}) {
88
-	NewEntry(logger).Errorf(format, args...)
92
+	if logger.Level >= ErrorLevel {
93
+		NewEntry(logger).Errorf(format, args...)
94
+	}
89 95
 }
90 96
 
91 97
 func (logger *Logger) Fatalf(format string, args ...interface{}) {
92
-	NewEntry(logger).Fatalf(format, args...)
98
+	if logger.Level >= FatalLevel {
99
+		NewEntry(logger).Fatalf(format, args...)
100
+	}
93 101
 }
94 102
 
95 103
 func (logger *Logger) Panicf(format string, args ...interface{}) {
96
-	NewEntry(logger).Panicf(format, args...)
104
+	if logger.Level >= PanicLevel {
105
+		NewEntry(logger).Panicf(format, args...)
106
+	}
97 107
 }
98 108
 
99 109
 func (logger *Logger) Debug(args ...interface{}) {
100
-	NewEntry(logger).Debug(args...)
110
+	if logger.Level >= DebugLevel {
111
+		NewEntry(logger).Debug(args...)
112
+	}
101 113
 }
102 114
 
103 115
 func (logger *Logger) Info(args ...interface{}) {
104
-	NewEntry(logger).Info(args...)
116
+	if logger.Level >= InfoLevel {
117
+		NewEntry(logger).Info(args...)
118
+	}
105 119
 }
106 120
 
107 121
 func (logger *Logger) Print(args ...interface{}) {
... ...
@@ -109,31 +127,45 @@ func (logger *Logger) Print(args ...interface{}) {
109 109
 }
110 110
 
111 111
 func (logger *Logger) Warn(args ...interface{}) {
112
-	NewEntry(logger).Warn(args...)
112
+	if logger.Level >= WarnLevel {
113
+		NewEntry(logger).Warn(args...)
114
+	}
113 115
 }
114 116
 
115 117
 func (logger *Logger) Warning(args ...interface{}) {
116
-	NewEntry(logger).Warn(args...)
118
+	if logger.Level >= WarnLevel {
119
+		NewEntry(logger).Warn(args...)
120
+	}
117 121
 }
118 122
 
119 123
 func (logger *Logger) Error(args ...interface{}) {
120
-	NewEntry(logger).Error(args...)
124
+	if logger.Level >= ErrorLevel {
125
+		NewEntry(logger).Error(args...)
126
+	}
121 127
 }
122 128
 
123 129
 func (logger *Logger) Fatal(args ...interface{}) {
124
-	NewEntry(logger).Fatal(args...)
130
+	if logger.Level >= FatalLevel {
131
+		NewEntry(logger).Fatal(args...)
132
+	}
125 133
 }
126 134
 
127 135
 func (logger *Logger) Panic(args ...interface{}) {
128
-	NewEntry(logger).Panic(args...)
136
+	if logger.Level >= PanicLevel {
137
+		NewEntry(logger).Panic(args...)
138
+	}
129 139
 }
130 140
 
131 141
 func (logger *Logger) Debugln(args ...interface{}) {
132
-	NewEntry(logger).Debugln(args...)
142
+	if logger.Level >= DebugLevel {
143
+		NewEntry(logger).Debugln(args...)
144
+	}
133 145
 }
134 146
 
135 147
 func (logger *Logger) Infoln(args ...interface{}) {
136
-	NewEntry(logger).Infoln(args...)
148
+	if logger.Level >= InfoLevel {
149
+		NewEntry(logger).Infoln(args...)
150
+	}
137 151
 }
138 152
 
139 153
 func (logger *Logger) Println(args ...interface{}) {
... ...
@@ -141,21 +173,31 @@ func (logger *Logger) Println(args ...interface{}) {
141 141
 }
142 142
 
143 143
 func (logger *Logger) Warnln(args ...interface{}) {
144
-	NewEntry(logger).Warnln(args...)
144
+	if logger.Level >= WarnLevel {
145
+		NewEntry(logger).Warnln(args...)
146
+	}
145 147
 }
146 148
 
147 149
 func (logger *Logger) Warningln(args ...interface{}) {
148
-	NewEntry(logger).Warnln(args...)
150
+	if logger.Level >= WarnLevel {
151
+		NewEntry(logger).Warnln(args...)
152
+	}
149 153
 }
150 154
 
151 155
 func (logger *Logger) Errorln(args ...interface{}) {
152
-	NewEntry(logger).Errorln(args...)
156
+	if logger.Level >= ErrorLevel {
157
+		NewEntry(logger).Errorln(args...)
158
+	}
153 159
 }
154 160
 
155 161
 func (logger *Logger) Fatalln(args ...interface{}) {
156
-	NewEntry(logger).Fatalln(args...)
162
+	if logger.Level >= FatalLevel {
163
+		NewEntry(logger).Fatalln(args...)
164
+	}
157 165
 }
158 166
 
159 167
 func (logger *Logger) Panicln(args ...interface{}) {
160
-	NewEntry(logger).Panicln(args...)
168
+	if logger.Level >= PanicLevel {
169
+		NewEntry(logger).Panicln(args...)
170
+	}
161 171
 }
... ...
@@ -1,4 +1,3 @@
1
-
2 1
 package logrus
3 2
 
4 3
 import "syscall"
... ...
@@ -3,7 +3,6 @@ package logrus
3 3
 import (
4 4
 	"bytes"
5 5
 	"fmt"
6
-	"regexp"
7 6
 	"sort"
8 7
 	"strings"
9 8
 	"time"
... ...
@@ -21,7 +20,6 @@ const (
21 21
 var (
22 22
 	baseTimestamp time.Time
23 23
 	isTerminal    bool
24
-	noQuoteNeeded *regexp.Regexp
25 24
 )
26 25
 
27 26
 func init() {
... ...
@@ -6,7 +6,7 @@ import (
6 6
 	"runtime"
7 7
 )
8 8
 
9
-func (logger *Logger) Writer() (*io.PipeWriter) {
9
+func (logger *Logger) Writer() *io.PipeWriter {
10 10
 	reader, writer := io.Pipe()
11 11
 
12 12
 	go logger.writerScanner(reader)