logrus vendoring
authorconorbroderick <cjayjayb@gmail.com>
Tue, 27 Jun 2017 12:17:47 +0000 (13:17 +0100)
committerBrian Brazil <brian.brazil@robustperception.io>
Tue, 27 Jun 2017 12:40:10 +0000 (13:40 +0100)
24 files changed:
vendor/github.com/Sirupsen/logrus/json_formatter.go [deleted file]
vendor/github.com/Sirupsen/logrus/terminal_solaris.go [deleted file]
vendor/github.com/Sirupsen/logrus/writer.go [deleted file]
vendor/github.com/sirupsen/logrus/CHANGELOG.md [moved from vendor/github.com/Sirupsen/logrus/CHANGELOG.md with 72% similarity]
vendor/github.com/sirupsen/logrus/LICENSE [moved from vendor/github.com/Sirupsen/logrus/LICENSE with 100% similarity]
vendor/github.com/sirupsen/logrus/README.md [moved from vendor/github.com/Sirupsen/logrus/README.md with 71% similarity]
vendor/github.com/sirupsen/logrus/alt_exit.go [new file with mode: 0644]
vendor/github.com/sirupsen/logrus/doc.go [moved from vendor/github.com/Sirupsen/logrus/doc.go with 100% similarity]
vendor/github.com/sirupsen/logrus/entry.go [moved from vendor/github.com/Sirupsen/logrus/entry.go with 89% similarity]
vendor/github.com/sirupsen/logrus/exported.go [moved from vendor/github.com/Sirupsen/logrus/exported.go with 100% similarity]
vendor/github.com/sirupsen/logrus/formatter.go [moved from vendor/github.com/Sirupsen/logrus/formatter.go with 85% similarity]
vendor/github.com/sirupsen/logrus/hooks.go [moved from vendor/github.com/Sirupsen/logrus/hooks.go with 100% similarity]
vendor/github.com/sirupsen/logrus/json_formatter.go [new file with mode: 0644]
vendor/github.com/sirupsen/logrus/logger.go [moved from vendor/github.com/Sirupsen/logrus/logger.go with 57% similarity]
vendor/github.com/sirupsen/logrus/logrus.go [moved from vendor/github.com/Sirupsen/logrus/logrus.go with 100% similarity]
vendor/github.com/sirupsen/logrus/terminal_appengine.go [new file with mode: 0644]
vendor/github.com/sirupsen/logrus/terminal_bsd.go [moved from vendor/github.com/Sirupsen/logrus/terminal_bsd.go with 88% similarity]
vendor/github.com/sirupsen/logrus/terminal_linux.go [moved from vendor/github.com/Sirupsen/logrus/terminal_linux.go with 92% similarity]
vendor/github.com/sirupsen/logrus/terminal_notwindows.go [moved from vendor/github.com/Sirupsen/logrus/terminal_notwindows.go with 57% similarity]
vendor/github.com/sirupsen/logrus/terminal_solaris.go [new file with mode: 0644]
vendor/github.com/sirupsen/logrus/terminal_windows.go [moved from vendor/github.com/Sirupsen/logrus/terminal_windows.go with 58% similarity]
vendor/github.com/sirupsen/logrus/text_formatter.go [moved from vendor/github.com/Sirupsen/logrus/text_formatter.go with 66% similarity]
vendor/github.com/sirupsen/logrus/writer.go [new file with mode: 0644]
vendor/vendor.json

