move back to upstream logrus
Henry committed
Mar 1, 2015 at 00:59 UTC
d69bdecbc7e084f37be3728d7ac581a1849e5c7e
41 files changed
+546
-69
Godeps/Godeps.json
+5
-5
@@ -31,6 +31,11 @@
31
"ImportPath": "github.com/ActiveState/tail",
32
"Rev": "068b72961a6bc5b4a82cf4fc14ccc724c0cfa73a"
33
},
34
+ {
35
+ "ImportPath": "github.com/Sirupsen/logrus",
36
+ "Comment": "v0.6.5",
37
+ "Rev": "c0f7e35ed2e48f188c37581b4b743cf7383f85c6"
38
+ },
39
{
40
"ImportPath": "github.com/braintree/manners",
41
"Comment": "0.3.1-2-g5280e25",
@@ -44,11 +49,6 @@
49
"ImportPath": "github.com/briantigerchow/inflect",
50
"Rev": "cef1f9cc2234281dc58ea10be7e9aad5e282ecab"
51
},
47
- {
48
- "ImportPath": "github.com/briantigerchow/logrus",
49
- "Comment": "v0.6.0-5-gf92b795",
50
- "Rev": "f92b7950b372b1db80bd3527e4d40e42555fe6c2"
51
- },
52
{
53
"ImportPath": "github.com/briantigerchow/pubsub",
54
"Rev": "39ce5f556423a4c7223b370fa17a3bbd75b2d197"
Godeps/_workspace/src/github.com/Sirupsen/logrus/.gitignore
renamed
Godeps/_workspace/src/github.com/Sirupsen/logrus/.travis.yml
new
+8
@@ -0,0 +1,8 @@
1
+language: go
2
+go:
3
+ - 1.2
4
+ - 1.3
5
+ - 1.4
6
+ - tip
7
+install:
8
+ - go get -t ./...
Godeps/_workspace/src/github.com/Sirupsen/logrus/LICENSE
renamed
Godeps/_workspace/src/github.com/Sirupsen/logrus/README.md
renamed
+43
-8
@@ -1,10 +1,11 @@
1
-# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [](https://travis-ci.org/Sirupsen/logrus)
1
+# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [](https://travis-ci.org/Sirupsen/logrus) [][godoc]
2
3
Logrus is a structured logger for Go (golang), completely API compatible with
4
the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not
5
-yet stable (pre 1.0), the core API is unlikely change much but please version
6
-control your Logrus to make sure you aren't fetching latest `master` on every
7
-build.**
5
+yet stable (pre 1.0). Logrus itself is completely stable and has been used in
6
+many large deployments. The core API is unlikely to change much but please
7
+version control your Logrus to make sure you aren't fetching latest `master` on
8
+every build.**
9
10
Nicely color-coded in development (when a TTY is attached, otherwise just
11
plain text):
@@ -33,7 +34,7 @@ ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
34
35
With the default `log.Formatter = new(logrus.TextFormatter)` when a TTY is not
36
attached, the output is compatible with the
36
-[l2met](http://r.32k.io/l2met-introduction) format:
37
+[logfmt](http://godoc.org/github.com/kr/logfmt) format:
38
39
```text
40
time="2014-04-20 15:36:23.830442383 -0400 EDT" level="info" msg="A group of walrus emerges from the ocean" animal="walrus" size=10
@@ -206,11 +207,18 @@ import (
207
log "github.com/Sirupsen/logrus"
208
"github.com/Sirupsen/logrus/hooks/airbrake"
209
"github.com/Sirupsen/logrus/hooks/syslog"
210
+ "log/syslog"
211
)
212
213
func init() {
214
log.AddHook(new(logrus_airbrake.AirbrakeHook))
213
- log.AddHook(logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, ""))
215
+
216
+ hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
217
+ if err != nil {
218
+ log.Error("Unable to connect to local syslog daemon")
219
+ } else {
220
+ log.AddHook(hook)
221
+ }
222
}
223
```
224
@@ -228,6 +236,15 @@ func init() {
236
* [`github.com/nubo/hiprus`](https://github.com/nubo/hiprus)
237
Send errors to a channel in hipchat.
238
239
+* [`github.com/sebest/logrusly`](https://github.com/sebest/logrusly)
240
+ Send logs to Loggly (https://www.loggly.com/)
241
+
242
+* [`github.com/johntdyer/slackrus`](https://github.com/johntdyer/slackrus)
243
+ Hook for Slack chat.
244
+
245
+* [`github.com/wercker/journalhook`](https://github.com/wercker/journalhook).
246
+ Hook for logging to `systemd-journald`.
247
+
248
#### Level logging
249
250
Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.
@@ -307,7 +324,7 @@ The built-in logging formatters are:
324
325
Third party logging formatters:
326
310
-* [`zalgo`](https://github.com/aybabtme/logzalgo): invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
327
+* [`zalgo`](https://github.com/aybabtme/logzalgo): invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
328
329
You can define your formatter by implementing the `Formatter` interface,
330
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
@@ -332,10 +349,28 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
349
}
350
```
351
352
+#### Logger as an `io.Writer`
353
+
354
+Logrus can be transormed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
355
+
356
+```go
357
+w := logger.Writer()
358
+defer w.Close()
359
+
360
+srv := http.Server{
361
+ // create a stdlib log.Logger that writes to
362
+ // logrus.Logger.
363
+ ErrorLog: log.New(w, "", 0),
364
+}
365
+```
366
+
367
+Each line written to that writer will be printed the usual way, using formatters
368
+and hooks. The level for those entries is `info`.
369
+
370
#### Rotation
371
372
Log rotation is not provided with Logrus. Log rotation should be done by an
338
-external program (like `logrotated(8)`) that can compress and delete old log
373
+external program (like `logrotate(8)`) that can compress and delete old log
374
entries. It should not be a feature of the application-level logger.
375
376
Godeps/_workspace/src/github.com/Sirupsen/logrus/entry.go
renamed
+4
@@ -126,6 +126,10 @@ func (entry *Entry) Warn(args ...interface{}) {
126
}
127
}
128
129
+func (entry *Entry) Warning(args ...interface{}) {
130
+ entry.Warn(args...)
131
+}
132
+
133
func (entry *Entry) Error(args ...interface{}) {
134
if entry.Logger.Level >= ErrorLevel {
135
entry.log(ErrorLevel, fmt.Sprint(args...))
Godeps/_workspace/src/github.com/Sirupsen/logrus/entry_test.go
renamed
Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/basic/basic.go
renamed
+1
-1
@@ -1,7 +1,7 @@
1
package main
2
3
import (
4
- "github.com/Sirupsen/logrus"
4
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
5
)
6
7
var log = logrus.New()
Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/hook/hook.go
renamed
+2
-2
@@ -1,8 +1,8 @@
1
package main
2
3
import (
4
- "github.com/Sirupsen/logrus"
5
- "github.com/Sirupsen/logrus/hooks/airbrake"
4
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
5
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake"
6
"github.com/tobi/airbrake-go"
7
)
8
Godeps/_workspace/src/github.com/Sirupsen/logrus/exported.go
renamed
+11
@@ -9,6 +9,10 @@ var (
9
std = New()
10
)
11
12
+func StandardLogger() *Logger {
13
+ return std
14
+}
15
+
16
// SetOutput sets the standard logger output.
17
func SetOutput(out io.Writer) {
18
std.mu.Lock()
@@ -30,6 +34,13 @@ func SetLevel(level Level) {
34
std.Level = level
35
}
36
37
+// GetLevel returns the standard logger level.
38
+func GetLevel() Level {
39
+ std.mu.Lock()
40
+ defer std.mu.Unlock()
41
+ return std.Level
42
+}
43
+
44
// AddHook adds a hook to the standard logger hooks.
45
func AddHook(hook Hook) {
46
std.mu.Lock()
Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter.go
renamed
+7
-7
@@ -26,19 +26,19 @@ type Formatter interface {
26
//
27
// It's not exported because it's still using Data in an opinionated way. It's to
28
// avoid code duplication between the two default formatters.
29
-func prefixFieldClashes(entry *Entry) {
30
- _, ok := entry.Data["time"]
29
+func prefixFieldClashes(data Fields) {
30
+ _, ok := data["time"]
31
if ok {
32
- entry.Data["fields.time"] = entry.Data["time"]
32
+ data["fields.time"] = data["time"]
33
}
34
35
- _, ok = entry.Data["msg"]
35
+ _, ok = data["msg"]
36
if ok {
37
- entry.Data["fields.msg"] = entry.Data["msg"]
37
+ data["fields.msg"] = data["msg"]
38
}
39
40
- _, ok = entry.Data["level"]
40
+ _, ok = data["level"]
41
if ok {
42
- entry.Data["fields.level"] = entry.Data["level"]
42
+ data["fields.level"] = data["level"]
43
}
44
}
Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter_bench_test.go
renamed
Godeps/_workspace/src/github.com/Sirupsen/logrus/hook_test.go
renamed
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks.go
renamed
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake/airbrake.go
renamed
+1
-1
@@ -1,7 +1,7 @@
1
package logrus_airbrake
2
3
import (
4
- "github.com/Sirupsen/logrus"
4
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
5
"github.com/tobi/airbrake-go"
6
)
7
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/README.md
renamed
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail.go
renamed
+3
-2
@@ -6,7 +6,7 @@ import (
6
"os"
7
"time"
8
9
- "github.com/Sirupsen/logrus"
9
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
10
)
11
12
const (
@@ -30,7 +30,8 @@ func NewPapertrailHook(host string, port int, appName string) (*PapertrailHook,
30
// Fire is called when a log event is fired.
31
func (hook *PapertrailHook) Fire(entry *logrus.Entry) error {
32
date := time.Now().Format(format)
33
- payload := fmt.Sprintf("<22> %s %s: [%s] %s", date, hook.AppName, entry.Data["level"], entry.Message)
33
+ msg, _ := entry.String()
34
+ payload := fmt.Sprintf("<22> %s %s: %s", date, hook.AppName, msg)
35
36
bytesWritten, err := hook.UDPConn.Write([]byte(payload))
37
if err != nil {
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail_test.go
renamed
+1
-1
@@ -4,7 +4,7 @@ import (
4
"fmt"
5
"testing"
6
7
- "github.com/Sirupsen/logrus"
7
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
8
"github.com/stvp/go-udp-testing"
9
)
10
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/sentry/README.md
new
+61
@@ -0,0 +1,61 @@
1
+# Sentry Hook for Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:" />
2
+
3
+[Sentry](https://getsentry.com) provides both self-hosted and hosted
4
+solutions for exception tracking.
5
+Both client and server are
6
+[open source](https://github.com/getsentry/sentry).
7
+
8
+## Usage
9
+
10
+Every sentry application defined on the server gets a different
11
+[DSN](https://www.getsentry.com/docs/). In the example below replace
12
+`YOUR_DSN` with the one created for your application.
13
+
14
+```go
15
+import (
16
+ "github.com/Sirupsen/logrus"
17
+ "github.com/Sirupsen/logrus/hooks/sentry"
18
+)
19
+
20
+func main() {
21
+ log := logrus.New()
22
+ hook, err := logrus_sentry.NewSentryHook(YOUR_DSN, []logrus.Level{
23
+ logrus.PanicLevel,
24
+ logrus.FatalLevel,
25
+ logrus.ErrorLevel,
26
+ })
27
+
28
+ if err == nil {
29
+ log.Hooks.Add(hook)
30
+ }
31
+}
32
+```
33
+
34
+## Special fields
35
+
36
+Some logrus fields have a special meaning in this hook,
37
+these are server_name and logger.
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
40
+is logging the event (hostname.example.com)
41
+- logger is the part of the application which is logging the event.
42
+In go this usually means setting it to the name of the package.
43
+
44
+## Timeout
45
+
46
+`Timeout` is the time the sentry hook will wait for a response
47
+from the sentry server.
48
+
49
+If this time elapses with no response from
50
+the server an error will be returned.
51
+
52
+If `Timeout` is set to 0 the SentryHook will not wait for a reply
53
+and will assume a correct delivery.
54
+
55
+The SentryHook has a default timeout of `100 milliseconds` when created
56
+with a call to `NewSentryHook`. This can be changed by assigning a value to the `Timeout` field:
57
+
58
+```go
59
+hook, _ := logrus_sentry.NewSentryHook(...)
60
+hook.Timeout = 20*time.Seconds
61
+```
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/sentry/sentry.go
new
+100
@@ -0,0 +1,100 @@
1
+package logrus_sentry
2
+
3
+import (
4
+ "fmt"
5
+ "time"
6
+
7
+ "github.com/getsentry/raven-go"
8
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
9
+)
10
+
11
+var (
12
+ severityMap = map[logrus.Level]raven.Severity{
13
+ logrus.DebugLevel: raven.DEBUG,
14
+ logrus.InfoLevel: raven.INFO,
15
+ logrus.WarnLevel: raven.WARNING,
16
+ logrus.ErrorLevel: raven.ERROR,
17
+ logrus.FatalLevel: raven.FATAL,
18
+ logrus.PanicLevel: raven.FATAL,
19
+ }
20
+)
21
+
22
+func getAndDel(d logrus.Fields, key string) (string, bool) {
23
+ var (
24
+ ok bool
25
+ v interface{}
26
+ val string
27
+ )
28
+ if v, ok = d[key]; !ok {
29
+ return "", false
30
+ }
31
+
32
+ if val, ok = v.(string); !ok {
33
+ return "", false
34
+ }
35
+ delete(d, key)
36
+ return val, true
37
+}
38
+
39
+// SentryHook delivers logs to a sentry server.
40
+type SentryHook struct {
41
+ // Timeout sets the time to wait for a delivery error from the sentry server.
42
+ // If this is set to zero the server will not wait for any response and will
43
+ // consider the message correctly sent
44
+ Timeout time.Duration
45
+
46
+ client *raven.Client
47
+ levels []logrus.Level
48
+}
49
+
50
+// NewSentryHook creates a hook to be added to an instance of logger
51
+// and initializes the raven client.
52
+// This method sets the timeout to 100 milliseconds.
53
+func NewSentryHook(DSN string, levels []logrus.Level) (*SentryHook, error) {
54
+ client, err := raven.NewClient(DSN, nil)
55
+ if err != nil {
56
+ return nil, err
57
+ }
58
+ return &SentryHook{100 * time.Millisecond, client, levels}, nil
59
+}
60
+
61
+// Called when an event should be sent to sentry
62
+// Special fields that sentry uses to give more information to the server
63
+// are extracted from entry.Data (if they are found)
64
+// These fields are: logger and server_name
65
+func (hook *SentryHook) Fire(entry *logrus.Entry) error {
66
+ packet := &raven.Packet{
67
+ Message: entry.Message,
68
+ Timestamp: raven.Timestamp(entry.Time),
69
+ Level: severityMap[entry.Level],
70
+ Platform: "go",
71
+ }
72
+
73
+ d := entry.Data
74
+
75
+ if logger, ok := getAndDel(d, "logger"); ok {
76
+ packet.Logger = logger
77
+ }
78
+ if serverName, ok := getAndDel(d, "server_name"); ok {
79
+ packet.ServerName = serverName
80
+ }
81
+ packet.Extra = map[string]interface{}(d)
82
+
83
+ _, errCh := hook.client.Capture(packet, nil)
84
+ timeout := hook.Timeout
85
+ if timeout != 0 {
86
+ timeoutCh := time.After(timeout)
87
+ select {
88
+ case err := <-errCh:
89
+ return err
90
+ case <-timeoutCh:
91
+ return fmt.Errorf("no response from sentry server in %s", timeout)
92
+ }
93
+ }
94
+ return nil
95
+}
96
+
97
+// Levels returns the available logging levels.
98
+func (hook *SentryHook) Levels() []logrus.Level {
99
+ return hook.levels
100
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/sentry/sentry_test.go
new
+97
@@ -0,0 +1,97 @@
1
+package logrus_sentry
2
+
3
+import (
4
+ "encoding/json"
5
+ "fmt"
6
+ "io/ioutil"
7
+ "net/http"
8
+ "net/http/httptest"
9
+ "strings"
10
+ "testing"
11
+
12
+ "github.com/getsentry/raven-go"
13
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
14
+)
15
+
16
+const (
17
+ message = "error message"
18
+ server_name = "testserver.internal"
19
+ logger_name = "test.logger"
20
+)
21
+
22
+func getTestLogger() *logrus.Logger {
23
+ l := logrus.New()
24
+ l.Out = ioutil.Discard
25
+ return l
26
+}
27
+
28
+func WithTestDSN(t *testing.T, tf func(string, <-chan *raven.Packet)) {
29
+ pch := make(chan *raven.Packet, 1)
30
+ s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
31
+ defer req.Body.Close()
32
+ d := json.NewDecoder(req.Body)
33
+ p := &raven.Packet{}
34
+ err := d.Decode(p)
35
+ if err != nil {
36
+ t.Fatal(err.Error())
37
+ }
38
+
39
+ pch <- p
40
+ }))
41
+ defer s.Close()
42
+
43
+ fragments := strings.SplitN(s.URL, "://", 2)
44
+ dsn := fmt.Sprintf(
45
+ "%s://public:secret@%s/sentry/project-id",
46
+ fragments[0],
47
+ fragments[1],
48
+ )
49
+ tf(dsn, pch)
50
+}
51
+
52
+func TestSpecialFields(t *testing.T) {
53
+ WithTestDSN(t, func(dsn string, pch <-chan *raven.Packet) {
54
+ logger := getTestLogger()
55
+
56
+ hook, err := NewSentryHook(dsn, []logrus.Level{
57
+ logrus.ErrorLevel,
58
+ })
59
+
60
+ if err != nil {
61
+ t.Fatal(err.Error())
62
+ }
63
+ logger.Hooks.Add(hook)
64
+ logger.WithFields(logrus.Fields{
65
+ "server_name": server_name,
66
+ "logger": logger_name,
67
+ }).Error(message)
68
+
69
+ packet := <-pch
70
+ if packet.Logger != logger_name {
71
+ t.Errorf("logger should have been %s, was %s", logger_name, packet.Logger)
72
+ }
73
+
74
+ if packet.ServerName != server_name {
75
+ t.Errorf("server_name should have been %s, was %s", server_name, packet.ServerName)
76
+ }
77
+ })
78
+}
79
+
80
+func TestSentryHandler(t *testing.T) {
81
+ WithTestDSN(t, func(dsn string, pch <-chan *raven.Packet) {
82
+ logger := getTestLogger()
83
+ hook, err := NewSentryHook(dsn, []logrus.Level{
84
+ logrus.ErrorLevel,
85
+ })
86
+ if err != nil {
87
+ t.Fatal(err.Error())
88
+ }
89
+ logger.Hooks.Add(hook)
90
+
91
+ logger.Error(message)
92
+ packet := <-pch
93
+ if packet.Message != message {
94
+ t.Errorf("message should have been %s, was %s", message, packet.Message)
95
+ }
96
+ })
97
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/README.md
renamed
+2
-2
@@ -6,7 +6,7 @@
6
import (
7
"log/syslog"
8
"github.com/Sirupsen/logrus"
9
- "github.com/Sirupsen/logrus/hooks/syslog"
9
+ logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
10
)
11
12
func main() {
@@ -17,4 +17,4 @@ func main() {
17
log.Hooks.Add(hook)
18
}
19
}
20
-```
\ No newline at end of file
20
+```
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog.go
renamed
+8
-8
@@ -2,7 +2,7 @@ package logrus_syslog
2
3
import (
4
"fmt"
5
- "github.com/Sirupsen/logrus"
5
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
6
"log/syslog"
7
"os"
8
)
@@ -29,18 +29,18 @@ func (hook *SyslogHook) Fire(entry *logrus.Entry) error {
29
return err
30
}
31
32
- switch entry.Data["level"] {
33
- case "panic":
32
+ switch entry.Level {
33
+ case logrus.PanicLevel:
34
return hook.Writer.Crit(line)
35
- case "fatal":
35
+ case logrus.FatalLevel:
36
return hook.Writer.Crit(line)
37
- case "error":
37
+ case logrus.ErrorLevel:
38
return hook.Writer.Err(line)
39
- case "warn":
39
+ case logrus.WarnLevel:
40
return hook.Writer.Warning(line)
41
- case "info":
41
+ case logrus.InfoLevel:
42
return hook.Writer.Info(line)
43
- case "debug":
43
+ case logrus.DebugLevel:
44
return hook.Writer.Debug(line)
45
default:
46
return nil
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go
renamed
+1
-1
@@ -1,7 +1,7 @@
1
package logrus_syslog
2
3
import (
4
- "github.com/Sirupsen/logrus"
4
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
5
"log/syslog"
6
"testing"
7
)
Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter.go
renamed
+9
-5
@@ -9,12 +9,16 @@ import (
9
type JSONFormatter struct{}
10
11
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
12
- prefixFieldClashes(entry)
13
- entry.Data["time"] = entry.Time.Format(time.RFC3339)
14
- entry.Data["msg"] = entry.Message
15
- entry.Data["level"] = entry.Level.String()
12
+ data := make(Fields, len(entry.Data)+3)
13
+ for k, v := range entry.Data {
14
+ data[k] = v
15
+ }
16
+ prefixFieldClashes(data)
17
+ data["time"] = entry.Time.Format(time.RFC3339)
18
+ data["msg"] = entry.Message
19
+ data["level"] = entry.Level.String()
20
17
- serialized, err := json.Marshal(entry.Data)
21
+ serialized, err := json.Marshal(data)
22
if err != nil {
23
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
24
}
Godeps/_workspace/src/github.com/Sirupsen/logrus/logger.go
renamed
+1
-1
@@ -38,7 +38,7 @@ type Logger struct {
38
// Out: os.Stderr,
39
// Formatter: new(JSONFormatter),
40
// Hooks: make(levelHooks),
41
-// Level: logrus.Debug,
41
+// Level: logrus.DebugLevel,
42
// }
43
//
44
// It's recommended to make this a global instance called `log`.
Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus.go
renamed
Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus_test.go
renamed
+56
-2
@@ -5,6 +5,7 @@ import (
5
"encoding/json"
6
"strconv"
7
"strings"
8
+ "sync"
9
"testing"
10
11
"github.com/stretchr/testify/assert"
@@ -44,8 +45,12 @@ func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields ma
45
}
46
kvArr := strings.Split(kv, "=")
47
key := strings.TrimSpace(kvArr[0])
47
- val, err := strconv.Unquote(kvArr[1])
48
- assert.NoError(t, err)
48
+ val := kvArr[1]
49
+ if kvArr[1][0] == '"' {
50
+ var err error
51
+ val, err = strconv.Unquote(val)
52
+ assert.NoError(t, err)
53
+ }
54
fields[key] = val
55
}
56
assertions(fields)
@@ -204,6 +209,38 @@ func TestDefaultFieldsAreNotPrefixed(t *testing.T) {
209
})
210
}
211
212
+func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) {
213
+
214
+ var buffer bytes.Buffer
215
+ var fields Fields
216
+
217
+ logger := New()
218
+ logger.Out = &buffer
219
+ logger.Formatter = new(JSONFormatter)
220
+
221
+ llog := logger.WithField("context", "eating raw fish")
222
+
223
+ llog.Info("looks delicious")
224
+
225
+ err := json.Unmarshal(buffer.Bytes(), &fields)
226
+ assert.NoError(t, err, "should have decoded first message")
227
+ assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
228
+ assert.Equal(t, fields["msg"], "looks delicious")
229
+ assert.Equal(t, fields["context"], "eating raw fish")
230
+
231
+ buffer.Reset()
232
+
233
+ llog.Warn("omg it is!")
234
+
235
+ err = json.Unmarshal(buffer.Bytes(), &fields)
236
+ assert.NoError(t, err, "should have decoded second message")
237
+ assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
238
+ assert.Equal(t, fields["msg"], "omg it is!")
239
+ assert.Equal(t, fields["context"], "eating raw fish")
240
+ assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry")
241
+
242
+}
243
+
244
func TestConvertLevelToString(t *testing.T) {
245
assert.Equal(t, "debug", DebugLevel.String())
246
assert.Equal(t, "info", InfoLevel.String())
@@ -245,3 +282,20 @@ func TestParseLevel(t *testing.T) {
282
l, err = ParseLevel("invalid")
283
assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error())
284
}
285
+
286
+func TestGetSetLevelRace(t *testing.T) {
287
+ wg := sync.WaitGroup{}
288
+ for i := 0; i < 100; i++ {
289
+ wg.Add(1)
290
+ go func(i int) {
291
+ defer wg.Done()
292
+ if i%2 == 0 {
293
+ SetLevel(InfoLevel)
294
+ } else {
295
+ GetLevel()
296
+ }
297
+ }(i)
298
+
299
+ }
300
+ wg.Wait()
301
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_darwin.go
renamed
Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_freebsd.go
renamed
Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_linux.go
renamed
Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_notwindows.go
renamed
+1
-1
@@ -3,7 +3,7 @@
3
// Use of this source code is governed by a BSD-style
4
// license that can be found in the LICENSE file.
5
6
-// +build linux,!appengine darwin freebsd
6
+// +build linux darwin freebsd openbsd
7
8
package logrus
9
Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_openbsd.go
new
+8
@@ -0,0 +1,8 @@
1
+
2
+package logrus
3
+
4
+import "syscall"
5
+
6
+const ioctlReadTermios = syscall.TIOCGETA
7
+
8
+type Termios syscall.Termios
Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_windows.go
renamed
Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter.go
renamed
+49
-9
@@ -3,6 +3,7 @@ package logrus
3
import (
4
"bytes"
5
"fmt"
6
+ "regexp"
7
"sort"
8
"strings"
9
"time"
@@ -19,6 +20,7 @@ const (
20
var (
21
baseTimestamp time.Time
22
isTerminal bool
23
+ noQuoteNeeded *regexp.Regexp
24
)
25
26
func init() {
@@ -32,13 +34,23 @@ func miniTS() int {
34
35
type TextFormatter struct {
36
// Set to true to bypass checking for a TTY before outputting colors.
35
- ForceColors bool
37
+ ForceColors bool
38
+
39
+ // Force disabling colors.
40
DisableColors bool
41
+
42
+ // Disable timestamp logging. useful when output is redirected to logging
43
+ // system that already adds timestamps.
44
+ DisableTimestamp bool
45
+
46
+ // Enable logging the full timestamp when a TTY is attached instead of just
47
+ // the time passed since beginning of execution.
48
+ FullTimestamp bool
49
}
50
51
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
52
41
- var keys []string
53
+ var keys []string = make([]string, 0, len(entry.Data))
54
for k := range entry.Data {
55
keys = append(keys, k)
56
}
@@ -46,14 +58,16 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
58
59
b := &bytes.Buffer{}
60
49
- prefixFieldClashes(entry)
61
+ prefixFieldClashes(entry.Data)
62
63
isColored := (f.ForceColors || isTerminal) && !f.DisableColors
64
65
if isColored {
54
- printColored(b, entry, keys)
66
+ f.printColored(b, entry, keys)
67
} else {
56
- f.appendKeyValue(b, "time", entry.Time.Format(time.RFC3339))
68
+ if !f.DisableTimestamp {
69
+ f.appendKeyValue(b, "time", entry.Time.Format(time.RFC3339))
70
+ }
71
f.appendKeyValue(b, "level", entry.Level.String())
72
f.appendKeyValue(b, "msg", entry.Message)
73
for _, key := range keys {
@@ -65,7 +79,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
79
return b.Bytes(), nil
80
}
81
68
-func printColored(b *bytes.Buffer, entry *Entry, keys []string) {
82
+func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string) {
83
var levelColor int
84
switch entry.Level {
85
case WarnLevel:
@@ -78,17 +92,43 @@ func printColored(b *bytes.Buffer, entry *Entry, keys []string) {
92
93
levelText := strings.ToUpper(entry.Level.String())[0:4]
94
81
- fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
95
+ if !f.FullTimestamp {
96
+ fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
97
+ } else {
98
+ fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(time.RFC3339), entry.Message)
99
+ }
100
for _, k := range keys {
101
v := entry.Data[k]
102
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%v", levelColor, k, v)
103
}
104
}
105
106
+func needsQuoting(text string) bool {
107
+ for _, ch := range text {
108
+ if !((ch >= 'a' && ch <= 'z') ||
109
+ (ch >= 'A' && ch <= 'Z') ||
110
+ (ch >= '0' && ch < '9') ||
111
+ ch == '-' || ch == '.') {
112
+ return false
113
+ }
114
+ }
115
+ return true
116
+}
117
+
118
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key, value interface{}) {
119
switch value.(type) {
90
- case string, error:
91
- fmt.Fprintf(b, "%v=%q ", key, value)
120
+ case string:
121
+ if needsQuoting(value.(string)) {
122
+ fmt.Fprintf(b, "%v=%s ", key, value)
123
+ } else {
124
+ fmt.Fprintf(b, "%v=%q ", key, value)
125
+ }
126
+ case error:
127
+ if needsQuoting(value.(error).Error()) {
128
+ fmt.Fprintf(b, "%v=%s ", key, value)
129
+ } else {
130
+ fmt.Fprintf(b, "%v=%q ", key, value)
131
+ }
132
default:
133
fmt.Fprintf(b, "%v=%v ", key, value)
134
}
Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter_test.go
new
+33
@@ -0,0 +1,33 @@
1
+package logrus
2
+
3
+import (
4
+ "bytes"
5
+ "errors"
6
+
7
+ "testing"
8
+)
9
+
10
+func TestQuoting(t *testing.T) {
11
+ tf := &TextFormatter{DisableColors: true}
12
+
13
+ checkQuoting := func(q bool, value interface{}) {
14
+ b, _ := tf.Format(WithField("test", value))
15
+ idx := bytes.Index(b, ([]byte)("test="))
16
+ cont := bytes.Contains(b[idx+5:], []byte{'"'})
17
+ if cont != q {
18
+ if q {
19
+ t.Errorf("quoting expected for: %#v", value)
20
+ } else {
21
+ t.Errorf("quoting not expected for: %#v", value)
22
+ }
23
+ }
24
+ }
25
+
26
+ checkQuoting(false, "abcd")
27
+ checkQuoting(false, "v1.0")
28
+ checkQuoting(true, "/foobar")
29
+ checkQuoting(true, "x y")
30
+ checkQuoting(true, "x,y")
31
+ checkQuoting(false, errors.New("invalid"))
32
+ checkQuoting(true, errors.New("invalid argument"))
33
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/writer.go
new
+31
@@ -0,0 +1,31 @@
1
+package logrus
2
+
3
+import (
4
+ "bufio"
5
+ "io"
6
+ "runtime"
7
+)
8
+
9
+func (logger *Logger) Writer() (*io.PipeWriter) {
10
+ reader, writer := io.Pipe()
11
+
12
+ go logger.writerScanner(reader)
13
+ runtime.SetFinalizer(writer, writerFinalizer)
14
+
15
+ return writer
16
+}
17
+
18
+func (logger *Logger) writerScanner(reader *io.PipeReader) {
19
+ scanner := bufio.NewScanner(reader)
20
+ for scanner.Scan() {
21
+ logger.Print(scanner.Text())
22
+ }
23
+ if err := scanner.Err(); err != nil {
24
+ logger.Errorf("Error while reading from Writer: %s", err)
25
+ }
26
+ reader.Close()
27
+}
28
+
29
+func writerFinalizer(writer *io.PipeWriter) {
30
+ writer.Close()
31
+}
Godeps/_workspace/src/github.com/briantigerchow/logrus/.travis.yml
deleted
-9
@@ -1,9 +0,0 @@
1
-language: go
2
-go:
3
- - 1.2
4
- - 1.3
5
- - tip
6
-install:
7
- - go get github.com/stretchr/testify
8
- - go get github.com/stvp/go-udp-testing
9
- - go get github.com/tobi/airbrake-go
thirdparty/eventlog/entry.go
+1
-2
@@ -3,8 +3,7 @@ package eventlog
3
import (
4
"time"
5
6
- "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/briantigerchow/logrus"
7
-
6
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
7
"github.com/jbenet/go-ipfs/util"
8
)
9
thirdparty/eventlog/option.go
+1
-1
@@ -4,7 +4,7 @@ import (
4
"io"
5
"os"
6
7
- "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/briantigerchow/logrus"
7
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
8
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2"
9
)
10
thirdparty/eventlog/polite_json_formatter.go
+1
-1
@@ -4,7 +4,7 @@ import (
4
"encoding/json"
5
"fmt"
6
7
- "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/briantigerchow/logrus"
7
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
8
)
9
10
// PoliteJSONFormatter marshals entries into JSON encoded slices (without