deps(logging) vendor logrus (MIT)
Experimenting with LD JSON logging License: MIT Signed-off-by: Brian Tiger Chow <brian@perfmode.com>
Brian Tiger Chow committed
Nov 16, 2014 at 01:00 UTC
9a38b28416916e0ecf9df9cffac4d846fca8323c
32 files changed
+2216
-8
Godeps/Godeps.json
+6
-1
@@ -1,6 +1,6 @@
1
{
2
"ImportPath": "github.com/jbenet/go-ipfs",
3
- "GoVersion": "go1.3",
3
+ "GoVersion": "go1.3.3",
4
"Packages": [
5
"./..."
6
],
@@ -48,6 +48,11 @@
48
"Comment": "null-15",
49
"Rev": "12e4b4183793ac4b061921e7980845e750679fd0"
50
},
51
+ {
52
+ "ImportPath": "github.com/Sirupsen/logrus",
53
+ "Comment": "v0.6.0-5-gf92b795",
54
+ "Rev": "f92b7950b372b1db80bd3527e4d40e42555fe6c2"
55
+ },
56
{
57
"ImportPath": "github.com/braintree/manners",
58
"Comment": "0.3.1-2-g5280e25",
Godeps/_workspace/src/github.com/Sirupsen/logrus/.gitignore
new
+1
@@ -0,0 +1 @@
1
+logrus
Godeps/_workspace/src/github.com/Sirupsen/logrus/.travis.yml
new
+9
@@ -0,0 +1,9 @@
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
Godeps/_workspace/src/github.com/Sirupsen/logrus/LICENSE
new
+21
@@ -0,0 +1,21 @@
1
+The MIT License (MIT)
2
+
3
+Copyright (c) 2014 Simon Eskildsen
4
+
5
+Permission is hereby granted, free of charge, to any person obtaining a copy
6
+of this software and associated documentation files (the "Software"), to deal
7
+in the Software without restriction, including without limitation the rights
8
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+copies of the Software, and to permit persons to whom the Software is
10
+furnished to do so, subject to the following conditions:
11
+
12
+The above copyright notice and this permission notice shall be included in
13
+all copies or substantial portions of the Software.
14
+
15
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+THE SOFTWARE.
Godeps/_workspace/src/github.com/Sirupsen/logrus/README.md
new
+342
@@ -0,0 +1,342 @@
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)
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.**
8
+
9
+Nicely color-coded in development (when a TTY is attached, otherwise just
10
+plain text):
11
+
12
+
13
+
14
+With `log.Formatter = new(logrus.JSONFormatter)`, for easy parsing by logstash
15
+or Splunk:
16
+
17
+```json
18
+{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
19
+ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
20
+
21
+{"level":"warning","msg":"The group's number increased tremendously!",
22
+"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
23
+
24
+{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
25
+"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
26
+
27
+{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
28
+"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
29
+
30
+{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
31
+"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
32
+```
33
+
34
+With the default `log.Formatter = new(logrus.TextFormatter)` when a TTY is not
35
+attached, the output is compatible with the
36
+[l2met](http://r.32k.io/l2met-introduction) format:
37
+
38
+```text
39
+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
40
+time="2014-04-20 15:36:23.830584199 -0400 EDT" level="warning" msg="The group's number increased tremendously!" omg=true number=122
41
+time="2014-04-20 15:36:23.830596521 -0400 EDT" level="info" msg="A giant walrus appears!" animal="walrus" size=10
42
+time="2014-04-20 15:36:23.830611837 -0400 EDT" level="info" msg="Tremendously sized cow enters the ocean." animal="walrus" size=9
43
+time="2014-04-20 15:36:23.830626464 -0400 EDT" level="fatal" msg="The ice breaks!" omg=true number=100
44
+```
45
+
46
+#### Example
47
+
48
+The simplest way to use Logrus is simply the package-level exported logger:
49
+
50
+```go
51
+package main
52
+
53
+import (
54
+ log "github.com/Sirupsen/logrus"
55
+)
56
+
57
+func main() {
58
+ log.WithFields(log.Fields{
59
+ "animal": "walrus",
60
+ }).Info("A walrus appears")
61
+}
62
+```
63
+
64
+Note that it's completely api-compatible with the stdlib logger, so you can
65
+replace your `log` imports everywhere with `log "github.com/Sirupsen/logrus"`
66
+and you'll now have the flexibility of Logrus. You can customize it all you
67
+want:
68
+
69
+```go
70
+package main
71
+
72
+import (
73
+ "os"
74
+ log "github.com/Sirupsen/logrus"
75
+ "github.com/Sirupsen/logrus/hooks/airbrake"
76
+)
77
+
78
+func init() {
79
+ // Log as JSON instead of the default ASCII formatter.
80
+ log.SetFormatter(&log.JSONFormatter{})
81
+
82
+ // Use the Airbrake hook to report errors that have Error severity or above to
83
+ // an exception tracker. You can create custom hooks, see the Hooks section.
84
+ log.AddHook(&logrus_airbrake.AirbrakeHook{})
85
+
86
+ // Output to stderr instead of stdout, could also be a file.
87
+ log.SetOutput(os.Stderr)
88
+
89
+ // Only log the warning severity or above.
90
+ log.SetLevel(log.WarnLevel)
91
+}
92
+
93
+func main() {
94
+ log.WithFields(log.Fields{
95
+ "animal": "walrus",
96
+ "size": 10,
97
+ }).Info("A group of walrus emerges from the ocean")
98
+
99
+ log.WithFields(log.Fields{
100
+ "omg": true,
101
+ "number": 122,
102
+ }).Warn("The group's number increased tremendously!")
103
+
104
+ log.WithFields(log.Fields{
105
+ "omg": true,
106
+ "number": 100,
107
+ }).Fatal("The ice breaks!")
108
+}
109
+```
110
+
111
+For more advanced usage such as logging to multiple locations from the same
112
+application, you can also create an instance of the `logrus` Logger:
113
+
114
+```go
115
+package main
116
+
117
+import (
118
+ "github.com/Sirupsen/logrus"
119
+)
120
+
121
+// Create a new instance of the logger. You can have any number of instances.
122
+var log = logrus.New()
123
+
124
+func main() {
125
+ // The API for setting attributes is a little different than the package level
126
+ // exported logger. See Godoc.
127
+ log.Out = os.Stderr
128
+
129
+ log.WithFields(logrus.Fields{
130
+ "animal": "walrus",
131
+ "size": 10,
132
+ }).Info("A group of walrus emerges from the ocean")
133
+}
134
+```
135
+
136
+#### Fields
137
+
138
+Logrus encourages careful, structured logging though logging fields instead of
139
+long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
140
+to send event %s to topic %s with key %d")`, you should log the much more
141
+discoverable:
142
+
143
+```go
144
+log.WithFields(log.Fields{
145
+ "event": event,
146
+ "topic": topic,
147
+ "key": key,
148
+}).Fatal("Failed to send event")
149
+```
150
+
151
+We've found this API forces you to think about logging in a way that produces
152
+much more useful logging messages. We've been in countless situations where just
153
+a single added field to a log statement that was already there would've saved us
154
+hours. The `WithFields` call is optional.
155
+
156
+In general, with Logrus using any of the `printf`-family functions should be
157
+seen as a hint you should add a field, however, you can still use the
158
+`printf`-family functions with Logrus.
159
+
160
+#### Hooks
161
+
162
+You can add hooks for logging levels. For example to send errors to an exception
163
+tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
164
+multiple places simultaneously, e.g. syslog.
165
+
166
+```go
167
+// Not the real implementation of the Airbrake hook. Just a simple sample.
168
+import (
169
+ log "github.com/Sirupsen/logrus"
170
+)
171
+
172
+func init() {
173
+ log.AddHook(new(AirbrakeHook))
174
+}
175
+
176
+type AirbrakeHook struct{}
177
+
178
+// `Fire()` takes the entry that the hook is fired for. `entry.Data[]` contains
179
+// the fields for the entry. See the Fields section of the README.
180
+func (hook *AirbrakeHook) Fire(entry *logrus.Entry) error {
181
+ err := airbrake.Notify(entry.Data["error"].(error))
182
+ if err != nil {
183
+ log.WithFields(log.Fields{
184
+ "source": "airbrake",
185
+ "endpoint": airbrake.Endpoint,
186
+ }).Info("Failed to send error to Airbrake")
187
+ }
188
+
189
+ return nil
190
+}
191
+
192
+// `Levels()` returns a slice of `Levels` the hook is fired for.
193
+func (hook *AirbrakeHook) Levels() []log.Level {
194
+ return []log.Level{
195
+ log.ErrorLevel,
196
+ log.FatalLevel,
197
+ log.PanicLevel,
198
+ }
199
+}
200
+```
201
+
202
+Logrus comes with built-in hooks. Add those, or your custom hook, in `init`:
203
+
204
+```go
205
+import (
206
+ log "github.com/Sirupsen/logrus"
207
+ "github.com/Sirupsen/logrus/hooks/airbrake"
208
+ "github.com/Sirupsen/logrus/hooks/syslog"
209
+)
210
+
211
+func init() {
212
+ log.AddHook(new(logrus_airbrake.AirbrakeHook))
213
+ log.AddHook(logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, ""))
214
+}
215
+```
216
+
217
+* [`github.com/Sirupsen/logrus/hooks/airbrake`](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go)
218
+ Send errors to an exception tracking service compatible with the Airbrake API.
219
+ Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes.
220
+
221
+* [`github.com/Sirupsen/logrus/hooks/papertrail`](https://github.com/Sirupsen/logrus/blob/master/hooks/papertrail/papertrail.go)
222
+ Send errors to the Papertrail hosted logging service via UDP.
223
+
224
+* [`github.com/Sirupsen/logrus/hooks/syslog`](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go)
225
+ Send errors to remote syslog server.
226
+ Uses standard library `log/syslog` behind the scenes.
227
+
228
+* [`github.com/nubo/hiprus`](https://github.com/nubo/hiprus)
229
+ Send errors to a channel in hipchat.
230
+
231
+#### Level logging
232
+
233
+Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.
234
+
235
+```go
236
+log.Debug("Useful debugging information.")
237
+log.Info("Something noteworthy happened!")
238
+log.Warn("You should probably take a look at this.")
239
+log.Error("Something failed but I'm not quitting.")
240
+// Calls os.Exit(1) after logging
241
+log.Fatal("Bye.")
242
+// Calls panic() after logging
243
+log.Panic("I'm bailing.")
244
+```
245
+
246
+You can set the logging level on a `Logger`, then it will only log entries with
247
+that severity or anything above it:
248
+
249
+```go
250
+// Will log anything that is info or above (warn, error, fatal, panic). Default.
251
+log.SetLevel(log.InfoLevel)
252
+```
253
+
254
+It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
255
+environment if your application has that.
256
+
257
+#### Entries
258
+
259
+Besides the fields added with `WithField` or `WithFields` some fields are
260
+automatically added to all logging events:
261
+
262
+1. `time`. The timestamp when the entry was created.
263
+2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after
264
+ the `AddFields` call. E.g. `Failed to send event.`
265
+3. `level`. The logging level. E.g. `info`.
266
+
267
+#### Environments
268
+
269
+Logrus has no notion of environment.
270
+
271
+If you wish for hooks and formatters to only be used in specific environments,
272
+you should handle that yourself. For example, if your application has a global
273
+variable `Environment`, which is a string representation of the environment you
274
+could do:
275
+
276
+```go
277
+import (
278
+ log "github.com/Sirupsen/logrus"
279
+)
280
+
281
+init() {
282
+ // do something here to set environment depending on an environment variable
283
+ // or command-line flag
284
+ if Environment == "production" {
285
+ log.SetFormatter(logrus.JSONFormatter)
286
+ } else {
287
+ // The TextFormatter is default, you don't actually have to do this.
288
+ log.SetFormatter(logrus.TextFormatter)
289
+ }
290
+}
291
+```
292
+
293
+This configuration is how `logrus` was intended to be used, but JSON in
294
+production is mostly only useful if you do log aggregation with tools like
295
+Splunk or Logstash.
296
+
297
+#### Formatters
298
+
299
+The built-in logging formatters are:
300
+
301
+* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
302
+ without colors.
303
+ * *Note:* to force colored output when there is no TTY, set the `ForceColors`
304
+ field to `true`. To force no colored output even if there is a TTY set the
305
+ `DisableColors` field to `true`
306
+* `logrus.JSONFormatter`. Logs fields as JSON.
307
+
308
+Third party logging formatters:
309
+
310
+* [`zalgo`](https://github.com/aybabtme/logzalgo): invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
311
+
312
+You can define your formatter by implementing the `Formatter` interface,
313
+requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
314
+`Fields` type (`map[string]interface{}`) with all your fields as well as the
315
+default ones (see Entries section above):
316
+
317
+```go
318
+type MyJSONFormatter struct {
319
+}
320
+
321
+log.SetFormatter(new(MyJSONFormatter))
322
+
323
+func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
324
+ // Note this doesn't include Time, Level and Message which are available on
325
+ // the Entry. Consult `godoc` on information about those fields or read the
326
+ // source of the official loggers.
327
+ serialized, err := json.Marshal(entry.Data)
328
+ if err != nil {
329
+ return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
330
+ }
331
+ return append(serialized, '\n'), nil
332
+}
333
+```
334
+
335
+#### Rotation
336
+
337
+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
339
+entries. It should not be a feature of the application-level logger.
340
+
341
+
342
+[godoc]: https://godoc.org/github.com/Sirupsen/logrus
Godeps/_workspace/src/github.com/Sirupsen/logrus/entry.go
new
+248
@@ -0,0 +1,248 @@
1
+package logrus
2
+
3
+import (
4
+ "bytes"
5
+ "fmt"
6
+ "io"
7
+ "os"
8
+ "time"
9
+)
10
+
11
+// An entry is the final or intermediate Logrus logging entry. It contains all
12
+// the fields passed with WithField{,s}. It's finally logged when Debug, Info,
13
+// Warn, Error, Fatal or Panic is called on it. These objects can be reused and
14
+// passed around as much as you wish to avoid field duplication.
15
+type Entry struct {
16
+ Logger *Logger
17
+
18
+ // Contains all the fields set by the user.
19
+ Data Fields
20
+
21
+ // Time at which the log entry was created
22
+ Time time.Time
23
+
24
+ // Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic
25
+ Level Level
26
+
27
+ // Message passed to Debug, Info, Warn, Error, Fatal or Panic
28
+ Message string
29
+}
30
+
31
+func NewEntry(logger *Logger) *Entry {
32
+ return &Entry{
33
+ Logger: logger,
34
+ // Default is three fields, give a little extra room
35
+ Data: make(Fields, 5),
36
+ }
37
+}
38
+
39
+// Returns a reader for the entry, which is a proxy to the formatter.
40
+func (entry *Entry) Reader() (*bytes.Buffer, error) {
41
+ serialized, err := entry.Logger.Formatter.Format(entry)
42
+ return bytes.NewBuffer(serialized), err
43
+}
44
+
45
+// Returns the string representation from the reader and ultimately the
46
+// formatter.
47
+func (entry *Entry) String() (string, error) {
48
+ reader, err := entry.Reader()
49
+ if err != nil {
50
+ return "", err
51
+ }
52
+
53
+ return reader.String(), err
54
+}
55
+
56
+// Add a single field to the Entry.
57
+func (entry *Entry) WithField(key string, value interface{}) *Entry {
58
+ return entry.WithFields(Fields{key: value})
59
+}
60
+
61
+// Add a map of fields to the Entry.
62
+func (entry *Entry) WithFields(fields Fields) *Entry {
63
+ data := Fields{}
64
+ for k, v := range entry.Data {
65
+ data[k] = v
66
+ }
67
+ for k, v := range fields {
68
+ data[k] = v
69
+ }
70
+ return &Entry{Logger: entry.Logger, Data: data}
71
+}
72
+
73
+func (entry *Entry) log(level Level, msg string) {
74
+ entry.Time = time.Now()
75
+ entry.Level = level
76
+ entry.Message = msg
77
+
78
+ if err := entry.Logger.Hooks.Fire(level, entry); err != nil {
79
+ entry.Logger.mu.Lock()
80
+ fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
81
+ entry.Logger.mu.Unlock()
82
+ }
83
+
84
+ reader, err := entry.Reader()
85
+ if err != nil {
86
+ entry.Logger.mu.Lock()
87
+ fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
88
+ entry.Logger.mu.Unlock()
89
+ }
90
+
91
+ entry.Logger.mu.Lock()
92
+ defer entry.Logger.mu.Unlock()
93
+
94
+ _, err = io.Copy(entry.Logger.Out, reader)
95
+ if err != nil {
96
+ fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
97
+ }
98
+
99
+ // To avoid Entry#log() returning a value that only would make sense for
100
+ // panic() to use in Entry#Panic(), we avoid the allocation by checking
101
+ // directly here.
102
+ if level <= PanicLevel {
103
+ panic(entry)
104
+ }
105
+}
106
+
107
+func (entry *Entry) Debug(args ...interface{}) {
108
+ if entry.Logger.Level >= DebugLevel {
109
+ entry.log(DebugLevel, fmt.Sprint(args...))
110
+ }
111
+}
112
+
113
+func (entry *Entry) Print(args ...interface{}) {
114
+ entry.Info(args...)
115
+}
116
+
117
+func (entry *Entry) Info(args ...interface{}) {
118
+ if entry.Logger.Level >= InfoLevel {
119
+ entry.log(InfoLevel, fmt.Sprint(args...))
120
+ }
121
+}
122
+
123
+func (entry *Entry) Warn(args ...interface{}) {
124
+ if entry.Logger.Level >= WarnLevel {
125
+ entry.log(WarnLevel, fmt.Sprint(args...))
126
+ }
127
+}
128
+
129
+func (entry *Entry) Error(args ...interface{}) {
130
+ if entry.Logger.Level >= ErrorLevel {
131
+ entry.log(ErrorLevel, fmt.Sprint(args...))
132
+ }
133
+}
134
+
135
+func (entry *Entry) Fatal(args ...interface{}) {
136
+ if entry.Logger.Level >= FatalLevel {
137
+ entry.log(FatalLevel, fmt.Sprint(args...))
138
+ }
139
+ os.Exit(1)
140
+}
141
+
142
+func (entry *Entry) Panic(args ...interface{}) {
143
+ if entry.Logger.Level >= PanicLevel {
144
+ entry.log(PanicLevel, fmt.Sprint(args...))
145
+ }
146
+ panic(fmt.Sprint(args...))
147
+}
148
+
149
+// Entry Printf family functions
150
+
151
+func (entry *Entry) Debugf(format string, args ...interface{}) {
152
+ if entry.Logger.Level >= DebugLevel {
153
+ entry.Debug(fmt.Sprintf(format, args...))
154
+ }
155
+}
156
+
157
+func (entry *Entry) Infof(format string, args ...interface{}) {
158
+ if entry.Logger.Level >= InfoLevel {
159
+ entry.Info(fmt.Sprintf(format, args...))
160
+ }
161
+}
162
+
163
+func (entry *Entry) Printf(format string, args ...interface{}) {
164
+ entry.Infof(format, args...)
165
+}
166
+
167
+func (entry *Entry) Warnf(format string, args ...interface{}) {
168
+ if entry.Logger.Level >= WarnLevel {
169
+ entry.Warn(fmt.Sprintf(format, args...))
170
+ }
171
+}
172
+
173
+func (entry *Entry) Warningf(format string, args ...interface{}) {
174
+ entry.Warnf(format, args...)
175
+}
176
+
177
+func (entry *Entry) Errorf(format string, args ...interface{}) {
178
+ if entry.Logger.Level >= ErrorLevel {
179
+ entry.Error(fmt.Sprintf(format, args...))
180
+ }
181
+}
182
+
183
+func (entry *Entry) Fatalf(format string, args ...interface{}) {
184
+ if entry.Logger.Level >= FatalLevel {
185
+ entry.Fatal(fmt.Sprintf(format, args...))
186
+ }
187
+}
188
+
189
+func (entry *Entry) Panicf(format string, args ...interface{}) {
190
+ if entry.Logger.Level >= PanicLevel {
191
+ entry.Panic(fmt.Sprintf(format, args...))
192
+ }
193
+}
194
+
195
+// Entry Println family functions
196
+
197
+func (entry *Entry) Debugln(args ...interface{}) {
198
+ if entry.Logger.Level >= DebugLevel {
199
+ entry.Debug(entry.sprintlnn(args...))
200
+ }
201
+}
202
+
203
+func (entry *Entry) Infoln(args ...interface{}) {
204
+ if entry.Logger.Level >= InfoLevel {
205
+ entry.Info(entry.sprintlnn(args...))
206
+ }
207
+}
208
+
209
+func (entry *Entry) Println(args ...interface{}) {
210
+ entry.Infoln(args...)
211
+}
212
+
213
+func (entry *Entry) Warnln(args ...interface{}) {
214
+ if entry.Logger.Level >= WarnLevel {
215
+ entry.Warn(entry.sprintlnn(args...))
216
+ }
217
+}
218
+
219
+func (entry *Entry) Warningln(args ...interface{}) {
220
+ entry.Warnln(args...)
221
+}
222
+
223
+func (entry *Entry) Errorln(args ...interface{}) {
224
+ if entry.Logger.Level >= ErrorLevel {
225
+ entry.Error(entry.sprintlnn(args...))
226
+ }
227
+}
228
+
229
+func (entry *Entry) Fatalln(args ...interface{}) {
230
+ if entry.Logger.Level >= FatalLevel {
231
+ entry.Fatal(entry.sprintlnn(args...))
232
+ }
233
+}
234
+
235
+func (entry *Entry) Panicln(args ...interface{}) {
236
+ if entry.Logger.Level >= PanicLevel {
237
+ entry.Panic(entry.sprintlnn(args...))
238
+ }
239
+}
240
+
241
+// Sprintlnn => Sprint no newline. This is to get the behavior of how
242
+// fmt.Sprintln where spaces are always added between operands, regardless of
243
+// their type. Instead of vendoring the Sprintln implementation to spare a
244
+// string allocation, we do the simplest thing.
245
+func (entry *Entry) sprintlnn(args ...interface{}) string {
246
+ msg := fmt.Sprintln(args...)
247
+ return msg[:len(msg)-1]
248
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/entry_test.go
new
+53
@@ -0,0 +1,53 @@
1
+package logrus
2
+
3
+import (
4
+ "bytes"
5
+ "fmt"
6
+ "testing"
7
+
8
+ "github.com/stretchr/testify/assert"
9
+)
10
+
11
+func TestEntryPanicln(t *testing.T) {
12
+ errBoom := fmt.Errorf("boom time")
13
+
14
+ defer func() {
15
+ p := recover()
16
+ assert.NotNil(t, p)
17
+
18
+ switch pVal := p.(type) {
19
+ case *Entry:
20
+ assert.Equal(t, "kaboom", pVal.Message)
21
+ assert.Equal(t, errBoom, pVal.Data["err"])
22
+ default:
23
+ t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
24
+ }
25
+ }()
26
+
27
+ logger := New()
28
+ logger.Out = &bytes.Buffer{}
29
+ entry := NewEntry(logger)
30
+ entry.WithField("err", errBoom).Panicln("kaboom")
31
+}
32
+
33
+func TestEntryPanicf(t *testing.T) {
34
+ errBoom := fmt.Errorf("boom again")
35
+
36
+ defer func() {
37
+ p := recover()
38
+ assert.NotNil(t, p)
39
+
40
+ switch pVal := p.(type) {
41
+ case *Entry:
42
+ assert.Equal(t, "kaboom true", pVal.Message)
43
+ assert.Equal(t, errBoom, pVal.Data["err"])
44
+ default:
45
+ t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
46
+ }
47
+ }()
48
+
49
+ logger := New()
50
+ logger.Out = &bytes.Buffer{}
51
+ entry := NewEntry(logger)
52
+ entry.WithField("err", errBoom).Panicf("kaboom %v", true)
53
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/basic/basic.go
new
+40
@@ -0,0 +1,40 @@
1
+package main
2
+
3
+import (
4
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
5
+)
6
+
7
+var log = logrus.New()
8
+
9
+func init() {
10
+ log.Formatter = new(logrus.JSONFormatter)
11
+ log.Formatter = new(logrus.TextFormatter) // default
12
+}
13
+
14
+func main() {
15
+ defer func() {
16
+ err := recover()
17
+ if err != nil {
18
+ log.WithFields(logrus.Fields{
19
+ "omg": true,
20
+ "err": err,
21
+ "number": 100,
22
+ }).Fatal("The ice breaks!")
23
+ }
24
+ }()
25
+
26
+ log.WithFields(logrus.Fields{
27
+ "animal": "walrus",
28
+ "size": 10,
29
+ }).Info("A group of walrus emerges from the ocean")
30
+
31
+ log.WithFields(logrus.Fields{
32
+ "omg": true,
33
+ "number": 122,
34
+ }).Warn("The group's number increased tremendously!")
35
+
36
+ log.WithFields(logrus.Fields{
37
+ "animal": "orca",
38
+ "size": 9009,
39
+ }).Panic("It's over 9000!")
40
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/hook/hook.go
new
+35
@@ -0,0 +1,35 @@
1
+package main
2
+
3
+import (
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
+
9
+var log = logrus.New()
10
+
11
+func init() {
12
+ log.Formatter = new(logrus.TextFormatter) // default
13
+ log.Hooks.Add(new(logrus_airbrake.AirbrakeHook))
14
+}
15
+
16
+func main() {
17
+ airbrake.Endpoint = "https://exceptions.whatever.com/notifier_api/v2/notices.xml"
18
+ airbrake.ApiKey = "whatever"
19
+ airbrake.Environment = "production"
20
+
21
+ log.WithFields(logrus.Fields{
22
+ "animal": "walrus",
23
+ "size": 10,
24
+ }).Info("A group of walrus emerges from the ocean")
25
+
26
+ log.WithFields(logrus.Fields{
27
+ "omg": true,
28
+ "number": 122,
29
+ }).Warn("The group's number increased tremendously!")
30
+
31
+ log.WithFields(logrus.Fields{
32
+ "omg": true,
33
+ "number": 100,
34
+ }).Fatal("The ice breaks!")
35
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/exported.go
new
+177
@@ -0,0 +1,177 @@
1
+package logrus
2
+
3
+import (
4
+ "io"
5
+)
6
+
7
+var (
8
+ // std is the name of the standard logger in stdlib `log`
9
+ std = New()
10
+)
11
+
12
+// SetOutput sets the standard logger output.
13
+func SetOutput(out io.Writer) {
14
+ std.mu.Lock()
15
+ defer std.mu.Unlock()
16
+ std.Out = out
17
+}
18
+
19
+// SetFormatter sets the standard logger formatter.
20
+func SetFormatter(formatter Formatter) {
21
+ std.mu.Lock()
22
+ defer std.mu.Unlock()
23
+ std.Formatter = formatter
24
+}
25
+
26
+// SetLevel sets the standard logger level.
27
+func SetLevel(level Level) {
28
+ std.mu.Lock()
29
+ defer std.mu.Unlock()
30
+ std.Level = level
31
+}
32
+
33
+// AddHook adds a hook to the standard logger hooks.
34
+func AddHook(hook Hook) {
35
+ std.mu.Lock()
36
+ defer std.mu.Unlock()
37
+ std.Hooks.Add(hook)
38
+}
39
+
40
+// WithField creates an entry from the standard logger and adds a field to
41
+// it. If you want multiple fields, use `WithFields`.
42
+//
43
+// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
44
+// or Panic on the Entry it returns.
45
+func WithField(key string, value interface{}) *Entry {
46
+ return std.WithField(key, value)
47
+}
48
+
49
+// WithFields creates an entry from the standard logger and adds multiple
50
+// fields to it. This is simply a helper for `WithField`, invoking it
51
+// once for each field.
52
+//
53
+// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
54
+// or Panic on the Entry it returns.
55
+func WithFields(fields Fields) *Entry {
56
+ return std.WithFields(fields)
57
+}
58
+
59
+// Debug logs a message at level Debug on the standard logger.
60
+func Debug(args ...interface{}) {
61
+ std.Debug(args...)
62
+}
63
+
64
+// Print logs a message at level Info on the standard logger.
65
+func Print(args ...interface{}) {
66
+ std.Print(args...)
67
+}
68
+
69
+// Info logs a message at level Info on the standard logger.
70
+func Info(args ...interface{}) {
71
+ std.Info(args...)
72
+}
73
+
74
+// Warn logs a message at level Warn on the standard logger.
75
+func Warn(args ...interface{}) {
76
+ std.Warn(args...)
77
+}
78
+
79
+// Warning logs a message at level Warn on the standard logger.
80
+func Warning(args ...interface{}) {
81
+ std.Warning(args...)
82
+}
83
+
84
+// Error logs a message at level Error on the standard logger.
85
+func Error(args ...interface{}) {
86
+ std.Error(args...)
87
+}
88
+
89
+// Panic logs a message at level Panic on the standard logger.
90
+func Panic(args ...interface{}) {
91
+ std.Panic(args...)
92
+}
93
+
94
+// Fatal logs a message at level Fatal on the standard logger.
95
+func Fatal(args ...interface{}) {
96
+ std.Fatal(args...)
97
+}
98
+
99
+// Debugf logs a message at level Debug on the standard logger.
100
+func Debugf(format string, args ...interface{}) {
101
+ std.Debugf(format, args...)
102
+}
103
+
104
+// Printf logs a message at level Info on the standard logger.
105
+func Printf(format string, args ...interface{}) {
106
+ std.Printf(format, args...)
107
+}
108
+
109
+// Infof logs a message at level Info on the standard logger.
110
+func Infof(format string, args ...interface{}) {
111
+ std.Infof(format, args...)
112
+}
113
+
114
+// Warnf logs a message at level Warn on the standard logger.
115
+func Warnf(format string, args ...interface{}) {
116
+ std.Warnf(format, args...)
117
+}
118
+
119
+// Warningf logs a message at level Warn on the standard logger.
120
+func Warningf(format string, args ...interface{}) {
121
+ std.Warningf(format, args...)
122
+}
123
+
124
+// Errorf logs a message at level Error on the standard logger.
125
+func Errorf(format string, args ...interface{}) {
126
+ std.Errorf(format, args...)
127
+}
128
+
129
+// Panicf logs a message at level Panic on the standard logger.
130
+func Panicf(format string, args ...interface{}) {
131
+ std.Panicf(format, args...)
132
+}
133
+
134
+// Fatalf logs a message at level Fatal on the standard logger.
135
+func Fatalf(format string, args ...interface{}) {
136
+ std.Fatalf(format, args...)
137
+}
138
+
139
+// Debugln logs a message at level Debug on the standard logger.
140
+func Debugln(args ...interface{}) {
141
+ std.Debugln(args...)
142
+}
143
+
144
+// Println logs a message at level Info on the standard logger.
145
+func Println(args ...interface{}) {
146
+ std.Println(args...)
147
+}
148
+
149
+// Infoln logs a message at level Info on the standard logger.
150
+func Infoln(args ...interface{}) {
151
+ std.Infoln(args...)
152
+}
153
+
154
+// Warnln logs a message at level Warn on the standard logger.
155
+func Warnln(args ...interface{}) {
156
+ std.Warnln(args...)
157
+}
158
+
159
+// Warningln logs a message at level Warn on the standard logger.
160
+func Warningln(args ...interface{}) {
161
+ std.Warningln(args...)
162
+}
163
+
164
+// Errorln logs a message at level Error on the standard logger.
165
+func Errorln(args ...interface{}) {
166
+ std.Errorln(args...)
167
+}
168
+
169
+// Panicln logs a message at level Panic on the standard logger.
170
+func Panicln(args ...interface{}) {
171
+ std.Panicln(args...)
172
+}
173
+
174
+// Fatalln logs a message at level Fatal on the standard logger.
175
+func Fatalln(args ...interface{}) {
176
+ std.Fatalln(args...)
177
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter.go
new
+44
@@ -0,0 +1,44 @@
1
+package logrus
2
+
3
+// The Formatter interface is used to implement a custom Formatter. It takes an
4
+// `Entry`. It exposes all the fields, including the default ones:
5
+//
6
+// * `entry.Data["msg"]`. The message passed from Info, Warn, Error ..
7
+// * `entry.Data["time"]`. The timestamp.
8
+// * `entry.Data["level"]. The level the entry was logged at.
9
+//
10
+// Any additional fields added with `WithField` or `WithFields` are also in
11
+// `entry.Data`. Format is expected to return an array of bytes which are then
12
+// logged to `logger.Out`.
13
+type Formatter interface {
14
+ Format(*Entry) ([]byte, error)
15
+}
16
+
17
+// This is to not silently overwrite `time`, `msg` and `level` fields when
18
+// dumping it. If this code wasn't there doing:
19
+//
20
+// logrus.WithField("level", 1).Info("hello")
21
+//
22
+// Would just silently drop the user provided level. Instead with this code
23
+// it'll logged as:
24
+//
25
+// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
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"]
31
+ if ok {
32
+ entry.Data["fields.time"] = entry.Data["time"]
33
+ }
34
+
35
+ _, ok = entry.Data["msg"]
36
+ if ok {
37
+ entry.Data["fields.msg"] = entry.Data["msg"]
38
+ }
39
+
40
+ _, ok = entry.Data["level"]
41
+ if ok {
42
+ entry.Data["fields.level"] = entry.Data["level"]
43
+ }
44
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter_bench_test.go
new
+88
@@ -0,0 +1,88 @@
1
+package logrus
2
+
3
+import (
4
+ "testing"
5
+ "time"
6
+)
7
+
8
+// smallFields is a small size data set for benchmarking
9
+var smallFields = Fields{
10
+ "foo": "bar",
11
+ "baz": "qux",
12
+ "one": "two",
13
+ "three": "four",
14
+}
15
+
16
+// largeFields is a large size data set for benchmarking
17
+var largeFields = Fields{
18
+ "foo": "bar",
19
+ "baz": "qux",
20
+ "one": "two",
21
+ "three": "four",
22
+ "five": "six",
23
+ "seven": "eight",
24
+ "nine": "ten",
25
+ "eleven": "twelve",
26
+ "thirteen": "fourteen",
27
+ "fifteen": "sixteen",
28
+ "seventeen": "eighteen",
29
+ "nineteen": "twenty",
30
+ "a": "b",
31
+ "c": "d",
32
+ "e": "f",
33
+ "g": "h",
34
+ "i": "j",
35
+ "k": "l",
36
+ "m": "n",
37
+ "o": "p",
38
+ "q": "r",
39
+ "s": "t",
40
+ "u": "v",
41
+ "w": "x",
42
+ "y": "z",
43
+ "this": "will",
44
+ "make": "thirty",
45
+ "entries": "yeah",
46
+}
47
+
48
+func BenchmarkSmallTextFormatter(b *testing.B) {
49
+ doBenchmark(b, &TextFormatter{DisableColors: true}, smallFields)
50
+}
51
+
52
+func BenchmarkLargeTextFormatter(b *testing.B) {
53
+ doBenchmark(b, &TextFormatter{DisableColors: true}, largeFields)
54
+}
55
+
56
+func BenchmarkSmallColoredTextFormatter(b *testing.B) {
57
+ doBenchmark(b, &TextFormatter{ForceColors: true}, smallFields)
58
+}
59
+
60
+func BenchmarkLargeColoredTextFormatter(b *testing.B) {
61
+ doBenchmark(b, &TextFormatter{ForceColors: true}, largeFields)
62
+}
63
+
64
+func BenchmarkSmallJSONFormatter(b *testing.B) {
65
+ doBenchmark(b, &JSONFormatter{}, smallFields)
66
+}
67
+
68
+func BenchmarkLargeJSONFormatter(b *testing.B) {
69
+ doBenchmark(b, &JSONFormatter{}, largeFields)
70
+}
71
+
72
+func doBenchmark(b *testing.B, formatter Formatter, fields Fields) {
73
+ entry := &Entry{
74
+ Time: time.Time{},
75
+ Level: InfoLevel,
76
+ Message: "message",
77
+ Data: fields,
78
+ }
79
+ var d []byte
80
+ var err error
81
+ for i := 0; i < b.N; i++ {
82
+ d, err = formatter.Format(entry)
83
+ if err != nil {
84
+ b.Fatal(err)
85
+ }
86
+ b.SetBytes(int64(len(d)))
87
+ }
88
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/hook_test.go
new
+122
@@ -0,0 +1,122 @@
1
+package logrus
2
+
3
+import (
4
+ "testing"
5
+
6
+ "github.com/stretchr/testify/assert"
7
+)
8
+
9
+type TestHook struct {
10
+ Fired bool
11
+}
12
+
13
+func (hook *TestHook) Fire(entry *Entry) error {
14
+ hook.Fired = true
15
+ return nil
16
+}
17
+
18
+func (hook *TestHook) Levels() []Level {
19
+ return []Level{
20
+ DebugLevel,
21
+ InfoLevel,
22
+ WarnLevel,
23
+ ErrorLevel,
24
+ FatalLevel,
25
+ PanicLevel,
26
+ }
27
+}
28
+
29
+func TestHookFires(t *testing.T) {
30
+ hook := new(TestHook)
31
+
32
+ LogAndAssertJSON(t, func(log *Logger) {
33
+ log.Hooks.Add(hook)
34
+ assert.Equal(t, hook.Fired, false)
35
+
36
+ log.Print("test")
37
+ }, func(fields Fields) {
38
+ assert.Equal(t, hook.Fired, true)
39
+ })
40
+}
41
+
42
+type ModifyHook struct {
43
+}
44
+
45
+func (hook *ModifyHook) Fire(entry *Entry) error {
46
+ entry.Data["wow"] = "whale"
47
+ return nil
48
+}
49
+
50
+func (hook *ModifyHook) Levels() []Level {
51
+ return []Level{
52
+ DebugLevel,
53
+ InfoLevel,
54
+ WarnLevel,
55
+ ErrorLevel,
56
+ FatalLevel,
57
+ PanicLevel,
58
+ }
59
+}
60
+
61
+func TestHookCanModifyEntry(t *testing.T) {
62
+ hook := new(ModifyHook)
63
+
64
+ LogAndAssertJSON(t, func(log *Logger) {
65
+ log.Hooks.Add(hook)
66
+ log.WithField("wow", "elephant").Print("test")
67
+ }, func(fields Fields) {
68
+ assert.Equal(t, fields["wow"], "whale")
69
+ })
70
+}
71
+
72
+func TestCanFireMultipleHooks(t *testing.T) {
73
+ hook1 := new(ModifyHook)
74
+ hook2 := new(TestHook)
75
+
76
+ LogAndAssertJSON(t, func(log *Logger) {
77
+ log.Hooks.Add(hook1)
78
+ log.Hooks.Add(hook2)
79
+
80
+ log.WithField("wow", "elephant").Print("test")
81
+ }, func(fields Fields) {
82
+ assert.Equal(t, fields["wow"], "whale")
83
+ assert.Equal(t, hook2.Fired, true)
84
+ })
85
+}
86
+
87
+type ErrorHook struct {
88
+ Fired bool
89
+}
90
+
91
+func (hook *ErrorHook) Fire(entry *Entry) error {
92
+ hook.Fired = true
93
+ return nil
94
+}
95
+
96
+func (hook *ErrorHook) Levels() []Level {
97
+ return []Level{
98
+ ErrorLevel,
99
+ }
100
+}
101
+
102
+func TestErrorHookShouldntFireOnInfo(t *testing.T) {
103
+ hook := new(ErrorHook)
104
+
105
+ LogAndAssertJSON(t, func(log *Logger) {
106
+ log.Hooks.Add(hook)
107
+ log.Info("test")
108
+ }, func(fields Fields) {
109
+ assert.Equal(t, hook.Fired, false)
110
+ })
111
+}
112
+
113
+func TestErrorHookShouldFireOnError(t *testing.T) {
114
+ hook := new(ErrorHook)
115
+
116
+ LogAndAssertJSON(t, func(log *Logger) {
117
+ log.Hooks.Add(hook)
118
+ log.Error("test")
119
+ }, func(fields Fields) {
120
+ assert.Equal(t, hook.Fired, true)
121
+ })
122
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks.go
new
+34
@@ -0,0 +1,34 @@
1
+package logrus
2
+
3
+// A hook to be fired when logging on the logging levels returned from
4
+// `Levels()` on your implementation of the interface. Note that this is not
5
+// fired in a goroutine or a channel with workers, you should handle such
6
+// functionality yourself if your call is non-blocking and you don't wish for
7
+// the logging calls for levels returned from `Levels()` to block.
8
+type Hook interface {
9
+ Levels() []Level
10
+ Fire(*Entry) error
11
+}
12
+
13
+// Internal type for storing the hooks on a logger instance.
14
+type levelHooks map[Level][]Hook
15
+
16
+// Add a hook to an instance of logger. This is called with
17
+// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface.
18
+func (hooks levelHooks) Add(hook Hook) {
19
+ for _, level := range hook.Levels() {
20
+ hooks[level] = append(hooks[level], hook)
21
+ }
22
+}
23
+
24
+// Fire all the hooks for the passed level. Used by `entry.log` to fire
25
+// appropriate hooks for a log entry.
26
+func (hooks levelHooks) Fire(level Level, entry *Entry) error {
27
+ for _, hook := range hooks[level] {
28
+ if err := hook.Fire(entry); err != nil {
29
+ return err
30
+ }
31
+ }
32
+
33
+ return nil
34
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake/airbrake.go
new
+54
@@ -0,0 +1,54 @@
1
+package logrus_airbrake
2
+
3
+import (
4
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
5
+ "github.com/tobi/airbrake-go"
6
+)
7
+
8
+// 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 (only sends exceptions when set to "production")
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
25
+ }
26
+
27
+ 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
+ }
35
+
36
+ airErr := airbrake.Notify(err)
37
+ 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")
43
+ }
44
+
45
+ return nil
46
+}
47
+
48
+func (hook *AirbrakeHook) Levels() []logrus.Level {
49
+ return []logrus.Level{
50
+ logrus.ErrorLevel,
51
+ logrus.FatalLevel,
52
+ logrus.PanicLevel,
53
+ }
54
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/README.md
new
+28
@@ -0,0 +1,28 @@
1
+# Papertrail Hook for Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:" />
2
+
3
+[Papertrail](https://papertrailapp.com) provides hosted log management. Once stored in Papertrail, you can [group](http://help.papertrailapp.com/kb/how-it-works/groups/) your logs on various dimensions, [search](http://help.papertrailapp.com/kb/how-it-works/search-syntax) them, and trigger [alerts](http://help.papertrailapp.com/kb/how-it-works/alerts).
4
+
5
+In most deployments, you'll want to send logs to Papertrail via their [remote_syslog](http://help.papertrailapp.com/kb/configuration/configuring-centralized-logging-from-text-log-files-in-unix/) daemon, which requires no application-specific configuration. This hook is intended for relatively low-volume logging, likely in managed cloud hosting deployments where installing `remote_syslog` is not possible.
6
+
7
+## Usage
8
+
9
+You can find your Papertrail UDP port on your [Papertrail account page](https://papertrailapp.com/account/destinations). Substitute it below for `YOUR_PAPERTRAIL_UDP_PORT`.
10
+
11
+For `YOUR_APP_NAME`, substitute a short string that will readily identify your application or service in the logs.
12
+
13
+```go
14
+import (
15
+ "log/syslog"
16
+ "github.com/Sirupsen/logrus"
17
+ "github.com/Sirupsen/logrus/hooks/papertrail"
18
+)
19
+
20
+func main() {
21
+ log := logrus.New()
22
+ hook, err := logrus_papertrail.NewPapertrailHook("logs.papertrailapp.com", YOUR_PAPERTRAIL_UDP_PORT, YOUR_APP_NAME)
23
+
24
+ if err == nil {
25
+ log.Hooks.Add(hook)
26
+ }
27
+}
28
+```
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail.go
new
+54
@@ -0,0 +1,54 @@
1
+package logrus_papertrail
2
+
3
+import (
4
+ "fmt"
5
+ "net"
6
+ "os"
7
+ "time"
8
+
9
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
10
+)
11
+
12
+const (
13
+ format = "Jan 2 15:04:05"
14
+)
15
+
16
+// PapertrailHook to send logs to a logging service compatible with the Papertrail API.
17
+type PapertrailHook struct {
18
+ Host string
19
+ Port int
20
+ AppName string
21
+ UDPConn net.Conn
22
+}
23
+
24
+// NewPapertrailHook creates a hook to be added to an instance of logger.
25
+func NewPapertrailHook(host string, port int, appName string) (*PapertrailHook, error) {
26
+ conn, err := net.Dial("udp", fmt.Sprintf("%s:%d", host, port))
27
+ return &PapertrailHook{host, port, appName, conn}, err
28
+}
29
+
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)
34
+
35
+ bytesWritten, err := hook.UDPConn.Write([]byte(payload))
36
+ if err != nil {
37
+ fmt.Fprintf(os.Stderr, "Unable to send log line to Papertrail via UDP. Wrote %d bytes before error: %v", bytesWritten, err)
38
+ return err
39
+ }
40
+
41
+ return nil
42
+}
43
+
44
+// Levels returns the available logging levels.
45
+func (hook *PapertrailHook) Levels() []logrus.Level {
46
+ return []logrus.Level{
47
+ logrus.PanicLevel,
48
+ logrus.FatalLevel,
49
+ logrus.ErrorLevel,
50
+ logrus.WarnLevel,
51
+ logrus.InfoLevel,
52
+ logrus.DebugLevel,
53
+ }
54
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail_test.go
new
+26
@@ -0,0 +1,26 @@
1
+package logrus_papertrail
2
+
3
+import (
4
+ "fmt"
5
+ "testing"
6
+
7
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
8
+ "github.com/stvp/go-udp-testing"
9
+)
10
+
11
+func TestWritingToUDP(t *testing.T) {
12
+ port := 16661
13
+ udp.SetAddr(fmt.Sprintf(":%d", port))
14
+
15
+ hook, err := NewPapertrailHook("localhost", port, "test")
16
+ if err != nil {
17
+ t.Errorf("Unable to connect to local UDP server.")
18
+ }
19
+
20
+ log := logrus.New()
21
+ log.Hooks.Add(hook)
22
+
23
+ udp.ShouldReceive(t, "foo", func() {
24
+ log.Info("foo")
25
+ })
26
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/README.md
new
+20
@@ -0,0 +1,20 @@
1
+# Syslog Hooks for Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/>
2
+
3
+## Usage
4
+
5
+```go
6
+import (
7
+ "log/syslog"
8
+ "github.com/Sirupsen/logrus"
9
+ "github.com/Sirupsen/logrus/hooks/syslog"
10
+)
11
+
12
+func main() {
13
+ log := logrus.New()
14
+ hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
15
+
16
+ if err == nil {
17
+ log.Hooks.Add(hook)
18
+ }
19
+}
20
+```
\ No newline at end of file
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog.go
new
+59
@@ -0,0 +1,59 @@
1
+package logrus_syslog
2
+
3
+import (
4
+ "fmt"
5
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
6
+ "log/syslog"
7
+ "os"
8
+)
9
+
10
+// SyslogHook to send logs via syslog.
11
+type SyslogHook struct {
12
+ Writer *syslog.Writer
13
+ SyslogNetwork string
14
+ SyslogRaddr string
15
+}
16
+
17
+// Creates a hook to be added to an instance of logger. This is called with
18
+// `hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_DEBUG, "")`
19
+// `if err == nil { log.Hooks.Add(hook) }`
20
+func NewSyslogHook(network, raddr string, priority syslog.Priority, tag string) (*SyslogHook, error) {
21
+ w, err := syslog.Dial(network, raddr, priority, tag)
22
+ return &SyslogHook{w, network, raddr}, err
23
+}
24
+
25
+func (hook *SyslogHook) Fire(entry *logrus.Entry) error {
26
+ line, err := entry.String()
27
+ if err != nil {
28
+ fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err)
29
+ return err
30
+ }
31
+
32
+ switch entry.Data["level"] {
33
+ case "panic":
34
+ return hook.Writer.Crit(line)
35
+ case "fatal":
36
+ return hook.Writer.Crit(line)
37
+ case "error":
38
+ return hook.Writer.Err(line)
39
+ case "warn":
40
+ return hook.Writer.Warning(line)
41
+ case "info":
42
+ return hook.Writer.Info(line)
43
+ case "debug":
44
+ return hook.Writer.Debug(line)
45
+ default:
46
+ return nil
47
+ }
48
+}
49
+
50
+func (hook *SyslogHook) Levels() []logrus.Level {
51
+ return []logrus.Level{
52
+ logrus.PanicLevel,
53
+ logrus.FatalLevel,
54
+ logrus.ErrorLevel,
55
+ logrus.WarnLevel,
56
+ logrus.InfoLevel,
57
+ logrus.DebugLevel,
58
+ }
59
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go
new
+26
@@ -0,0 +1,26 @@
1
+package logrus_syslog
2
+
3
+import (
4
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
5
+ "log/syslog"
6
+ "testing"
7
+)
8
+
9
+func TestLocalhostAddAndPrint(t *testing.T) {
10
+ log := logrus.New()
11
+ hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
12
+
13
+ if err != nil {
14
+ t.Errorf("Unable to connect to local syslog.")
15
+ }
16
+
17
+ log.Hooks.Add(hook)
18
+
19
+ for _, level := range hook.Levels() {
20
+ if len(log.Hooks[level]) != 1 {
21
+ t.Errorf("SyslogHook was not added. The length of log.Hooks[%v]: %v", level, len(log.Hooks[level]))
22
+ }
23
+ }
24
+
25
+ log.Info("Congratulations!")
26
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter.go
new
+22
@@ -0,0 +1,22 @@
1
+package logrus
2
+
3
+import (
4
+ "encoding/json"
5
+ "fmt"
6
+ "time"
7
+)
8
+
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()
16
+
17
+ serialized, err := json.Marshal(entry.Data)
18
+ if err != nil {
19
+ return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
20
+ }
21
+ return append(serialized, '\n'), nil
22
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/logger.go
new
+161
@@ -0,0 +1,161 @@
1
+package logrus
2
+
3
+import (
4
+ "io"
5
+ "os"
6
+ "sync"
7
+)
8
+
9
+type Logger struct {
10
+ // The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
11
+ // file, or leave it default which is `os.Stdout`. You can also set this to
12
+ // something more adventorous, such as logging to Kafka.
13
+ Out io.Writer
14
+ // Hooks for the logger instance. These allow firing events based on logging
15
+ // levels and log entries. For example, to send errors to an error tracking
16
+ // service, log to StatsD or dump the core on fatal errors.
17
+ Hooks levelHooks
18
+ // All log entries pass through the formatter before logged to Out. The
19
+ // included formatters are `TextFormatter` and `JSONFormatter` for which
20
+ // TextFormatter is the default. In development (when a TTY is attached) it
21
+ // logs with colors, but to a file it wouldn't. You can easily implement your
22
+ // own that implements the `Formatter` interface, see the `README` or included
23
+ // formatters for examples.
24
+ Formatter Formatter
25
+ // The logging level the logger should log at. This is typically (and defaults
26
+ // to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
27
+ // logged. `logrus.Debug` is useful in
28
+ Level Level
29
+ // Used to sync writing to the log.
30
+ mu sync.Mutex
31
+}
32
+
33
+// Creates a new logger. Configuration should be set by changing `Formatter`,
34
+// `Out` and `Hooks` directly on the default logger instance. You can also just
35
+// instantiate your own:
36
+//
37
+// var log = &Logger{
38
+// Out: os.Stderr,
39
+// Formatter: new(JSONFormatter),
40
+// Hooks: make(levelHooks),
41
+// Level: logrus.Debug,
42
+// }
43
+//
44
+// It's recommended to make this a global instance called `log`.
45
+func New() *Logger {
46
+ return &Logger{
47
+ Out: os.Stdout,
48
+ Formatter: new(TextFormatter),
49
+ Hooks: make(levelHooks),
50
+ Level: InfoLevel,
51
+ }
52
+}
53
+
54
+// Adds a field to the log entry, note that you it doesn't log until you call
55
+// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
56
+// Ff you want multiple fields, use `WithFields`.
57
+func (logger *Logger) WithField(key string, value interface{}) *Entry {
58
+ return NewEntry(logger).WithField(key, value)
59
+}
60
+
61
+// Adds a struct of fields to the log entry. All it does is call `WithField` for
62
+// each `Field`.
63
+func (logger *Logger) WithFields(fields Fields) *Entry {
64
+ return NewEntry(logger).WithFields(fields)
65
+}
66
+
67
+func (logger *Logger) Debugf(format string, args ...interface{}) {
68
+ NewEntry(logger).Debugf(format, args...)
69
+}
70
+
71
+func (logger *Logger) Infof(format string, args ...interface{}) {
72
+ NewEntry(logger).Infof(format, args...)
73
+}
74
+
75
+func (logger *Logger) Printf(format string, args ...interface{}) {
76
+ NewEntry(logger).Printf(format, args...)
77
+}
78
+
79
+func (logger *Logger) Warnf(format string, args ...interface{}) {
80
+ NewEntry(logger).Warnf(format, args...)
81
+}
82
+
83
+func (logger *Logger) Warningf(format string, args ...interface{}) {
84
+ NewEntry(logger).Warnf(format, args...)
85
+}
86
+
87
+func (logger *Logger) Errorf(format string, args ...interface{}) {
88
+ NewEntry(logger).Errorf(format, args...)
89
+}
90
+
91
+func (logger *Logger) Fatalf(format string, args ...interface{}) {
92
+ NewEntry(logger).Fatalf(format, args...)
93
+}
94
+
95
+func (logger *Logger) Panicf(format string, args ...interface{}) {
96
+ NewEntry(logger).Panicf(format, args...)
97
+}
98
+
99
+func (logger *Logger) Debug(args ...interface{}) {
100
+ NewEntry(logger).Debug(args...)
101
+}
102
+
103
+func (logger *Logger) Info(args ...interface{}) {
104
+ NewEntry(logger).Info(args...)
105
+}
106
+
107
+func (logger *Logger) Print(args ...interface{}) {
108
+ NewEntry(logger).Info(args...)
109
+}
110
+
111
+func (logger *Logger) Warn(args ...interface{}) {
112
+ NewEntry(logger).Warn(args...)
113
+}
114
+
115
+func (logger *Logger) Warning(args ...interface{}) {
116
+ NewEntry(logger).Warn(args...)
117
+}
118
+
119
+func (logger *Logger) Error(args ...interface{}) {
120
+ NewEntry(logger).Error(args...)
121
+}
122
+
123
+func (logger *Logger) Fatal(args ...interface{}) {
124
+ NewEntry(logger).Fatal(args...)
125
+}
126
+
127
+func (logger *Logger) Panic(args ...interface{}) {
128
+ NewEntry(logger).Panic(args...)
129
+}
130
+
131
+func (logger *Logger) Debugln(args ...interface{}) {
132
+ NewEntry(logger).Debugln(args...)
133
+}
134
+
135
+func (logger *Logger) Infoln(args ...interface{}) {
136
+ NewEntry(logger).Infoln(args...)
137
+}
138
+
139
+func (logger *Logger) Println(args ...interface{}) {
140
+ NewEntry(logger).Println(args...)
141
+}
142
+
143
+func (logger *Logger) Warnln(args ...interface{}) {
144
+ NewEntry(logger).Warnln(args...)
145
+}
146
+
147
+func (logger *Logger) Warningln(args ...interface{}) {
148
+ NewEntry(logger).Warnln(args...)
149
+}
150
+
151
+func (logger *Logger) Errorln(args ...interface{}) {
152
+ NewEntry(logger).Errorln(args...)
153
+}
154
+
155
+func (logger *Logger) Fatalln(args ...interface{}) {
156
+ NewEntry(logger).Fatalln(args...)
157
+}
158
+
159
+func (logger *Logger) Panicln(args ...interface{}) {
160
+ NewEntry(logger).Panicln(args...)
161
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus.go
new
+94
@@ -0,0 +1,94 @@
1
+package logrus
2
+
3
+import (
4
+ "fmt"
5
+ "log"
6
+)
7
+
8
+// Fields type, used to pass to `WithFields`.
9
+type Fields map[string]interface{}
10
+
11
+// Level type
12
+type Level uint8
13
+
14
+// Convert the Level to a string. E.g. PanicLevel becomes "panic".
15
+func (level Level) String() string {
16
+ switch level {
17
+ case DebugLevel:
18
+ return "debug"
19
+ case InfoLevel:
20
+ return "info"
21
+ case WarnLevel:
22
+ return "warning"
23
+ case ErrorLevel:
24
+ return "error"
25
+ case FatalLevel:
26
+ return "fatal"
27
+ case PanicLevel:
28
+ return "panic"
29
+ }
30
+
31
+ return "unknown"
32
+}
33
+
34
+// ParseLevel takes a string level and returns the Logrus log level constant.
35
+func ParseLevel(lvl string) (Level, error) {
36
+ switch lvl {
37
+ case "panic":
38
+ return PanicLevel, nil
39
+ case "fatal":
40
+ return FatalLevel, nil
41
+ case "error":
42
+ return ErrorLevel, nil
43
+ case "warn", "warning":
44
+ return WarnLevel, nil
45
+ case "info":
46
+ return InfoLevel, nil
47
+ case "debug":
48
+ return DebugLevel, nil
49
+ }
50
+
51
+ var l Level
52
+ return l, fmt.Errorf("not a valid logrus Level: %q", lvl)
53
+}
54
+
55
+// These are the different logging levels. You can set the logging level to log
56
+// on your instance of logger, obtained with `logrus.New()`.
57
+const (
58
+ // PanicLevel level, highest level of severity. Logs and then calls panic with the
59
+ // message passed to Debug, Info, ...
60
+ PanicLevel Level = iota
61
+ // FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the
62
+ // logging level is set to Panic.
63
+ FatalLevel
64
+ // ErrorLevel level. Logs. Used for errors that should definitely be noted.
65
+ // Commonly used for hooks to send errors to an error tracking service.
66
+ ErrorLevel
67
+ // WarnLevel level. Non-critical entries that deserve eyes.
68
+ WarnLevel
69
+ // InfoLevel level. General operational entries about what's going on inside the
70
+ // application.
71
+ InfoLevel
72
+ // DebugLevel level. Usually only enabled when debugging. Very verbose logging.
73
+ DebugLevel
74
+)
75
+
76
+// Won't compile if StdLogger can't be realized by a log.Logger
77
+var _ StdLogger = &log.Logger{}
78
+
79
+// StdLogger is what your logrus-enabled library should take, that way
80
+// it'll accept a stdlib logger and a logrus logger. There's no standard
81
+// interface, this is the closest we get, unfortunately.
82
+type StdLogger interface {
83
+ Print(...interface{})
84
+ Printf(string, ...interface{})
85
+ Println(...interface{})
86
+
87
+ Fatal(...interface{})
88
+ Fatalf(string, ...interface{})
89
+ Fatalln(...interface{})
90
+
91
+ Panic(...interface{})
92
+ Panicf(string, ...interface{})
93
+ Panicln(...interface{})
94
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus_test.go
new
+247
@@ -0,0 +1,247 @@
1
+package logrus
2
+
3
+import (
4
+ "bytes"
5
+ "encoding/json"
6
+ "strconv"
7
+ "strings"
8
+ "testing"
9
+
10
+ "github.com/stretchr/testify/assert"
11
+)
12
+
13
+func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) {
14
+ var buffer bytes.Buffer
15
+ var fields Fields
16
+
17
+ logger := New()
18
+ logger.Out = &buffer
19
+ logger.Formatter = new(JSONFormatter)
20
+
21
+ log(logger)
22
+
23
+ err := json.Unmarshal(buffer.Bytes(), &fields)
24
+ assert.Nil(t, err)
25
+
26
+ assertions(fields)
27
+}
28
+
29
+func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) {
30
+ var buffer bytes.Buffer
31
+
32
+ logger := New()
33
+ logger.Out = &buffer
34
+ logger.Formatter = &TextFormatter{
35
+ DisableColors: true,
36
+ }
37
+
38
+ log(logger)
39
+
40
+ fields := make(map[string]string)
41
+ for _, kv := range strings.Split(buffer.String(), " ") {
42
+ if !strings.Contains(kv, "=") {
43
+ continue
44
+ }
45
+ kvArr := strings.Split(kv, "=")
46
+ key := strings.TrimSpace(kvArr[0])
47
+ val, err := strconv.Unquote(kvArr[1])
48
+ assert.NoError(t, err)
49
+ fields[key] = val
50
+ }
51
+ assertions(fields)
52
+}
53
+
54
+func TestPrint(t *testing.T) {
55
+ LogAndAssertJSON(t, func(log *Logger) {
56
+ log.Print("test")
57
+ }, func(fields Fields) {
58
+ assert.Equal(t, fields["msg"], "test")
59
+ assert.Equal(t, fields["level"], "info")
60
+ })
61
+}
62
+
63
+func TestInfo(t *testing.T) {
64
+ LogAndAssertJSON(t, func(log *Logger) {
65
+ log.Info("test")
66
+ }, func(fields Fields) {
67
+ assert.Equal(t, fields["msg"], "test")
68
+ assert.Equal(t, fields["level"], "info")
69
+ })
70
+}
71
+
72
+func TestWarn(t *testing.T) {
73
+ LogAndAssertJSON(t, func(log *Logger) {
74
+ log.Warn("test")
75
+ }, func(fields Fields) {
76
+ assert.Equal(t, fields["msg"], "test")
77
+ assert.Equal(t, fields["level"], "warning")
78
+ })
79
+}
80
+
81
+func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) {
82
+ LogAndAssertJSON(t, func(log *Logger) {
83
+ log.Infoln("test", "test")
84
+ }, func(fields Fields) {
85
+ assert.Equal(t, fields["msg"], "test test")
86
+ })
87
+}
88
+
89
+func TestInfolnShouldAddSpacesBetweenStringAndNonstring(t *testing.T) {
90
+ LogAndAssertJSON(t, func(log *Logger) {
91
+ log.Infoln("test", 10)
92
+ }, func(fields Fields) {
93
+ assert.Equal(t, fields["msg"], "test 10")
94
+ })
95
+}
96
+
97
+func TestInfolnShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
98
+ LogAndAssertJSON(t, func(log *Logger) {
99
+ log.Infoln(10, 10)
100
+ }, func(fields Fields) {
101
+ assert.Equal(t, fields["msg"], "10 10")
102
+ })
103
+}
104
+
105
+func TestInfoShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
106
+ LogAndAssertJSON(t, func(log *Logger) {
107
+ log.Infoln(10, 10)
108
+ }, func(fields Fields) {
109
+ assert.Equal(t, fields["msg"], "10 10")
110
+ })
111
+}
112
+
113
+func TestInfoShouldNotAddSpacesBetweenStringAndNonstring(t *testing.T) {
114
+ LogAndAssertJSON(t, func(log *Logger) {
115
+ log.Info("test", 10)
116
+ }, func(fields Fields) {
117
+ assert.Equal(t, fields["msg"], "test10")
118
+ })
119
+}
120
+
121
+func TestInfoShouldNotAddSpacesBetweenStrings(t *testing.T) {
122
+ LogAndAssertJSON(t, func(log *Logger) {
123
+ log.Info("test", "test")
124
+ }, func(fields Fields) {
125
+ assert.Equal(t, fields["msg"], "testtest")
126
+ })
127
+}
128
+
129
+func TestWithFieldsShouldAllowAssignments(t *testing.T) {
130
+ var buffer bytes.Buffer
131
+ var fields Fields
132
+
133
+ logger := New()
134
+ logger.Out = &buffer
135
+ logger.Formatter = new(JSONFormatter)
136
+
137
+ localLog := logger.WithFields(Fields{
138
+ "key1": "value1",
139
+ })
140
+
141
+ localLog.WithField("key2", "value2").Info("test")
142
+ err := json.Unmarshal(buffer.Bytes(), &fields)
143
+ assert.Nil(t, err)
144
+
145
+ assert.Equal(t, "value2", fields["key2"])
146
+ assert.Equal(t, "value1", fields["key1"])
147
+
148
+ buffer = bytes.Buffer{}
149
+ fields = Fields{}
150
+ localLog.Info("test")
151
+ err = json.Unmarshal(buffer.Bytes(), &fields)
152
+ assert.Nil(t, err)
153
+
154
+ _, ok := fields["key2"]
155
+ assert.Equal(t, false, ok)
156
+ assert.Equal(t, "value1", fields["key1"])
157
+}
158
+
159
+func TestUserSuppliedFieldDoesNotOverwriteDefaults(t *testing.T) {
160
+ LogAndAssertJSON(t, func(log *Logger) {
161
+ log.WithField("msg", "hello").Info("test")
162
+ }, func(fields Fields) {
163
+ assert.Equal(t, fields["msg"], "test")
164
+ })
165
+}
166
+
167
+func TestUserSuppliedMsgFieldHasPrefix(t *testing.T) {
168
+ LogAndAssertJSON(t, func(log *Logger) {
169
+ log.WithField("msg", "hello").Info("test")
170
+ }, func(fields Fields) {
171
+ assert.Equal(t, fields["msg"], "test")
172
+ assert.Equal(t, fields["fields.msg"], "hello")
173
+ })
174
+}
175
+
176
+func TestUserSuppliedTimeFieldHasPrefix(t *testing.T) {
177
+ LogAndAssertJSON(t, func(log *Logger) {
178
+ log.WithField("time", "hello").Info("test")
179
+ }, func(fields Fields) {
180
+ assert.Equal(t, fields["fields.time"], "hello")
181
+ })
182
+}
183
+
184
+func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) {
185
+ LogAndAssertJSON(t, func(log *Logger) {
186
+ log.WithField("level", 1).Info("test")
187
+ }, func(fields Fields) {
188
+ assert.Equal(t, fields["level"], "info")
189
+ assert.Equal(t, fields["fields.level"], 1)
190
+ })
191
+}
192
+
193
+func TestDefaultFieldsAreNotPrefixed(t *testing.T) {
194
+ LogAndAssertText(t, func(log *Logger) {
195
+ ll := log.WithField("herp", "derp")
196
+ ll.Info("hello")
197
+ ll.Info("bye")
198
+ }, func(fields map[string]string) {
199
+ for _, fieldName := range []string{"fields.level", "fields.time", "fields.msg"} {
200
+ if _, ok := fields[fieldName]; ok {
201
+ t.Fatalf("should not have prefixed %q: %v", fieldName, fields)
202
+ }
203
+ }
204
+ })
205
+}
206
+
207
+func TestConvertLevelToString(t *testing.T) {
208
+ assert.Equal(t, "debug", DebugLevel.String())
209
+ assert.Equal(t, "info", InfoLevel.String())
210
+ assert.Equal(t, "warning", WarnLevel.String())
211
+ assert.Equal(t, "error", ErrorLevel.String())
212
+ assert.Equal(t, "fatal", FatalLevel.String())
213
+ assert.Equal(t, "panic", PanicLevel.String())
214
+}
215
+
216
+func TestParseLevel(t *testing.T) {
217
+ l, err := ParseLevel("panic")
218
+ assert.Nil(t, err)
219
+ assert.Equal(t, PanicLevel, l)
220
+
221
+ l, err = ParseLevel("fatal")
222
+ assert.Nil(t, err)
223
+ assert.Equal(t, FatalLevel, l)
224
+
225
+ l, err = ParseLevel("error")
226
+ assert.Nil(t, err)
227
+ assert.Equal(t, ErrorLevel, l)
228
+
229
+ l, err = ParseLevel("warn")
230
+ assert.Nil(t, err)
231
+ assert.Equal(t, WarnLevel, l)
232
+
233
+ l, err = ParseLevel("warning")
234
+ assert.Nil(t, err)
235
+ assert.Equal(t, WarnLevel, l)
236
+
237
+ l, err = ParseLevel("info")
238
+ assert.Nil(t, err)
239
+ assert.Equal(t, InfoLevel, l)
240
+
241
+ l, err = ParseLevel("debug")
242
+ assert.Nil(t, err)
243
+ assert.Equal(t, DebugLevel, l)
244
+
245
+ l, err = ParseLevel("invalid")
246
+ assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error())
247
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_darwin.go
new
+12
@@ -0,0 +1,12 @@
1
+// Based on ssh/terminal:
2
+// Copyright 2013 The Go Authors. All rights reserved.
3
+// Use of this source code is governed by a BSD-style
4
+// license that can be found in the LICENSE file.
5
+
6
+package logrus
7
+
8
+import "syscall"
9
+
10
+const ioctlReadTermios = syscall.TIOCGETA
11
+
12
+type Termios syscall.Termios
Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_freebsd.go
new
+20
@@ -0,0 +1,20 @@
1
+/*
2
+ Go 1.2 doesn't include Termios for FreeBSD. This should be added in 1.3 and this could be merged with terminal_darwin.
3
+*/
4
+package logrus
5
+
6
+import (
7
+ "syscall"
8
+)
9
+
10
+const ioctlReadTermios = syscall.TIOCGETA
11
+
12
+type Termios struct {
13
+ Iflag uint32
14
+ Oflag uint32
15
+ Cflag uint32
16
+ Lflag uint32
17
+ Cc [20]uint8
18
+ Ispeed uint32
19
+ Ospeed uint32
20
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_linux.go
new
+12
@@ -0,0 +1,12 @@
1
+// Based on ssh/terminal:
2
+// Copyright 2013 The Go Authors. All rights reserved.
3
+// Use of this source code is governed by a BSD-style
4
+// license that can be found in the LICENSE file.
5
+
6
+package logrus
7
+
8
+import "syscall"
9
+
10
+const ioctlReadTermios = syscall.TCGETS
11
+
12
+type Termios syscall.Termios
Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_notwindows.go
new
+21
@@ -0,0 +1,21 @@
1
+// Based on ssh/terminal:
2
+// Copyright 2011 The Go Authors. All rights reserved.
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
7
+
8
+package logrus
9
+
10
+import (
11
+ "syscall"
12
+ "unsafe"
13
+)
14
+
15
+// IsTerminal returns true if the given file descriptor is a terminal.
16
+func IsTerminal() bool {
17
+ fd := syscall.Stdout
18
+ var termios Termios
19
+ _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
20
+ return err == 0
21
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_windows.go
new
+27
@@ -0,0 +1,27 @@
1
+// Based on ssh/terminal:
2
+// Copyright 2011 The Go Authors. All rights reserved.
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 windows
7
+
8
+package logrus
9
+
10
+import (
11
+ "syscall"
12
+ "unsafe"
13
+)
14
+
15
+var kernel32 = syscall.NewLazyDLL("kernel32.dll")
16
+
17
+var (
18
+ procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
19
+)
20
+
21
+// IsTerminal returns true if the given file descriptor is a terminal.
22
+func IsTerminal() bool {
23
+ fd := syscall.Stdout
24
+ var st uint32
25
+ r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
26
+ return r != 0 && e == 0
27
+}
Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter.go
new
+95
@@ -0,0 +1,95 @@
1
+package logrus
2
+
3
+import (
4
+ "bytes"
5
+ "fmt"
6
+ "sort"
7
+ "strings"
8
+ "time"
9
+)
10
+
11
+const (
12
+ nocolor = 0
13
+ red = 31
14
+ green = 32
15
+ yellow = 33
16
+ blue = 34
17
+)
18
+
19
+var (
20
+ baseTimestamp time.Time
21
+ isTerminal bool
22
+)
23
+
24
+func init() {
25
+ baseTimestamp = time.Now()
26
+ isTerminal = IsTerminal()
27
+}
28
+
29
+func miniTS() int {
30
+ return int(time.Since(baseTimestamp) / time.Second)
31
+}
32
+
33
+type TextFormatter struct {
34
+ // Set to true to bypass checking for a TTY before outputting colors.
35
+ ForceColors bool
36
+ DisableColors bool
37
+}
38
+
39
+func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
40
+
41
+ var keys []string
42
+ for k := range entry.Data {
43
+ keys = append(keys, k)
44
+ }
45
+ sort.Strings(keys)
46
+
47
+ b := &bytes.Buffer{}
48
+
49
+ prefixFieldClashes(entry)
50
+
51
+ isColored := (f.ForceColors || isTerminal) && !f.DisableColors
52
+
53
+ if isColored {
54
+ printColored(b, entry, keys)
55
+ } else {
56
+ f.appendKeyValue(b, "time", entry.Time.Format(time.RFC3339))
57
+ f.appendKeyValue(b, "level", entry.Level.String())
58
+ f.appendKeyValue(b, "msg", entry.Message)
59
+ for _, key := range keys {
60
+ f.appendKeyValue(b, key, entry.Data[key])
61
+ }
62
+ }
63
+
64
+ b.WriteByte('\n')
65
+ return b.Bytes(), nil
66
+}
67
+
68
+func printColored(b *bytes.Buffer, entry *Entry, keys []string) {
69
+ var levelColor int
70
+ switch entry.Level {
71
+ case WarnLevel:
72
+ levelColor = yellow
73
+ case ErrorLevel, FatalLevel, PanicLevel:
74
+ levelColor = red
75
+ default:
76
+ levelColor = blue
77
+ }
78
+
79
+ levelText := strings.ToUpper(entry.Level.String())[0:4]
80
+
81
+ fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
82
+ for _, k := range keys {
83
+ v := entry.Data[k]
84
+ fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%v", levelColor, k, v)
85
+ }
86
+}
87
+
88
+func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key, value interface{}) {
89
+ switch value.(type) {
90
+ case string, error:
91
+ fmt.Fprintf(b, "%v=%q ", key, value)
92
+ default:
93
+ fmt.Fprintf(b, "%v=%v ", key, value)
94
+ }
95
+}
util/elog/log.go
+18
-7
@@ -1,7 +1,10 @@
1
package elog
2
3
import (
4
+ "os"
5
+
6
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
7
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus"
8
logging "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-logging"
9
"github.com/jbenet/go-ipfs/util"
10
)
@@ -27,6 +30,8 @@ type EventLogger interface {
30
Event(ctx context.Context, event string, m ...Loggable)
31
}
32
33
+// StandardLogger provides API compatibility with standard printf loggers
34
+// eg. go-logging
35
type StandardLogger interface {
36
Critical(args ...interface{})
37
Criticalf(format string, args ...interface{})
@@ -46,14 +51,15 @@ type StandardLogger interface {
51
Warningf(format string, args ...interface{})
52
}
53
49
-// Logger retrieves a particular event logger
54
+// Logger retrieves an event logger by name
55
func Logger(system string) EventLogger {
51
- return &eventLogger{util.Logger(system)}
56
+ return &eventLogger{system: system, Logger: util.Logger(system)}
57
}
58
59
// eventLogger implements the EventLogger and wraps a go-logging Logger
60
type eventLogger struct {
61
*logging.Logger
62
+ system string
63
}
64
65
func (el *eventLogger) Event(ctx context.Context, event string, metadata ...Loggable) {
@@ -66,10 +72,15 @@ func (el *eventLogger) Event(ctx context.Context, event string, metadata ...Logg
72
accum = DeepMerge(accum, datum.Loggable())
73
}
74
accum["event"] = event
75
+ accum["system"] = el.system
76
70
- str, err := accum.JsonString()
71
- if err != nil {
72
- return
73
- }
74
- el.Logger.Info(str)
77
+ logrus.WithFields(map[string]interface{}(accum)).Info(event)
78
+}
79
+
80
+func init() {
81
+ // TODO use both text formatter and json formatter
82
+ // logrus.SetFormatter(new(logrus.TextFormatter))
83
+ logrus.SetFormatter(new(logrus.JSONFormatter))
84
+ logrus.SetOutput(os.Stderr)
85
+ logrus.SetLevel(logrus.DebugLevel)
86
}