From 46ec8c70faf38987d1cfa3488af8dc35311310c1 Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Tue, 3 Apr 2018 11:48:06 +0800 Subject: [PATCH] Update packages github.com/Sirupsen/logrus, github.com/gorilla/context and github.com/garyburd/redigo to new version --- src/Gopkg.lock | 17 +- src/Gopkg.toml | 12 + .../github.com/Sirupsen/logrus/.travis.yml | 17 +- .../github.com/Sirupsen/logrus/CHANGELOG.md | 57 ++++ .../github.com/Sirupsen/logrus/README.md | 211 +++++++++--- .../github.com/Sirupsen/logrus/alt_exit.go | 2 +- .../Sirupsen/logrus/alt_exit_test.go | 25 +- .../github.com/Sirupsen/logrus/appveyor.yml | 14 + src/vendor/github.com/Sirupsen/logrus/doc.go | 4 +- .../github.com/Sirupsen/logrus/entry.go | 114 ++++--- .../github.com/Sirupsen/logrus/entry_test.go | 38 +++ .../Sirupsen/logrus/example_basic_test.go | 69 ++++ .../Sirupsen/logrus/example_hook_test.go | 35 ++ .../Sirupsen/logrus/examples/basic/basic.go | 50 --- .../Sirupsen/logrus/examples/hook/hook.go | 30 -- .../github.com/Sirupsen/logrus/exported.go | 4 +- .../github.com/Sirupsen/logrus/formatter.go | 2 +- .../Sirupsen/logrus/formatter_bench_test.go | 3 + .../github.com/Sirupsen/logrus/hook_test.go | 22 ++ .../Sirupsen/logrus/hooks/syslog/README.md | 14 +- .../Sirupsen/logrus/hooks/syslog/syslog.go | 5 +- .../logrus/hooks/syslog/syslog_test.go | 5 +- .../Sirupsen/logrus/hooks/test/test.go | 48 ++- .../Sirupsen/logrus/hooks/test/test_test.go | 28 +- .../Sirupsen/logrus/json_formatter.go | 48 ++- .../Sirupsen/logrus/json_formatter_test.go | 81 ++++- .../github.com/Sirupsen/logrus/logger.go | 213 +++++++++--- .../Sirupsen/logrus/logger_bench_test.go | 61 ++++ .../github.com/Sirupsen/logrus/logrus.go | 2 +- .../github.com/Sirupsen/logrus/logrus_test.go | 25 ++ .../Sirupsen/logrus/terminal_bsd.go | 7 +- .../logrus/terminal_check_appengine.go | 11 + .../logrus/terminal_check_notappengine.go | 19 ++ .../Sirupsen/logrus/terminal_linux.go | 8 +- .../Sirupsen/logrus/terminal_notwindows.go | 21 -- .../Sirupsen/logrus/terminal_solaris.go | 15 - .../Sirupsen/logrus/terminal_windows.go | 27 -- .../Sirupsen/logrus/text_formatter.go | 91 +++-- .../Sirupsen/logrus/text_formatter_test.go | 92 ++++- .../github.com/Sirupsen/logrus/writer.go | 29 +- .../github.com/garyburd/redigo/.travis.yml | 11 +- .../github.com/garyburd/redigo/redis/conn.go | 62 +++- .../garyburd/redigo/redis/conn_test.go | 56 ++- .../redigo/redis/{pre_go17.go => go16.go} | 6 +- .../github.com/garyburd/redigo/redis/go17.go | 8 +- .../github.com/garyburd/redigo/redis/go18.go | 9 + .../garyburd/redigo/redis/list_test.go | 85 +++++ .../github.com/garyburd/redigo/redis/log.go | 17 + .../github.com/garyburd/redigo/redis/pool.go | 319 +++++++++++------- .../garyburd/redigo/redis/pool17.go | 35 ++ .../garyburd/redigo/redis/pool17_test.go | 58 ++++ .../garyburd/redigo/redis/pool_test.go | 5 +- .../garyburd/redigo/redis/pubsub.go | 17 +- .../redigo/redis/pubsub_example_test.go | 165 +++++++++ .../garyburd/redigo/redis/pubsub_test.go | 88 +---- .../github.com/garyburd/redigo/redis/redis.go | 56 +++ .../garyburd/redigo/redis/redis_test.go | 71 ++++ .../garyburd/redigo/redis/reply_test.go | 5 + .../garyburd/redigo/redis/scan_test.go | 6 +- .../garyburd/redigo/redis/test_test.go | 17 +- .../github.com/gorilla/context/.travis.yml | 7 +- .../gorilla/context/context_test.go | 2 +- 62 files changed, 2024 insertions(+), 657 deletions(-) create mode 100644 src/vendor/github.com/Sirupsen/logrus/appveyor.yml create mode 100644 src/vendor/github.com/Sirupsen/logrus/example_basic_test.go create mode 100644 src/vendor/github.com/Sirupsen/logrus/example_hook_test.go delete mode 100644 src/vendor/github.com/Sirupsen/logrus/examples/basic/basic.go delete mode 100644 src/vendor/github.com/Sirupsen/logrus/examples/hook/hook.go create mode 100644 src/vendor/github.com/Sirupsen/logrus/logger_bench_test.go create mode 100644 src/vendor/github.com/Sirupsen/logrus/terminal_check_appengine.go create mode 100644 src/vendor/github.com/Sirupsen/logrus/terminal_check_notappengine.go delete mode 100644 src/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go delete mode 100644 src/vendor/github.com/Sirupsen/logrus/terminal_solaris.go delete mode 100644 src/vendor/github.com/Sirupsen/logrus/terminal_windows.go rename src/vendor/github.com/garyburd/redigo/redis/{pre_go17.go => go16.go} (78%) create mode 100644 src/vendor/github.com/garyburd/redigo/redis/go18.go create mode 100644 src/vendor/github.com/garyburd/redigo/redis/list_test.go create mode 100644 src/vendor/github.com/garyburd/redigo/redis/pool17.go create mode 100644 src/vendor/github.com/garyburd/redigo/redis/pool17_test.go create mode 100644 src/vendor/github.com/garyburd/redigo/redis/pubsub_example_test.go create mode 100644 src/vendor/github.com/garyburd/redigo/redis/redis_test.go diff --git a/src/Gopkg.lock b/src/Gopkg.lock index e9d84757c..dae090893 100644 --- a/src/Gopkg.lock +++ b/src/Gopkg.lock @@ -4,7 +4,8 @@ [[projects]] name = "github.com/Sirupsen/logrus" packages = ["."] - revision = "a283a10442df8dc09befd873fab202bf8a253d6a" + revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc" + version = "v1.0.5" [[projects]] name = "github.com/Unknwon/goconfig" @@ -118,8 +119,8 @@ "internal", "redis" ] - revision = "47dc60e71eed504e3ef8e77ee3c6fe720f3be57f" - version = "v1.3.0" + revision = "a69d19351219b6dd56f274f96d85a7014a2ec34e" + version = "v1.6.0" [[projects]] name = "github.com/go-sql-driver/mysql" @@ -147,7 +148,8 @@ [[projects]] name = "github.com/gorilla/context" packages = ["."] - revision = "aed02d124ae4a0e94fea4541c8effd05bf0c8296" + revision = "1ea25387ff6f684839d82767c1733ff4d4d15d0a" + version = "v1.1" [[projects]] name = "github.com/gorilla/handlers" @@ -210,7 +212,10 @@ [[projects]] name = "golang.org/x/crypto" - packages = ["pbkdf2"] + packages = [ + "pbkdf2", + "ssh/terminal" + ] revision = "5f961cd492ac9d43fc33a8ef646bae79d113fd97" [[projects]] @@ -270,6 +275,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "ce26a28bd76c7bcfc4ed4dcd95a1522968cbe752a45cf7759b9e13734a31afc2" + inputs-digest = "e690fed3ef15e012cf97d5a1eb70fcb90ea051b54d7472e4c804291d08a58c80" solver-name = "gps-cdcl" solver-version = 1 diff --git a/src/Gopkg.toml b/src/Gopkg.toml index 22cc81e07..3d1d9c3f3 100644 --- a/src/Gopkg.toml +++ b/src/Gopkg.toml @@ -66,4 +66,16 @@ ignored = ["github.com/vmware/harbor/tests*"] [[constraint]] name = "github.com/gorilla/mux" + version = "=1.6.0" + +[[override]] + name = "github.com/Sirupsen/logrus" + version = "=1.0.5" + +[[override]] + name = "github.com/gorilla/context" + version = "=1.1" + +[[override]] + name = "github.com/garyburd/redigo" version = "=1.6.0" \ No newline at end of file diff --git a/src/vendor/github.com/Sirupsen/logrus/.travis.yml b/src/vendor/github.com/Sirupsen/logrus/.travis.yml index dee4eb2cc..a23296a53 100644 --- a/src/vendor/github.com/Sirupsen/logrus/.travis.yml +++ b/src/vendor/github.com/Sirupsen/logrus/.travis.yml @@ -1,10 +1,15 @@ language: go go: - - 1.3 - - 1.4 - - 1.5 - - 1.6 + - 1.6.x + - 1.7.x + - 1.8.x - tip +env: + - GOMAXPROCS=4 GORACE=halt_on_error=1 install: - - go get -t ./... -script: GOMAXPROCS=4 GORACE="halt_on_error=1" go test -race -v ./... + - go get github.com/stretchr/testify/assert + - go get gopkg.in/gemnasium/logrus-airbrake-hook.v2 + - go get golang.org/x/sys/unix + - go get golang.org/x/sys/windows +script: + - go test -race -v ./... diff --git a/src/vendor/github.com/Sirupsen/logrus/CHANGELOG.md b/src/vendor/github.com/Sirupsen/logrus/CHANGELOG.md index f2c2bc211..1bd1deb29 100644 --- a/src/vendor/github.com/Sirupsen/logrus/CHANGELOG.md +++ b/src/vendor/github.com/Sirupsen/logrus/CHANGELOG.md @@ -1,3 +1,60 @@ +# 1.0.5 + +* Fix hooks race (#707) +* Fix panic deadlock (#695) + +# 1.0.4 + +* Fix race when adding hooks (#612) +* Fix terminal check in AppEngine (#635) + +# 1.0.3 + +* Replace example files with testable examples + +# 1.0.2 + +* bug: quote non-string values in text formatter (#583) +* Make (*Logger) SetLevel a public method + +# 1.0.1 + +* bug: fix escaping in text formatter (#575) + +# 1.0.0 + +* Officially changed name to lower-case +* bug: colors on Windows 10 (#541) +* bug: fix race in accessing level (#512) + +# 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) diff --git a/src/vendor/github.com/Sirupsen/logrus/README.md b/src/vendor/github.com/Sirupsen/logrus/README.md index f8302c373..f77819b16 100644 --- a/src/vendor/github.com/Sirupsen/logrus/README.md +++ b/src/vendor/github.com/Sirupsen/logrus/README.md @@ -1,11 +1,24 @@ -# Logrus :walrus: [![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus) [![GoDoc](https://godoc.org/github.com/Sirupsen/logrus?status.svg)](https://godoc.org/github.com/Sirupsen/logrus) +# Logrus :walrus: [![Build Status](https://travis-ci.org/sirupsen/logrus.svg?branch=master)](https://travis-ci.org/sirupsen/logrus) [![GoDoc](https://godoc.org/github.com/sirupsen/logrus?status.svg)](https://godoc.org/github.com/sirupsen/logrus) 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 -many large deployments. The core API is unlikely to change much but please -version control your Logrus to make sure you aren't fetching latest `master` on -every build.** +the standard library logger. + +**Seeing weird case-sensitive problems?** It's in the past been possible to +import Logrus as both upper- and lower-case. Due to the Go package environment, +this caused issues in the community and we needed a standard. Some environments +experienced problems with the upper-case variant, so the lower-case was decided. +Everything using `logrus` will need to use the lower-case: +`github.com/sirupsen/logrus`. Any package that isn't, should be changed. + +To fix Glide, see [these +comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437). +For an in-depth explanation of the casing issue, see [this +comment](https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276). + +**Are you interested in assisting in maintaining Logrus?** Currently I have a +lot of obligations, and I am unable to provide Logrus with the maintainership it +needs. If you'd like to help, please reach out to me at `simon at author's +username dot com`. Nicely color-coded in development (when a TTY is attached, otherwise just plain text): @@ -46,6 +59,12 @@ time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x20822 exit status 1 ``` +#### Case-sensitivity + +The organization's name was changed to lower-case--and this will not be changed +back. If you are getting import conflicts due to case sensitivity, please use +the lower-case import: `github.com/sirupsen/logrus`. + #### Example The simplest way to use Logrus is simply the package-level exported logger: @@ -54,7 +73,7 @@ The simplest way to use Logrus is simply the package-level exported logger: package main import ( - log "github.com/Sirupsen/logrus" + log "github.com/sirupsen/logrus" ) func main() { @@ -65,7 +84,7 @@ func main() { ``` Note that it's completely api-compatible with the stdlib logger, so you can -replace your `log` imports everywhere with `log "github.com/Sirupsen/logrus"` +replace your `log` imports everywhere with `log "github.com/sirupsen/logrus"` and you'll now have the flexibility of Logrus. You can customize it all you want: @@ -74,15 +93,16 @@ package main import ( "os" - log "github.com/Sirupsen/logrus" + log "github.com/sirupsen/logrus" ) 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,7 +143,8 @@ application, you can also create an instance of the `logrus` Logger: package main import ( - "github.com/Sirupsen/logrus" + "os" + "github.com/sirupsen/logrus" ) // Create a new instance of the logger. You can have any number of instances. @@ -132,7 +153,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", @@ -143,7 +172,7 @@ func main() { #### Fields -Logrus encourages careful, structured logging though logging fields instead of +Logrus encourages careful, structured logging through logging fields instead of long, unparseable error messages. For example, instead of: `log.Fatalf("Failed to send event %s to topic %s with key %d")`, you should log the much more discoverable: @@ -165,6 +194,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 @@ -176,9 +219,9 @@ Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in ```go import ( - log "github.com/Sirupsen/logrus" - "gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "aibrake" - logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog" + log "github.com/sirupsen/logrus" + "gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "airbrake" + logrus_syslog "github.com/sirupsen/logrus/hooks/syslog" "log/syslog" ) @@ -200,35 +243,58 @@ 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. | -| [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. | -| [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. | -| [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/) | -| [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 | -| [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 | -| [Influxus] (http://github.com/vlad-doru/influxus) | Hook for concurrently logging to [InfluxDB] (http://influxdata.com/) | -| [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb | -| [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 | -| [Redis-Hook](https://github.com/rogierlommers/logrus-redis-hook) | Hook for logging to a ELK stack (through Redis) | +| [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) | -| [KafkaLogrus](https://github.com/goibibo/KafkaLogrus) | Hook for logging to kafka | -| [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) | +| [Application Insights](https://github.com/jjcollinge/logrus-appinsights) | Hook for logging to [Application Insights](https://azure.microsoft.com/en-us/services/application-insights/) +| [AzureTableHook](https://github.com/kpfaulkner/azuretablehook/) | Hook for logging to Azure Table Storage| +| [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking 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| -| [Sumorus](https://github.com/doublefree/sumorus) | Hook for logging to [SumoLogic](https://www.sumologic.com/)| +| [Firehose](https://github.com/beaubrewer/logrus_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. | +| [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` | +| [KafkaLogrus](https://github.com/tracer0tong/kafkalogrus) | Hook for logging to Kafka | +| [Kafka REST Proxy](https://github.com/Nordstrom/logrus-kafka-rest-proxy) | Hook for logging to [Kafka REST Proxy](https://docs.confluent.io/current/kafka-rest/docs) | +| [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem | +| [Logbeat](https://github.com/macandmia/logbeat) | Hook for logging to [Opbeat](https://opbeat.com/) | +| [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 | +| [Mattermost](https://github.com/shuLhan/mattermost-integration/tree/master/hooks/logrus) | Hook for logging to [Mattermost](https://mattermost.com/) | +| [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb | +| [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 | +| [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) | +| [Promrus](https://github.com/weaveworks/promrus) | Expose number of log messages as [Prometheus](https://prometheus.io/) metrics | +| [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) | +| [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. | +| [Syslog TLS](https://github.com/shinji62/logrus-syslog-ng) | Send errors to remote syslog server with TLS support. | +| [Telegram](https://github.com/rossmcdonald/telegram_hook) | Hook for logging errors to [Telegram](https://telegram.org/) | +| [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/) | +| [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 | +| [SQS-Hook](https://github.com/tsarpaul/logrus_sqs) | Hook for logging to [Amazon Simple Queue Service (SQS)](https://aws.amazon.com/sqs/) | #### Level logging @@ -277,7 +343,7 @@ could do: ```go import ( - log "github.com/Sirupsen/logrus" + log "github.com/sirupsen/logrus" ) init() { @@ -304,11 +370,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. + * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter). Third party logging formatters: +* [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be parsed by Kubernetes and Google Container Engine. * [`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̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦. @@ -354,6 +424,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 @@ -365,6 +447,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 @@ -374,15 +457,24 @@ Logrus has a built in facility for asserting the presence of log messages. This * a test logger (`test.NewNullLogger`) that just records log messages (and does not output any): ```go -logger, hook := NewNullLogger() -logger.Error("Hello error") +import( + "github.com/sirupsen/logrus" + "github.com/sirupsen/logrus/hooks/test" + "github.com/stretchr/testify/assert" + "testing" +) -assert.Equal(1, len(hook.Entries)) -assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level) -assert.Equal("Hello error", hook.LastEntry().Message) +func TestSomething(t*testing.T){ + logger, hook := test.NewNullLogger() + logger.Error("Helloerror") -hook.Reset() -assert.Nil(hook.LastEntry()) + assert.Equal(t, 1, len(hook.Entries)) + assert.Equal(t, logrus.ErrorLevel, hook.LastEntry().Level) + assert.Equal(t, "Helloerror", hook.LastEntry().Message) + + hook.Reset() + assert.Nil(t, hook.LastEntry()) +} ``` #### Fatal handlers @@ -400,3 +492,20 @@ handler := func() { 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/src/vendor/github.com/Sirupsen/logrus/alt_exit.go b/src/vendor/github.com/Sirupsen/logrus/alt_exit.go index b4c9e8475..8af90637a 100644 --- a/src/vendor/github.com/Sirupsen/logrus/alt_exit.go +++ b/src/vendor/github.com/Sirupsen/logrus/alt_exit.go @@ -1,7 +1,7 @@ package logrus // The following code was sourced and modified from the -// https://bitbucket.org/tebeka/atexit package governed by the following license: +// https://github.com/tebeka/atexit package governed by the following license: // // Copyright (c) 2012 Miki Tebeka . // diff --git a/src/vendor/github.com/Sirupsen/logrus/alt_exit_test.go b/src/vendor/github.com/Sirupsen/logrus/alt_exit_test.go index 022b77830..a08b1a898 100644 --- a/src/vendor/github.com/Sirupsen/logrus/alt_exit_test.go +++ b/src/vendor/github.com/Sirupsen/logrus/alt_exit_test.go @@ -2,7 +2,10 @@ package logrus import ( "io/ioutil" + "log" + "os" "os/exec" + "path/filepath" "testing" "time" ) @@ -11,30 +14,36 @@ func TestRegister(t *testing.T) { current := len(handlers) RegisterExitHandler(func() {}) if len(handlers) != current+1 { - t.Fatalf("can't add handler") + t.Fatalf("expected %d handlers, got %d", current+1, len(handlers)) } } func TestHandler(t *testing.T) { - gofile := "/tmp/testprog.go" + tempDir, err := ioutil.TempDir("", "test_handler") + if err != nil { + log.Fatalf("can't create temp dir. %q", err) + } + defer os.RemoveAll(tempDir) + + gofile := filepath.Join(tempDir, "gofile.go") if err := ioutil.WriteFile(gofile, testprog, 0666); err != nil { - t.Fatalf("can't create go file") + t.Fatalf("can't create go file. %q", err) } - outfile := "/tmp/testprog.out" + outfile := filepath.Join(tempDir, "outfile.out") arg := time.Now().UTC().String() - err := exec.Command("go", "run", gofile, outfile, arg).Run() + err = exec.Command("go", "run", gofile, outfile, arg).Run() if err == nil { t.Fatalf("completed normally, should have failed") } data, err := ioutil.ReadFile(outfile) if err != nil { - t.Fatalf("can't read output file %s", outfile) + t.Fatalf("can't read output file %s. %q", outfile, err) } if string(data) != arg { - t.Fatalf("bad data") + t.Fatalf("bad data. Expected %q, got %q", data, arg) } } @@ -44,7 +53,7 @@ var testprog = []byte(` package main import ( - "github.com/Sirupsen/logrus" + "github.com/sirupsen/logrus" "flag" "fmt" "io/ioutil" diff --git a/src/vendor/github.com/Sirupsen/logrus/appveyor.yml b/src/vendor/github.com/Sirupsen/logrus/appveyor.yml new file mode 100644 index 000000000..96c2ce15f --- /dev/null +++ b/src/vendor/github.com/Sirupsen/logrus/appveyor.yml @@ -0,0 +1,14 @@ +version: "{build}" +platform: x64 +clone_folder: c:\gopath\src\github.com\sirupsen\logrus +environment: + GOPATH: c:\gopath +branches: + only: + - master +install: + - set PATH=%GOPATH%\bin;c:\go\bin;%PATH% + - go version +build_script: + - go get -t + - go test diff --git a/src/vendor/github.com/Sirupsen/logrus/doc.go b/src/vendor/github.com/Sirupsen/logrus/doc.go index dddd5f877..da67aba06 100644 --- a/src/vendor/github.com/Sirupsen/logrus/doc.go +++ b/src/vendor/github.com/Sirupsen/logrus/doc.go @@ -7,7 +7,7 @@ The simplest way to use Logrus is simply the package-level exported logger: package main import ( - log "github.com/Sirupsen/logrus" + log "github.com/sirupsen/logrus" ) func main() { @@ -21,6 +21,6 @@ The simplest way to use Logrus is simply the package-level exported logger: Output: time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10 -For a full guide visit https://github.com/Sirupsen/logrus +For a full guide visit https://github.com/sirupsen/logrus */ package logrus diff --git a/src/vendor/github.com/Sirupsen/logrus/entry.go b/src/vendor/github.com/Sirupsen/logrus/entry.go index 54bfc57d7..778f4c9f0 100644 --- a/src/vendor/github.com/Sirupsen/logrus/entry.go +++ b/src/vendor/github.com/Sirupsen/logrus/entry.go @@ -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" @@ -25,10 +35,14 @@ type Entry struct { Time time.Time // Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic + // This field will be set on entry firing and the value will be equal to the one in Logger struct field. Level Level // 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 +53,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,30 +89,21 @@ 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 - if err := entry.Logger.Hooks.Fire(level, &entry); err != nil { - entry.Logger.mu.Lock() - fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) - entry.Logger.mu.Unlock() - } + entry.fireHooks() - reader, err := entry.Reader() - if err != nil { - entry.Logger.mu.Lock() - fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) - entry.Logger.mu.Unlock() - } + buffer = bufferPool.Get().(*bytes.Buffer) + buffer.Reset() + defer bufferPool.Put(buffer) + entry.Buffer = buffer - entry.Logger.mu.Lock() - defer entry.Logger.mu.Unlock() + entry.write() - _, err = io.Copy(entry.Logger.Out, reader) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) - } + entry.Buffer = nil // To avoid Entry#log() returning a value that only would make sense for // panic() to use in Entry#Panic(), we avoid the allocation by checking @@ -114,8 +113,33 @@ func (entry Entry) log(level Level, msg string) { } } +// This function is not declared with a pointer value because otherwise +// race conditions will occur when using multiple goroutines +func (entry Entry) fireHooks() { + entry.Logger.mu.Lock() + defer entry.Logger.mu.Unlock() + err := entry.Logger.Hooks.Fire(entry.Level, &entry) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) + } +} + +func (entry *Entry) write() { + serialized, err := entry.Logger.Formatter.Format(entry) + entry.Logger.mu.Lock() + defer entry.Logger.mu.Unlock() + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) + } else { + _, err = entry.Logger.Out.Write(serialized) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) + } + } +} + func (entry *Entry) Debug(args ...interface{}) { - if entry.Logger.Level >= DebugLevel { + if entry.Logger.level() >= DebugLevel { entry.log(DebugLevel, fmt.Sprint(args...)) } } @@ -125,13 +149,13 @@ func (entry *Entry) Print(args ...interface{}) { } func (entry *Entry) Info(args ...interface{}) { - if entry.Logger.Level >= InfoLevel { + if entry.Logger.level() >= InfoLevel { entry.log(InfoLevel, fmt.Sprint(args...)) } } func (entry *Entry) Warn(args ...interface{}) { - if entry.Logger.Level >= WarnLevel { + if entry.Logger.level() >= WarnLevel { entry.log(WarnLevel, fmt.Sprint(args...)) } } @@ -141,20 +165,20 @@ func (entry *Entry) Warning(args ...interface{}) { } func (entry *Entry) Error(args ...interface{}) { - if entry.Logger.Level >= ErrorLevel { + if entry.Logger.level() >= ErrorLevel { entry.log(ErrorLevel, fmt.Sprint(args...)) } } func (entry *Entry) Fatal(args ...interface{}) { - if entry.Logger.Level >= FatalLevel { + if entry.Logger.level() >= FatalLevel { entry.log(FatalLevel, fmt.Sprint(args...)) } Exit(1) } func (entry *Entry) Panic(args ...interface{}) { - if entry.Logger.Level >= PanicLevel { + if entry.Logger.level() >= PanicLevel { entry.log(PanicLevel, fmt.Sprint(args...)) } panic(fmt.Sprint(args...)) @@ -163,13 +187,13 @@ func (entry *Entry) Panic(args ...interface{}) { // Entry Printf family functions func (entry *Entry) Debugf(format string, args ...interface{}) { - if entry.Logger.Level >= DebugLevel { + if entry.Logger.level() >= DebugLevel { entry.Debug(fmt.Sprintf(format, args...)) } } func (entry *Entry) Infof(format string, args ...interface{}) { - if entry.Logger.Level >= InfoLevel { + if entry.Logger.level() >= InfoLevel { entry.Info(fmt.Sprintf(format, args...)) } } @@ -179,7 +203,7 @@ func (entry *Entry) Printf(format string, args ...interface{}) { } func (entry *Entry) Warnf(format string, args ...interface{}) { - if entry.Logger.Level >= WarnLevel { + if entry.Logger.level() >= WarnLevel { entry.Warn(fmt.Sprintf(format, args...)) } } @@ -189,20 +213,20 @@ func (entry *Entry) Warningf(format string, args ...interface{}) { } func (entry *Entry) Errorf(format string, args ...interface{}) { - if entry.Logger.Level >= ErrorLevel { + if entry.Logger.level() >= ErrorLevel { entry.Error(fmt.Sprintf(format, args...)) } } func (entry *Entry) Fatalf(format string, args ...interface{}) { - if entry.Logger.Level >= FatalLevel { + if entry.Logger.level() >= FatalLevel { entry.Fatal(fmt.Sprintf(format, args...)) } Exit(1) } func (entry *Entry) Panicf(format string, args ...interface{}) { - if entry.Logger.Level >= PanicLevel { + if entry.Logger.level() >= PanicLevel { entry.Panic(fmt.Sprintf(format, args...)) } } @@ -210,13 +234,13 @@ func (entry *Entry) Panicf(format string, args ...interface{}) { // Entry Println family functions func (entry *Entry) Debugln(args ...interface{}) { - if entry.Logger.Level >= DebugLevel { + if entry.Logger.level() >= DebugLevel { entry.Debug(entry.sprintlnn(args...)) } } func (entry *Entry) Infoln(args ...interface{}) { - if entry.Logger.Level >= InfoLevel { + if entry.Logger.level() >= InfoLevel { entry.Info(entry.sprintlnn(args...)) } } @@ -226,7 +250,7 @@ func (entry *Entry) Println(args ...interface{}) { } func (entry *Entry) Warnln(args ...interface{}) { - if entry.Logger.Level >= WarnLevel { + if entry.Logger.level() >= WarnLevel { entry.Warn(entry.sprintlnn(args...)) } } @@ -236,20 +260,20 @@ func (entry *Entry) Warningln(args ...interface{}) { } func (entry *Entry) Errorln(args ...interface{}) { - if entry.Logger.Level >= ErrorLevel { + if entry.Logger.level() >= ErrorLevel { entry.Error(entry.sprintlnn(args...)) } } func (entry *Entry) Fatalln(args ...interface{}) { - if entry.Logger.Level >= FatalLevel { + if entry.Logger.level() >= FatalLevel { entry.Fatal(entry.sprintlnn(args...)) } Exit(1) } func (entry *Entry) Panicln(args ...interface{}) { - if entry.Logger.Level >= PanicLevel { + if entry.Logger.level() >= PanicLevel { entry.Panic(entry.sprintlnn(args...)) } } diff --git a/src/vendor/github.com/Sirupsen/logrus/entry_test.go b/src/vendor/github.com/Sirupsen/logrus/entry_test.go index 99c3b41d5..a81e2b383 100644 --- a/src/vendor/github.com/Sirupsen/logrus/entry_test.go +++ b/src/vendor/github.com/Sirupsen/logrus/entry_test.go @@ -75,3 +75,41 @@ func TestEntryPanicf(t *testing.T) { entry := NewEntry(logger) entry.WithField("err", errBoom).Panicf("kaboom %v", true) } + +const ( + badMessage = "this is going to panic" + panicMessage = "this is broken" +) + +type panickyHook struct{} + +func (p *panickyHook) Levels() []Level { + return []Level{InfoLevel} +} + +func (p *panickyHook) Fire(entry *Entry) error { + if entry.Message == badMessage { + panic(panicMessage) + } + + return nil +} + +func TestEntryHooksPanic(t *testing.T) { + logger := New() + logger.Out = &bytes.Buffer{} + logger.Level = InfoLevel + logger.Hooks.Add(&panickyHook{}) + + defer func() { + p := recover() + assert.NotNil(t, p) + assert.Equal(t, panicMessage, p) + + entry := NewEntry(logger) + entry.Info("another message") + }() + + entry := NewEntry(logger) + entry.Info(badMessage) +} diff --git a/src/vendor/github.com/Sirupsen/logrus/example_basic_test.go b/src/vendor/github.com/Sirupsen/logrus/example_basic_test.go new file mode 100644 index 000000000..a2acf550c --- /dev/null +++ b/src/vendor/github.com/Sirupsen/logrus/example_basic_test.go @@ -0,0 +1,69 @@ +package logrus_test + +import ( + "github.com/sirupsen/logrus" + "os" +) + +func Example_basic() { + var log = logrus.New() + log.Formatter = new(logrus.JSONFormatter) + log.Formatter = new(logrus.TextFormatter) //default + log.Formatter.(*logrus.TextFormatter).DisableTimestamp = true // remove timestamp from test output + log.Level = logrus.DebugLevel + log.Out = os.Stdout + + // 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") + // } + + defer func() { + err := recover() + if err != nil { + entry := err.(*logrus.Entry) + log.WithFields(logrus.Fields{ + "omg": true, + "err_animal": entry.Data["animal"], + "err_size": entry.Data["size"], + "err_level": entry.Level, + "err_message": entry.Message, + "number": 100, + }).Error("The ice breaks!") // or use Fatal() to force the process to exit with a nonzero code + } + }() + + log.WithFields(logrus.Fields{ + "animal": "walrus", + "number": 8, + }).Debug("Started observing beach") + + log.WithFields(logrus.Fields{ + "animal": "walrus", + "size": 10, + }).Info("A group of walrus emerges from the ocean") + + log.WithFields(logrus.Fields{ + "omg": true, + "number": 122, + }).Warn("The group's number increased tremendously!") + + log.WithFields(logrus.Fields{ + "temperature": -4, + }).Debug("Temperature changes") + + log.WithFields(logrus.Fields{ + "animal": "orca", + "size": 9009, + }).Panic("It's over 9000!") + + // Output: + // level=debug msg="Started observing beach" animal=walrus number=8 + // level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10 + // level=warning msg="The group's number increased tremendously!" number=122 omg=true + // level=debug msg="Temperature changes" temperature=-4 + // level=panic msg="It's over 9000!" animal=orca size=9009 + // level=error msg="The ice breaks!" err_animal=orca err_level=panic err_message="It's over 9000!" err_size=9009 number=100 omg=true +} diff --git a/src/vendor/github.com/Sirupsen/logrus/example_hook_test.go b/src/vendor/github.com/Sirupsen/logrus/example_hook_test.go new file mode 100644 index 000000000..d4ddffca3 --- /dev/null +++ b/src/vendor/github.com/Sirupsen/logrus/example_hook_test.go @@ -0,0 +1,35 @@ +package logrus_test + +import ( + "github.com/sirupsen/logrus" + "gopkg.in/gemnasium/logrus-airbrake-hook.v2" + "os" +) + +func Example_hook() { + var log = logrus.New() + log.Formatter = new(logrus.TextFormatter) // default + log.Formatter.(*logrus.TextFormatter).DisableTimestamp = true // remove timestamp from test output + log.Hooks.Add(airbrake.NewHook(123, "xyz", "development")) + log.Out = os.Stdout + + log.WithFields(logrus.Fields{ + "animal": "walrus", + "size": 10, + }).Info("A group of walrus emerges from the ocean") + + log.WithFields(logrus.Fields{ + "omg": true, + "number": 122, + }).Warn("The group's number increased tremendously!") + + log.WithFields(logrus.Fields{ + "omg": true, + "number": 100, + }).Error("The ice breaks!") + + // Output: + // level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10 + // level=warning msg="The group's number increased tremendously!" number=122 omg=true + // level=error msg="The ice breaks!" number=100 omg=true +} diff --git a/src/vendor/github.com/Sirupsen/logrus/examples/basic/basic.go b/src/vendor/github.com/Sirupsen/logrus/examples/basic/basic.go deleted file mode 100644 index a1623ec00..000000000 --- a/src/vendor/github.com/Sirupsen/logrus/examples/basic/basic.go +++ /dev/null @@ -1,50 +0,0 @@ -package main - -import ( - "github.com/Sirupsen/logrus" -) - -var log = logrus.New() - -func init() { - log.Formatter = new(logrus.JSONFormatter) - log.Formatter = new(logrus.TextFormatter) // default - log.Level = logrus.DebugLevel -} - -func main() { - defer func() { - err := recover() - if err != nil { - log.WithFields(logrus.Fields{ - "omg": true, - "err": err, - "number": 100, - }).Fatal("The ice breaks!") - } - }() - - log.WithFields(logrus.Fields{ - "animal": "walrus", - "number": 8, - }).Debug("Started observing beach") - - log.WithFields(logrus.Fields{ - "animal": "walrus", - "size": 10, - }).Info("A group of walrus emerges from the ocean") - - log.WithFields(logrus.Fields{ - "omg": true, - "number": 122, - }).Warn("The group's number increased tremendously!") - - log.WithFields(logrus.Fields{ - "temperature": -4, - }).Debug("Temperature changes") - - log.WithFields(logrus.Fields{ - "animal": "orca", - "size": 9009, - }).Panic("It's over 9000!") -} diff --git a/src/vendor/github.com/Sirupsen/logrus/examples/hook/hook.go b/src/vendor/github.com/Sirupsen/logrus/examples/hook/hook.go deleted file mode 100644 index 3187f6d3e..000000000 --- a/src/vendor/github.com/Sirupsen/logrus/examples/hook/hook.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "github.com/Sirupsen/logrus" - "gopkg.in/gemnasium/logrus-airbrake-hook.v2" -) - -var log = logrus.New() - -func init() { - log.Formatter = new(logrus.TextFormatter) // default - log.Hooks.Add(airbrake.NewHook(123, "xyz", "development")) -} - -func main() { - log.WithFields(logrus.Fields{ - "animal": "walrus", - "size": 10, - }).Info("A group of walrus emerges from the ocean") - - log.WithFields(logrus.Fields{ - "omg": true, - "number": 122, - }).Warn("The group's number increased tremendously!") - - log.WithFields(logrus.Fields{ - "omg": true, - "number": 100, - }).Fatal("The ice breaks!") -} diff --git a/src/vendor/github.com/Sirupsen/logrus/exported.go b/src/vendor/github.com/Sirupsen/logrus/exported.go index 9a0120ac1..013183eda 100644 --- a/src/vendor/github.com/Sirupsen/logrus/exported.go +++ b/src/vendor/github.com/Sirupsen/logrus/exported.go @@ -31,14 +31,14 @@ func SetFormatter(formatter Formatter) { func SetLevel(level Level) { std.mu.Lock() defer std.mu.Unlock() - std.Level = level + std.SetLevel(level) } // GetLevel returns the standard logger level. func GetLevel() Level { std.mu.Lock() defer std.mu.Unlock() - return std.Level + return std.level() } // AddHook adds a hook to the standard logger hooks. diff --git a/src/vendor/github.com/Sirupsen/logrus/formatter.go b/src/vendor/github.com/Sirupsen/logrus/formatter.go index b5fbe934d..b183ff5b1 100644 --- a/src/vendor/github.com/Sirupsen/logrus/formatter.go +++ b/src/vendor/github.com/Sirupsen/logrus/formatter.go @@ -2,7 +2,7 @@ package logrus import "time" -const DefaultTimestampFormat = time.RFC3339 +const defaultTimestampFormat = time.RFC3339 // The Formatter interface is used to implement a custom Formatter. It takes an // `Entry`. It exposes all the fields, including the default ones: diff --git a/src/vendor/github.com/Sirupsen/logrus/formatter_bench_test.go b/src/vendor/github.com/Sirupsen/logrus/formatter_bench_test.go index c6d290c77..d9481589f 100644 --- a/src/vendor/github.com/Sirupsen/logrus/formatter_bench_test.go +++ b/src/vendor/github.com/Sirupsen/logrus/formatter_bench_test.go @@ -80,11 +80,14 @@ func BenchmarkLargeJSONFormatter(b *testing.B) { } func doBenchmark(b *testing.B, formatter Formatter, fields Fields) { + logger := New() + entry := &Entry{ Time: time.Time{}, Level: InfoLevel, Message: "message", Data: fields, + Logger: logger, } var d []byte var err error diff --git a/src/vendor/github.com/Sirupsen/logrus/hook_test.go b/src/vendor/github.com/Sirupsen/logrus/hook_test.go index 13f34cb6f..4fea7514e 100644 --- a/src/vendor/github.com/Sirupsen/logrus/hook_test.go +++ b/src/vendor/github.com/Sirupsen/logrus/hook_test.go @@ -1,6 +1,7 @@ package logrus import ( + "sync" "testing" "github.com/stretchr/testify/assert" @@ -120,3 +121,24 @@ func TestErrorHookShouldFireOnError(t *testing.T) { assert.Equal(t, hook.Fired, true) }) } + +func TestAddHookRace(t *testing.T) { + var wg sync.WaitGroup + wg.Add(2) + hook := new(ErrorHook) + LogAndAssertJSON(t, func(log *Logger) { + go func() { + defer wg.Done() + log.AddHook(hook) + }() + go func() { + defer wg.Done() + log.Error("test") + }() + wg.Wait() + }, func(fields Fields) { + // the line may have been logged + // before the hook was added, so we can't + // actually assert on the hook + }) +} diff --git a/src/vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md b/src/vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md index 066704b37..1bbc0f72d 100644 --- a/src/vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md +++ b/src/vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md @@ -5,13 +5,13 @@ ```go import ( "log/syslog" - "github.com/Sirupsen/logrus" - logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog" + "github.com/sirupsen/logrus" + lSyslog "github.com/sirupsen/logrus/hooks/syslog" ) func main() { log := logrus.New() - hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "") + hook, err := lSyslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "") if err == nil { log.Hooks.Add(hook) @@ -24,16 +24,16 @@ If you want to connect to local syslog (Ex. "/dev/log" or "/var/run/syslog" or " ```go import ( "log/syslog" - "github.com/Sirupsen/logrus" - logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog" + "github.com/sirupsen/logrus" + lSyslog "github.com/sirupsen/logrus/hooks/syslog" ) func main() { log := logrus.New() - hook, err := logrus_syslog.NewSyslogHook("", "", syslog.LOG_INFO, "") + hook, err := lSyslog.NewSyslogHook("", "", syslog.LOG_INFO, "") if err == nil { log.Hooks.Add(hook) } } -``` \ No newline at end of file +``` diff --git a/src/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go b/src/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go index a36e20032..329ce0d60 100644 --- a/src/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go +++ b/src/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go @@ -1,12 +1,13 @@ // +build !windows,!nacl,!plan9 -package logrus_syslog +package syslog import ( "fmt" - "github.com/Sirupsen/logrus" "log/syslog" "os" + + "github.com/sirupsen/logrus" ) // SyslogHook to send logs via syslog. diff --git a/src/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go b/src/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go index 42762dc10..5ec3a4445 100644 --- a/src/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go +++ b/src/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go @@ -1,9 +1,10 @@ -package logrus_syslog +package syslog import ( - "github.com/Sirupsen/logrus" "log/syslog" "testing" + + "github.com/sirupsen/logrus" ) func TestLocalhostAddAndPrint(t *testing.T) { diff --git a/src/vendor/github.com/Sirupsen/logrus/hooks/test/test.go b/src/vendor/github.com/Sirupsen/logrus/hooks/test/test.go index 068812535..62c4845df 100644 --- a/src/vendor/github.com/Sirupsen/logrus/hooks/test/test.go +++ b/src/vendor/github.com/Sirupsen/logrus/hooks/test/test.go @@ -1,17 +1,25 @@ +// The Test package is used for testing logrus. It is here for backwards +// compatibility from when logrus' organization was upper-case. Please use +// lower-case logrus and the `null` package instead of this one. package test import ( "io/ioutil" + "sync" - "github.com/Sirupsen/logrus" + "github.com/sirupsen/logrus" ) -// test.Hook is a hook designed for dealing with logs in test scenarios. +// Hook is a hook designed for dealing with logs in test scenarios. type Hook struct { + // Entries is an array of all entries that have been received by this hook. + // For safe access, use the AllEntries() method, rather than reading this + // value directly. Entries []*logrus.Entry + mu sync.RWMutex } -// Installs a test hook for the global logger. +// NewGlobal installs a test hook for the global logger. func NewGlobal() *Hook { hook := new(Hook) @@ -21,7 +29,7 @@ func NewGlobal() *Hook { } -// Installs a test hook for a given local logger. +// NewLocal installs a test hook for a given local logger. func NewLocal(logger *logrus.Logger) *Hook { hook := new(Hook) @@ -31,7 +39,7 @@ func NewLocal(logger *logrus.Logger) *Hook { } -// Creates a discarding logger and installs the test hook. +// NewNullLogger creates a discarding logger and installs the test hook. func NewNullLogger() (*logrus.Logger, *Hook) { logger := logrus.New() @@ -42,6 +50,8 @@ func NewNullLogger() (*logrus.Logger, *Hook) { } func (t *Hook) Fire(e *logrus.Entry) error { + t.mu.Lock() + defer t.mu.Unlock() t.Entries = append(t.Entries, e) return nil } @@ -51,17 +61,35 @@ func (t *Hook) Levels() []logrus.Level { } // LastEntry returns the last entry that was logged or nil. -func (t *Hook) LastEntry() (l *logrus.Entry) { - - if i := len(t.Entries) - 1; i < 0 { +func (t *Hook) LastEntry() *logrus.Entry { + t.mu.RLock() + defer t.mu.RUnlock() + i := len(t.Entries) - 1 + if i < 0 { return nil - } else { - return t.Entries[i] } + // Make a copy, for safety + e := *t.Entries[i] + return &e +} +// AllEntries returns all entries that were logged. +func (t *Hook) AllEntries() []*logrus.Entry { + t.mu.RLock() + defer t.mu.RUnlock() + // Make a copy so the returned value won't race with future log requests + entries := make([]*logrus.Entry, len(t.Entries)) + for i, entry := range t.Entries { + // Make a copy, for safety + e := *entry + entries[i] = &e + } + return entries } // Reset removes all Entries from this test hook. func (t *Hook) Reset() { + t.mu.Lock() + defer t.mu.Unlock() t.Entries = make([]*logrus.Entry, 0) } diff --git a/src/vendor/github.com/Sirupsen/logrus/hooks/test/test_test.go b/src/vendor/github.com/Sirupsen/logrus/hooks/test/test_test.go index d69455ba0..dea768e6c 100644 --- a/src/vendor/github.com/Sirupsen/logrus/hooks/test/test_test.go +++ b/src/vendor/github.com/Sirupsen/logrus/hooks/test/test_test.go @@ -1,14 +1,14 @@ package test import ( + "sync" "testing" - "github.com/Sirupsen/logrus" + "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" ) func TestAllHooks(t *testing.T) { - assert := assert.New(t) logger, hook := NewNullLogger() @@ -35,5 +35,27 @@ func TestAllHooks(t *testing.T) { assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level) assert.Equal("Hello error", hook.LastEntry().Message) assert.Equal(1, len(hook.Entries)) - +} + +func TestLoggingWithHooksRace(t *testing.T) { + assert := assert.New(t) + logger, hook := NewNullLogger() + + var wg sync.WaitGroup + wg.Add(100) + + for i := 0; i < 100; i++ { + go func() { + logger.Info("info") + wg.Done() + }() + } + + assert.Equal(logrus.InfoLevel, hook.LastEntry().Level) + assert.Equal("info", hook.LastEntry().Message) + + wg.Wait() + + entries := hook.AllEntries() + assert.Equal(100, len(entries)) } diff --git a/src/vendor/github.com/Sirupsen/logrus/json_formatter.go b/src/vendor/github.com/Sirupsen/logrus/json_formatter.go index 2ad6dc5cf..fb01c1b10 100644 --- a/src/vendor/github.com/Sirupsen/logrus/json_formatter.go +++ b/src/vendor/github.com/Sirupsen/logrus/json_formatter.go @@ -5,18 +5,54 @@ import ( "fmt" ) +type fieldKey string + +// FieldMap allows customization of the key names for default fields. +type FieldMap map[fieldKey]string + +// Default key names for the default fields +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) +} + +// JSONFormatter formats logs into parsable json 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 default fields. + // As an example: + // formatter := &JSONFormatter{ + // FieldMap: FieldMap{ + // FieldKeyTime: "@timestamp", + // FieldKeyLevel: "@level", + // FieldKeyMsg: "@message", + // }, + // } + FieldMap FieldMap } +// Format renders a single log entry 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 + // https://github.com/sirupsen/logrus/issues/137 data[k] = v.Error() default: data[k] = v @@ -26,12 +62,14 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { timestampFormat := f.TimestampFormat if timestampFormat == "" { - timestampFormat = DefaultTimestampFormat + timestampFormat = defaultTimestampFormat } - data["time"] = entry.Time.Format(timestampFormat) - data["msg"] = entry.Message - data["level"] = entry.Level.String() + 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 { diff --git a/src/vendor/github.com/Sirupsen/logrus/json_formatter_test.go b/src/vendor/github.com/Sirupsen/logrus/json_formatter_test.go index 1d7087325..51093a79b 100644 --- a/src/vendor/github.com/Sirupsen/logrus/json_formatter_test.go +++ b/src/vendor/github.com/Sirupsen/logrus/json_formatter_test.go @@ -3,7 +3,7 @@ package logrus import ( "encoding/json" "errors" - + "strings" "testing" ) @@ -118,3 +118,82 @@ func TestJSONEntryEndsWithNewline(t *testing.T) { t.Fatal("Expected JSON log entry to end with a newline") } } + +func TestJSONMessageKey(t *testing.T) { + formatter := &JSONFormatter{ + FieldMap: FieldMap{ + FieldKeyMsg: "message", + }, + } + + b, err := formatter.Format(&Entry{Message: "oh hai"}) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + s := string(b) + if !(strings.Contains(s, "message") && strings.Contains(s, "oh hai")) { + t.Fatal("Expected JSON to format message key") + } +} + +func TestJSONLevelKey(t *testing.T) { + formatter := &JSONFormatter{ + FieldMap: FieldMap{ + FieldKeyLevel: "somelevel", + }, + } + + b, err := formatter.Format(WithField("level", "something")) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + s := string(b) + if !strings.Contains(s, "somelevel") { + t.Fatal("Expected JSON to format level key") + } +} + +func TestJSONTimeKey(t *testing.T) { + formatter := &JSONFormatter{ + FieldMap: FieldMap{ + FieldKeyTime: "timeywimey", + }, + } + + b, err := formatter.Format(WithField("level", "something")) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + s := string(b) + if !strings.Contains(s, "timeywimey") { + t.Fatal("Expected JSON to format time key") + } +} + +func TestJSONDisableTimestamp(t *testing.T) { + formatter := &JSONFormatter{ + DisableTimestamp: true, + } + + b, err := formatter.Format(WithField("level", "something")) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + s := string(b) + if strings.Contains(s, FieldKeyTime) { + t.Error("Did not prevent timestamp", s) + } +} + +func TestJSONEnableTimestamp(t *testing.T) { + formatter := &JSONFormatter{} + + b, err := formatter.Format(WithField("level", "something")) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + s := string(b) + if !strings.Contains(s, FieldKeyTime) { + t.Error("Timestamp not present", s) + } +} diff --git a/src/vendor/github.com/Sirupsen/logrus/logger.go b/src/vendor/github.com/Sirupsen/logrus/logger.go index 9052a8065..fdaf8a653 100644 --- a/src/vendor/github.com/Sirupsen/logrus/logger.go +++ b/src/vendor/github.com/Sirupsen/logrus/logger.go @@ -4,6 +4,7 @@ import ( "io" "os" "sync" + "sync/atomic" ) type Logger struct { @@ -24,10 +25,33 @@ type Logger struct { Formatter Formatter // The logging level the logger should log at. This is typically (and defaults // to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be - // logged. `logrus.Debug` is useful in + // logged. 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 +75,249 @@ func New() *Logger { } } +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...) + if logger.level() >= DebugLevel { + 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...) + if logger.level() >= InfoLevel { + 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...) + if logger.level() >= WarnLevel { + 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...) + if logger.level() >= WarnLevel { + 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...) + if logger.level() >= ErrorLevel { + 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...) + if logger.level() >= FatalLevel { + entry := logger.newEntry() + entry.Fatalf(format, args...) + logger.releaseEntry(entry) } Exit(1) } func (logger *Logger) Panicf(format string, args ...interface{}) { - if logger.Level >= PanicLevel { - NewEntry(logger).Panicf(format, args...) + if logger.level() >= PanicLevel { + entry := logger.newEntry() + entry.Panicf(format, args...) + logger.releaseEntry(entry) } } func (logger *Logger) Debug(args ...interface{}) { - if logger.Level >= DebugLevel { - NewEntry(logger).Debug(args...) + if logger.level() >= DebugLevel { + entry := logger.newEntry() + entry.Debug(args...) + logger.releaseEntry(entry) } } func (logger *Logger) Info(args ...interface{}) { - if logger.Level >= InfoLevel { - NewEntry(logger).Info(args...) + if logger.level() >= InfoLevel { + 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...) + if logger.level() >= WarnLevel { + entry := logger.newEntry() + entry.Warn(args...) + logger.releaseEntry(entry) } } func (logger *Logger) Warning(args ...interface{}) { - if logger.Level >= WarnLevel { - NewEntry(logger).Warn(args...) + if logger.level() >= WarnLevel { + entry := logger.newEntry() + entry.Warn(args...) + logger.releaseEntry(entry) } } func (logger *Logger) Error(args ...interface{}) { - if logger.Level >= ErrorLevel { - NewEntry(logger).Error(args...) + if logger.level() >= ErrorLevel { + entry := logger.newEntry() + entry.Error(args...) + logger.releaseEntry(entry) } } func (logger *Logger) Fatal(args ...interface{}) { - if logger.Level >= FatalLevel { - NewEntry(logger).Fatal(args...) + if logger.level() >= FatalLevel { + entry := logger.newEntry() + entry.Fatal(args...) + logger.releaseEntry(entry) } Exit(1) } func (logger *Logger) Panic(args ...interface{}) { - if logger.Level >= PanicLevel { - NewEntry(logger).Panic(args...) + if logger.level() >= PanicLevel { + entry := logger.newEntry() + entry.Panic(args...) + logger.releaseEntry(entry) } } func (logger *Logger) Debugln(args ...interface{}) { - if logger.Level >= DebugLevel { - NewEntry(logger).Debugln(args...) + if logger.level() >= DebugLevel { + entry := logger.newEntry() + entry.Debugln(args...) + logger.releaseEntry(entry) } } func (logger *Logger) Infoln(args ...interface{}) { - if logger.Level >= InfoLevel { - NewEntry(logger).Infoln(args...) + if logger.level() >= InfoLevel { + 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...) + if logger.level() >= WarnLevel { + entry := logger.newEntry() + entry.Warnln(args...) + logger.releaseEntry(entry) } } func (logger *Logger) Warningln(args ...interface{}) { - if logger.Level >= WarnLevel { - NewEntry(logger).Warnln(args...) + if logger.level() >= WarnLevel { + entry := logger.newEntry() + entry.Warnln(args...) + logger.releaseEntry(entry) } } func (logger *Logger) Errorln(args ...interface{}) { - if logger.Level >= ErrorLevel { - NewEntry(logger).Errorln(args...) + if logger.level() >= ErrorLevel { + entry := logger.newEntry() + entry.Errorln(args...) + logger.releaseEntry(entry) } } func (logger *Logger) Fatalln(args ...interface{}) { - if logger.Level >= FatalLevel { - NewEntry(logger).Fatalln(args...) + if logger.level() >= FatalLevel { + entry := logger.newEntry() + entry.Fatalln(args...) + logger.releaseEntry(entry) } Exit(1) } func (logger *Logger) Panicln(args ...interface{}) { - if logger.Level >= PanicLevel { - NewEntry(logger).Panicln(args...) + if logger.level() >= PanicLevel { + 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() +} + +func (logger *Logger) level() Level { + return Level(atomic.LoadUint32((*uint32)(&logger.Level))) +} + +func (logger *Logger) SetLevel(level Level) { + atomic.StoreUint32((*uint32)(&logger.Level), uint32(level)) +} + +func (logger *Logger) AddHook(hook Hook) { + logger.mu.Lock() + defer logger.mu.Unlock() + logger.Hooks.Add(hook) +} diff --git a/src/vendor/github.com/Sirupsen/logrus/logger_bench_test.go b/src/vendor/github.com/Sirupsen/logrus/logger_bench_test.go new file mode 100644 index 000000000..dd23a3535 --- /dev/null +++ b/src/vendor/github.com/Sirupsen/logrus/logger_bench_test.go @@ -0,0 +1,61 @@ +package logrus + +import ( + "os" + "testing" +) + +// smallFields is a small size data set for benchmarking +var loggerFields = Fields{ + "foo": "bar", + "baz": "qux", + "one": "two", + "three": "four", +} + +func BenchmarkDummyLogger(b *testing.B) { + nullf, err := os.OpenFile("/dev/null", os.O_WRONLY, 0666) + if err != nil { + b.Fatalf("%v", err) + } + defer nullf.Close() + doLoggerBenchmark(b, nullf, &TextFormatter{DisableColors: true}, smallFields) +} + +func BenchmarkDummyLoggerNoLock(b *testing.B) { + nullf, err := os.OpenFile("/dev/null", os.O_WRONLY|os.O_APPEND, 0666) + if err != nil { + b.Fatalf("%v", err) + } + defer nullf.Close() + doLoggerBenchmarkNoLock(b, nullf, &TextFormatter{DisableColors: true}, smallFields) +} + +func doLoggerBenchmark(b *testing.B, out *os.File, formatter Formatter, fields Fields) { + logger := Logger{ + Out: out, + Level: InfoLevel, + Formatter: formatter, + } + entry := logger.WithFields(fields) + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + entry.Info("aaa") + } + }) +} + +func doLoggerBenchmarkNoLock(b *testing.B, out *os.File, formatter Formatter, fields Fields) { + logger := Logger{ + Out: out, + Level: InfoLevel, + Formatter: formatter, + } + logger.SetNoLock() + entry := logger.WithFields(fields) + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + entry.Info("aaa") + } + }) +} diff --git a/src/vendor/github.com/Sirupsen/logrus/logrus.go b/src/vendor/github.com/Sirupsen/logrus/logrus.go index e59669111..dd3899974 100644 --- a/src/vendor/github.com/Sirupsen/logrus/logrus.go +++ b/src/vendor/github.com/Sirupsen/logrus/logrus.go @@ -10,7 +10,7 @@ import ( type Fields map[string]interface{} // Level type -type Level uint8 +type Level uint32 // Convert the Level to a string. E.g. PanicLevel becomes "panic". func (level Level) String() string { diff --git a/src/vendor/github.com/Sirupsen/logrus/logrus_test.go b/src/vendor/github.com/Sirupsen/logrus/logrus_test.go index bfc478055..78cbc2825 100644 --- a/src/vendor/github.com/Sirupsen/logrus/logrus_test.go +++ b/src/vendor/github.com/Sirupsen/logrus/logrus_test.go @@ -359,3 +359,28 @@ func TestLogrusInterface(t *testing.T) { e := logger.WithField("another", "value") fn(e) } + +// Implements io.Writer using channels for synchronization, so we can wait on +// the Entry.Writer goroutine to write in a non-racey way. This does assume that +// there is a single call to Logger.Out for each message. +type channelWriter chan []byte + +func (cw channelWriter) Write(p []byte) (int, error) { + cw <- p + return len(p), nil +} + +func TestEntryWriter(t *testing.T) { + cw := channelWriter(make(chan []byte, 1)) + log := New() + log.Out = cw + log.Formatter = new(JSONFormatter) + log.WithField("foo", "bar").WriterLevel(WarnLevel).Write([]byte("hello\n")) + + bs := <-cw + var fields Fields + err := json.Unmarshal(bs, &fields) + assert.Nil(t, err) + assert.Equal(t, fields["foo"], "bar") + assert.Equal(t, fields["level"], "warning") +} diff --git a/src/vendor/github.com/Sirupsen/logrus/terminal_bsd.go b/src/vendor/github.com/Sirupsen/logrus/terminal_bsd.go index 71f8d67a5..4880d13d2 100644 --- a/src/vendor/github.com/Sirupsen/logrus/terminal_bsd.go +++ b/src/vendor/github.com/Sirupsen/logrus/terminal_bsd.go @@ -1,9 +1,10 @@ // +build darwin freebsd openbsd netbsd dragonfly +// +build !appengine,!gopherjs package logrus -import "syscall" +import "golang.org/x/sys/unix" -const ioctlReadTermios = syscall.TIOCGETA +const ioctlReadTermios = unix.TIOCGETA -type Termios syscall.Termios +type Termios unix.Termios diff --git a/src/vendor/github.com/Sirupsen/logrus/terminal_check_appengine.go b/src/vendor/github.com/Sirupsen/logrus/terminal_check_appengine.go new file mode 100644 index 000000000..3de08e802 --- /dev/null +++ b/src/vendor/github.com/Sirupsen/logrus/terminal_check_appengine.go @@ -0,0 +1,11 @@ +// +build appengine gopherjs + +package logrus + +import ( + "io" +) + +func checkIfTerminal(w io.Writer) bool { + return true +} diff --git a/src/vendor/github.com/Sirupsen/logrus/terminal_check_notappengine.go b/src/vendor/github.com/Sirupsen/logrus/terminal_check_notappengine.go new file mode 100644 index 000000000..067047a12 --- /dev/null +++ b/src/vendor/github.com/Sirupsen/logrus/terminal_check_notappengine.go @@ -0,0 +1,19 @@ +// +build !appengine,!gopherjs + +package logrus + +import ( + "io" + "os" + + "golang.org/x/crypto/ssh/terminal" +) + +func checkIfTerminal(w io.Writer) bool { + switch v := w.(type) { + case *os.File: + return terminal.IsTerminal(int(v.Fd())) + default: + return false + } +} diff --git a/src/vendor/github.com/Sirupsen/logrus/terminal_linux.go b/src/vendor/github.com/Sirupsen/logrus/terminal_linux.go index a2c0b40db..f29a0097c 100644 --- a/src/vendor/github.com/Sirupsen/logrus/terminal_linux.go +++ b/src/vendor/github.com/Sirupsen/logrus/terminal_linux.go @@ -3,10 +3,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !appengine,!gopherjs + package logrus -import "syscall" +import "golang.org/x/sys/unix" -const ioctlReadTermios = syscall.TCGETS +const ioctlReadTermios = unix.TCGETS -type Termios syscall.Termios +type Termios unix.Termios diff --git a/src/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go b/src/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go deleted file mode 100644 index b343b3a37..000000000 --- a/src/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go +++ /dev/null @@ -1,21 +0,0 @@ -// Based on ssh/terminal: -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build linux darwin freebsd openbsd netbsd dragonfly - -package logrus - -import ( - "syscall" - "unsafe" -) - -// IsTerminal returns true if stderr's file descriptor is a terminal. -func IsTerminal() bool { - fd := syscall.Stderr - var termios Termios - _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) - return err == 0 -} diff --git a/src/vendor/github.com/Sirupsen/logrus/terminal_solaris.go b/src/vendor/github.com/Sirupsen/logrus/terminal_solaris.go deleted file mode 100644 index 3e70bf7bf..000000000 --- a/src/vendor/github.com/Sirupsen/logrus/terminal_solaris.go +++ /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/src/vendor/github.com/Sirupsen/logrus/terminal_windows.go b/src/vendor/github.com/Sirupsen/logrus/terminal_windows.go deleted file mode 100644 index 0146845d1..000000000 --- a/src/vendor/github.com/Sirupsen/logrus/terminal_windows.go +++ /dev/null @@ -1,27 +0,0 @@ -// Based on ssh/terminal: -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build windows - -package logrus - -import ( - "syscall" - "unsafe" -) - -var kernel32 = syscall.NewLazyDLL("kernel32.dll") - -var ( - procGetConsoleMode = kernel32.NewProc("GetConsoleMode") -) - -// 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 -} diff --git a/src/vendor/github.com/Sirupsen/logrus/text_formatter.go b/src/vendor/github.com/Sirupsen/logrus/text_formatter.go index 6afd0e031..61b21caea 100644 --- a/src/vendor/github.com/Sirupsen/logrus/text_formatter.go +++ b/src/vendor/github.com/Sirupsen/logrus/text_formatter.go @@ -3,9 +3,9 @@ package logrus import ( "bytes" "fmt" - "runtime" "sort" "strings" + "sync" "time" ) @@ -14,24 +14,19 @@ const ( red = 31 green = 32 yellow = 33 - blue = 34 + blue = 36 gray = 37 ) var ( baseTimestamp time.Time - isTerminal bool ) func init() { baseTimestamp = time.Now() - isTerminal = IsTerminal() -} - -func miniTS() int { - return int(time.Since(baseTimestamp) / time.Second) } +// TextFormatter formats logs into text type TextFormatter struct { // Set to true to bypass checking for a TTY before outputting colors. ForceColors bool @@ -54,10 +49,26 @@ 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 + + // Whether the logger's out is to a terminal + isTerminal bool + + sync.Once } +func (f *TextFormatter) init(entry *Entry) { + if entry.Logger != nil { + f.isTerminal = checkIfTerminal(entry.Logger.Out) + } +} + +// Format renders a single log entry 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,17 +76,21 @@ 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 == "" { - timestampFormat = DefaultTimestampFormat + timestampFormat = defaultTimestampFormat } if isColored { f.printColored(b, entry, keys, timestampFormat) @@ -111,23 +126,29 @@ 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 == '.') { + ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '@' || ch == '^' || ch == '+') { return true } } @@ -135,27 +156,23 @@ func needsQuoting(text string) bool { } func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) { - + if b.Len() > 0 { + b.WriteByte(' ') + } b.WriteString(key) b.WriteByte('=') + f.appendValue(b, value) +} - switch value := value.(type) { - case string: - if !needsQuoting(value) { - b.WriteString(value) - } else { - fmt.Fprintf(b, "%q", value) - } - case error: - errmsg := value.Error() - if !needsQuoting(errmsg) { - b.WriteString(errmsg) - } else { - fmt.Fprintf(b, "%q", value) - } - default: - fmt.Fprint(b, value) +func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { + stringVal, ok := value.(string) + if !ok { + stringVal = fmt.Sprint(value) } - b.WriteByte(' ') + if !f.needsQuoting(stringVal) { + b.WriteString(stringVal) + } else { + b.WriteString(fmt.Sprintf("%q", stringVal)) + } } diff --git a/src/vendor/github.com/Sirupsen/logrus/text_formatter_test.go b/src/vendor/github.com/Sirupsen/logrus/text_formatter_test.go index e25a44f67..d93b931e5 100644 --- a/src/vendor/github.com/Sirupsen/logrus/text_formatter_test.go +++ b/src/vendor/github.com/Sirupsen/logrus/text_formatter_test.go @@ -3,17 +3,38 @@ package logrus import ( "bytes" "errors" + "fmt" + "strings" "testing" "time" ) +func TestFormatting(t *testing.T) { + tf := &TextFormatter{DisableColors: true} + + testCases := []struct { + value string + expected string + }{ + {`foo`, "time=\"0001-01-01T00:00:00Z\" level=panic test=foo\n"}, + } + + for _, tc := range testCases { + b, _ := tf.Format(WithField("test", tc.value)) + + if string(b) != tc.expected { + t.Errorf("formatting expected for %q (result was %q instead of %q)", tc.value, string(b), tc.expected) + } + } +} + func TestQuoting(t *testing.T) { tf := &TextFormatter{DisableColors: true} checkQuoting := func(q bool, value interface{}) { b, _ := tf.Format(WithField("test", value)) idx := bytes.Index(b, ([]byte)("test=")) - cont := bytes.Contains(b[idx+5:], []byte{'"'}) + cont := bytes.Contains(b[idx+5:], []byte("\"")) if cont != q { if q { t.Errorf("quoting expected for: %#v", value) @@ -23,14 +44,67 @@ func TestQuoting(t *testing.T) { } } + checkQuoting(false, "") checkQuoting(false, "abcd") checkQuoting(false, "v1.0") checkQuoting(false, "1234567890") - checkQuoting(true, "/foobar") + checkQuoting(false, "/foobar") + checkQuoting(false, "foo_bar") + checkQuoting(false, "foo@bar") + checkQuoting(false, "foobar^") + checkQuoting(false, "+/-_^@f.oobar") + checkQuoting(true, "foobar$") + checkQuoting(true, "&foobar") checkQuoting(true, "x y") checkQuoting(true, "x,y") checkQuoting(false, errors.New("invalid")) checkQuoting(true, errors.New("invalid argument")) + + // Test for quoting empty fields. + tf.QuoteEmptyFields = true + checkQuoting(true, "") + checkQuoting(false, "abcd") + checkQuoting(true, errors.New("invalid argument")) +} + +func TestEscaping(t *testing.T) { + tf := &TextFormatter{DisableColors: true} + + testCases := []struct { + value string + expected string + }{ + {`ba"r`, `ba\"r`}, + {`ba'r`, `ba'r`}, + } + + for _, tc := range testCases { + b, _ := tf.Format(WithField("test", tc.value)) + if !bytes.Contains(b, []byte(tc.expected)) { + t.Errorf("escaping expected for %q (result was %q instead of %q)", tc.value, string(b), tc.expected) + } + } +} + +func TestEscaping_Interface(t *testing.T) { + tf := &TextFormatter{DisableColors: true} + + ts := time.Now() + + testCases := []struct { + value interface{} + expected string + }{ + {ts, fmt.Sprintf("\"%s\"", ts.String())}, + {errors.New("error: something went wrong"), "\"error: something went wrong\""}, + } + + for _, tc := range testCases { + b, _ := tf.Format(WithField("test", tc.value)) + if !bytes.Contains(b, []byte(tc.expected)) { + t.Errorf("escaping expected for %q (result was %q instead of %q)", tc.value, string(b), tc.expected) + } + } } func TestTimestampFormat(t *testing.T) { @@ -39,10 +113,7 @@ func TestTimestampFormat(t *testing.T) { customStr, _ := customFormatter.Format(WithField("test", "test")) timeStart := bytes.Index(customStr, ([]byte)("time=")) timeEnd := bytes.Index(customStr, ([]byte)("level=")) - timeStr := customStr[timeStart+5 : timeEnd-1] - if timeStr[0] == '"' && timeStr[len(timeStr)-1] == '"' { - timeStr = timeStr[1 : len(timeStr)-1] - } + timeStr := customStr[timeStart+5+len("\"") : timeEnd-1-len("\"")] if format == "" { format = time.RFC3339 } @@ -57,5 +128,14 @@ func TestTimestampFormat(t *testing.T) { checkTimeStr("") } +func TestDisableTimestampWithColoredOutput(t *testing.T) { + tf := &TextFormatter{DisableTimestamp: true, ForceColors: true} + + b, _ := tf.Format(WithField("test", "test")) + if strings.Contains(string(b), "[0000]") { + t.Error("timestamp not expected when DisableTimestamp is true") + } +} + // TODO add tests for sorting etc., this requires a parser for the text // formatter output. diff --git a/src/vendor/github.com/Sirupsen/logrus/writer.go b/src/vendor/github.com/Sirupsen/logrus/writer.go index f74d2aa5f..7bdebedc6 100644 --- a/src/vendor/github.com/Sirupsen/logrus/writer.go +++ b/src/vendor/github.com/Sirupsen/logrus/writer.go @@ -11,39 +11,48 @@ func (logger *Logger) Writer() *io.PipeWriter { } 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 = logger.Debug + printFunc = entry.Debug case InfoLevel: - printFunc = logger.Info + printFunc = entry.Info case WarnLevel: - printFunc = logger.Warn + printFunc = entry.Warn case ErrorLevel: - printFunc = logger.Error + printFunc = entry.Error case FatalLevel: - printFunc = logger.Fatal + printFunc = entry.Fatal case PanicLevel: - printFunc = logger.Panic + printFunc = entry.Panic default: - printFunc = logger.Print + printFunc = entry.Print } - go logger.writerScanner(reader, printFunc) + go entry.writerScanner(reader, printFunc) runtime.SetFinalizer(writer, writerFinalizer) return writer } -func (logger *Logger) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) { +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 { - logger.Errorf("Error while reading from Writer: %s", err) + entry.Errorf("Error while reading from Writer: %s", err) } reader.Close() } diff --git a/src/vendor/github.com/garyburd/redigo/.travis.yml b/src/vendor/github.com/garyburd/redigo/.travis.yml index 158f16142..25d626529 100644 --- a/src/vendor/github.com/garyburd/redigo/.travis.yml +++ b/src/vendor/github.com/garyburd/redigo/.travis.yml @@ -5,11 +5,12 @@ services: go: - 1.4 - - 1.5 - - 1.6 - - 1.7 - - 1.8 - - 1.9 + - 1.5.x + - 1.6.x + - 1.7.x + - 1.8.x + - 1.9.x + - 1.10.x - tip script: diff --git a/src/vendor/github.com/garyburd/redigo/redis/conn.go b/src/vendor/github.com/garyburd/redigo/redis/conn.go index 500646910..5aa0f32f2 100644 --- a/src/vendor/github.com/garyburd/redigo/redis/conn.go +++ b/src/vendor/github.com/garyburd/redigo/redis/conn.go @@ -29,6 +29,10 @@ import ( "time" ) +var ( + _ ConnWithTimeout = (*conn)(nil) +) + // conn is the low-level implementation of Conn type conn struct { // Shared @@ -72,6 +76,7 @@ type DialOption struct { type dialOptions struct { readTimeout time.Duration writeTimeout time.Duration + dialer *net.Dialer dial func(network, addr string) (net.Conn, error) db int password string @@ -94,17 +99,27 @@ func DialWriteTimeout(d time.Duration) DialOption { }} } -// DialConnectTimeout specifies the timeout for connecting to the Redis server. +// DialConnectTimeout specifies the timeout for connecting to the Redis server when +// no DialNetDial option is specified. func DialConnectTimeout(d time.Duration) DialOption { return DialOption{func(do *dialOptions) { - dialer := net.Dialer{Timeout: d} - do.dial = dialer.Dial + do.dialer.Timeout = d + }} +} + +// DialKeepAlive specifies the keep-alive period for TCP connections to the Redis server +// when no DialNetDial option is specified. +// If zero, keep-alives are not enabled. If no DialKeepAlive option is specified then +// the default of 5 minutes is used to ensure that half-closed TCP sessions are detected. +func DialKeepAlive(d time.Duration) DialOption { + return DialOption{func(do *dialOptions) { + do.dialer.KeepAlive = d }} } // DialNetDial specifies a custom dial function for creating TCP -// connections. If this option is left out, then net.Dial is -// used. DialNetDial overrides DialConnectTimeout. +// connections, otherwise a net.Dialer customized via the other options is used. +// DialNetDial overrides DialConnectTimeout and DialKeepAlive. func DialNetDial(dial func(network, addr string) (net.Conn, error)) DialOption { return DialOption{func(do *dialOptions) { do.dial = dial @@ -154,11 +169,16 @@ func DialUseTLS(useTLS bool) DialOption { // address using the specified options. func Dial(network, address string, options ...DialOption) (Conn, error) { do := dialOptions{ - dial: net.Dial, + dialer: &net.Dialer{ + KeepAlive: time.Minute * 5, + }, } for _, option := range options { option.f(&do) } + if do.dial == nil { + do.dial = do.dialer.Dial + } netConn, err := do.dial(network, address) if err != nil { @@ -166,7 +186,12 @@ func Dial(network, address string, options ...DialOption) (Conn, error) { } if do.useTLS { - tlsConfig := cloneTLSClientConfig(do.tlsConfig, do.skipVerify) + var tlsConfig *tls.Config + if do.tlsConfig == nil { + tlsConfig = &tls.Config{InsecureSkipVerify: do.skipVerify} + } else { + tlsConfig = cloneTLSConfig(do.tlsConfig) + } if tlsConfig.ServerName == "" { host, _, err := net.SplitHostPort(address) if err != nil { @@ -555,10 +580,17 @@ func (c *conn) Flush() error { return nil } -func (c *conn) Receive() (reply interface{}, err error) { - if c.readTimeout != 0 { - c.conn.SetReadDeadline(time.Now().Add(c.readTimeout)) +func (c *conn) Receive() (interface{}, error) { + return c.ReceiveWithTimeout(c.readTimeout) +} + +func (c *conn) ReceiveWithTimeout(timeout time.Duration) (reply interface{}, err error) { + var deadline time.Time + if timeout != 0 { + deadline = time.Now().Add(timeout) } + c.conn.SetReadDeadline(deadline) + if reply, err = c.readReply(); err != nil { return nil, c.fatal(err) } @@ -581,6 +613,10 @@ func (c *conn) Receive() (reply interface{}, err error) { } func (c *conn) Do(cmd string, args ...interface{}) (interface{}, error) { + return c.DoWithTimeout(c.readTimeout, cmd, args...) +} + +func (c *conn) DoWithTimeout(readTimeout time.Duration, cmd string, args ...interface{}) (interface{}, error) { c.mu.Lock() pending := c.pending c.pending = 0 @@ -604,9 +640,11 @@ func (c *conn) Do(cmd string, args ...interface{}) (interface{}, error) { return nil, c.fatal(err) } - if c.readTimeout != 0 { - c.conn.SetReadDeadline(time.Now().Add(c.readTimeout)) + var deadline time.Time + if readTimeout != 0 { + deadline = time.Now().Add(readTimeout) } + c.conn.SetReadDeadline(deadline) if cmd == "" { reply := make([]interface{}, pending) diff --git a/src/vendor/github.com/garyburd/redigo/redis/conn_test.go b/src/vendor/github.com/garyburd/redigo/redis/conn_test.go index 175f7ba65..12fe5263e 100644 --- a/src/vendor/github.com/garyburd/redigo/redis/conn_test.go +++ b/src/vendor/github.com/garyburd/redigo/redis/conn_test.go @@ -34,14 +34,16 @@ import ( type testConn struct { io.Reader io.Writer + readDeadline time.Time + writeDeadline time.Time } -func (*testConn) Close() error { return nil } -func (*testConn) LocalAddr() net.Addr { return nil } -func (*testConn) RemoteAddr() net.Addr { return nil } -func (*testConn) SetDeadline(t time.Time) error { return nil } -func (*testConn) SetReadDeadline(t time.Time) error { return nil } -func (*testConn) SetWriteDeadline(t time.Time) error { return nil } +func (*testConn) Close() error { return nil } +func (*testConn) LocalAddr() net.Addr { return nil } +func (*testConn) RemoteAddr() net.Addr { return nil } +func (c *testConn) SetDeadline(t time.Time) error { c.readDeadline = t; c.writeDeadline = t; return nil } +func (c *testConn) SetReadDeadline(t time.Time) error { c.readDeadline = t; return nil } +func (c *testConn) SetWriteDeadline(t time.Time) error { c.writeDeadline = t; return nil } func dialTestConn(r string, w io.Writer) redis.DialOption { return redis.DialNetDial(func(network, addr string) (net.Conn, error) { @@ -821,3 +823,45 @@ Bjqn3yoLHaoZVvbWOi0C2TCN4FjXjaLNZGifQPbIcaA= clientTLSConfig.RootCAs = x509.NewCertPool() clientTLSConfig.RootCAs.AddCert(certificate) } + +func TestWithTimeout(t *testing.T) { + for _, recv := range []bool{true, false} { + for _, defaultTimout := range []time.Duration{0, time.Minute} { + var buf bytes.Buffer + nc := &testConn{Reader: strings.NewReader("+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n"), Writer: &buf} + c, _ := redis.Dial("", "", redis.DialReadTimeout(defaultTimout), redis.DialNetDial(func(network, addr string) (net.Conn, error) { return nc, nil })) + for i := 0; i < 4; i++ { + var minDeadline, maxDeadline time.Time + + // Alternate between default and specified timeout. + if i%2 == 0 { + if defaultTimout != 0 { + minDeadline = time.Now().Add(defaultTimout) + } + if recv { + c.Receive() + } else { + c.Do("PING") + } + if defaultTimout != 0 { + maxDeadline = time.Now().Add(defaultTimout) + } + } else { + timeout := 10 * time.Minute + minDeadline = time.Now().Add(timeout) + if recv { + redis.ReceiveWithTimeout(c, timeout) + } else { + redis.DoWithTimeout(c, timeout, "PING") + } + maxDeadline = time.Now().Add(timeout) + } + + // Expect set deadline in expected range. + if nc.readDeadline.Before(minDeadline) || nc.readDeadline.After(maxDeadline) { + t.Errorf("recv %v, %d: do deadline error: %v, %v, %v", recv, i, minDeadline, nc.readDeadline, maxDeadline) + } + } + } + } +} diff --git a/src/vendor/github.com/garyburd/redigo/redis/pre_go17.go b/src/vendor/github.com/garyburd/redigo/redis/go16.go similarity index 78% rename from src/vendor/github.com/garyburd/redigo/redis/pre_go17.go rename to src/vendor/github.com/garyburd/redigo/redis/go16.go index 0212f60fb..f6b1a7ccd 100644 --- a/src/vendor/github.com/garyburd/redigo/redis/pre_go17.go +++ b/src/vendor/github.com/garyburd/redigo/redis/go16.go @@ -4,11 +4,7 @@ package redis import "crypto/tls" -// similar cloneTLSClientConfig in the stdlib, but also honor skipVerify for the nil case -func cloneTLSClientConfig(cfg *tls.Config, skipVerify bool) *tls.Config { - if cfg == nil { - return &tls.Config{InsecureSkipVerify: skipVerify} - } +func cloneTLSConfig(cfg *tls.Config) *tls.Config { return &tls.Config{ Rand: cfg.Rand, Time: cfg.Time, diff --git a/src/vendor/github.com/garyburd/redigo/redis/go17.go b/src/vendor/github.com/garyburd/redigo/redis/go17.go index 3f951e5ef..5f3637911 100644 --- a/src/vendor/github.com/garyburd/redigo/redis/go17.go +++ b/src/vendor/github.com/garyburd/redigo/redis/go17.go @@ -1,14 +1,10 @@ -// +build go1.7 +// +build go1.7,!go1.8 package redis import "crypto/tls" -// similar cloneTLSClientConfig in the stdlib, but also honor skipVerify for the nil case -func cloneTLSClientConfig(cfg *tls.Config, skipVerify bool) *tls.Config { - if cfg == nil { - return &tls.Config{InsecureSkipVerify: skipVerify} - } +func cloneTLSConfig(cfg *tls.Config) *tls.Config { return &tls.Config{ Rand: cfg.Rand, Time: cfg.Time, diff --git a/src/vendor/github.com/garyburd/redigo/redis/go18.go b/src/vendor/github.com/garyburd/redigo/redis/go18.go new file mode 100644 index 000000000..558363be3 --- /dev/null +++ b/src/vendor/github.com/garyburd/redigo/redis/go18.go @@ -0,0 +1,9 @@ +// +build go1.8 + +package redis + +import "crypto/tls" + +func cloneTLSConfig(cfg *tls.Config) *tls.Config { + return cfg.Clone() +} diff --git a/src/vendor/github.com/garyburd/redigo/redis/list_test.go b/src/vendor/github.com/garyburd/redigo/redis/list_test.go new file mode 100644 index 000000000..a1e9d3378 --- /dev/null +++ b/src/vendor/github.com/garyburd/redigo/redis/list_test.go @@ -0,0 +1,85 @@ +// Copyright 2018 Gary Burd +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +// +build go1.9 + +package redis + +import "testing" + +func TestPoolList(t *testing.T) { + var idle idleList + var a, b, c idleConn + + check := func(ics ...*idleConn) { + if idle.count != len(ics) { + t.Fatal("idle.count != len(ics)") + } + if len(ics) == 0 { + if idle.front != nil { + t.Fatalf("front not nil") + } + if idle.back != nil { + t.Fatalf("back not nil") + } + return + } + if idle.front != ics[0] { + t.Fatal("front != ics[0]") + } + if idle.back != ics[len(ics)-1] { + t.Fatal("back != ics[len(ics)-1]") + } + if idle.front.prev != nil { + t.Fatal("front.prev != nil") + } + if idle.back.next != nil { + t.Fatal("back.next != nil") + } + for i := 1; i < len(ics)-1; i++ { + if ics[i-1].next != ics[i] { + t.Fatal("ics[i-1].next != ics[i]") + } + if ics[i+1].prev != ics[i] { + t.Fatal("ics[i+1].prev != ics[i]") + } + } + } + + idle.pushFront(&c) + check(&c) + idle.pushFront(&b) + check(&b, &c) + idle.pushFront(&a) + check(&a, &b, &c) + idle.popFront() + check(&b, &c) + idle.popFront() + check(&c) + idle.popFront() + check() + + idle.pushFront(&c) + check(&c) + idle.pushFront(&b) + check(&b, &c) + idle.pushFront(&a) + check(&a, &b, &c) + idle.popBack() + check(&a, &b) + idle.popBack() + check(&a) + idle.popBack() + check() +} diff --git a/src/vendor/github.com/garyburd/redigo/redis/log.go b/src/vendor/github.com/garyburd/redigo/redis/log.go index 129b86d67..b2996611c 100644 --- a/src/vendor/github.com/garyburd/redigo/redis/log.go +++ b/src/vendor/github.com/garyburd/redigo/redis/log.go @@ -18,6 +18,11 @@ import ( "bytes" "fmt" "log" + "time" +) + +var ( + _ ConnWithTimeout = (*loggingConn)(nil) ) // NewLoggingConn returns a logging wrapper around a connection. @@ -104,6 +109,12 @@ func (c *loggingConn) Do(commandName string, args ...interface{}) (interface{}, return reply, err } +func (c *loggingConn) DoWithTimeout(timeout time.Duration, commandName string, args ...interface{}) (interface{}, error) { + reply, err := DoWithTimeout(c.Conn, timeout, commandName, args...) + c.print("DoWithTimeout", commandName, args, reply, err) + return reply, err +} + func (c *loggingConn) Send(commandName string, args ...interface{}) error { err := c.Conn.Send(commandName, args...) c.print("Send", commandName, args, nil, err) @@ -115,3 +126,9 @@ func (c *loggingConn) Receive() (interface{}, error) { c.print("Receive", "", nil, reply, err) return reply, err } + +func (c *loggingConn) ReceiveWithTimeout(timeout time.Duration) (interface{}, error) { + reply, err := ReceiveWithTimeout(c.Conn, timeout) + c.print("ReceiveWithTimeout", "", nil, reply, err) + return reply, err +} diff --git a/src/vendor/github.com/garyburd/redigo/redis/pool.go b/src/vendor/github.com/garyburd/redigo/redis/pool.go index 109be283f..3e6f4260a 100644 --- a/src/vendor/github.com/garyburd/redigo/redis/pool.go +++ b/src/vendor/github.com/garyburd/redigo/redis/pool.go @@ -16,18 +16,23 @@ package redis import ( "bytes" - "container/list" "crypto/rand" "crypto/sha1" "errors" "io" "strconv" "sync" + "sync/atomic" "time" "github.com/garyburd/redigo/internal" ) +var ( + _ ConnWithTimeout = (*pooledConnection)(nil) + _ ConnWithTimeout = (*errorConnection)(nil) +) + var nowFunc = time.Now // for testing // ErrPoolExhausted is returned from a pool connection method (Do, Send, @@ -96,7 +101,7 @@ var ( // return nil, err // } // return c, nil -// } +// }, // } // // Use the TestOnBorrow function to check the health of an idle connection @@ -145,19 +150,13 @@ type Pool struct { // for a connection to be returned to the pool before returning. Wait bool - // mu protects fields defined below. - mu sync.Mutex - cond *sync.Cond - closed bool - active int + chInitialized uint32 // set to 1 when field ch is initialized - // Stack of idleConn with most recently used at the front. - idle list.List -} - -type idleConn struct { - c Conn - t time.Time + mu sync.Mutex // mu protects the following fields + closed bool // set to true when the pool is closed. + active int // the number of open connections in the pool + ch chan struct{} // limits open connections when p.Wait is true + idle idleList // idle connections } // NewPool creates a new pool. @@ -173,7 +172,7 @@ func NewPool(newFn func() (Conn, error), maxIdle int) *Pool { // getting an underlying connection, then the connection Err, Do, Send, Flush // and Receive methods return that error. func (p *Pool) Get() Conn { - c, err := p.get() + c, err := p.get(nil) if err != nil { return errorConnection{err} } @@ -182,7 +181,8 @@ func (p *Pool) Get() Conn { // PoolStats contains pool statistics. type PoolStats struct { - // ActiveCount is the number of connections in the pool. The count includes idle connections and connections in use. + // ActiveCount is the number of connections in the pool. The count includes + // idle connections and connections in use. ActiveCount int // IdleCount is the number of idle connections in the pool. IdleCount int @@ -193,14 +193,15 @@ func (p *Pool) Stats() PoolStats { p.mu.Lock() stats := PoolStats{ ActiveCount: p.active, - IdleCount: p.idle.Len(), + IdleCount: p.idle.count, } p.mu.Unlock() return stats } -// ActiveCount returns the number of connections in the pool. The count includes idle connections and connections in use. +// ActiveCount returns the number of connections in the pool. The count +// includes idle connections and connections in use. func (p *Pool) ActiveCount() int { p.mu.Lock() active := p.active @@ -211,7 +212,7 @@ func (p *Pool) ActiveCount() int { // IdleCount returns the number of idle connections in the pool. func (p *Pool) IdleCount() int { p.mu.Lock() - idle := p.idle.Len() + idle := p.idle.count p.mu.Unlock() return idle } @@ -219,132 +220,146 @@ func (p *Pool) IdleCount() int { // Close releases the resources used by the pool. func (p *Pool) Close() error { p.mu.Lock() - idle := p.idle - p.idle.Init() + if p.closed { + p.mu.Unlock() + return nil + } p.closed = true - p.active -= idle.Len() - if p.cond != nil { - p.cond.Broadcast() + p.active -= p.idle.count + ic := p.idle.front + p.idle.count = 0 + p.idle.front, p.idle.back = nil, nil + if p.ch != nil { + close(p.ch) } p.mu.Unlock() - for e := idle.Front(); e != nil; e = e.Next() { - e.Value.(idleConn).c.Close() + for ; ic != nil; ic = ic.next { + ic.c.Close() } return nil } -// release decrements the active count and signals waiters. The caller must -// hold p.mu during the call. -func (p *Pool) release() { - p.active -= 1 - if p.cond != nil { - p.cond.Signal() +func (p *Pool) lazyInit() { + // Fast path. + if atomic.LoadUint32(&p.chInitialized) == 1 { + return } + // Slow path. + p.mu.Lock() + if p.chInitialized == 0 { + p.ch = make(chan struct{}, p.MaxActive) + if p.closed { + close(p.ch) + } else { + for i := 0; i < p.MaxActive; i++ { + p.ch <- struct{}{} + } + } + atomic.StoreUint32(&p.chInitialized, 1) + } + p.mu.Unlock() } // get prunes stale connections and returns a connection from the idle list or // creates a new connection. -func (p *Pool) get() (Conn, error) { +func (p *Pool) get(ctx interface { + Done() <-chan struct{} + Err() error +}) (Conn, error) { + + // Handle limit for p.Wait == true. + if p.Wait && p.MaxActive > 0 { + p.lazyInit() + if ctx == nil { + <-p.ch + } else { + select { + case <-p.ch: + case <-ctx.Done(): + return nil, ctx.Err() + } + } + } + p.mu.Lock() - // Prune stale connections. - - if timeout := p.IdleTimeout; timeout > 0 { - for i, n := 0, p.idle.Len(); i < n; i++ { - e := p.idle.Back() - if e == nil { - break - } - ic := e.Value.(idleConn) - if ic.t.Add(timeout).After(nowFunc()) { - break - } - p.idle.Remove(e) - p.release() + // Prune stale connections at the back of the idle list. + if p.IdleTimeout > 0 { + n := p.idle.count + for i := 0; i < n && p.idle.back != nil && p.idle.back.t.Add(p.IdleTimeout).Before(nowFunc()); i++ { + c := p.idle.back.c + p.idle.popBack() p.mu.Unlock() - ic.c.Close() + c.Close() p.mu.Lock() + p.active-- } } - for { - // Get idle connection. - - for i, n := 0, p.idle.Len(); i < n; i++ { - e := p.idle.Front() - if e == nil { - break - } - ic := e.Value.(idleConn) - p.idle.Remove(e) - test := p.TestOnBorrow - p.mu.Unlock() - if test == nil || test(ic.c, ic.t) == nil { - return ic.c, nil - } - ic.c.Close() - p.mu.Lock() - p.release() + // Get idle connection from the front of idle list. + for p.idle.front != nil { + ic := p.idle.front + p.idle.popFront() + p.mu.Unlock() + if p.TestOnBorrow == nil || p.TestOnBorrow(ic.c, ic.t) == nil { + return ic.c, nil } - - // Check for pool closed before dialing a new connection. - - if p.closed { - p.mu.Unlock() - return nil, errors.New("redigo: get on closed pool") - } - - // Dial new connection if under limit. - - if p.MaxActive == 0 || p.active < p.MaxActive { - dial := p.Dial - p.active += 1 - p.mu.Unlock() - c, err := dial() - if err != nil { - p.mu.Lock() - p.release() - p.mu.Unlock() - c = nil - } - return c, err - } - - if !p.Wait { - p.mu.Unlock() - return nil, ErrPoolExhausted - } - - if p.cond == nil { - p.cond = sync.NewCond(&p.mu) - } - p.cond.Wait() + ic.c.Close() + p.mu.Lock() + p.active-- } + + // Check for pool closed before dialing a new connection. + if p.closed { + p.mu.Unlock() + return nil, errors.New("redigo: get on closed pool") + } + + // Handle limit for p.Wait == false. + if !p.Wait && p.MaxActive > 0 && p.active >= p.MaxActive { + p.mu.Unlock() + return nil, ErrPoolExhausted + } + + p.active++ + p.mu.Unlock() + c, err := p.Dial() + if err != nil { + c = nil + p.mu.Lock() + p.active-- + if p.ch != nil && !p.closed { + p.ch <- struct{}{} + } + p.mu.Unlock() + } + return c, err } func (p *Pool) put(c Conn, forceClose bool) error { - err := c.Err() p.mu.Lock() - if !p.closed && err == nil && !forceClose { - p.idle.PushFront(idleConn{t: nowFunc(), c: c}) - if p.idle.Len() > p.MaxIdle { - c = p.idle.Remove(p.idle.Back()).(idleConn).c + if !p.closed && !forceClose { + p.idle.pushFront(&idleConn{t: nowFunc(), c: c}) + if p.idle.count > p.MaxIdle { + c = p.idle.back.c + p.idle.popBack() } else { c = nil } } - if c == nil { - if p.cond != nil { - p.cond.Signal() - } + if c != nil { p.mu.Unlock() - return nil + c.Close() + p.mu.Lock() + p.active-- } - p.release() + if p.ch != nil && !p.closed { + p.ch <- struct{}{} + } p.mu.Unlock() - return c.Close() + return nil } type pooledConnection struct { @@ -404,7 +419,7 @@ func (pc *pooledConnection) Close() error { } } c.Do("") - pc.p.put(c, pc.state != 0) + pc.p.put(c, pc.state != 0 || c.Err() != nil) return nil } @@ -418,6 +433,16 @@ func (pc *pooledConnection) Do(commandName string, args ...interface{}) (reply i return pc.c.Do(commandName, args...) } +func (pc *pooledConnection) DoWithTimeout(timeout time.Duration, commandName string, args ...interface{}) (reply interface{}, err error) { + cwt, ok := pc.c.(ConnWithTimeout) + if !ok { + return nil, errTimeoutNotSupported + } + ci := internal.LookupCommandInfo(commandName) + pc.state = (pc.state | ci.Set) &^ ci.Clear + return cwt.DoWithTimeout(timeout, commandName, args...) +} + func (pc *pooledConnection) Send(commandName string, args ...interface{}) error { ci := internal.LookupCommandInfo(commandName) pc.state = (pc.state | ci.Set) &^ ci.Clear @@ -432,11 +457,71 @@ func (pc *pooledConnection) Receive() (reply interface{}, err error) { return pc.c.Receive() } +func (pc *pooledConnection) ReceiveWithTimeout(timeout time.Duration) (reply interface{}, err error) { + cwt, ok := pc.c.(ConnWithTimeout) + if !ok { + return nil, errTimeoutNotSupported + } + return cwt.ReceiveWithTimeout(timeout) +} + type errorConnection struct{ err error } func (ec errorConnection) Do(string, ...interface{}) (interface{}, error) { return nil, ec.err } -func (ec errorConnection) Send(string, ...interface{}) error { return ec.err } -func (ec errorConnection) Err() error { return ec.err } -func (ec errorConnection) Close() error { return ec.err } -func (ec errorConnection) Flush() error { return ec.err } -func (ec errorConnection) Receive() (interface{}, error) { return nil, ec.err } +func (ec errorConnection) DoWithTimeout(time.Duration, string, ...interface{}) (interface{}, error) { + return nil, ec.err +} +func (ec errorConnection) Send(string, ...interface{}) error { return ec.err } +func (ec errorConnection) Err() error { return ec.err } +func (ec errorConnection) Close() error { return nil } +func (ec errorConnection) Flush() error { return ec.err } +func (ec errorConnection) Receive() (interface{}, error) { return nil, ec.err } +func (ec errorConnection) ReceiveWithTimeout(time.Duration) (interface{}, error) { return nil, ec.err } + +type idleList struct { + count int + front, back *idleConn +} + +type idleConn struct { + c Conn + t time.Time + next, prev *idleConn +} + +func (l *idleList) pushFront(ic *idleConn) { + ic.next = l.front + ic.prev = nil + if l.count == 0 { + l.back = ic + } else { + l.front.prev = ic + } + l.front = ic + l.count++ + return +} + +func (l *idleList) popFront() { + ic := l.front + l.count-- + if l.count == 0 { + l.front, l.back = nil, nil + } else { + ic.next.prev = nil + l.front = ic.next + } + ic.next, ic.prev = nil, nil +} + +func (l *idleList) popBack() { + ic := l.back + l.count-- + if l.count == 0 { + l.front, l.back = nil, nil + } else { + ic.prev.next = nil + l.back = ic.prev + } + ic.next, ic.prev = nil, nil +} diff --git a/src/vendor/github.com/garyburd/redigo/redis/pool17.go b/src/vendor/github.com/garyburd/redigo/redis/pool17.go new file mode 100644 index 000000000..57a22644f --- /dev/null +++ b/src/vendor/github.com/garyburd/redigo/redis/pool17.go @@ -0,0 +1,35 @@ +// Copyright 2018 Gary Burd +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +// +build go1.7 + +package redis + +import "context" + +// GetContext gets a connection using the provided context. +// +// The provided Context must be non-nil. If the context expires before the +// connection is complete, an error is returned. Any expiration on the context +// will not affect the returned connection. +// +// If the function completes without error, then the application must close the +// returned connection. +func (p *Pool) GetContext(ctx context.Context) (Conn, error) { + c, err := p.get(ctx) + if err != nil { + return errorConnection{err}, err + } + return &pooledConnection{p: p, c: c}, nil +} diff --git a/src/vendor/github.com/garyburd/redigo/redis/pool17_test.go b/src/vendor/github.com/garyburd/redigo/redis/pool17_test.go new file mode 100644 index 000000000..51ef4b643 --- /dev/null +++ b/src/vendor/github.com/garyburd/redigo/redis/pool17_test.go @@ -0,0 +1,58 @@ +// Copyright 2018 Gary Burd +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +// +build go1.7 + +package redis_test + +import ( + "context" + "testing" + + "github.com/garyburd/redigo/redis" +) + +func TestWaitPoolGetAfterClose(t *testing.T) { + d := poolDialer{t: t} + p := &redis.Pool{ + MaxIdle: 1, + MaxActive: 1, + Dial: d.dial, + Wait: true, + } + p.Close() + _, err := p.GetContext(context.Background()) + if err == nil { + t.Fatal("expected error") + } +} + +func TestWaitPoolGetCanceledContext(t *testing.T) { + d := poolDialer{t: t} + p := &redis.Pool{ + MaxIdle: 1, + MaxActive: 1, + Dial: d.dial, + Wait: true, + } + defer p.Close() + ctx, f := context.WithCancel(context.Background()) + f() + c := p.Get() + defer c.Close() + _, err := p.GetContext(ctx) + if err != context.Canceled { + t.Fatalf("got error %v, want %v", err, context.Canceled) + } +} diff --git a/src/vendor/github.com/garyburd/redigo/redis/pool_test.go b/src/vendor/github.com/garyburd/redigo/redis/pool_test.go index b1c510679..e8b55781e 100644 --- a/src/vendor/github.com/garyburd/redigo/redis/pool_test.go +++ b/src/vendor/github.com/garyburd/redigo/redis/pool_test.go @@ -231,7 +231,7 @@ func TestPoolTimeout(t *testing.T) { d.check("1", p, 1, 1, 0) - now = now.Add(p.IdleTimeout) + now = now.Add(p.IdleTimeout + 1) c = p.Get() c.Do("PING") @@ -445,9 +445,6 @@ func startGoroutines(p *redis.Pool, cmd string, args ...interface{}) chan error }() } - // Wait for goroutines to block. - time.Sleep(time.Second / 4) - return errs } diff --git a/src/vendor/github.com/garyburd/redigo/redis/pubsub.go b/src/vendor/github.com/garyburd/redigo/redis/pubsub.go index 9d43f4ae9..f0ac82532 100644 --- a/src/vendor/github.com/garyburd/redigo/redis/pubsub.go +++ b/src/vendor/github.com/garyburd/redigo/redis/pubsub.go @@ -14,7 +14,10 @@ package redis -import "errors" +import ( + "errors" + "time" +) // Subscription represents a subscribe or unsubscribe notification. type Subscription struct { @@ -103,7 +106,17 @@ func (c PubSubConn) Ping(data string) error { // or error. The return value is intended to be used directly in a type switch // as illustrated in the PubSubConn example. func (c PubSubConn) Receive() interface{} { - reply, err := Values(c.Conn.Receive()) + return c.receiveInternal(c.Conn.Receive()) +} + +// ReceiveWithTimeout is like Receive, but it allows the application to +// override the connection's default timeout. +func (c PubSubConn) ReceiveWithTimeout(timeout time.Duration) interface{} { + return c.receiveInternal(ReceiveWithTimeout(c.Conn, timeout)) +} + +func (c PubSubConn) receiveInternal(replyArg interface{}, errArg error) interface{} { + reply, err := Values(replyArg, errArg) if err != nil { return err } diff --git a/src/vendor/github.com/garyburd/redigo/redis/pubsub_example_test.go b/src/vendor/github.com/garyburd/redigo/redis/pubsub_example_test.go new file mode 100644 index 000000000..61e2f7414 --- /dev/null +++ b/src/vendor/github.com/garyburd/redigo/redis/pubsub_example_test.go @@ -0,0 +1,165 @@ +// Copyright 2012 Gary Burd +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +// +build go1.7 + +package redis_test + +import ( + "context" + "fmt" + "time" + + "github.com/garyburd/redigo/redis" +) + +// listenPubSubChannels listens for messages on Redis pubsub channels. The +// onStart function is called after the channels are subscribed. The onMessage +// function is called for each message. +func listenPubSubChannels(ctx context.Context, redisServerAddr string, + onStart func() error, + onMessage func(channel string, data []byte) error, + channels ...string) error { + // A ping is set to the server with this period to test for the health of + // the connection and server. + const healthCheckPeriod = time.Minute + + c, err := redis.Dial("tcp", redisServerAddr, + // Read timeout on server should be greater than ping period. + redis.DialReadTimeout(healthCheckPeriod+10*time.Second), + redis.DialWriteTimeout(10*time.Second)) + if err != nil { + return err + } + defer c.Close() + + psc := redis.PubSubConn{Conn: c} + + if err := psc.Subscribe(redis.Args{}.AddFlat(channels)...); err != nil { + return err + } + + done := make(chan error, 1) + + // Start a goroutine to receive notifications from the server. + go func() { + for { + switch n := psc.Receive().(type) { + case error: + done <- n + return + case redis.Message: + if err := onMessage(n.Channel, n.Data); err != nil { + done <- err + return + } + case redis.Subscription: + switch n.Count { + case len(channels): + // Notify application when all channels are subscribed. + if err := onStart(); err != nil { + done <- err + return + } + case 0: + // Return from the goroutine when all channels are unsubscribed. + done <- nil + return + } + } + } + }() + + ticker := time.NewTicker(healthCheckPeriod) + defer ticker.Stop() +loop: + for err == nil { + select { + case <-ticker.C: + // Send ping to test health of connection and server. If + // corresponding pong is not received, then receive on the + // connection will timeout and the receive goroutine will exit. + if err = psc.Ping(""); err != nil { + break loop + } + case <-ctx.Done(): + break loop + case err := <-done: + // Return error from the receive goroutine. + return err + } + } + + // Signal the receiving goroutine to exit by unsubscribing from all channels. + psc.Unsubscribe() + + // Wait for goroutine to complete. + return <-done +} + +func publish() { + c, err := dial() + if err != nil { + fmt.Println(err) + return + } + defer c.Close() + + c.Do("PUBLISH", "c1", "hello") + c.Do("PUBLISH", "c2", "world") + c.Do("PUBLISH", "c1", "goodbye") +} + +// This example shows how receive pubsub notifications with cancelation and +// health checks. +func ExamplePubSubConn() { + redisServerAddr, err := serverAddr() + if err != nil { + fmt.Println(err) + return + } + + ctx, cancel := context.WithCancel(context.Background()) + + err = listenPubSubChannels(ctx, + redisServerAddr, + func() error { + // The start callback is a good place to backfill missed + // notifications. For the purpose of this example, a goroutine is + // started to send notifications. + go publish() + return nil + }, + func(channel string, message []byte) error { + fmt.Printf("channel: %s, message: %s\n", channel, message) + + // For the purpose of this example, cancel the listener's context + // after receiving last message sent by publish(). + if string(message) == "goodbye" { + cancel() + } + return nil + }, + "c1", "c2") + + if err != nil { + fmt.Println(err) + return + } + + // Output: + // channel: c1, message: hello + // channel: c2, message: world + // channel: c1, message: goodbye +} diff --git a/src/vendor/github.com/garyburd/redigo/redis/pubsub_test.go b/src/vendor/github.com/garyburd/redigo/redis/pubsub_test.go index b95513155..6ccdbee7e 100644 --- a/src/vendor/github.com/garyburd/redigo/redis/pubsub_test.go +++ b/src/vendor/github.com/garyburd/redigo/redis/pubsub_test.go @@ -15,93 +15,13 @@ package redis_test import ( - "fmt" "reflect" - "sync" "testing" + "time" "github.com/garyburd/redigo/redis" ) -func publish(channel, value interface{}) { - c, err := dial() - if err != nil { - fmt.Println(err) - return - } - defer c.Close() - c.Do("PUBLISH", channel, value) -} - -// Applications can receive pushed messages from one goroutine and manage subscriptions from another goroutine. -func ExamplePubSubConn() { - c, err := dial() - if err != nil { - fmt.Println(err) - return - } - defer c.Close() - var wg sync.WaitGroup - wg.Add(2) - - psc := redis.PubSubConn{Conn: c} - - // This goroutine receives and prints pushed notifications from the server. - // The goroutine exits when the connection is unsubscribed from all - // channels or there is an error. - go func() { - defer wg.Done() - for { - switch n := psc.Receive().(type) { - case redis.Message: - fmt.Printf("Message: %s %s\n", n.Channel, n.Data) - case redis.PMessage: - fmt.Printf("PMessage: %s %s %s\n", n.Pattern, n.Channel, n.Data) - case redis.Subscription: - fmt.Printf("Subscription: %s %s %d\n", n.Kind, n.Channel, n.Count) - if n.Count == 0 { - return - } - case error: - fmt.Printf("error: %v\n", n) - return - } - } - }() - - // This goroutine manages subscriptions for the connection. - go func() { - defer wg.Done() - - psc.Subscribe("example") - psc.PSubscribe("p*") - - // The following function calls publish a message using another - // connection to the Redis server. - publish("example", "hello") - publish("example", "world") - publish("pexample", "foo") - publish("pexample", "bar") - - // Unsubscribe from all connections. This will cause the receiving - // goroutine to exit. - psc.Unsubscribe() - psc.PUnsubscribe() - }() - - wg.Wait() - - // Output: - // Subscription: subscribe example 1 - // Subscription: psubscribe p* 2 - // Message: example hello - // Message: example world - // PMessage: p* pexample foo - // PMessage: p* pexample bar - // Subscription: unsubscribe example 1 - // Subscription: punsubscribe p* 0 -} - func expectPushed(t *testing.T, c redis.PubSubConn, message string, expected interface{}) { actual := c.Receive() if !reflect.DeepEqual(actual, expected) { @@ -145,4 +65,10 @@ func TestPushed(t *testing.T) { c.Conn.Send("PING") c.Conn.Flush() expectPushed(t, c, `Send("PING")`, redis.Pong{}) + + c.Ping("timeout") + got := c.ReceiveWithTimeout(time.Minute) + if want := (redis.Pong{Data: "timeout"}); want != got { + t.Errorf("recv /w timeout got %v, want %v", got, want) + } } diff --git a/src/vendor/github.com/garyburd/redigo/redis/redis.go b/src/vendor/github.com/garyburd/redigo/redis/redis.go index 6bd9824b6..141fa4a91 100644 --- a/src/vendor/github.com/garyburd/redigo/redis/redis.go +++ b/src/vendor/github.com/garyburd/redigo/redis/redis.go @@ -14,6 +14,11 @@ package redis +import ( + "errors" + "time" +) + // Error represents an error returned in a command reply. type Error string @@ -59,3 +64,54 @@ type Scanner interface { // loss of information. RedisScan(src interface{}) error } + +// ConnWithTimeout is an optional interface that allows the caller to override +// a connection's default read timeout. This interface is useful for executing +// the BLPOP, BRPOP, BRPOPLPUSH, XREAD and other commands that block at the +// server. +// +// A connection's default read timeout is set with the DialReadTimeout dial +// option. Applications should rely on the default timeout for commands that do +// not block at the server. +// +// All of the Conn implementations in this package satisfy the ConnWithTimeout +// interface. +// +// Use the DoWithTimeout and ReceiveWithTimeout helper functions to simplify +// use of this interface. +type ConnWithTimeout interface { + Conn + + // Do sends a command to the server and returns the received reply. + // The timeout overrides the read timeout set when dialing the + // connection. + DoWithTimeout(timeout time.Duration, commandName string, args ...interface{}) (reply interface{}, err error) + + // Receive receives a single reply from the Redis server. The timeout + // overrides the read timeout set when dialing the connection. + ReceiveWithTimeout(timeout time.Duration) (reply interface{}, err error) +} + +var errTimeoutNotSupported = errors.New("redis: connection does not support ConnWithTimeout") + +// DoWithTimeout executes a Redis command with the specified read timeout. If +// the connection does not satisfy the ConnWithTimeout interface, then an error +// is returned. +func DoWithTimeout(c Conn, timeout time.Duration, cmd string, args ...interface{}) (interface{}, error) { + cwt, ok := c.(ConnWithTimeout) + if !ok { + return nil, errTimeoutNotSupported + } + return cwt.DoWithTimeout(timeout, cmd, args...) +} + +// ReceiveWithTimeout receives a reply with the specified read timeout. If the +// connection does not satisfy the ConnWithTimeout interface, then an error is +// returned. +func ReceiveWithTimeout(c Conn, timeout time.Duration) (interface{}, error) { + cwt, ok := c.(ConnWithTimeout) + if !ok { + return nil, errTimeoutNotSupported + } + return cwt.ReceiveWithTimeout(timeout) +} diff --git a/src/vendor/github.com/garyburd/redigo/redis/redis_test.go b/src/vendor/github.com/garyburd/redigo/redis/redis_test.go new file mode 100644 index 000000000..494b88de7 --- /dev/null +++ b/src/vendor/github.com/garyburd/redigo/redis/redis_test.go @@ -0,0 +1,71 @@ +// Copyright 2017 Gary Burd +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package redis_test + +import ( + "testing" + "time" + + "github.com/garyburd/redigo/redis" +) + +type timeoutTestConn int + +func (tc timeoutTestConn) Do(string, ...interface{}) (interface{}, error) { + return time.Duration(-1), nil +} +func (tc timeoutTestConn) DoWithTimeout(timeout time.Duration, cmd string, args ...interface{}) (interface{}, error) { + return timeout, nil +} + +func (tc timeoutTestConn) Receive() (interface{}, error) { + return time.Duration(-1), nil +} +func (tc timeoutTestConn) ReceiveWithTimeout(timeout time.Duration) (interface{}, error) { + return timeout, nil +} + +func (tc timeoutTestConn) Send(string, ...interface{}) error { return nil } +func (tc timeoutTestConn) Err() error { return nil } +func (tc timeoutTestConn) Close() error { return nil } +func (tc timeoutTestConn) Flush() error { return nil } + +func testTimeout(t *testing.T, c redis.Conn) { + r, err := c.Do("PING") + if r != time.Duration(-1) || err != nil { + t.Errorf("Do() = %v, %v, want %v, %v", r, err, time.Duration(-1), nil) + } + r, err = redis.DoWithTimeout(c, time.Minute, "PING") + if r != time.Minute || err != nil { + t.Errorf("DoWithTimeout() = %v, %v, want %v, %v", r, err, time.Minute, nil) + } + r, err = c.Receive() + if r != time.Duration(-1) || err != nil { + t.Errorf("Receive() = %v, %v, want %v, %v", r, err, time.Duration(-1), nil) + } + r, err = redis.ReceiveWithTimeout(c, time.Minute) + if r != time.Minute || err != nil { + t.Errorf("ReceiveWithTimeout() = %v, %v, want %v, %v", r, err, time.Minute, nil) + } +} + +func TestConnTimeout(t *testing.T) { + testTimeout(t, timeoutTestConn(0)) +} + +func TestPoolConnTimeout(t *testing.T) { + p := &redis.Pool{Dial: func() (redis.Conn, error) { return timeoutTestConn(0), nil }} + testTimeout(t, p.Get()) +} diff --git a/src/vendor/github.com/garyburd/redigo/redis/reply_test.go b/src/vendor/github.com/garyburd/redigo/redis/reply_test.go index 12976c566..138845463 100644 --- a/src/vendor/github.com/garyburd/redigo/redis/reply_test.go +++ b/src/vendor/github.com/garyburd/redigo/redis/reply_test.go @@ -140,6 +140,11 @@ func dial() (redis.Conn, error) { return redis.DialDefaultServer() } +// serverAddr wraps DefaultServerAddr() with a more suitable function name for examples. +func serverAddr() (string, error) { + return redis.DefaultServerAddr() +} + func ExampleBool() { c, err := dial() if err != nil { diff --git a/src/vendor/github.com/garyburd/redigo/redis/scan_test.go b/src/vendor/github.com/garyburd/redigo/redis/scan_test.go index 0c1956185..7930c27ce 100644 --- a/src/vendor/github.com/garyburd/redigo/redis/scan_test.go +++ b/src/vendor/github.com/garyburd/redigo/redis/scan_test.go @@ -84,8 +84,8 @@ var scanConversionTests = []struct { {"1m", durationScan{Duration: time.Minute}}, {[]byte("1m"), durationScan{Duration: time.Minute}}, {time.Minute.Nanoseconds(), durationScan{Duration: time.Minute}}, - {[]interface{}{[]byte("1m")}, []durationScan{durationScan{Duration: time.Minute}}}, - {[]interface{}{[]byte("1m")}, []*durationScan{&durationScan{Duration: time.Minute}}}, + {[]interface{}{[]byte("1m")}, []durationScan{{Duration: time.Minute}}}, + {[]interface{}{[]byte("1m")}, []*durationScan{{Duration: time.Minute}}}, } func TestScanConversion(t *testing.T) { @@ -318,7 +318,7 @@ var scanSliceTests = []struct { []interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")}, nil, true, - []*struct{ A, B string }{{"a1", "b1"}, {"a2", "b2"}}, + []*struct{ A, B string }{{A: "a1", B: "b1"}, {A: "a2", B: "b2"}}, }, { []interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")}, diff --git a/src/vendor/github.com/garyburd/redigo/redis/test_test.go b/src/vendor/github.com/garyburd/redigo/redis/test_test.go index 08208185d..5c758486b 100644 --- a/src/vendor/github.com/garyburd/redigo/redis/test_test.go +++ b/src/vendor/github.com/garyburd/redigo/redis/test_test.go @@ -38,6 +38,7 @@ var ( ErrNegativeInt = errNegativeInt serverPath = flag.String("redis-server", "redis-server", "Path to redis server binary") + serverAddress = flag.String("redis-address", "127.0.0.1", "The address of the server") serverBasePort = flag.Int("redis-port", 16379, "Beginning of port range for test servers") serverLogName = flag.String("redis-log", "", "Write Redis server logs to `filename`") serverLog = ioutil.Discard @@ -126,28 +127,32 @@ func stopDefaultServer() { } } -// startDefaultServer starts the default server if not already running. -func startDefaultServer() error { +// DefaultServerAddr starts the test server if not already started and returns +// the address of that server. +func DefaultServerAddr() (string, error) { defaultServerMu.Lock() defer defaultServerMu.Unlock() + addr := fmt.Sprintf("%v:%d", *serverAddress, *serverBasePort) if defaultServer != nil || defaultServerErr != nil { - return defaultServerErr + return addr, defaultServerErr } defaultServer, defaultServerErr = NewServer( "default", "--port", strconv.Itoa(*serverBasePort), + "--bind", *serverAddress, "--save", "", "--appendonly", "no") - return defaultServerErr + return addr, defaultServerErr } // DialDefaultServer starts the test server if not already started and dials a // connection to the server. func DialDefaultServer() (Conn, error) { - if err := startDefaultServer(); err != nil { + addr, err := DefaultServerAddr() + if err != nil { return nil, err } - c, err := Dial("tcp", fmt.Sprintf(":%d", *serverBasePort), DialReadTimeout(1*time.Second), DialWriteTimeout(1*time.Second)) + c, err := Dial("tcp", addr, DialReadTimeout(1*time.Second), DialWriteTimeout(1*time.Second)) if err != nil { return nil, err } diff --git a/src/vendor/github.com/gorilla/context/.travis.yml b/src/vendor/github.com/gorilla/context/.travis.yml index 24882fc7b..faca4dad3 100644 --- a/src/vendor/github.com/gorilla/context/.travis.yml +++ b/src/vendor/github.com/gorilla/context/.travis.yml @@ -8,11 +8,12 @@ matrix: - go: 1.5 - go: 1.6 - go: tip - allow_failures: - - go: tip + +install: + - go get golang.org/x/tools/cmd/vet script: - go get -t -v ./... - diff -u <(echo -n) <(gofmt -d .) - - go vet $(go list ./... | grep -v /vendor/) + - go tool vet . - go test -v -race ./... diff --git a/src/vendor/github.com/gorilla/context/context_test.go b/src/vendor/github.com/gorilla/context/context_test.go index d70e91a23..9814c501e 100644 --- a/src/vendor/github.com/gorilla/context/context_test.go +++ b/src/vendor/github.com/gorilla/context/context_test.go @@ -69,7 +69,7 @@ func TestContext(t *testing.T) { // GetAllOk() for empty request values, ok = GetAllOk(emptyR) - assertEqual(len(values), 0) + assertEqual(value, nil) assertEqual(ok, false) // Delete()