diff --git a/vendor/github.com/Sirupsen/logrus/json_formatter.go b/vendor/github.com/Sirupsen/logrus/json_formatter.go
deleted file mode 100644 (file)
index 2ad6dc5..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-package logrus
-
-import (
-       "encoding/json"
-       "fmt"
-)
-
-type JSONFormatter struct {
-       // TimestampFormat sets the format used for marshaling timestamps.
-       TimestampFormat string
-}
-
-func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
-       data := make(Fields, len(entry.Data)+3)
-       for k, v := range entry.Data {
-               switch v := v.(type) {
-               case error:
-                       // Otherwise errors are ignored by `encoding/json`
-                       // https://github.com/Sirupsen/logrus/issues/137
-                       data[k] = v.Error()
-               default:
-                       data[k] = v
-               }
-       }
-       prefixFieldClashes(data)
-
-       timestampFormat := f.TimestampFormat
-       if timestampFormat == "" {
-               timestampFormat = DefaultTimestampFormat
-       }
-
-       data["time"] = entry.Time.Format(timestampFormat)
-       data["msg"] = entry.Message
-       data["level"] = entry.Level.String()
-
-       serialized, err := json.Marshal(data)
-       if err != nil {
-               return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
-       }
-       return append(serialized, '\n'), nil
-}
diff --git a/vendor/github.com/Sirupsen/logrus/terminal_solaris.go b/vendor/github.com/Sirupsen/logrus/terminal_solaris.go
deleted file mode 100644 (file)
index 3e70bf7..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-// +build solaris
-
-package logrus
-
-import (
-       "os"
-
-       "golang.org/x/sys/unix"
-)
-
-// IsTerminal returns true if the given file descriptor is a terminal.
-func IsTerminal() bool {
-       _, err := unix.IoctlGetTermios(int(os.Stdout.Fd()), unix.TCGETA)
-       return err == nil
-}
diff --git a/vendor/github.com/Sirupsen/logrus/writer.go b/vendor/github.com/Sirupsen/logrus/writer.go
deleted file mode 100644 (file)
index 1e30b1c..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-package logrus
-
-import (
-       "bufio"
-       "io"
-       "runtime"
-)
-
-func (logger *Logger) Writer() *io.PipeWriter {
-       reader, writer := io.Pipe()
-
-       go logger.writerScanner(reader)
-       runtime.SetFinalizer(writer, writerFinalizer)
-
-       return writer
-}
-
-func (logger *Logger) writerScanner(reader *io.PipeReader) {
-       scanner := bufio.NewScanner(reader)
-       for scanner.Scan() {
-               logger.Print(scanner.Text())
-       }
-       if err := scanner.Err(); err != nil {
-               logger.Errorf("Error while reading from Writer: %s", err)
-       }
-       reader.Close()
-}
-
-func writerFinalizer(writer *io.PipeWriter) {
-       writer.Close()
-}
similarity index 72%
rename from vendor/github.com/Sirupsen/logrus/CHANGELOG.md
rename to vendor/github.com/sirupsen/logrus/CHANGELOG.md
index f2c2bc21118ace0e2f1b4773993f1f0e736b2366..747e4d89a2c08a7c7793759c7a9bf8f51d08a8d7 100644 (file)
@@ -1,3 +1,31 @@
+# 0.11.5
+
+* feature: add writer and writerlevel to entry (#372)
+
+# 0.11.4
+
+* bug: fix undefined variable on solaris (#493)
+
+# 0.11.3
+
+* formatter: configure quoting of empty values (#484)
+* formatter: configure quoting character (default is `"`) (#484)
+* bug: fix not importing io correctly in non-linux environments (#481)
+
+# 0.11.2
+
+* bug: fix windows terminal detection (#476)
+
+# 0.11.1
+
+* bug: fix tty detection with custom out (#471)
+
+# 0.11.0
+
+* performance: Use bufferpool to allocate (#370)
+* terminal: terminal detection for app-engine (#343)
+* feature: exit handler (#375)
+
 # 0.10.0
 
 * feature: Add a test hook (#180)
similarity index 71%
rename from vendor/github.com/Sirupsen/logrus/README.md
rename to vendor/github.com/sirupsen/logrus/README.md
index 6e1721a743f485715f3a3284927fd0f7dbbc5e4f..c3228761106a03902c5a978dcc0d0e72835429cb 100644 (file)
@@ -1,5 +1,11 @@
 # 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)&nbsp;[![GoDoc](https://godoc.org/github.com/Sirupsen/logrus?status.svg)](https://godoc.org/github.com/Sirupsen/logrus)
 
+**Seeing weird case-sensitive problems?** See [this
+issue](https://github.com/sirupsen/logrus/issues/451#issuecomment-264332021).
+This change has been reverted. I apologize for causing this. I greatly
+underestimated the impact this would have. Logrus strives for stability and
+backwards compatibility and failed to provide that.
+
 Logrus is a structured logger for Go (golang), completely API compatible with
 the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not
 yet stable (pre 1.0). Logrus itself is completely stable and has been used in
@@ -81,8 +87,9 @@ func init() {
   // Log as JSON instead of the default ASCII formatter.
   log.SetFormatter(&log.JSONFormatter{})
 
-  // Output to stderr instead of stdout, could also be a file.
-  log.SetOutput(os.Stderr)
+  // Output to stdout instead of the default stderr
+  // Can be any io.Writer, see below for File example
+  log.SetOutput(os.Stdout)
 
   // Only log the warning severity or above.
   log.SetLevel(log.WarnLevel)
@@ -123,6 +130,7 @@ application, you can also create an instance of the `logrus` Logger:
 package main
 
 import (
+  "os"
   "github.com/Sirupsen/logrus"
 )
 
@@ -132,7 +140,15 @@ var log = logrus.New()
 func main() {
   // The API for setting attributes is a little different than the package level
   // exported logger. See Godoc.
-  log.Out = os.Stderr
+  log.Out = os.Stdout
+
+  // You could set this to any `io.Writer` such as a file
+  // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666)
+  // if err == nil {
+  //  log.Out = file
+  // } else {
+  //  log.Info("Failed to log to file, using default stderr")
+  // }
 
   log.WithFields(logrus.Fields{
     "animal": "walrus",
@@ -165,6 +181,20 @@ In general, with Logrus using any of the `printf`-family functions should be
 seen as a hint you should add a field, however, you can still use the
 `printf`-family functions with Logrus.
 
+#### Default Fields
+
+Often it's helpful to have fields _always_ attached to log statements in an
+application or parts of one. For example, you may want to always log the
+`request_id` and `user_ip` in the context of a request. Instead of writing
+`log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})` on
+every line, you can create a `logrus.Entry` to pass around instead:
+
+```go
+requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})
+requestLogger.Info("something happened on that request") # will log request_id and user_ip
+requestLogger.Warn("something not great happened")
+```
+
 #### Hooks
 
 You can add hooks for logging levels. For example to send errors to an exception
@@ -200,33 +230,49 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v
 
 | Hook  | Description |
 | ----- | ----------- |
-| [Airbrake](https://github.com/gemnasium/logrus-airbrake-hook) | Send errors to the Airbrake API V3. Uses the official [`gobrake`](https://github.com/airbrake/gobrake) behind the scenes. |
 | [Airbrake "legacy"](https://github.com/gemnasium/logrus-airbrake-legacy-hook) | Send errors to an exception tracking service compatible with the Airbrake API V2. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. |
-| [Papertrail](https://github.com/polds/logrus-papertrail-hook) | Send errors to the [Papertrail](https://papertrailapp.com) hosted logging service via UDP. |
-| [Syslog](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) | Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. |
+| [Airbrake](https://github.com/gemnasium/logrus-airbrake-hook) | Send errors to the Airbrake API V3. Uses the official [`gobrake`](https://github.com/airbrake/gobrake) behind the scenes. |
+| [Amazon Kinesis](https://github.com/evalphobia/logrus_kinesis) | Hook for logging to [Amazon Kinesis](https://aws.amazon.com/kinesis/) |
+| [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) |
 | [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. |
-| [Sentry](https://github.com/evalphobia/logrus_sentry) | Send errors to the Sentry error logging and aggregation service. |
+| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic |
+| [Discordrus](https://github.com/kz/discordrus) | Hook for logging to [Discord](https://discordapp.com/) |
+| [ElasticSearch](https://github.com/sohlich/elogrus) | Hook for logging to ElasticSearch|
+| [Firehose](https://github.com/beaubrewer/firehose) | Hook for logging to [Amazon Firehose](https://aws.amazon.com/kinesis/firehose/)
+| [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd |
+| [Go-Slack](https://github.com/multiplay/go-slack) | Hook for logging to [Slack](https://slack.com) |
+| [Graylog](https://github.com/gemnasium/logrus-graylog-hook) | Hook for logging to [Graylog](http://graylog2.org/) |
 | [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. |
-| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) |
-| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. |
+| [Honeybadger](https://github.com/agonzalezro/logrus_honeybadger) | Hook for sending exceptions to Honeybadger |
+| [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb |
+| [Influxus] (http://github.com/vlad-doru/influxus) | Hook for concurrently logging to [InfluxDB] (http://influxdata.com/) |
 | [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` |
-| [Graylog](https://github.com/gemnasium/logrus-graylog-hook) | Hook for logging to [Graylog](http://graylog2.org/) |
-| [Raygun](https://github.com/squirkle/logrus-raygun-hook) | Hook for logging to [Raygun.io](http://raygun.io/) |
+| [KafkaLogrus](https://github.com/goibibo/KafkaLogrus) | Hook for logging to kafka |
 | [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem |
-| [Honeybadger](https://github.com/agonzalezro/logrus_honeybadger) | Hook for sending exceptions to Honeybadger |
+| [Logentries](https://github.com/jcftang/logentriesrus) | Hook for logging to [Logentries](https://logentries.com/) |
+| [Logentrus](https://github.com/puddingfactory/logentrus) | Hook for logging to [Logentries](https://logentries.com/) |
+| [Logmatic.io](https://github.com/logmatic/logmatic-go) | Hook for logging to [Logmatic.io](http://logmatic.io/) |
+| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) |
+| [Logstash](https://github.com/bshuster-repo/logrus-logstash-hook) | Hook for logging to [Logstash](https://www.elastic.co/products/logstash) |
 | [Mail](https://github.com/zbindenren/logrus_mail) | Hook for sending exceptions via mail |
-| [Rollrus](https://github.com/heroku/rollrus) | Hook for sending errors to rollbar |
-| [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd |
 | [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb |
-| [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb |
+| [NATS-Hook](https://github.com/rybit/nats_logrus_hook) | Hook for logging to [NATS](https://nats.io) |
 | [Octokit](https://github.com/dorajistyle/logrus-octokit-hook) | Hook for logging to github via octokit |
-| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic |
+| [Papertrail](https://github.com/polds/logrus-papertrail-hook) | Send errors to the [Papertrail](https://papertrailapp.com) hosted logging service via UDP. |
+| [PostgreSQL](https://github.com/gemnasium/logrus-postgresql-hook) | Send logs to [PostgreSQL](http://postgresql.org) |
+| [Pushover](https://github.com/toorop/logrus_pushover) | Send error via [Pushover](https://pushover.net) |
+| [Raygun](https://github.com/squirkle/logrus-raygun-hook) | Hook for logging to [Raygun.io](http://raygun.io/) |
 | [Redis-Hook](https://github.com/rogierlommers/logrus-redis-hook) | Hook for logging to a ELK stack (through Redis) |
-| [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) |
-| [KafkaLogrus](https://github.com/goibibo/KafkaLogrus) | Hook for logging to kafka |
+| [Rollrus](https://github.com/heroku/rollrus) | Hook for sending errors to rollbar |
+| [Scribe](https://github.com/sagar8192/logrus-scribe-hook) | Hook for logging to [Scribe](https://github.com/facebookarchive/scribe)|
+| [Sentry](https://github.com/evalphobia/logrus_sentry) | Send errors to the Sentry error logging and aggregation service. |
+| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. |
+| [Stackdriver](https://github.com/knq/sdhook) | Hook for logging to [Google Stackdriver](https://cloud.google.com/logging/) |
+| [Sumorus](https://github.com/doublefree/sumorus) | Hook for logging to [SumoLogic](https://www.sumologic.com/)|
+| [Syslog](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) | Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. |
+| [TraceView](https://github.com/evalphobia/logrus_appneta) | Hook for logging to [AppNeta TraceView](https://www.appneta.com/products/traceview/) |
 | [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) |
-| [ElasticSearch](https://github.com/sohlich/elogrus) | Hook for logging to ElasticSearch|
-
+| [logz.io](https://github.com/ripcurld00d/logrus-logzio-hook) | Hook for logging to [logz.io](https://logz.io), a Log as a Service using Logstash |
 
 #### Level logging
 
@@ -302,16 +348,15 @@ The built-in logging formatters are:
   without colors.
   * *Note:* to force colored output when there is no TTY, set the `ForceColors`
     field to `true`.  To force no colored output even if there is a TTY  set the
-    `DisableColors` field to `true`
+    `DisableColors` field to `true`. For Windows, see
+    [github.com/mattn/go-colorable](https://github.com/mattn/go-colorable).
+  * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#TextFormatter).
 * `logrus.JSONFormatter`. Logs fields as JSON.
-* `logrus/formatters/logstash.LogstashFormatter`. Logs fields as [Logstash](http://logstash.net) Events.
-
-    ```go
-      logrus.SetFormatter(&logstash.LogstashFormatter{Type: "application_name"})
-    ```
+  * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter).
 
 Third party logging formatters:
 
+* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
 * [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
 * [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
 
@@ -356,6 +401,18 @@ srv := http.Server{
 Each line written to that writer will be printed the usual way, using formatters
 and hooks. The level for those entries is `info`.
 
+This means that we can override the standard library logger easily:
+
+```go
+logger := logrus.New()
+logger.Formatter = &logrus.JSONFormatter{}
+
+// Use logrus for standard log output
+// Note that `log` here references stdlib's log
+// Not logrus imported under the name `log`.
+log.SetOutput(logger.Writer())
+```
+
 #### Rotation
 
 Log rotation is not provided with Logrus. Log rotation should be done by an
@@ -367,6 +424,7 @@ entries. It should not be a feature of the application-level logger.
 | Tool | Description |
 | ---- | ----------- |
 |[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.|
+|[Logrus Viper Helper](https://github.com/heirko/go-contrib/tree/master/logrusHelper)|An Helper around Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of [Logrus Mate](https://github.com/gogap/logrus_mate). [sample](https://github.com/heirko/iris-contrib/blob/master/middleware/logrus-logger/example) |
 
 #### Testing
 
@@ -386,3 +444,36 @@ assert.Equal("Hello error", hook.LastEntry().Message)
 hook.Reset()
 assert.Nil(hook.LastEntry())
 ```
+
+#### Fatal handlers
+
+Logrus can register one or more functions that will be called when any `fatal`
+level message is logged. The registered handlers will be executed before
+logrus performs a `os.Exit(1)`. This behavior may be helpful if callers need
+to gracefully shutdown. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted.
+
+```
+...
+handler := func() {
+  // gracefully shutdown something...
+}
+logrus.RegisterExitHandler(handler)
+...
+```
+
+#### Thread safety
+
+By default Logger is protected by mutex for concurrent writes, this mutex is invoked when calling hooks and writing logs.
+If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking.
+
+Situation when locking is not needed includes:
+
+* You have no hooks registered, or hooks calling is already thread-safe.
+
+* Writing to logger.Out is already thread-safe, for example:
+
+  1) logger.Out is protected by locks.
+
+  2) logger.Out is a os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allow multi-thread/multi-process writing)
+
+     (Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/)
diff --git a/vendor/github.com/sirupsen/logrus/alt_exit.go b/vendor/github.com/sirupsen/logrus/alt_exit.go
new file mode 100644 (file)
index 0000000..8af9063
--- /dev/null
@@ -0,0 +1,64 @@
+package logrus
+
+// The following code was sourced and modified from the
+// https://github.com/tebeka/atexit package governed by the following license:
+//
+// Copyright (c) 2012 Miki Tebeka <miki.tebeka@gmail.com>.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+import (
+       "fmt"
+       "os"
+)
+
+var handlers = []func(){}
+
+func runHandler(handler func()) {
+       defer func() {
+               if err := recover(); err != nil {
+                       fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err)
+               }
+       }()
+
+       handler()
+}
+
+func runHandlers() {
+       for _, handler := range handlers {
+               runHandler(handler)
+       }
+}
+
+// Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code)
+func Exit(code int) {
+       runHandlers()
+       os.Exit(code)
+}
+
+// RegisterExitHandler adds a Logrus Exit handler, call logrus.Exit to invoke
+// all handlers. The handlers will also be invoked when any Fatal log entry is
+// made.
+//
+// This method is useful when a caller wishes to use logrus to log a fatal
+// message but also needs to gracefully shutdown. An example usecase could be
+// closing database connections, or sending a alert that the application is
+// closing.
+func RegisterExitHandler(handler func()) {
+       handlers = append(handlers, handler)
+}
similarity index 89%
rename from vendor/github.com/Sirupsen/logrus/entry.go
rename to vendor/github.com/sirupsen/logrus/entry.go
index 89e966e7bfe7049f4bdc1b1f5d10a025e3bd3f0f..4edbe7a2de60f26d561367cc2d1424a3bbd733ee 100644 (file)
@@ -3,11 +3,21 @@ package logrus
 import (
        "bytes"
        "fmt"
-       "io"
        "os"
+       "sync"
        "time"
 )
 
+var bufferPool *sync.Pool
+
+func init() {
+       bufferPool = &sync.Pool{
+               New: func() interface{} {
+                       return new(bytes.Buffer)
+               },
+       }
+}
+
 // Defines the key when adding errors using WithError.
 var ErrorKey = "error"
 
@@ -29,6 +39,9 @@ type Entry struct {
 
        // Message passed to Debug, Info, Warn, Error, Fatal or Panic
        Message string
+
+       // When formatter is called in entry.log(), an Buffer may be set to entry
+       Buffer *bytes.Buffer
 }
 
 func NewEntry(logger *Logger) *Entry {
@@ -39,21 +52,15 @@ func NewEntry(logger *Logger) *Entry {
        }
 }
 
-// Returns a reader for the entry, which is a proxy to the formatter.
-func (entry *Entry) Reader() (*bytes.Buffer, error) {
-       serialized, err := entry.Logger.Formatter.Format(entry)
-       return bytes.NewBuffer(serialized), err
-}
-
 // Returns the string representation from the reader and ultimately the
 // formatter.
 func (entry *Entry) String() (string, error) {
-       reader, err := entry.Reader()
+       serialized, err := entry.Logger.Formatter.Format(entry)
        if err != nil {
                return "", err
        }
-
-       return reader.String(), err
+       str := string(serialized)
+       return str, nil
 }
 
 // Add an error as single field (using the key defined in ErrorKey) to the Entry.
@@ -81,6 +88,7 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
 // This function is not declared with a pointer value because otherwise
 // race conditions will occur when using multiple goroutines
 func (entry Entry) log(level Level, msg string) {
+       var buffer *bytes.Buffer
        entry.Time = time.Now()
        entry.Level = level
        entry.Message = msg
@@ -90,20 +98,23 @@ func (entry Entry) log(level Level, msg string) {
                fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
                entry.Logger.mu.Unlock()
        }
-
-       reader, err := entry.Reader()
+       buffer = bufferPool.Get().(*bytes.Buffer)
+       buffer.Reset()
+       defer bufferPool.Put(buffer)
+       entry.Buffer = buffer
+       serialized, err := entry.Logger.Formatter.Format(&entry)
+       entry.Buffer = nil
        if err != nil {
                entry.Logger.mu.Lock()
                fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
                entry.Logger.mu.Unlock()
-       }
-
-       entry.Logger.mu.Lock()
-       defer entry.Logger.mu.Unlock()
-
-       _, err = io.Copy(entry.Logger.Out, reader)
-       if err != nil {
-               fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
+       } else {
+               entry.Logger.mu.Lock()
+               _, err = entry.Logger.Out.Write(serialized)
+               if err != nil {
+                       fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
+               }
+               entry.Logger.mu.Unlock()
        }
 
        // To avoid Entry#log() returning a value that only would make sense for
@@ -150,7 +161,7 @@ func (entry *Entry) Fatal(args ...interface{}) {
        if entry.Logger.Level >= FatalLevel {
                entry.log(FatalLevel, fmt.Sprint(args...))
        }
-       os.Exit(1)
+       Exit(1)
 }
 
 func (entry *Entry) Panic(args ...interface{}) {
@@ -198,7 +209,7 @@ func (entry *Entry) Fatalf(format string, args ...interface{}) {
        if entry.Logger.Level >= FatalLevel {
                entry.Fatal(fmt.Sprintf(format, args...))
        }
-       os.Exit(1)
+       Exit(1)
 }
 
 func (entry *Entry) Panicf(format string, args ...interface{}) {
@@ -245,7 +256,7 @@ func (entry *Entry) Fatalln(args ...interface{}) {
        if entry.Logger.Level >= FatalLevel {
                entry.Fatal(entry.sprintlnn(args...))
        }
-       os.Exit(1)
+       Exit(1)
 }
 
 func (entry *Entry) Panicln(args ...interface{}) {
similarity index 85%
rename from vendor/github.com/Sirupsen/logrus/formatter.go
rename to vendor/github.com/sirupsen/logrus/formatter.go
index 104d689f187eb50d9a4ab7d4142e94baf5cf6ed8..b5fbe934d12ef027374c50d4b2e995a4ace2d36c 100644 (file)
@@ -31,18 +31,15 @@ type Formatter interface {
 // It's not exported because it's still using Data in an opinionated way. It's to
 // avoid code duplication between the two default formatters.
 func prefixFieldClashes(data Fields) {
-       _, ok := data["time"]
-       if ok {
-               data["fields.time"] = data["time"]
+       if t, ok := data["time"]; ok {
+               data["fields.time"] = t
        }
 
-       _, ok = data["msg"]
-       if ok {
-               data["fields.msg"] = data["msg"]
+       if m, ok := data["msg"]; ok {
+               data["fields.msg"] = m
        }
 
-       _, ok = data["level"]
-       if ok {
-               data["fields.level"] = data["level"]
+       if l, ok := data["level"]; ok {
+               data["fields.level"] = l
        }
 }
diff --git a/vendor/github.com/sirupsen/logrus/json_formatter.go b/vendor/github.com/sirupsen/logrus/json_formatter.go
new file mode 100644 (file)
index 0000000..266554e
--- /dev/null
@@ -0,0 +1,74 @@
+package logrus
+
+import (
+       "encoding/json"
+       "fmt"
+)
+
+type fieldKey string
+type FieldMap map[fieldKey]string
+
+const (
+       FieldKeyMsg   = "msg"
+       FieldKeyLevel = "level"
+       FieldKeyTime  = "time"
+)
+
+func (f FieldMap) resolve(key fieldKey) string {
+       if k, ok := f[key]; ok {
+               return k
+       }
+
+       return string(key)
+}
+
+type JSONFormatter struct {
+       // TimestampFormat sets the format used for marshaling timestamps.
+       TimestampFormat string
+
+       // DisableTimestamp allows disabling automatic timestamps in output
+       DisableTimestamp bool
+
+       // FieldMap allows users to customize the names of keys for various fields.
+       // As an example:
+       // formatter := &JSONFormatter{
+       //      FieldMap: FieldMap{
+       //               FieldKeyTime: "@timestamp",
+       //               FieldKeyLevel: "@level",
+       //               FieldKeyLevel: "@message",
+       //    },
+       // }
+       FieldMap FieldMap
+}
+
+func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
+       data := make(Fields, len(entry.Data)+3)
+       for k, v := range entry.Data {
+               switch v := v.(type) {
+               case error:
+                       // Otherwise errors are ignored by `encoding/json`
+                       // https://github.com/Sirupsen/logrus/issues/137
+                       data[k] = v.Error()
+               default:
+                       data[k] = v
+               }
+       }
+       prefixFieldClashes(data)
+
+       timestampFormat := f.TimestampFormat
+       if timestampFormat == "" {
+               timestampFormat = DefaultTimestampFormat
+       }
+
+       if !f.DisableTimestamp {
+               data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat)
+       }
+       data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message
+       data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String()
+
+       serialized, err := json.Marshal(data)
+       if err != nil {
+               return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
+       }
+       return append(serialized, '\n'), nil
+}
similarity index 57%
rename from vendor/github.com/Sirupsen/logrus/logger.go
rename to vendor/github.com/sirupsen/logrus/logger.go
index 2fdb23176129cdb10ceb70318dbeb8b6fb1d0d87..b769f3d3522c1baa000a02b0c9763b8356f23e75 100644 (file)
@@ -26,8 +26,31 @@ type Logger struct {
        // to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
        // logged. `logrus.Debug` is useful in
        Level Level
-       // Used to sync writing to the log.
-       mu sync.Mutex
+       // Used to sync writing to the log. Locking is enabled by Default
+       mu MutexWrap
+       // Reusable empty entry
+       entryPool sync.Pool
+}
+
+type MutexWrap struct {
+       lock     sync.Mutex
+       disabled bool
+}
+
+func (mw *MutexWrap) Lock() {
+       if !mw.disabled {
+               mw.lock.Lock()
+       }
+}
+
+func (mw *MutexWrap) Unlock() {
+       if !mw.disabled {
+               mw.lock.Unlock()
+       }
+}
+
+func (mw *MutexWrap) Disable() {
+       mw.disabled = true
 }
 
 // Creates a new logger. Configuration should be set by changing `Formatter`,
@@ -51,162 +74,235 @@ func New() *Logger {
        }
 }
 
-// Adds a field to the log entry, note that you it doesn't log until you call
+func (logger *Logger) newEntry() *Entry {
+       entry, ok := logger.entryPool.Get().(*Entry)
+       if ok {
+               return entry
+       }
+       return NewEntry(logger)
+}
+
+func (logger *Logger) releaseEntry(entry *Entry) {
+       logger.entryPool.Put(entry)
+}
+
+// Adds a field to the log entry, note that it doesn't log until you call
 // Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
 // If you want multiple fields, use `WithFields`.
 func (logger *Logger) WithField(key string, value interface{}) *Entry {
-       return NewEntry(logger).WithField(key, value)
+       entry := logger.newEntry()
+       defer logger.releaseEntry(entry)
+       return entry.WithField(key, value)
 }
 
 // Adds a struct of fields to the log entry. All it does is call `WithField` for
 // each `Field`.
 func (logger *Logger) WithFields(fields Fields) *Entry {
-       return NewEntry(logger).WithFields(fields)
+       entry := logger.newEntry()
+       defer logger.releaseEntry(entry)
+       return entry.WithFields(fields)
 }
 
 // Add an error as single field to the log entry.  All it does is call
 // `WithError` for the given `error`.
 func (logger *Logger) WithError(err error) *Entry {
-       return NewEntry(logger).WithError(err)
+       entry := logger.newEntry()
+       defer logger.releaseEntry(entry)
+       return entry.WithError(err)
 }
 
 func (logger *Logger) Debugf(format string, args ...interface{}) {
        if logger.Level >= DebugLevel {
-               NewEntry(logger).Debugf(format, args...)
+               entry := logger.newEntry()
+               entry.Debugf(format, args...)
+               logger.releaseEntry(entry)
        }
 }
 
 func (logger *Logger) Infof(format string, args ...interface{}) {
        if logger.Level >= InfoLevel {
-               NewEntry(logger).Infof(format, args...)
+               entry := logger.newEntry()
+               entry.Infof(format, args...)
+               logger.releaseEntry(entry)
        }
 }
 
 func (logger *Logger) Printf(format string, args ...interface{}) {
-       NewEntry(logger).Printf(format, args...)
+       entry := logger.newEntry()
+       entry.Printf(format, args...)
+       logger.releaseEntry(entry)
 }
 
 func (logger *Logger) Warnf(format string, args ...interface{}) {
        if logger.Level >= WarnLevel {
-               NewEntry(logger).Warnf(format, args...)
+               entry := logger.newEntry()
+               entry.Warnf(format, args...)
+               logger.releaseEntry(entry)
        }
 }
 
 func (logger *Logger) Warningf(format string, args ...interface{}) {
        if logger.Level >= WarnLevel {
-               NewEntry(logger).Warnf(format, args...)
+               entry := logger.newEntry()
+               entry.Warnf(format, args...)
+               logger.releaseEntry(entry)
        }
 }
 
 func (logger *Logger) Errorf(format string, args ...interface{}) {
        if logger.Level >= ErrorLevel {
-               NewEntry(logger).Errorf(format, args...)
+               entry := logger.newEntry()
+               entry.Errorf(format, args...)
+               logger.releaseEntry(entry)
        }
 }
 
 func (logger *Logger) Fatalf(format string, args ...interface{}) {
        if logger.Level >= FatalLevel {
-               NewEntry(logger).Fatalf(format, args...)
+               entry := logger.newEntry()
+               entry.Fatalf(format, args...)
+               logger.releaseEntry(entry)
        }
-       os.Exit(1)
+       Exit(1)
 }
 
 func (logger *Logger) Panicf(format string, args ...interface{}) {
        if logger.Level >= PanicLevel {
-               NewEntry(logger).Panicf(format, args...)
+               entry := logger.newEntry()
+               entry.Panicf(format, args...)
+               logger.releaseEntry(entry)
        }
 }
 
 func (logger *Logger) Debug(args ...interface{}) {
        if logger.Level >= DebugLevel {
-               NewEntry(logger).Debug(args...)
+               entry := logger.newEntry()
+               entry.Debug(args...)
+               logger.releaseEntry(entry)
        }
 }
 
 func (logger *Logger) Info(args ...interface{}) {
        if logger.Level >= InfoLevel {
-               NewEntry(logger).Info(args...)
+               entry := logger.newEntry()
+               entry.Info(args...)
+               logger.releaseEntry(entry)
        }
 }
 
 func (logger *Logger) Print(args ...interface{}) {
-       NewEntry(logger).Info(args...)
+       entry := logger.newEntry()
+       entry.Info(args...)
+       logger.releaseEntry(entry)
 }
 
 func (logger *Logger) Warn(args ...interface{}) {
        if logger.Level >= WarnLevel {
-               NewEntry(logger).Warn(args...)
+               entry := logger.newEntry()
+               entry.Warn(args...)
+               logger.releaseEntry(entry)
        }
 }
 
 func (logger *Logger) Warning(args ...interface{}) {
        if logger.Level >= WarnLevel {
-               NewEntry(logger).Warn(args...)
+               entry := logger.newEntry()
+               entry.Warn(args...)
+               logger.releaseEntry(entry)
        }
 }
 
 func (logger *Logger) Error(args ...interface{}) {
        if logger.Level >= ErrorLevel {
-               NewEntry(logger).Error(args...)
+               entry := logger.newEntry()
+               entry.Error(args...)
+               logger.releaseEntry(entry)
        }
 }
 
 func (logger *Logger) Fatal(args ...interface{}) {
        if logger.Level >= FatalLevel {
-               NewEntry(logger).Fatal(args...)
+               entry := logger.newEntry()
+               entry.Fatal(args...)
+               logger.releaseEntry(entry)
        }
-       os.Exit(1)
+       Exit(1)
 }
 
 func (logger *Logger) Panic(args ...interface{}) {
        if logger.Level >= PanicLevel {
-               NewEntry(logger).Panic(args...)
+               entry := logger.newEntry()
+               entry.Panic(args...)
+               logger.releaseEntry(entry)
        }
 }
 
 func (logger *Logger) Debugln(args ...interface{}) {
        if logger.Level >= DebugLevel {
-               NewEntry(logger).Debugln(args...)
+               entry := logger.newEntry()
+               entry.Debugln(args...)
+               logger.releaseEntry(entry)
        }
 }
 
 func (logger *Logger) Infoln(args ...interface{}) {
        if logger.Level >= InfoLevel {
-               NewEntry(logger).Infoln(args...)
+               entry := logger.newEntry()
+               entry.Infoln(args...)
+               logger.releaseEntry(entry)
        }
 }
 
 func (logger *Logger) Println(args ...interface{}) {
-       NewEntry(logger).Println(args...)
+       entry := logger.newEntry()
+       entry.Println(args...)
+       logger.releaseEntry(entry)
 }
 
 func (logger *Logger) Warnln(args ...interface{}) {
        if logger.Level >= WarnLevel {
-               NewEntry(logger).Warnln(args...)
+               entry := logger.newEntry()
+               entry.Warnln(args...)
+               logger.releaseEntry(entry)
        }
 }
 
 func (logger *Logger) Warningln(args ...interface{}) {
        if logger.Level >= WarnLevel {
-               NewEntry(logger).Warnln(args...)
+               entry := logger.newEntry()
+               entry.Warnln(args...)
+               logger.releaseEntry(entry)
        }
 }
 
 func (logger *Logger) Errorln(args ...interface{}) {
        if logger.Level >= ErrorLevel {
-               NewEntry(logger).Errorln(args...)
+               entry := logger.newEntry()
+               entry.Errorln(args...)
+               logger.releaseEntry(entry)
        }
 }
 
 func (logger *Logger) Fatalln(args ...interface{}) {
        if logger.Level >= FatalLevel {
-               NewEntry(logger).Fatalln(args...)
+               entry := logger.newEntry()
+               entry.Fatalln(args...)
+               logger.releaseEntry(entry)
        }
-       os.Exit(1)
+       Exit(1)
 }
 
 func (logger *Logger) Panicln(args ...interface{}) {
        if logger.Level >= PanicLevel {
-               NewEntry(logger).Panicln(args...)
+               entry := logger.newEntry()
+               entry.Panicln(args...)
+               logger.releaseEntry(entry)
        }
 }
+
+//When file is opened with appending mode, it's safe to
+//write concurrently to a file (within 4k message on Linux).
+//In these cases user can choose to disable the lock.
+func (logger *Logger) SetNoLock() {
+       logger.mu.Disable()
+}
diff --git a/vendor/github.com/sirupsen/logrus/terminal_appengine.go b/vendor/github.com/sirupsen/logrus/terminal_appengine.go
new file mode 100644 (file)
index 0000000..e011a86
--- /dev/null
@@ -0,0 +1,10 @@
+// +build appengine
+
+package logrus
+
+import "io"
+
+// IsTerminal returns true if stderr's file descriptor is a terminal.
+func IsTerminal(f io.Writer) bool {
+       return true
+}
similarity index 88%
rename from vendor/github.com/Sirupsen/logrus/terminal_bsd.go
rename to vendor/github.com/sirupsen/logrus/terminal_bsd.go
index 71f8d67a55d77645325695b8be8c3e5d1ca26928..5f6be4d3c04449a0b35a7d9302d30a365965d87f 100644 (file)
@@ -1,4 +1,5 @@
 // +build darwin freebsd openbsd netbsd dragonfly
+// +build !appengine
 
 package logrus
 
similarity index 92%
rename from vendor/github.com/Sirupsen/logrus/terminal_linux.go
rename to vendor/github.com/sirupsen/logrus/terminal_linux.go
index a2c0b40db612b7c1be1646a5480eb1f6f646060e..308160ca804ac884d57e1879c9a4599efe4bdd28 100644 (file)
@@ -3,6 +3,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build !appengine
+
 package logrus
 
 import "syscall"
similarity index 57%
rename from vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
rename to vendor/github.com/sirupsen/logrus/terminal_notwindows.go
index b343b3a3755c8628306e4a5a8cd1ab4dc863bcd8..190297abf3803502c8c3364df454798855d527cd 100644 (file)
@@ -4,18 +4,25 @@
 // license that can be found in the LICENSE file.
 
 // +build linux darwin freebsd openbsd netbsd dragonfly
+// +build !appengine
 
 package logrus
 
 import (
+       "io"
+       "os"
        "syscall"
        "unsafe"
 )
 
 // IsTerminal returns true if stderr's file descriptor is a terminal.
-func IsTerminal() bool {
-       fd := syscall.Stderr
+func IsTerminal(f io.Writer) bool {
        var termios Termios
-       _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
-       return err == 0
+       switch v := f.(type) {
+       case *os.File:
+               _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(v.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
+               return err == 0
+       default:
+               return false
+       }
 }
diff --git a/vendor/github.com/sirupsen/logrus/terminal_solaris.go b/vendor/github.com/sirupsen/logrus/terminal_solaris.go
new file mode 100644 (file)
index 0000000..3c86b1a
--- /dev/null
@@ -0,0 +1,21 @@
+// +build solaris,!appengine
+
+package logrus
+
+import (
+       "io"
+       "os"
+
+       "golang.org/x/sys/unix"
+)
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+func IsTerminal(f io.Writer) bool {
+       switch v := f.(type) {
+       case *os.File:
+               _, err := unix.IoctlGetTermios(int(v.Fd()), unix.TCGETA)
+               return err == nil
+       default:
+               return false
+       }
+}
similarity index 58%
rename from vendor/github.com/Sirupsen/logrus/terminal_windows.go
rename to vendor/github.com/sirupsen/logrus/terminal_windows.go
index 0146845d16c0fe7c973f57b903e816d521d19d73..05d2f91f16b7b0af3ec6febf5e1d262ffdcc56ed 100644 (file)
@@ -3,11 +3,13 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build windows
+// +build windows,!appengine
 
 package logrus
 
 import (
+       "io"
+       "os"
        "syscall"
        "unsafe"
 )
@@ -19,9 +21,13 @@ var (
 )
 
 // IsTerminal returns true if stderr's file descriptor is a terminal.
-func IsTerminal() bool {
-       fd := syscall.Stderr
-       var st uint32
-       r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
-       return r != 0 && e == 0
+func IsTerminal(f io.Writer) bool {
+       switch v := f.(type) {
+       case *os.File:
+               var st uint32
+               r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(v.Fd()), uintptr(unsafe.Pointer(&st)), 0)
+               return r != 0 && e == 0
+       default:
+               return false
+       }
 }
similarity index 66%
rename from vendor/github.com/Sirupsen/logrus/text_formatter.go
rename to vendor/github.com/sirupsen/logrus/text_formatter.go
index 06ef2023374a297742e88ebc89c3009a53421f55..ba8885406132128e429550445f9643aa4aefe5bd 100644 (file)
@@ -3,9 +3,9 @@ package logrus
 import (
        "bytes"
        "fmt"
-       "runtime"
        "sort"
        "strings"
+       "sync"
        "time"
 )
 
@@ -20,16 +20,10 @@ const (
 
 var (
        baseTimestamp time.Time
-       isTerminal    bool
 )
 
 func init() {
        baseTimestamp = time.Now()
-       isTerminal = IsTerminal()
-}
-
-func miniTS() int {
-       return int(time.Since(baseTimestamp) / time.Second)
 }
 
 type TextFormatter struct {
@@ -54,10 +48,32 @@ type TextFormatter struct {
        // that log extremely frequently and don't use the JSON formatter this may not
        // be desired.
        DisableSorting bool
+
+       // QuoteEmptyFields will wrap empty fields in quotes if true
+       QuoteEmptyFields bool
+
+       // QuoteCharacter can be set to the override the default quoting character "
+       // with something else. For example: ', or `.
+       QuoteCharacter string
+
+       // Whether the logger's out is to a terminal
+       isTerminal bool
+
+       sync.Once
+}
+
+func (f *TextFormatter) init(entry *Entry) {
+       if len(f.QuoteCharacter) == 0 {
+               f.QuoteCharacter = "\""
+       }
+       if entry.Logger != nil {
+               f.isTerminal = IsTerminal(entry.Logger.Out)
+       }
 }
 
 func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
-       var keys []string = make([]string, 0, len(entry.Data))
+       var b *bytes.Buffer
+       keys := make([]string, 0, len(entry.Data))
        for k := range entry.Data {
                keys = append(keys, k)
        }
@@ -65,13 +81,17 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
        if !f.DisableSorting {
                sort.Strings(keys)
        }
-
-       b := &bytes.Buffer{}
+       if entry.Buffer != nil {
+               b = entry.Buffer
+       } else {
+               b = &bytes.Buffer{}
+       }
 
        prefixFieldClashes(entry.Data)
 
-       isColorTerminal := isTerminal && (runtime.GOOS != "windows")
-       isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors
+       f.Do(func() { f.init(entry) })
+
+       isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors
 
        timestampFormat := f.TimestampFormat
        if timestampFormat == "" {
@@ -111,51 +131,59 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
 
        levelText := strings.ToUpper(entry.Level.String())[0:4]
 
-       if !f.FullTimestamp {
-               fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
+       if f.DisableTimestamp {
+               fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m %-44s ", levelColor, levelText, entry.Message)
+       } else if !f.FullTimestamp {
+               fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), entry.Message)
        } else {
                fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message)
        }
        for _, k := range keys {
                v := entry.Data[k]
-               fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%+v", levelColor, k, v)
+               fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
+               f.appendValue(b, v)
        }
 }
 
-func needsQuoting(text string) bool {
+func (f *TextFormatter) needsQuoting(text string) bool {
+       if f.QuoteEmptyFields && len(text) == 0 {
+               return true
+       }
        for _, ch := range text {
                if !((ch >= 'a' && ch <= 'z') ||
                        (ch >= 'A' && ch <= 'Z') ||
                        (ch >= '0' && ch <= '9') ||
                        ch == '-' || ch == '.') {
-                       return false
+                       return true
                }
        }
-       return true
+       return false
 }
 
 func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
 
        b.WriteString(key)
        b.WriteByte('=')
+       f.appendValue(b, value)
+       b.WriteByte(' ')
+}
 
+func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
        switch value := value.(type) {
        case string:
-               if needsQuoting(value) {
+               if !f.needsQuoting(value) {
                        b.WriteString(value)
                } else {
-                       fmt.Fprintf(b, "%q", value)
+                       fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, value, f.QuoteCharacter)
                }
        case error:
                errmsg := value.Error()
-               if needsQuoting(errmsg) {
+               if !f.needsQuoting(errmsg) {
                        b.WriteString(errmsg)
                } else {
-                       fmt.Fprintf(b, "%q", value)
+                       fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, errmsg, f.QuoteCharacter)
                }
        default:
                fmt.Fprint(b, value)
        }
-
-       b.WriteByte(' ')
 }
diff --git a/vendor/github.com/sirupsen/logrus/writer.go b/vendor/github.com/sirupsen/logrus/writer.go
new file mode 100644 (file)
index 0000000..7bdebed
--- /dev/null
@@ -0,0 +1,62 @@
+package logrus
+
+import (
+       "bufio"
+       "io"
+       "runtime"
+)
+
+func (logger *Logger) Writer() *io.PipeWriter {
+       return logger.WriterLevel(InfoLevel)
+}
+
+func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
+       return NewEntry(logger).WriterLevel(level)
+}
+
+func (entry *Entry) Writer() *io.PipeWriter {
+       return entry.WriterLevel(InfoLevel)
+}
+
+func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
+       reader, writer := io.Pipe()
+
+       var printFunc func(args ...interface{})
+
+       switch level {
+       case DebugLevel:
+               printFunc = entry.Debug
+       case InfoLevel:
+               printFunc = entry.Info
+       case WarnLevel:
+               printFunc = entry.Warn
+       case ErrorLevel:
+               printFunc = entry.Error
+       case FatalLevel:
+               printFunc = entry.Fatal
+       case PanicLevel:
+               printFunc = entry.Panic
+       default:
+               printFunc = entry.Print
+       }
+
+       go entry.writerScanner(reader, printFunc)
+       runtime.SetFinalizer(writer, writerFinalizer)
+
+       return writer
+}
+
+func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
+       scanner := bufio.NewScanner(reader)
+       for scanner.Scan() {
+               printFunc(scanner.Text())
+       }
+       if err := scanner.Err(); err != nil {
+               entry.Errorf("Error while reading from Writer: %s", err)
+       }
+       reader.Close()
+}
+
+func writerFinalizer(writer *io.PipeWriter) {
+       writer.Close()
+}
index b88d4de95fe08dc73d667f158271ca96adff8fb4..cc00c27f6008df17d48272787478b69e77efd9df 100644 (file)
                        "revision": "406e5b7bfd8201a36e2bb5f7bdae0b03380c2ce8",
                        "revisionTime": "2015-10-29T15:50:50-04:00"
                },
+               {
+                       "checksumSHA1": "fabVNshbE6WKqfO9/ymL8JGFq+k=",
+                       "path": "github.com/sirupsen/logrus",
+                       "revision": "10f801ebc38b33738c9d17d50860f484a0988ff5",
+                       "revisionTime": "2017-03-17T14:32:14Z"
+               },
                {
                        "checksumSHA1": "11zFB6NFMfY7R7pwOhDCurgIjJY=",
                        "path": "golang.org/x/net/icmp",