diff --git a/Dockerfile.env b/Dockerfile.env
deleted file mode 100644
index d409b603..00000000
--- a/Dockerfile.env
+++ /dev/null
@@ -1,10 +0,0 @@
-# build environment used in .drone.yml
-#
-# docker build --rm=true -t drone/golang:1.5 -f Dockerfile.env .
-
-FROM golang:1.5
-ADD contrib/*.sh /usr/local/bin/
-RUN chmod +x /usr/local/bin/setup-sassc.sh && \
- chmod +x /usr/local/bin/setup-sqlite.sh && \
- /usr/local/bin/setup-sassc.sh && \
- /usr/local/bin/setup-sqlite.sh
diff --git a/drone.go b/drone.go
index c92281aa..fe517f61 100644
--- a/drone.go
+++ b/drone.go
@@ -1,7 +1,8 @@
package main
import (
- "flag"
+ "net/http"
+ "time"
"github.com/drone/drone/engine"
"github.com/drone/drone/remote"
@@ -10,27 +11,34 @@ import (
"github.com/drone/drone/router/middleware/context"
"github.com/drone/drone/router/middleware/header"
"github.com/drone/drone/shared/envconfig"
- "github.com/drone/drone/shared/server"
"github.com/drone/drone/store/datastore"
"github.com/Sirupsen/logrus"
+ "github.com/gin-gonic/contrib/ginrus"
+ "github.com/ianschenck/envflag"
+ _ "github.com/joho/godotenv/autoload"
)
var (
- dotenv = flag.String("config", ".env", "")
- debug = flag.Bool("debug", false, "")
+ addr = envflag.String("SERVER_ADDR", ":8000", "")
+ cert = envflag.String("SERVER_CERT", "", "")
+ key = envflag.String("SERVER_KEY", "", "")
+
+ debug = envflag.Bool("DEBUG", false, "")
)
func main() {
- flag.Parse()
+ envflag.Parse()
// debug level if requested by user
if *debug {
logrus.SetLevel(logrus.DebugLevel)
+ } else {
+ logrus.SetLevel(logrus.WarnLevel)
}
// Load the configuration from env file
- env := envconfig.Load(*dotenv)
+ env := envconfig.Load(".env")
// Setup the database driver
store_ := datastore.Load(env)
@@ -42,14 +50,22 @@ func main() {
engine_ := engine.Load(env, store_)
// setup the server and start the listener
- server_ := server.Load(env)
- server_.Run(
- router.Load(
- header.Version,
- cache.Default(),
- context.SetStore(store_),
- context.SetRemote(remote_),
- context.SetEngine(engine_),
- ),
+ handler := router.Load(
+ ginrus.Ginrus(logrus.StandardLogger(), time.RFC3339, true),
+ header.Version,
+ cache.Default(),
+ context.SetStore(store_),
+ context.SetRemote(remote_),
+ context.SetEngine(engine_),
)
+
+ if *cert != "" {
+ logrus.Fatal(
+ http.ListenAndServeTLS(*addr, *cert, *key, handler),
+ )
+ } else {
+ logrus.Fatal(
+ http.ListenAndServe(*addr, handler),
+ )
+ }
}
diff --git a/router/router.go b/router/router.go
index 6d313646..b44ccb32 100644
--- a/router/router.go
+++ b/router/router.go
@@ -17,7 +17,9 @@ import (
)
func Load(middleware ...gin.HandlerFunc) http.Handler {
- e := gin.Default()
+ e := gin.New()
+ e.Use(gin.Recovery())
+
e.SetHTMLTemplate(template.Load())
e.StaticFS("/static", static.FileSystem())
diff --git a/shared/server/server.go b/shared/server/server.go
deleted file mode 100644
index 9c3fafe8..00000000
--- a/shared/server/server.go
+++ /dev/null
@@ -1,36 +0,0 @@
-package server
-
-import (
- "net/http"
-
- log "github.com/Sirupsen/logrus"
- "github.com/drone/drone/shared/envconfig"
-)
-
-type Server struct {
- Addr string
- Cert string
- Key string
-}
-
-func Load(env envconfig.Env) *Server {
- return &Server{
- Addr: env.String("SERVER_ADDR", ":8000"),
- Cert: env.String("SERVER_CERT", ""),
- Key: env.String("SERVER_KEY", ""),
- }
-}
-
-func (s *Server) Run(handler http.Handler) {
- log.Infof("starting server %s", s.Addr)
-
- if len(s.Cert) != 0 {
- log.Fatal(
- http.ListenAndServeTLS(s.Addr, s.Cert, s.Key, handler),
- )
- } else {
- log.Fatal(
- http.ListenAndServe(s.Addr, handler),
- )
- }
-}
diff --git a/vendor/github.com/Sirupsen/logrus/CHANGELOG.md b/vendor/github.com/Sirupsen/logrus/CHANGELOG.md
new file mode 100644
index 00000000..f2c2bc21
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/CHANGELOG.md
@@ -0,0 +1,66 @@
+# 0.10.0
+
+* feature: Add a test hook (#180)
+* feature: `ParseLevel` is now case-insensitive (#326)
+* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308)
+* performance: avoid re-allocations on `WithFields` (#335)
+
+# 0.9.0
+
+* logrus/text_formatter: don't emit empty msg
+* logrus/hooks/airbrake: move out of main repository
+* logrus/hooks/sentry: move out of main repository
+* logrus/hooks/papertrail: move out of main repository
+* logrus/hooks/bugsnag: move out of main repository
+* logrus/core: run tests with `-race`
+* logrus/core: detect TTY based on `stderr`
+* logrus/core: support `WithError` on logger
+* logrus/core: Solaris support
+
+# 0.8.7
+
+* logrus/core: fix possible race (#216)
+* logrus/doc: small typo fixes and doc improvements
+
+
+# 0.8.6
+
+* hooks/raven: allow passing an initialized client
+
+# 0.8.5
+
+* logrus/core: revert #208
+
+# 0.8.4
+
+* formatter/text: fix data race (#218)
+
+# 0.8.3
+
+* logrus/core: fix entry log level (#208)
+* logrus/core: improve performance of text formatter by 40%
+* logrus/core: expose `LevelHooks` type
+* logrus/core: add support for DragonflyBSD and NetBSD
+* formatter/text: print structs more verbosely
+
+# 0.8.2
+
+* logrus: fix more Fatal family functions
+
+# 0.8.1
+
+* logrus: fix not exiting on `Fatalf` and `Fatalln`
+
+# 0.8.0
+
+* logrus: defaults to stderr instead of stdout
+* hooks/sentry: add special field for `*http.Request`
+* formatter/text: ignore Windows for colors
+
+# 0.7.3
+
+* formatter/\*: allow configuration of timestamp layout
+
+# 0.7.2
+
+* formatter/text: Add configuration option for time format (#158)
diff --git a/vendor/github.com/Sirupsen/logrus/README.md b/vendor/github.com/Sirupsen/logrus/README.md
index ef6501f1..6e1721a7 100644
--- a/vendor/github.com/Sirupsen/logrus/README.md
+++ b/vendor/github.com/Sirupsen/logrus/README.md
@@ -1,4 +1,4 @@
-# Logrus [![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus) [![godoc reference](https://godoc.org/github.com/Sirupsen/logrus?status.png)][godoc]
+# Logrus [![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
@@ -12,7 +12,7 @@ plain text):
![Colored](http://i.imgur.com/PY7qMwd.png)
-With `log.Formatter = new(logrus.JSONFormatter)`, for easy parsing by logstash
+With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash
or Splunk:
```json
@@ -32,16 +32,18 @@ ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
```
-With the default `log.Formatter = new(logrus.TextFormatter)` when a TTY is not
+With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not
attached, the output is compatible with the
[logfmt](http://godoc.org/github.com/kr/logfmt) format:
```text
-time="2014-04-20 15:36:23.830442383 -0400 EDT" level="info" msg="A group of walrus emerges from the ocean" animal="walrus" size=10
-time="2014-04-20 15:36:23.830584199 -0400 EDT" level="warning" msg="The group's number increased tremendously!" omg=true number=122
-time="2014-04-20 15:36:23.830596521 -0400 EDT" level="info" msg="A giant walrus appears!" animal="walrus" size=10
-time="2014-04-20 15:36:23.830611837 -0400 EDT" level="info" msg="Tremendously sized cow enters the ocean." animal="walrus" size=9
-time="2014-04-20 15:36:23.830626464 -0400 EDT" level="fatal" msg="The ice breaks!" omg=true number=100
+time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
+time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
+time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
+time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
+time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
+time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
+exit status 1
```
#### Example
@@ -73,17 +75,12 @@ package main
import (
"os"
log "github.com/Sirupsen/logrus"
- "github.com/Sirupsen/logrus/hooks/airbrake"
)
func init() {
// Log as JSON instead of the default ASCII formatter.
log.SetFormatter(&log.JSONFormatter{})
- // Use the Airbrake hook to report errors that have Error severity or above to
- // an exception tracker. You can create custom hooks, see the Hooks section.
- log.AddHook(&logrus_airbrake.AirbrakeHook{})
-
// Output to stderr instead of stdout, could also be a file.
log.SetOutput(os.Stderr)
@@ -106,6 +103,16 @@ func main() {
"omg": true,
"number": 100,
}).Fatal("The ice breaks!")
+
+ // A common pattern is to re-use fields between logging statements by re-using
+ // the logrus.Entry returned from WithFields()
+ contextLogger := log.WithFields(log.Fields{
+ "common": "this is a common field",
+ "other": "I also should be logged always",
+ })
+
+ contextLogger.Info("I'll be logged with common and other field")
+ contextLogger.Info("Me too")
}
```
@@ -164,54 +171,22 @@ You can add hooks for logging levels. For example to send errors to an exception
tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
multiple places simultaneously, e.g. syslog.
-```go
-// Not the real implementation of the Airbrake hook. Just a simple sample.
-import (
- log "github.com/Sirupsen/logrus"
-)
-
-func init() {
- log.AddHook(new(AirbrakeHook))
-}
-
-type AirbrakeHook struct{}
-
-// `Fire()` takes the entry that the hook is fired for. `entry.Data[]` contains
-// the fields for the entry. See the Fields section of the README.
-func (hook *AirbrakeHook) Fire(entry *logrus.Entry) error {
- err := airbrake.Notify(entry.Data["error"].(error))
- if err != nil {
- log.WithFields(log.Fields{
- "source": "airbrake",
- "endpoint": airbrake.Endpoint,
- }).Info("Failed to send error to Airbrake")
- }
-
- return nil
-}
-
-// `Levels()` returns a slice of `Levels` the hook is fired for.
-func (hook *AirbrakeHook) Levels() []log.Level {
- return []log.Level{
- log.ErrorLevel,
- log.FatalLevel,
- log.PanicLevel,
- }
-}
-```
-
-Logrus comes with built-in hooks. Add those, or your custom hook, in `init`:
+Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
+`init`:
```go
import (
log "github.com/Sirupsen/logrus"
- "github.com/Sirupsen/logrus/hooks/airbrake"
- "github.com/Sirupsen/logrus/hooks/syslog"
+ "gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "aibrake"
+ logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
"log/syslog"
)
func init() {
- log.AddHook(new(logrus_airbrake.AirbrakeHook))
+
+ // Use the Airbrake hook to report errors that have Error severity or above to
+ // an exception tracker. You can create custom hooks, see the Hooks section.
+ log.AddHook(airbrake.NewHook(123, "xyz", "production"))
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
if err != nil {
@@ -221,26 +196,37 @@ func init() {
}
}
```
+Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
-* [`github.com/Sirupsen/logrus/hooks/airbrake`](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go)
- Send errors to an exception tracking service compatible with the Airbrake API.
- Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes.
+| 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 |
+| [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) |
+| [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/) |
+| [ElasticSearch](https://github.com/sohlich/elogrus) | Hook for logging to ElasticSearch|
-* [`github.com/Sirupsen/logrus/hooks/papertrail`](https://github.com/Sirupsen/logrus/blob/master/hooks/papertrail/papertrail.go)
- Send errors to the Papertrail hosted logging service via UDP.
-
-* [`github.com/Sirupsen/logrus/hooks/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.
-
-* [`github.com/nubo/hiprus`](https://github.com/nubo/hiprus)
- Send errors to a channel in hipchat.
-
-* [`github.com/sebest/logrusly`](https://github.com/sebest/logrusly)
- Send logs to Loggly (https://www.loggly.com/)
-
-* [`github.com/johntdyer/slackrus`](https://github.com/johntdyer/slackrus)
- Hook for Slack chat.
#### Level logging
@@ -296,10 +282,10 @@ init() {
// do something here to set environment depending on an environment variable
// or command-line flag
if Environment == "production" {
- log.SetFormatter(logrus.JSONFormatter)
+ log.SetFormatter(&log.JSONFormatter{})
} else {
// The TextFormatter is default, you don't actually have to do this.
- log.SetFormatter(logrus.TextFormatter)
+ log.SetFormatter(&log.TextFormatter{})
}
}
```
@@ -318,10 +304,16 @@ The built-in logging formatters are:
field to `true`. To force no colored output even if there is a TTY set the
`DisableColors` field to `true`
* `logrus.JSONFormatter`. Logs fields as JSON.
+* `logrus/formatters/logstash.LogstashFormatter`. Logs fields as [Logstash](http://logstash.net) Events.
+
+ ```go
+ logrus.SetFormatter(&logstash.LogstashFormatter{Type: "application_name"})
+ ```
Third party logging formatters:
-* [`zalgo`](https://github.com/aybabtme/logzalgo): invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
+* [`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̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
You can define your formatter by implementing the `Formatter` interface,
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
@@ -334,7 +326,7 @@ type MyJSONFormatter struct {
log.SetFormatter(new(MyJSONFormatter))
-func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
+func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
// Note this doesn't include Time, Level and Message which are available on
// the Entry. Consult `godoc` on information about those fields or read the
// source of the official loggers.
@@ -348,7 +340,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
#### Logger as an `io.Writer`
-Logrus can be transormed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
+Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
```go
w := logger.Writer()
@@ -370,5 +362,27 @@ Log rotation is not provided with Logrus. Log rotation should be done by an
external program (like `logrotate(8)`) that can compress and delete old log
entries. It should not be a feature of the application-level logger.
+#### Tools
-[godoc]: https://godoc.org/github.com/Sirupsen/logrus
+| 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.|
+
+#### Testing
+
+Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides:
+
+* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook
+* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
+
+```go
+logger, hook := NewNullLogger()
+logger.Error("Hello error")
+
+assert.Equal(1, len(hook.Entries))
+assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
+assert.Equal("Hello error", hook.LastEntry().Message)
+
+hook.Reset()
+assert.Nil(hook.LastEntry())
+```
diff --git a/vendor/github.com/Sirupsen/logrus/doc.go b/vendor/github.com/Sirupsen/logrus/doc.go
new file mode 100644
index 00000000..dddd5f87
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/doc.go
@@ -0,0 +1,26 @@
+/*
+Package logrus is a structured logger for Go, completely API compatible with the standard library logger.
+
+
+The simplest way to use Logrus is simply the package-level exported logger:
+
+ package main
+
+ import (
+ log "github.com/Sirupsen/logrus"
+ )
+
+ func main() {
+ log.WithFields(log.Fields{
+ "animal": "walrus",
+ "number": 1,
+ "size": 10,
+ }).Info("A walrus appears")
+ }
+
+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
+*/
+package logrus
diff --git a/vendor/github.com/Sirupsen/logrus/entry.go b/vendor/github.com/Sirupsen/logrus/entry.go
index 17fe6f70..89e966e7 100644
--- a/vendor/github.com/Sirupsen/logrus/entry.go
+++ b/vendor/github.com/Sirupsen/logrus/entry.go
@@ -8,6 +8,9 @@ import (
"time"
)
+// Defines the key when adding errors using WithError.
+var ErrorKey = "error"
+
// An entry is the final or intermediate Logrus logging entry. It contains all
// the fields passed with WithField{,s}. It's finally logged when Debug, Info,
// Warn, Error, Fatal or Panic is called on it. These objects can be reused and
@@ -53,6 +56,11 @@ func (entry *Entry) String() (string, error) {
return reader.String(), err
}
+// Add an error as single field (using the key defined in ErrorKey) to the Entry.
+func (entry *Entry) WithError(err error) *Entry {
+ return entry.WithField(ErrorKey, err)
+}
+
// Add a single field to the Entry.
func (entry *Entry) WithField(key string, value interface{}) *Entry {
return entry.WithFields(Fields{key: value})
@@ -60,7 +68,7 @@ func (entry *Entry) WithField(key string, value interface{}) *Entry {
// Add a map of fields to the Entry.
func (entry *Entry) WithFields(fields Fields) *Entry {
- data := Fields{}
+ data := make(Fields, len(entry.Data)+len(fields))
for k, v := range entry.Data {
data[k] = v
}
@@ -70,12 +78,14 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
return &Entry{Logger: entry.Logger, Data: data}
}
-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) log(level Level, msg string) {
entry.Time = time.Now()
entry.Level = level
entry.Message = msg
- if err := entry.Logger.Hooks.Fire(level, entry); err != nil {
+ 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()
@@ -100,7 +110,7 @@ func (entry *Entry) log(level Level, msg string) {
// panic() to use in Entry#Panic(), we avoid the allocation by checking
// directly here.
if level <= PanicLevel {
- panic(entry)
+ panic(&entry)
}
}
@@ -188,6 +198,7 @@ func (entry *Entry) Fatalf(format string, args ...interface{}) {
if entry.Logger.Level >= FatalLevel {
entry.Fatal(fmt.Sprintf(format, args...))
}
+ os.Exit(1)
}
func (entry *Entry) Panicf(format string, args ...interface{}) {
@@ -234,6 +245,7 @@ func (entry *Entry) Fatalln(args ...interface{}) {
if entry.Logger.Level >= FatalLevel {
entry.Fatal(entry.sprintlnn(args...))
}
+ os.Exit(1)
}
func (entry *Entry) Panicln(args ...interface{}) {
diff --git a/vendor/github.com/Sirupsen/logrus/exported.go b/vendor/github.com/Sirupsen/logrus/exported.go
index fd092fc7..9a0120ac 100644
--- a/vendor/github.com/Sirupsen/logrus/exported.go
+++ b/vendor/github.com/Sirupsen/logrus/exported.go
@@ -36,6 +36,8 @@ func SetLevel(level Level) {
// GetLevel returns the standard logger level.
func GetLevel() Level {
+ std.mu.Lock()
+ defer std.mu.Unlock()
return std.Level
}
@@ -46,6 +48,11 @@ func AddHook(hook Hook) {
std.Hooks.Add(hook)
}
+// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key.
+func WithError(err error) *Entry {
+ return std.WithField(ErrorKey, err)
+}
+
// WithField creates an entry from the standard logger and adds a field to
// it. If you want multiple fields, use `WithFields`.
//
diff --git a/vendor/github.com/Sirupsen/logrus/formatter.go b/vendor/github.com/Sirupsen/logrus/formatter.go
index 038ce9fd..104d689f 100644
--- a/vendor/github.com/Sirupsen/logrus/formatter.go
+++ b/vendor/github.com/Sirupsen/logrus/formatter.go
@@ -1,5 +1,9 @@
package logrus
+import "time"
+
+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/vendor/github.com/Sirupsen/logrus/hooks.go b/vendor/github.com/Sirupsen/logrus/hooks.go
index 0da2b365..3f151cdc 100644
--- a/vendor/github.com/Sirupsen/logrus/hooks.go
+++ b/vendor/github.com/Sirupsen/logrus/hooks.go
@@ -11,11 +11,11 @@ type Hook interface {
}
// Internal type for storing the hooks on a logger instance.
-type levelHooks map[Level][]Hook
+type LevelHooks map[Level][]Hook
// Add a hook to an instance of logger. This is called with
// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface.
-func (hooks levelHooks) Add(hook Hook) {
+func (hooks LevelHooks) Add(hook Hook) {
for _, level := range hook.Levels() {
hooks[level] = append(hooks[level], hook)
}
@@ -23,7 +23,7 @@ func (hooks levelHooks) Add(hook Hook) {
// Fire all the hooks for the passed level. Used by `entry.log` to fire
// appropriate hooks for a log entry.
-func (hooks levelHooks) Fire(level Level, entry *Entry) error {
+func (hooks LevelHooks) Fire(level Level, entry *Entry) error {
for _, hook := range hooks[level] {
if err := hook.Fire(entry); err != nil {
return err
diff --git a/vendor/github.com/Sirupsen/logrus/json_formatter.go b/vendor/github.com/Sirupsen/logrus/json_formatter.go
index b09227c2..2ad6dc5c 100644
--- a/vendor/github.com/Sirupsen/logrus/json_formatter.go
+++ b/vendor/github.com/Sirupsen/logrus/json_formatter.go
@@ -3,18 +3,33 @@ package logrus
import (
"encoding/json"
"fmt"
- "time"
)
-type JSONFormatter struct{}
+type JSONFormatter struct {
+ // TimestampFormat sets the format used for marshaling timestamps.
+ TimestampFormat string
+}
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
data := make(Fields, len(entry.Data)+3)
for k, v := range entry.Data {
- data[k] = v
+ switch v := v.(type) {
+ case error:
+ // Otherwise errors are ignored by `encoding/json`
+ // https://github.com/Sirupsen/logrus/issues/137
+ data[k] = v.Error()
+ default:
+ data[k] = v
+ }
}
prefixFieldClashes(data)
- data["time"] = entry.Time.Format(time.RFC3339)
+
+ timestampFormat := f.TimestampFormat
+ if timestampFormat == "" {
+ timestampFormat = DefaultTimestampFormat
+ }
+
+ data["time"] = entry.Time.Format(timestampFormat)
data["msg"] = entry.Message
data["level"] = entry.Level.String()
diff --git a/vendor/github.com/Sirupsen/logrus/logger.go b/vendor/github.com/Sirupsen/logrus/logger.go
index b392e547..2fdb2317 100644
--- a/vendor/github.com/Sirupsen/logrus/logger.go
+++ b/vendor/github.com/Sirupsen/logrus/logger.go
@@ -8,13 +8,13 @@ import (
type Logger struct {
// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
- // file, or leave it default which is `os.Stdout`. You can also set this to
+ // file, or leave it default which is `os.Stderr`. You can also set this to
// something more adventorous, such as logging to Kafka.
Out io.Writer
// Hooks for the logger instance. These allow firing events based on logging
// levels and log entries. For example, to send errors to an error tracking
// service, log to StatsD or dump the core on fatal errors.
- Hooks levelHooks
+ Hooks LevelHooks
// All log entries pass through the formatter before logged to Out. The
// included formatters are `TextFormatter` and `JSONFormatter` for which
// TextFormatter is the default. In development (when a TTY is attached) it
@@ -37,23 +37,23 @@ type Logger struct {
// var log = &Logger{
// Out: os.Stderr,
// Formatter: new(JSONFormatter),
-// Hooks: make(levelHooks),
+// Hooks: make(LevelHooks),
// Level: logrus.DebugLevel,
// }
//
// It's recommended to make this a global instance called `log`.
func New() *Logger {
return &Logger{
- Out: os.Stdout,
+ Out: os.Stderr,
Formatter: new(TextFormatter),
- Hooks: make(levelHooks),
+ Hooks: make(LevelHooks),
Level: InfoLevel,
}
}
// Adds a field to the log entry, note that you it doesn't log until you call
// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
-// Ff you want multiple fields, use `WithFields`.
+// If you want multiple fields, use `WithFields`.
func (logger *Logger) WithField(key string, value interface{}) *Entry {
return NewEntry(logger).WithField(key, value)
}
@@ -64,12 +64,22 @@ func (logger *Logger) WithFields(fields Fields) *Entry {
return NewEntry(logger).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)
+}
+
func (logger *Logger) Debugf(format string, args ...interface{}) {
- NewEntry(logger).Debugf(format, args...)
+ if logger.Level >= DebugLevel {
+ NewEntry(logger).Debugf(format, args...)
+ }
}
func (logger *Logger) Infof(format string, args ...interface{}) {
- NewEntry(logger).Infof(format, args...)
+ if logger.Level >= InfoLevel {
+ NewEntry(logger).Infof(format, args...)
+ }
}
func (logger *Logger) Printf(format string, args ...interface{}) {
@@ -77,31 +87,46 @@ func (logger *Logger) Printf(format string, args ...interface{}) {
}
func (logger *Logger) Warnf(format string, args ...interface{}) {
- NewEntry(logger).Warnf(format, args...)
+ if logger.Level >= WarnLevel {
+ NewEntry(logger).Warnf(format, args...)
+ }
}
func (logger *Logger) Warningf(format string, args ...interface{}) {
- NewEntry(logger).Warnf(format, args...)
+ if logger.Level >= WarnLevel {
+ NewEntry(logger).Warnf(format, args...)
+ }
}
func (logger *Logger) Errorf(format string, args ...interface{}) {
- NewEntry(logger).Errorf(format, args...)
+ if logger.Level >= ErrorLevel {
+ NewEntry(logger).Errorf(format, args...)
+ }
}
func (logger *Logger) Fatalf(format string, args ...interface{}) {
- NewEntry(logger).Fatalf(format, args...)
+ if logger.Level >= FatalLevel {
+ NewEntry(logger).Fatalf(format, args...)
+ }
+ os.Exit(1)
}
func (logger *Logger) Panicf(format string, args ...interface{}) {
- NewEntry(logger).Panicf(format, args...)
+ if logger.Level >= PanicLevel {
+ NewEntry(logger).Panicf(format, args...)
+ }
}
func (logger *Logger) Debug(args ...interface{}) {
- NewEntry(logger).Debug(args...)
+ if logger.Level >= DebugLevel {
+ NewEntry(logger).Debug(args...)
+ }
}
func (logger *Logger) Info(args ...interface{}) {
- NewEntry(logger).Info(args...)
+ if logger.Level >= InfoLevel {
+ NewEntry(logger).Info(args...)
+ }
}
func (logger *Logger) Print(args ...interface{}) {
@@ -109,31 +134,46 @@ func (logger *Logger) Print(args ...interface{}) {
}
func (logger *Logger) Warn(args ...interface{}) {
- NewEntry(logger).Warn(args...)
+ if logger.Level >= WarnLevel {
+ NewEntry(logger).Warn(args...)
+ }
}
func (logger *Logger) Warning(args ...interface{}) {
- NewEntry(logger).Warn(args...)
+ if logger.Level >= WarnLevel {
+ NewEntry(logger).Warn(args...)
+ }
}
func (logger *Logger) Error(args ...interface{}) {
- NewEntry(logger).Error(args...)
+ if logger.Level >= ErrorLevel {
+ NewEntry(logger).Error(args...)
+ }
}
func (logger *Logger) Fatal(args ...interface{}) {
- NewEntry(logger).Fatal(args...)
+ if logger.Level >= FatalLevel {
+ NewEntry(logger).Fatal(args...)
+ }
+ os.Exit(1)
}
func (logger *Logger) Panic(args ...interface{}) {
- NewEntry(logger).Panic(args...)
+ if logger.Level >= PanicLevel {
+ NewEntry(logger).Panic(args...)
+ }
}
func (logger *Logger) Debugln(args ...interface{}) {
- NewEntry(logger).Debugln(args...)
+ if logger.Level >= DebugLevel {
+ NewEntry(logger).Debugln(args...)
+ }
}
func (logger *Logger) Infoln(args ...interface{}) {
- NewEntry(logger).Infoln(args...)
+ if logger.Level >= InfoLevel {
+ NewEntry(logger).Infoln(args...)
+ }
}
func (logger *Logger) Println(args ...interface{}) {
@@ -141,21 +181,32 @@ func (logger *Logger) Println(args ...interface{}) {
}
func (logger *Logger) Warnln(args ...interface{}) {
- NewEntry(logger).Warnln(args...)
+ if logger.Level >= WarnLevel {
+ NewEntry(logger).Warnln(args...)
+ }
}
func (logger *Logger) Warningln(args ...interface{}) {
- NewEntry(logger).Warnln(args...)
+ if logger.Level >= WarnLevel {
+ NewEntry(logger).Warnln(args...)
+ }
}
func (logger *Logger) Errorln(args ...interface{}) {
- NewEntry(logger).Errorln(args...)
+ if logger.Level >= ErrorLevel {
+ NewEntry(logger).Errorln(args...)
+ }
}
func (logger *Logger) Fatalln(args ...interface{}) {
- NewEntry(logger).Fatalln(args...)
+ if logger.Level >= FatalLevel {
+ NewEntry(logger).Fatalln(args...)
+ }
+ os.Exit(1)
}
func (logger *Logger) Panicln(args ...interface{}) {
- NewEntry(logger).Panicln(args...)
+ if logger.Level >= PanicLevel {
+ NewEntry(logger).Panicln(args...)
+ }
}
diff --git a/vendor/github.com/Sirupsen/logrus/logrus.go b/vendor/github.com/Sirupsen/logrus/logrus.go
index 43ee12e9..e5966911 100644
--- a/vendor/github.com/Sirupsen/logrus/logrus.go
+++ b/vendor/github.com/Sirupsen/logrus/logrus.go
@@ -3,6 +3,7 @@ package logrus
import (
"fmt"
"log"
+ "strings"
)
// Fields type, used to pass to `WithFields`.
@@ -33,7 +34,7 @@ func (level Level) String() string {
// ParseLevel takes a string level and returns the Logrus log level constant.
func ParseLevel(lvl string) (Level, error) {
- switch lvl {
+ switch strings.ToLower(lvl) {
case "panic":
return PanicLevel, nil
case "fatal":
@@ -52,6 +53,16 @@ func ParseLevel(lvl string) (Level, error) {
return l, fmt.Errorf("not a valid logrus Level: %q", lvl)
}
+// A constant exposing all logging levels
+var AllLevels = []Level{
+ PanicLevel,
+ FatalLevel,
+ ErrorLevel,
+ WarnLevel,
+ InfoLevel,
+ DebugLevel,
+}
+
// These are the different logging levels. You can set the logging level to log
// on your instance of logger, obtained with `logrus.New()`.
const (
@@ -74,7 +85,11 @@ const (
)
// Won't compile if StdLogger can't be realized by a log.Logger
-var _ StdLogger = &log.Logger{}
+var (
+ _ StdLogger = &log.Logger{}
+ _ StdLogger = &Entry{}
+ _ StdLogger = &Logger{}
+)
// StdLogger is what your logrus-enabled library should take, that way
// it'll accept a stdlib logger and a logrus logger. There's no standard
@@ -92,3 +107,37 @@ type StdLogger interface {
Panicf(string, ...interface{})
Panicln(...interface{})
}
+
+// The FieldLogger interface generalizes the Entry and Logger types
+type FieldLogger interface {
+ WithField(key string, value interface{}) *Entry
+ WithFields(fields Fields) *Entry
+ WithError(err error) *Entry
+
+ Debugf(format string, args ...interface{})
+ Infof(format string, args ...interface{})
+ Printf(format string, args ...interface{})
+ Warnf(format string, args ...interface{})
+ Warningf(format string, args ...interface{})
+ Errorf(format string, args ...interface{})
+ Fatalf(format string, args ...interface{})
+ Panicf(format string, args ...interface{})
+
+ Debug(args ...interface{})
+ Info(args ...interface{})
+ Print(args ...interface{})
+ Warn(args ...interface{})
+ Warning(args ...interface{})
+ Error(args ...interface{})
+ Fatal(args ...interface{})
+ Panic(args ...interface{})
+
+ Debugln(args ...interface{})
+ Infoln(args ...interface{})
+ Println(args ...interface{})
+ Warnln(args ...interface{})
+ Warningln(args ...interface{})
+ Errorln(args ...interface{})
+ Fatalln(args ...interface{})
+ Panicln(args ...interface{})
+}
diff --git a/vendor/github.com/Sirupsen/logrus/terminal_openbsd.go b/vendor/github.com/Sirupsen/logrus/terminal_bsd.go
similarity index 68%
rename from vendor/github.com/Sirupsen/logrus/terminal_openbsd.go
rename to vendor/github.com/Sirupsen/logrus/terminal_bsd.go
index d238bfa0..71f8d67a 100644
--- a/vendor/github.com/Sirupsen/logrus/terminal_openbsd.go
+++ b/vendor/github.com/Sirupsen/logrus/terminal_bsd.go
@@ -1,3 +1,4 @@
+// +build darwin freebsd openbsd netbsd dragonfly
package logrus
diff --git a/vendor/github.com/Sirupsen/logrus/terminal_darwin.go b/vendor/github.com/Sirupsen/logrus/terminal_darwin.go
deleted file mode 100644
index 8fe02a4a..00000000
--- a/vendor/github.com/Sirupsen/logrus/terminal_darwin.go
+++ /dev/null
@@ -1,12 +0,0 @@
-// Based on ssh/terminal:
-// Copyright 2013 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.
-
-package logrus
-
-import "syscall"
-
-const ioctlReadTermios = syscall.TIOCGETA
-
-type Termios syscall.Termios
diff --git a/vendor/github.com/Sirupsen/logrus/terminal_freebsd.go b/vendor/github.com/Sirupsen/logrus/terminal_freebsd.go
deleted file mode 100644
index 0428ee5d..00000000
--- a/vendor/github.com/Sirupsen/logrus/terminal_freebsd.go
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- Go 1.2 doesn't include Termios for FreeBSD. This should be added in 1.3 and this could be merged with terminal_darwin.
-*/
-package logrus
-
-import (
- "syscall"
-)
-
-const ioctlReadTermios = syscall.TIOCGETA
-
-type Termios struct {
- Iflag uint32
- Oflag uint32
- Cflag uint32
- Lflag uint32
- Cc [20]uint8
- Ispeed uint32
- Ospeed uint32
-}
diff --git a/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go b/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
index b8bebc13..b343b3a3 100644
--- a/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
+++ b/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
@@ -3,7 +3,7 @@
// 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
+// +build linux darwin freebsd openbsd netbsd dragonfly
package logrus
@@ -12,9 +12,9 @@ import (
"unsafe"
)
-// IsTerminal returns true if the given file descriptor is a terminal.
+// IsTerminal returns true if stderr's file descriptor is a terminal.
func IsTerminal() bool {
- fd := syscall.Stdout
+ 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/vendor/github.com/Sirupsen/logrus/terminal_solaris.go b/vendor/github.com/Sirupsen/logrus/terminal_solaris.go
new file mode 100644
index 00000000..3e70bf7b
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/terminal_solaris.go
@@ -0,0 +1,15 @@
+// +build solaris
+
+package logrus
+
+import (
+ "os"
+
+ "golang.org/x/sys/unix"
+)
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+func IsTerminal() bool {
+ _, err := unix.IoctlGetTermios(int(os.Stdout.Fd()), unix.TCGETA)
+ return err == nil
+}
diff --git a/vendor/github.com/Sirupsen/logrus/terminal_windows.go b/vendor/github.com/Sirupsen/logrus/terminal_windows.go
index 2e09f6f7..0146845d 100644
--- a/vendor/github.com/Sirupsen/logrus/terminal_windows.go
+++ b/vendor/github.com/Sirupsen/logrus/terminal_windows.go
@@ -18,9 +18,9 @@ var (
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
)
-// IsTerminal returns true if the given file descriptor is a terminal.
+// IsTerminal returns true if stderr's file descriptor is a terminal.
func IsTerminal() bool {
- fd := syscall.Stdout
+ 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/vendor/github.com/Sirupsen/logrus/text_formatter.go b/vendor/github.com/Sirupsen/logrus/text_formatter.go
index 4e517341..06ef2023 100644
--- a/vendor/github.com/Sirupsen/logrus/text_formatter.go
+++ b/vendor/github.com/Sirupsen/logrus/text_formatter.go
@@ -3,7 +3,7 @@ package logrus
import (
"bytes"
"fmt"
- "regexp"
+ "runtime"
"sort"
"strings"
"time"
@@ -15,12 +15,12 @@ const (
green = 32
yellow = 33
blue = 34
+ gray = 37
)
var (
baseTimestamp time.Time
isTerminal bool
- noQuoteNeeded *regexp.Regexp
)
func init() {
@@ -34,35 +34,59 @@ func miniTS() int {
type TextFormatter struct {
// Set to true to bypass checking for a TTY before outputting colors.
- ForceColors bool
+ ForceColors bool
+
+ // Force disabling colors.
DisableColors bool
- // Set to true to disable timestamp logging (useful when the output
- // is redirected to a logging system already adding a timestamp)
+
+ // Disable timestamp logging. useful when output is redirected to logging
+ // system that already adds timestamps.
DisableTimestamp bool
+
+ // Enable logging the full timestamp when a TTY is attached instead of just
+ // the time passed since beginning of execution.
+ FullTimestamp bool
+
+ // TimestampFormat to use for display when a full timestamp is printed
+ TimestampFormat string
+
+ // The fields are sorted by default for a consistent output. For applications
+ // that log extremely frequently and don't use the JSON formatter this may not
+ // be desired.
+ DisableSorting bool
}
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
-
var keys []string = make([]string, 0, len(entry.Data))
for k := range entry.Data {
keys = append(keys, k)
}
- sort.Strings(keys)
+
+ if !f.DisableSorting {
+ sort.Strings(keys)
+ }
b := &bytes.Buffer{}
prefixFieldClashes(entry.Data)
- isColored := (f.ForceColors || isTerminal) && !f.DisableColors
+ isColorTerminal := isTerminal && (runtime.GOOS != "windows")
+ isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors
+ timestampFormat := f.TimestampFormat
+ if timestampFormat == "" {
+ timestampFormat = DefaultTimestampFormat
+ }
if isColored {
- printColored(b, entry, keys)
+ f.printColored(b, entry, keys, timestampFormat)
} else {
if !f.DisableTimestamp {
- f.appendKeyValue(b, "time", entry.Time.Format(time.RFC3339))
+ f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat))
}
f.appendKeyValue(b, "level", entry.Level.String())
- f.appendKeyValue(b, "msg", entry.Message)
+ if entry.Message != "" {
+ f.appendKeyValue(b, "msg", entry.Message)
+ }
for _, key := range keys {
f.appendKeyValue(b, key, entry.Data[key])
}
@@ -72,9 +96,11 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
return b.Bytes(), nil
}
-func printColored(b *bytes.Buffer, entry *Entry, keys []string) {
+func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) {
var levelColor int
switch entry.Level {
+ case DebugLevel:
+ levelColor = gray
case WarnLevel:
levelColor = yellow
case ErrorLevel, FatalLevel, PanicLevel:
@@ -85,10 +111,14 @@ func printColored(b *bytes.Buffer, entry *Entry, keys []string) {
levelText := strings.ToUpper(entry.Level.String())[0:4]
- fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
+ if !f.FullTimestamp {
+ fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), 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=%+v", levelColor, k, v)
}
}
@@ -96,7 +126,7 @@ func needsQuoting(text string) bool {
for _, ch := range text {
if !((ch >= 'a' && ch <= 'z') ||
(ch >= 'A' && ch <= 'Z') ||
- (ch >= '0' && ch < '9') ||
+ (ch >= '0' && ch <= '9') ||
ch == '-' || ch == '.') {
return false
}
@@ -104,21 +134,28 @@ func needsQuoting(text string) bool {
return true
}
-func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key, value interface{}) {
- switch value.(type) {
+func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
+
+ b.WriteString(key)
+ b.WriteByte('=')
+
+ switch value := value.(type) {
case string:
- if needsQuoting(value.(string)) {
- fmt.Fprintf(b, "%v=%s ", key, value)
+ if needsQuoting(value) {
+ b.WriteString(value)
} else {
- fmt.Fprintf(b, "%v=%q ", key, value)
+ fmt.Fprintf(b, "%q", value)
}
case error:
- if needsQuoting(value.(error).Error()) {
- fmt.Fprintf(b, "%v=%s ", key, value)
+ errmsg := value.Error()
+ if needsQuoting(errmsg) {
+ b.WriteString(errmsg)
} else {
- fmt.Fprintf(b, "%v=%q ", key, value)
+ fmt.Fprintf(b, "%q", value)
}
default:
- fmt.Fprintf(b, "%v=%v ", key, value)
+ fmt.Fprint(b, value)
}
+
+ b.WriteByte(' ')
}
diff --git a/vendor/github.com/Sirupsen/logrus/writer.go b/vendor/github.com/Sirupsen/logrus/writer.go
index 90d3e01b..1e30b1c7 100644
--- a/vendor/github.com/Sirupsen/logrus/writer.go
+++ b/vendor/github.com/Sirupsen/logrus/writer.go
@@ -6,7 +6,7 @@ import (
"runtime"
)
-func (logger *Logger) Writer() (*io.PipeWriter) {
+func (logger *Logger) Writer() *io.PipeWriter {
reader, writer := io.Pipe()
go logger.writerScanner(reader)
diff --git a/vendor/github.com/gin-gonic/contrib/ginrus/ginrus.go b/vendor/github.com/gin-gonic/contrib/ginrus/ginrus.go
new file mode 100644
index 00000000..464efecd
--- /dev/null
+++ b/vendor/github.com/gin-gonic/contrib/ginrus/ginrus.go
@@ -0,0 +1,51 @@
+// Package ginrus provides log handling using logrus package.
+//
+// Based on github.com/stephenmuss/ginerus but adds more options.
+package ginrus
+
+import (
+ "time"
+
+ "github.com/Sirupsen/logrus"
+ "github.com/gin-gonic/gin"
+)
+
+// Ginrus returns a gin.HandlerFunc (middleware) that logs requests using logrus.
+//
+// Requests with errors are logged using logrus.Error().
+// Requests without errors are logged using logrus.Info().
+//
+// It receives:
+// 1. A time package format string (e.g. time.RFC3339).
+// 2. A boolean stating whether to use UTC time zone or local.
+func Ginrus(logger *logrus.Logger, timeFormat string, utc bool) gin.HandlerFunc {
+ return func(c *gin.Context) {
+ start := time.Now()
+ // some evil middlewares modify this values
+ path := c.Request.URL.Path
+ c.Next()
+
+ end := time.Now()
+ latency := end.Sub(start)
+ if utc {
+ end = end.UTC()
+ }
+
+ entry := logger.WithFields(logrus.Fields{
+ "status": c.Writer.Status(),
+ "method": c.Request.Method,
+ "path": path,
+ "ip": c.ClientIP(),
+ "latency": latency,
+ "user-agent": c.Request.UserAgent(),
+ "time": end.Format(timeFormat),
+ })
+
+ if len(c.Errors) > 0 {
+ // Append error field if this is an erroneous request.
+ entry.Error(c.Errors.String())
+ } else {
+ entry.Info()
+ }
+ }
+}
diff --git a/vendor/github.com/ianschenck/envflag/LICENSE b/vendor/github.com/ianschenck/envflag/LICENSE
new file mode 100644
index 00000000..dfdbd2ad
--- /dev/null
+++ b/vendor/github.com/ianschenck/envflag/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2013, Ian Schenck
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright notice, this
+ list of conditions and the following disclaimer in the documentation and/or
+ other materials provided with the distribution.
+
+ Neither the name of Ian Schenck nor the names of its contributors
+ may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/vendor/github.com/ianschenck/envflag/README.md b/vendor/github.com/ianschenck/envflag/README.md
new file mode 100644
index 00000000..360daff3
--- /dev/null
+++ b/vendor/github.com/ianschenck/envflag/README.md
@@ -0,0 +1,37 @@
+envflag
+=======
+
+Golang flags, but bolted onto the environment rather than the command-line.
+
+Read the [godocs](http://godoc.org/github.com/ianschenck/envflag).
+
+Motivation
+==========
+
+Some like the distinction that command-line flags control behavior
+while environment variables configure. Also
+[12-factor](http://12factor.net/) recommends the use of environment
+variables for configuration. The interface of the golang flag package
+is well designed and easy to use, and allows for other lists
+(os.Environ() vs os.Args) to be parsed as flags. It makes sense then
+to use the same interface, the same types, and the same parsing
+(caveat: there is some ugly string hacking to make environment
+variables look like flags) to the same ends.
+
+Differences
+===========
+
+Calling `flag.Parse()` will not parse environment flags. Calling
+`envflag.Parse()` will not parse command-line flags. There is no good
+reason to combine these two when the net savings is a single line in a
+`func main()`. Furthermore, doing so would require users to accept a
+precedence order of my choosing.
+
+The presence of an environment variable named `h` or `help` will
+probably cause problems (print Usage and os.Exit(0)). Work around this
+by defining those flags somewhere (and ignoring them).
+
+Before calling `Flagset.Parse` on `EnvironmentFlags`, the environment
+variables being passed to `Parse` are trimmed down using
+`Lookup`. This behavior is different from `flag.Parse` in that extra
+environment variables are ignored (and won't crash `envflag.Parse`).
diff --git a/vendor/github.com/ianschenck/envflag/envflag.go b/vendor/github.com/ianschenck/envflag/envflag.go
new file mode 100644
index 00000000..128244cf
--- /dev/null
+++ b/vendor/github.com/ianschenck/envflag/envflag.go
@@ -0,0 +1,192 @@
+// Copyright 2013 Ian Schenck. Use of this source code is governed by
+// a license that can be found in the LICENSE file.
+
+/*
+ Package envflag adds environment variable flags to the flag package.
+
+ Usage:
+
+ Define flags using envflag.String(), Bool(), Int(), etc. This package
+ works nearly the same as the stdlib flag package. Parsing the
+ Environment flags is done by calling envflag.Parse()
+
+ It will *not* attempt to parse any normally-defined command-line
+ flags. Command-line flags are explicitly left alone and separate.
+*/
+package envflag
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "strings"
+ "time"
+)
+
+// VisitAll visits the environment flags in lexicographical order,
+// calling fn for each. It visits all flags, even those not set.
+func VisitAll(fn func(*flag.Flag)) {
+ EnvironmentFlags.VisitAll(fn)
+}
+
+// Visit visits the environment flags in lexicographical order,
+// calling fn for each. It visits only those flags that have been
+// set.
+func Visit(fn func(*flag.Flag)) {
+ EnvironmentFlags.Visit(fn)
+}
+
+// Lookup returns the Flag structure of the named environment flag,
+// returning nil if none exists.
+func Lookup(name string) *flag.Flag {
+ return EnvironmentFlags.Lookup(name)
+}
+
+// Set sets the value of the named environment flag.
+func Set(name, value string) error {
+ return EnvironmentFlags.Set(name, value)
+}
+
+// BoolVar defines a bool flag with specified name, default value, and
+// usage string. The argument p points to a bool variable in which to
+// store the value of the flag.
+func BoolVar(p *bool, name string, value bool, usage string) {
+ EnvironmentFlags.BoolVar(p, name, value, usage)
+}
+
+// Bool defines a bool flag with specified name, default value, and
+// usage string. The return value is the address of a bool variable
+// that stores the value of the flag.
+func Bool(name string, value bool, usage string) *bool {
+ return EnvironmentFlags.Bool(name, value, usage)
+}
+
+// IntVar defines an int flag with specified name, default value, and
+// usage string. The argument p points to an int variable in which to
+// store the value of the flag.
+func IntVar(p *int, name string, value int, usage string) {
+ EnvironmentFlags.IntVar(p, name, value, usage)
+}
+
+// Int defines an int flag with specified name, default value, and
+// usage string. The return value is the address of an int variable
+// that stores the value of the flag.
+func Int(name string, value int, usage string) *int {
+ return EnvironmentFlags.Int(name, value, usage)
+}
+
+// Int64Var defines an int64 flag with specified name, default value,
+// and usage string. The argument p points to an int64 variable in
+// which to store the value of the flag.
+func Int64Var(p *int64, name string, value int64, usage string) {
+ EnvironmentFlags.Int64Var(p, name, value, usage)
+}
+
+// Int64 defines an int64 flag with specified name, default value, and
+// usage string. The return value is the address of an int64 variable
+// that stores the value of the flag.
+func Int64(name string, value int64, usage string) *int64 {
+ return EnvironmentFlags.Int64(name, value, usage)
+}
+
+// UintVar defines a uint flag with specified name, default value, and
+// usage string. The argument p points to a uint variable in which to
+// store the value of the flag.
+func UintVar(p *uint, name string, value uint, usage string) {
+ EnvironmentFlags.UintVar(p, name, value, usage)
+}
+
+// Uint defines a uint flag with specified name, default value, and
+// usage string. The return value is the address of a uint variable
+// that stores the value of the flag.
+func Uint(name string, value uint, usage string) *uint {
+ return EnvironmentFlags.Uint(name, value, usage)
+}
+
+// Uint64Var defines a uint64 flag with specified name, default value,
+// and usage string. The argument p points to a uint64 variable in
+// which to store the value of the flag.
+func Uint64Var(p *uint64, name string, value uint64, usage string) {
+ EnvironmentFlags.Uint64Var(p, name, value, usage)
+}
+
+// Uint64 defines a uint64 flag with specified name, default value,
+// and usage string. The return value is the address of a uint64
+// variable that stores the value of the flag.
+func Uint64(name string, value uint64, usage string) *uint64 {
+ return EnvironmentFlags.Uint64(name, value, usage)
+}
+
+// StringVar defines a string flag with specified name, default value,
+// and usage string. The argument p points to a string variable in
+// which to store the value of the flag.
+func StringVar(p *string, name string, value string, usage string) {
+ EnvironmentFlags.StringVar(p, name, value, usage)
+}
+
+// String defines a string flag with specified name, default value,
+// and usage string. The return value is the address of a string
+// variable that stores the value of the flag.
+func String(name string, value string, usage string) *string {
+ return EnvironmentFlags.String(name, value, usage)
+}
+
+// Float64Var defines a float64 flag with specified name, default
+// value, and usage string. The argument p points to a float64
+// variable in which to store the value of the flag.
+func Float64Var(p *float64, name string, value float64, usage string) {
+ EnvironmentFlags.Float64Var(p, name, value, usage)
+}
+
+// Float64 defines a float64 flag with specified name, default value,
+// and usage string. The return value is the address of a float64
+// variable that stores the value of the flag.
+func Float64(name string, value float64, usage string) *float64 {
+ return EnvironmentFlags.Float64(name, value, usage)
+}
+
+// DurationVar defines a time.Duration flag with specified name,
+// default value, and usage string. The argument p points to a
+// time.Duration variable in which to store the value of the flag.
+func DurationVar(p *time.Duration, name string, value time.Duration, usage string) {
+ EnvironmentFlags.DurationVar(p, name, value, usage)
+}
+
+// Duration defines a time.Duration flag with specified name, default
+// value, and usage string. The return value is the address of a
+// time.Duration variable that stores the value of the flag.
+func Duration(name string, value time.Duration, usage string) *time.Duration {
+ return EnvironmentFlags.Duration(name, value, usage)
+}
+
+// PrintDefaults prints to standard error the default values of all
+// defined environment flags.
+func PrintDefaults() {
+ EnvironmentFlags.PrintDefaults()
+}
+
+// Parse parses the environment flags from os.Environ. Must be called
+// after all flags are defined and before flags are accessed by the
+// program.
+func Parse() {
+ env := os.Environ()
+ // Clean up and "fake" some flag k/v pairs.
+ args := make([]string, 0, len(env))
+ for _, value := range env {
+ if Lookup(value[:strings.Index(value, "=")]) == nil {
+ continue
+ }
+ args = append(args, fmt.Sprintf("-%s", value))
+ }
+ EnvironmentFlags.Parse(args)
+}
+
+// Parsed returns true if the environment flags have been parsed.
+func Parsed() bool {
+ return EnvironmentFlags.Parsed()
+}
+
+// EnvironmentFlags is the default set of environment flags, parsed
+// from os.Environ(). The top-level functions such as BoolVar, Arg,
+// and on are wrappers for the methods of EnvironmentFlags.
+var EnvironmentFlags = flag.NewFlagSet("environment", flag.ExitOnError)
diff --git a/vendor/github.com/joho/godotenv/LICENCE b/vendor/github.com/joho/godotenv/LICENCE
new file mode 100644
index 00000000..e7ddd51b
--- /dev/null
+++ b/vendor/github.com/joho/godotenv/LICENCE
@@ -0,0 +1,23 @@
+Copyright (c) 2013 John Barton
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
diff --git a/vendor/github.com/joho/godotenv/README.md b/vendor/github.com/joho/godotenv/README.md
new file mode 100644
index 00000000..05c47e6f
--- /dev/null
+++ b/vendor/github.com/joho/godotenv/README.md
@@ -0,0 +1,127 @@
+# GoDotEnv [![wercker status](https://app.wercker.com/status/507594c2ec7e60f19403a568dfea0f78 "wercker status")](https://app.wercker.com/project/bykey/507594c2ec7e60f19403a568dfea0f78)
+
+A Go (golang) port of the Ruby dotenv project (which loads env vars from a .env file)
+
+From the original Library:
+
+> Storing configuration in the environment is one of the tenets of a twelve-factor app. Anything that is likely to change between deployment environments–such as resource handles for databases or credentials for external services–should be extracted from the code into environment variables.
+>
+> But it is not always practical to set environment variables on development machines or continuous integration servers where multiple projects are run. Dotenv load variables from a .env file into ENV when the environment is bootstrapped.
+
+It can be used as a library (for loading in env for your own daemons etc) or as a bin command.
+
+There is test coverage and CI for both linuxish and windows environments, but I make no guarantees about the bin version working on windows.
+
+## Installation
+
+As a library
+
+```shell
+go get github.com/joho/godotenv
+```
+
+or if you want to use it as a bin command
+```shell
+go get github.com/joho/godotenv/cmd/godotenv
+```
+
+## Usage
+
+Add your application configuration to your `.env` file in the root of your project:
+
+```shell
+S3_BUCKET=YOURS3BUCKET
+SECRET_KEY=YOURSECRETKEYGOESHERE
+```
+
+Then in your Go app you can do something like
+
+```go
+package main
+
+import (
+ "github.com/joho/godotenv"
+ "log"
+ "os"
+)
+
+func main() {
+ err := godotenv.Load()
+ if err != nil {
+ log.Fatal("Error loading .env file")
+ }
+
+ s3Bucket := os.Getenv("S3_BUCKET")
+ secretKey := os.Getenv("SECRET_KEY")
+
+ // now do something with s3 or whatever
+}
+```
+
+If you're even lazier than that, you can just take advantage of the autoload package which will read in `.env` on import
+
+```go
+import _ "github.com/joho/godotenv/autoload"
+```
+
+While `.env` in the project root is the default, you don't have to be constrained, both examples below are 100% legit
+
+```go
+_ = godotenv.Load("somerandomfile")
+_ = godotenv.Load("filenumberone.env", "filenumbertwo.env")
+```
+
+If you want to be really fancy with your env file you can do comments and exports (below is a valid env file)
+
+```shell
+# I am a comment and that is OK
+SOME_VAR=someval
+FOO=BAR # comments at line end are OK too
+export BAR=BAZ
+```
+
+Or finally you can do YAML(ish) style
+
+```yaml
+FOO: bar
+BAR: baz
+```
+
+as a final aside, if you don't want godotenv munging your env you can just get a map back instead
+
+```go
+var myEnv map[string]string
+myEnv, err := godotenv.Read()
+
+s3Bucket := myEnv["S3_BUCKET"]
+```
+
+### Command Mode
+
+Assuming you've installed the command as above and you've got `$GOPATH/bin` in your `$PATH`
+
+```
+godotenv -f /some/path/to/.env some_command with some args
+```
+
+If you don't specify `-f` it will fall back on the default of loading `.env` in `PWD`
+
+## Contributing
+
+Contributions are most welcome! The parser itself is pretty stupidly naive and I wouldn't be surprised if it breaks with edge cases.
+
+*code changes without tests will not be accepted*
+
+1. Fork it
+2. Create your feature branch (`git checkout -b my-new-feature`)
+3. Commit your changes (`git commit -am 'Added some feature'`)
+4. Push to the branch (`git push origin my-new-feature`)
+5. Create new Pull Request
+
+## CI
+
+Linux: [![wercker status](https://app.wercker.com/status/507594c2ec7e60f19403a568dfea0f78/m "wercker status")](https://app.wercker.com/project/bykey/507594c2ec7e60f19403a568dfea0f78) Windows: [![Build status](https://ci.appveyor.com/api/projects/status/9v40vnfvvgde64u4)](https://ci.appveyor.com/project/joho/godotenv)
+
+## Who?
+
+The original library [dotenv](https://github.com/bkeepers/dotenv) was written by [Brandon Keepers](http://opensoul.org/), and this port was done by [John Barton](http://whoisjohnbarton.com) based off the tests/fixtures in the original library.
diff --git a/vendor/github.com/joho/godotenv/autoload/autoload.go b/vendor/github.com/joho/godotenv/autoload/autoload.go
new file mode 100644
index 00000000..fbcd2bdf
--- /dev/null
+++ b/vendor/github.com/joho/godotenv/autoload/autoload.go
@@ -0,0 +1,15 @@
+package autoload
+
+/*
+ You can just read the .env file on import just by doing
+
+ import _ "github.com/joho/godotenv/autoload"
+
+ And bob's your mother's brother
+*/
+
+import "github.com/joho/godotenv"
+
+func init() {
+ godotenv.Load()
+}
diff --git a/vendor/github.com/joho/godotenv/godotenv.go b/vendor/github.com/joho/godotenv/godotenv.go
new file mode 100644
index 00000000..94b2676b
--- /dev/null
+++ b/vendor/github.com/joho/godotenv/godotenv.go
@@ -0,0 +1,229 @@
+// Package godotenv is a go port of the ruby dotenv library (https://github.com/bkeepers/dotenv)
+//
+// Examples/readme can be found on the github page at https://github.com/joho/godotenv
+//
+// The TL;DR is that you make a .env file that looks something like
+//
+// SOME_ENV_VAR=somevalue
+//
+// and then in your go code you can call
+//
+// godotenv.Load()
+//
+// and all the env vars declared in .env will be avaiable through os.Getenv("SOME_ENV_VAR")
+package godotenv
+
+import (
+ "bufio"
+ "errors"
+ "os"
+ "os/exec"
+ "strings"
+)
+
+// Load will read your env file(s) and load them into ENV for this process.
+//
+// Call this function as close as possible to the start of your program (ideally in main)
+//
+// If you call Load without any args it will default to loading .env in the current path
+//
+// You can otherwise tell it which files to load (there can be more than one) like
+//
+// godotenv.Load("fileone", "filetwo")
+//
+// It's important to note that it WILL NOT OVERRIDE an env variable that already exists - consider the .env file to set dev vars or sensible defaults
+func Load(filenames ...string) (err error) {
+ filenames = filenamesOrDefault(filenames)
+
+ for _, filename := range filenames {
+ err = loadFile(filename, false)
+ if err != nil {
+ return // return early on a spazout
+ }
+ }
+ return
+}
+
+// Overload will read your env file(s) and load them into ENV for this process.
+//
+// Call this function as close as possible to the start of your program (ideally in main)
+//
+// If you call Overload without any args it will default to loading .env in the current path
+//
+// You can otherwise tell it which files to load (there can be more than one) like
+//
+// godotenv.Overload("fileone", "filetwo")
+//
+// It's important to note this WILL OVERRIDE an env variable that already exists - consider the .env file to forcefilly set all vars.
+func Overload(filenames ...string) (err error) {
+ filenames = filenamesOrDefault(filenames)
+
+ for _, filename := range filenames {
+ err = loadFile(filename, true)
+ if err != nil {
+ return // return early on a spazout
+ }
+ }
+ return
+}
+
+// Read all env (with same file loading semantics as Load) but return values as
+// a map rather than automatically writing values into env
+func Read(filenames ...string) (envMap map[string]string, err error) {
+ filenames = filenamesOrDefault(filenames)
+ envMap = make(map[string]string)
+
+ for _, filename := range filenames {
+ individualEnvMap, individualErr := readFile(filename)
+
+ if individualErr != nil {
+ err = individualErr
+ return // return early on a spazout
+ }
+
+ for key, value := range individualEnvMap {
+ envMap[key] = value
+ }
+ }
+
+ return
+}
+
+// Exec loads env vars from the specified filenames (empty map falls back to default)
+// then executes the cmd specified.
+//
+// Simply hooks up os.Stdin/err/out to the command and calls Run()
+//
+// If you want more fine grained control over your command it's recommended
+// that you use `Load()` or `Read()` and the `os/exec` package yourself.
+func Exec(filenames []string, cmd string, cmdArgs []string) error {
+ Load(filenames...)
+
+ command := exec.Command(cmd, cmdArgs...)
+ command.Stdin = os.Stdin
+ command.Stdout = os.Stdout
+ command.Stderr = os.Stderr
+ return command.Run()
+}
+
+func filenamesOrDefault(filenames []string) []string {
+ if len(filenames) == 0 {
+ return []string{".env"}
+ }
+ return filenames
+}
+
+func loadFile(filename string, overload bool) error {
+ envMap, err := readFile(filename)
+ if err != nil {
+ return err
+ }
+
+ for key, value := range envMap {
+ if os.Getenv(key) == "" || overload {
+ os.Setenv(key, value)
+ }
+ }
+
+ return nil
+}
+
+func readFile(filename string) (envMap map[string]string, err error) {
+ file, err := os.Open(filename)
+ if err != nil {
+ return
+ }
+ defer file.Close()
+
+ envMap = make(map[string]string)
+
+ var lines []string
+ scanner := bufio.NewScanner(file)
+ for scanner.Scan() {
+ lines = append(lines, scanner.Text())
+ }
+
+ for _, fullLine := range lines {
+ if !isIgnoredLine(fullLine) {
+ key, value, err := parseLine(fullLine)
+
+ if err == nil {
+ envMap[key] = value
+ }
+ }
+ }
+ return
+}
+
+func parseLine(line string) (key string, value string, err error) {
+ if len(line) == 0 {
+ err = errors.New("zero length string")
+ return
+ }
+
+ // ditch the comments (but keep quoted hashes)
+ if strings.Contains(line, "#") {
+ segmentsBetweenHashes := strings.Split(line, "#")
+ quotesAreOpen := false
+ var segmentsToKeep []string
+ for _, segment := range segmentsBetweenHashes {
+ if strings.Count(segment, "\"") == 1 || strings.Count(segment, "'") == 1 {
+ if quotesAreOpen {
+ quotesAreOpen = false
+ segmentsToKeep = append(segmentsToKeep, segment)
+ } else {
+ quotesAreOpen = true
+ }
+ }
+
+ if len(segmentsToKeep) == 0 || quotesAreOpen {
+ segmentsToKeep = append(segmentsToKeep, segment)
+ }
+ }
+
+ line = strings.Join(segmentsToKeep, "#")
+ }
+
+ // now split key from value
+ splitString := strings.SplitN(line, "=", 2)
+
+ if len(splitString) != 2 {
+ // try yaml mode!
+ splitString = strings.SplitN(line, ":", 2)
+ }
+
+ if len(splitString) != 2 {
+ err = errors.New("Can't separate key from value")
+ return
+ }
+
+ // Parse the key
+ key = splitString[0]
+ if strings.HasPrefix(key, "export") {
+ key = strings.TrimPrefix(key, "export")
+ }
+ key = strings.Trim(key, " ")
+
+ // Parse the value
+ value = splitString[1]
+ // trim
+ value = strings.Trim(value, " ")
+
+ // check if we've got quoted values
+ if strings.Count(value, "\"") == 2 || strings.Count(value, "'") == 2 {
+ // pull the quotes off the edges
+ value = strings.Trim(value, "\"'")
+
+ // expand quotes
+ value = strings.Replace(value, "\\\"", "\"", -1)
+ // expand newlines
+ value = strings.Replace(value, "\\n", "\n", -1)
+ }
+
+ return
+}
+
+func isIgnoredLine(line string) bool {
+ trimmedLine := strings.Trim(line, " \n\t")
+ return len(trimmedLine) == 0 || strings.HasPrefix(trimmedLine, "#")
+}
diff --git a/vendor/github.com/joho/godotenv/wercker.yml b/vendor/github.com/joho/godotenv/wercker.yml
new file mode 100644
index 00000000..c716ac92
--- /dev/null
+++ b/vendor/github.com/joho/godotenv/wercker.yml
@@ -0,0 +1 @@
+box: pjvds/golang
diff --git a/vendor/vendor.json b/vendor/vendor.json
index 6b229542..de9f6847 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -9,8 +9,8 @@
},
{
"path": "github.com/Sirupsen/logrus",
- "revision": "273bd5984cd7deae8d4b71b0ba9bfc5767f7284b",
- "revisionTime": "2015-02-17T12:42:44-05:00"
+ "revision": "4b6ea7319e214d98c938f12692336f7ca9348d6b",
+ "revisionTime": "2016-03-17T14:11:10Z"
},
{
"origin": "github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew",
@@ -53,6 +53,11 @@
"revision": "889391d730237f8aca06ce3e62975112983f96b4",
"revisionTime": "2016-01-23T18:11:54-03:00"
},
+ {
+ "path": "github.com/gin-gonic/contrib/ginrus",
+ "revision": "14f66d54cdb96059bafca98665bcc6d9df4951f2",
+ "revisionTime": "2015-08-15T19:25:43+02:00"
+ },
{
"path": "github.com/gin-gonic/gin",
"revision": "3d002e382355cafc15d706b92899b1961d5b79e9",
@@ -98,6 +103,21 @@
"revision": "1b0c7f6e9ab3d7f500fd7d50c7ad835ff428139b",
"revisionTime": "2014-04-09T13:11:00+02:00"
},
+ {
+ "path": "github.com/ianschenck/envflag",
+ "revision": "9111d830d133f952887a936367fb0211c3134f0d",
+ "revisionTime": "2014-07-20T15:03:42-06:00"
+ },
+ {
+ "path": "github.com/joho/godotenv",
+ "revision": "4ed13390c0acd2ff4e371e64d8b97c8954138243",
+ "revisionTime": "2015-09-07T11:02:28+10:00"
+ },
+ {
+ "path": "github.com/joho/godotenv/autoload",
+ "revision": "4ed13390c0acd2ff4e371e64d8b97c8954138243",
+ "revisionTime": "2015-09-07T11:02:28+10:00"
+ },
{
"path": "github.com/koding/cache",
"revision": "487fc0ca06f9aa1a02d796f5510784b47d5afae2",