@cryptotaxi247 / kubo / commits / 9a38b2841

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:"/>&nbsp;[![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](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 +![Colored](http://i.imgur.com/PY7qMwd.png)
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 }