Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-pages.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Newdigate <andrew@gitlab.com>2019-07-24 10:50:11 +0300
committerAndrew Newdigate <andrew@gitlab.com>2019-07-24 10:50:11 +0300
commitf80e05dcbde8561d8482e6409ba1559e137c6baf (patch)
treecc8bcc706bd9761469cf1bcca4e55c63ba1f24dc
parentf4ece218fc1c4cb7f6fd34f9a767704a17b69c62 (diff)
Switch logging to LabKit log packageuse-labkit-logging
-rw-r--r--app.go29
-rw-r--r--logging.go84
-rw-r--r--logging_test.go50
-rw-r--r--vendor/github.com/client9/reopen/LICENSE22
-rw-r--r--vendor/github.com/client9/reopen/Makefile27
-rw-r--r--vendor/github.com/client9/reopen/README.md74
-rw-r--r--vendor/github.com/client9/reopen/reopen.go254
-rw-r--r--vendor/github.com/konsorten/go-windows-terminal-sequences/LICENSE9
-rw-r--r--vendor/github.com/konsorten/go-windows-terminal-sequences/README.md41
-rw-r--r--vendor/github.com/konsorten/go-windows-terminal-sequences/go.mod1
-rw-r--r--vendor/github.com/konsorten/go-windows-terminal-sequences/sequences.go36
-rw-r--r--vendor/github.com/konsorten/go-windows-terminal-sequences/sequences_dummy.go11
-rw-r--r--vendor/github.com/sebest/xff/LICENSE20
-rw-r--r--vendor/github.com/sebest/xff/README.md44
-rw-r--r--vendor/github.com/sebest/xff/xff.go158
-rw-r--r--vendor/github.com/sirupsen/logrus/CHANGELOG.md82
-rw-r--r--vendor/github.com/sirupsen/logrus/README.md101
-rw-r--r--vendor/github.com/sirupsen/logrus/alt_exit.go18
-rw-r--r--vendor/github.com/sirupsen/logrus/entry.go302
-rw-r--r--vendor/github.com/sirupsen/logrus/exported.go66
-rw-r--r--vendor/github.com/sirupsen/logrus/formatter.go51
-rw-r--r--vendor/github.com/sirupsen/logrus/go.mod10
-rw-r--r--vendor/github.com/sirupsen/logrus/go.sum16
-rw-r--r--vendor/github.com/sirupsen/logrus/json_formatter.go72
-rw-r--r--vendor/github.com/sirupsen/logrus/logger.go240
-rw-r--r--vendor/github.com/sirupsen/logrus/logrus.go75
-rw-r--r--vendor/github.com/sirupsen/logrus/terminal_bsd.go10
-rw-r--r--vendor/github.com/sirupsen/logrus/terminal_check_bsd.go13
-rw-r--r--vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go11
-rw-r--r--vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go6
-rw-r--r--vendor/github.com/sirupsen/logrus/terminal_check_solaris.go11
-rw-r--r--vendor/github.com/sirupsen/logrus/terminal_check_unix.go13
-rw-r--r--vendor/github.com/sirupsen/logrus/terminal_check_windows.go34
-rw-r--r--vendor/github.com/sirupsen/logrus/terminal_linux.go14
-rw-r--r--vendor/github.com/sirupsen/logrus/text_formatter.go216
-rw-r--r--vendor/github.com/sirupsen/logrus/writer.go2
-rw-r--r--vendor/gitlab.com/gitlab-org/labkit/log/access_logger.go171
-rw-r--r--vendor/gitlab.com/gitlab-org/labkit/log/access_logger_fields.go64
-rw-r--r--vendor/gitlab.com/gitlab-org/labkit/log/access_logger_options.go58
-rw-r--r--vendor/gitlab.com/gitlab-org/labkit/log/clock.go24
-rw-r--r--vendor/gitlab.com/gitlab-org/labkit/log/combined_formatter.go53
-rw-r--r--vendor/gitlab.com/gitlab-org/labkit/log/doc.go16
-rw-r--r--vendor/gitlab.com/gitlab-org/labkit/log/initialization.go82
-rw-r--r--vendor/gitlab.com/gitlab-org/labkit/log/logger.go36
-rw-r--r--vendor/gitlab.com/gitlab-org/labkit/log/logger_options.go105
-rw-r--r--vendor/gitlab.com/gitlab-org/labkit/log/logrus.go40
-rw-r--r--vendor/vendor.json54
47 files changed, 2383 insertions, 543 deletions
diff --git a/app.go b/app.go
index 5c545252..faab88b0 100644
--- a/app.go
+++ b/app.go
@@ -8,7 +8,6 @@ import (
"net"
"net/http"
"os"
- "strconv"
"strings"
"sync"
"time"
@@ -16,7 +15,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/rs/cors"
- log "github.com/sirupsen/logrus"
+
"gitlab.com/gitlab-org/labkit/errortracking"
mimedb "gitlab.com/lupine/go-mimedb"
@@ -28,7 +27,7 @@ import (
"gitlab.com/gitlab-org/gitlab-pages/internal/domain"
"gitlab.com/gitlab-org/gitlab-pages/internal/httperrors"
"gitlab.com/gitlab-org/gitlab-pages/internal/netutil"
- "gitlab.com/gitlab-org/gitlab-pages/metrics"
+ "gitlab.com/gitlab-org/labkit/log"
)
const (
@@ -177,22 +176,6 @@ func (a *theApp) tryAuxiliaryHandlers(w http.ResponseWriter, r *http.Request, ht
return false
}
-func (a *theApp) loggingMiddleware(handler http.Handler) http.Handler {
- logrusEntry := log.WithField("system", "http")
-
- return http.HandlerFunc(func(ww http.ResponseWriter, r *http.Request) {
- w := newLoggingResponseWriter(ww, logrusEntry)
- defer w.Log(r)
-
- metrics.SessionsActive.Inc()
- defer metrics.SessionsActive.Dec()
-
- handler.ServeHTTP(&w, r)
-
- metrics.ProcessedRequests.WithLabelValues(strconv.Itoa(w.status), r.Method).Inc()
- })
-}
-
func (a *theApp) routerMiddleware(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
https := extractHTTPSFlag(r)
@@ -276,7 +259,7 @@ func (a *theApp) buildHandlerPipeline(proxy bool) http.Handler {
handler = corsHandler.Handler(handler)
}
handler = a.routerMiddleware(handler)
- handler = a.loggingMiddleware(handler)
+ handler = log.AccessLogger(handler)
if proxy {
handler = a.proxyInitialMiddleware(handler)
@@ -399,8 +382,8 @@ func (a *theApp) listenAdminHTTPS(wg *sync.WaitGroup) {
l, err := net.FileListener(os.NewFile(fd, "[admin-socket-https]"))
if err != nil {
- errMsg := fmt.Sprintf("failed to listen on FD %d: %v", fd, err)
- log.Error(errMsg)
+ errMsg := fmt.Errorf("failed to listen on FD %d: %v", fd, err)
+ log.WithError(errMsg).Error("error")
capturingFatal(err, errortracking.WithField("listener", "admin https socket"))
}
defer l.Close()
@@ -430,7 +413,7 @@ func runApp(config appConfig) {
if len(config.CustomHeaders) != 0 {
customHeaders, err := headerConfig.ParseHeaderString(config.CustomHeaders)
if err != nil {
- log.Fatal(err)
+ log.WithError(err).Fatal("Unable to parse header string")
}
a.CustomHeaders = customHeaders
}
diff --git a/logging.go b/logging.go
index b96fc8d6..26b49b6d 100644
--- a/logging.go
+++ b/logging.go
@@ -1,11 +1,6 @@
package main
import (
- "fmt"
- "net/http"
- "net/url"
- "time"
-
log "github.com/sirupsen/logrus"
)
@@ -28,85 +23,6 @@ func configureLogging(format string, verbose bool) {
}
}
-type loggingResponseWriter struct {
- rw http.ResponseWriter
- status int
- written int64
- started time.Time
- logrusEntry *log.Entry
-}
-
-func newLoggingResponseWriter(rw http.ResponseWriter, logrusEntry *log.Entry) loggingResponseWriter {
- return loggingResponseWriter{
- rw: rw,
- started: time.Now(),
- logrusEntry: logrusEntry,
- }
-}
-
-func (l *loggingResponseWriter) Header() http.Header {
- return l.rw.Header()
-}
-
-func (l *loggingResponseWriter) Write(data []byte) (n int, err error) {
- if l.status == 0 {
- l.WriteHeader(http.StatusOK)
- }
- n, err = l.rw.Write(data)
- l.written += int64(n)
- return
-}
-
-func (l *loggingResponseWriter) WriteHeader(status int) {
- if l.status != 0 {
- return
- }
-
- l.status = status
- l.rw.WriteHeader(status)
-}
-
-func (l *loggingResponseWriter) extractLogFields(r *http.Request) log.Fields {
- referer := r.Referer()
- parsedReferer, err := url.Parse(referer)
-
- // The referer query string may contain credentials, so remove if possible
- if err == nil {
- parsedReferer.RawQuery = ""
- referer = parsedReferer.String()
- }
-
- return log.Fields{
- "host": r.Host,
- "remoteAddr": r.RemoteAddr,
- "method": r.Method,
- "uri": r.URL.Path, //The request query string may contain credentials
- "proto": r.Proto,
- "status": l.status,
- "written": l.written,
- "referer": referer,
- "userAgent": r.UserAgent(),
- "duration": time.Since(l.started).Seconds(),
- }
-}
-
-func (l *loggingResponseWriter) Log(r *http.Request) {
- fields := l.extractLogFields(r)
-
- switch accessLogFormat {
- case "text":
- fmt.Printf("%s %s - - [%s] %q %d %d %q %q %f\n",
- fields["host"], fields["remoteAddr"], l.started,
- fmt.Sprintf("%s %s %s", fields["method"], fields["uri"], fields["proto"]),
- fields["status"], fields["written"], fields["referer"], fields["userAgent"], fields["duration"],
- )
- case "json":
- l.logrusEntry.WithFields(fields).Info("access")
- default:
- panic("invalid access log format")
- }
-}
-
func fatal(err error) {
log.WithError(err).Fatal()
}
diff --git a/logging_test.go b/logging_test.go
deleted file mode 100644
index 42831436..00000000
--- a/logging_test.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package main
-
-import (
- "fmt"
- "net/http"
- "net/http/httptest"
- "testing"
-
- log "github.com/sirupsen/logrus"
- "github.com/stretchr/testify/assert"
-)
-
-func testLogWithStatus(ww http.ResponseWriter, r *http.Request) {
- w := newLoggingResponseWriter(ww, log.WithField("system", "http"))
- defer w.Log(r)
- w.WriteHeader(http.StatusOK)
- fmt.Fprint(&w, "with-status")
-}
-
-func testLogWithoutStatus(ww http.ResponseWriter, r *http.Request) {
- w := newLoggingResponseWriter(ww, log.WithField("system", "http"))
- defer w.Log(r)
- fmt.Fprint(&w, "no-status")
-}
-
-func testLogWithDoubleStatus(ww http.ResponseWriter, r *http.Request) {
- w := newLoggingResponseWriter(ww, log.WithField("system", "http"))
- defer w.Log(r)
- w.WriteHeader(http.StatusOK)
- http.Redirect(&w, r, "/test", 301)
-}
-
-func TestExtractLogFieldsHidesQueryStrings(t *testing.T) {
- w := httptest.NewRecorder()
- r := httptest.NewRequest("GET", "/foo?token=bar", nil)
- r.Header.Set("Referer", "http://invalid.com/bar?token=baz")
-
- l := newLoggingResponseWriter(w, log.WithField("system", "http"))
-
- fields := l.extractLogFields(r)
-
- assert.Equal(t, fields["uri"], "/foo")
- assert.Equal(t, fields["referer"], "http://invalid.com/bar")
-}
-
-func TestLoggingWriter(t *testing.T) {
- assert.HTTPBodyContains(t, testLogWithStatus, "GET", "/test", nil, "with-status")
- assert.HTTPBodyContains(t, testLogWithoutStatus, "GET", "/test", nil, "no-status")
- assert.HTTPSuccess(t, testLogWithDoubleStatus, "GET", "/test", nil)
-}
diff --git a/vendor/github.com/client9/reopen/LICENSE b/vendor/github.com/client9/reopen/LICENSE
new file mode 100644
index 00000000..4345e0ba
--- /dev/null
+++ b/vendor/github.com/client9/reopen/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Nick Galbreath
+
+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/client9/reopen/Makefile b/vendor/github.com/client9/reopen/Makefile
new file mode 100644
index 00000000..81eb6214
--- /dev/null
+++ b/vendor/github.com/client9/reopen/Makefile
@@ -0,0 +1,27 @@
+
+build: hooks ## build and lint
+ ./scripts/build.sh
+
+test: ## just test
+ go test -cover .
+
+clean: ## cleanup
+ rm -f ./example1/example1
+ rm -f ./example2/example2
+ go clean ./...
+ git gc
+
+# https://www.client9.com/automatically-install-git-hooks/
+.git/hooks/pre-commit: scripts/pre-commit.sh
+ cp -f scripts/pre-commit.sh .git/hooks/pre-commit
+.git/hooks/commit-msg: scripts/commit-msg.sh
+ cp -f scripts/commit-msg.sh .git/hooks/commit-msg
+hooks: .git/hooks/pre-commit .git/hooks/commit-msg ## install git precommit hooks
+
+# https://www.client9.com/self-documenting-makefiles/
+help:
+ @awk -F ':|##' '/^[^\t].+?:.*?##/ {\
+ printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \
+ }' $(MAKEFILE_LIST)
+.DEFAULT_GOAL=help
+.PHONY=help
diff --git a/vendor/github.com/client9/reopen/README.md b/vendor/github.com/client9/reopen/README.md
new file mode 100644
index 00000000..ff9f5bfe
--- /dev/null
+++ b/vendor/github.com/client9/reopen/README.md
@@ -0,0 +1,74 @@
+[![Build Status](https://travis-ci.org/client9/reopen.svg)](https://travis-ci.org/client9/reopen) [![Go Report Card](http://goreportcard.com/badge/client9/reopen)](http://goreportcard.com/report/client9/reopen) [![GoDoc](https://godoc.org/github.com/client9/reopen?status.svg)](https://godoc.org/github.com/client9/reopen) [![license](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://raw.githubusercontent.com/client9/reopen/master/LICENSE)
+
+Makes a standard os.File a "reopenable writer" and allows SIGHUP signals
+to reopen log files, as needed by
+[logrotated](https://fedorahosted.org/logrotate/). This is inspired
+by the C/Posix
+[freopen](http://pubs.opengroup.org/onlinepubs/009695399/functions/freopen.html)
+
+The simple version `reopen.NewFileWriter` does unbuffered writing. A
+call to `.Reopen` closes the existing file handle, and then re-opens
+it using the original filename.
+
+The more advanced version `reopen.NewBufferedFileWriter` buffers input
+and flushes when the internal buffer is full (with care) or if 30 seconds has
+elapsed.
+
+There is also `reopen.Stderr` and `reopen.Stdout` which implements the `reopen.Reopener` interface (and does nothing on a reopen call).
+
+`reopen.Discard` wraps `ioutil.Discard`
+
+Samples are in `example1` and `example2`. The `run.sh` scripts are a
+dumb test where the file is rotated underneath the server, and nothing
+is lost. This is not the most robust test but gives you an idea of how it works.
+
+
+Here's some sample code.
+
+```go
+package main
+
+/* Simple logrotate logger
+ */
+import (
+ "fmt"
+ "log"
+ "net/http"
+ "os"
+ "os/signal"
+ "syscall"
+
+ "github.com/client9/reopen"
+)
+
+func main() {
+ // setup logger to write to our new *reopenable* log file
+
+ f, err := reopen.NewFileWriter("/tmp/example.log")
+ if err != nil {
+ log.Fatalf("Unable to set output log: %s", err)
+ }
+ log.SetOutput(f)
+
+ // Handle SIGHUP
+ //
+ // channel is number of signals needed to catch (more or less)
+ // we only are working with one here, SIGHUP
+ sighup := make(chan os.Signal, 1)
+ signal.Notify(sighup, syscall.SIGHUP)
+ go func() {
+ for {
+ <-sighup
+ fmt.Println("Got a sighup")
+ f.Reopen()
+ }
+ }()
+
+ // dumb http server that just prints and logs the path
+ http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+ log.Printf("%s", r.URL.Path)
+ fmt.Fprintf(w, "%s\n", r.URL.Path)
+ })
+ log.Fatal(http.ListenAndServe("127.0.0.1:8123", nil))
+}
+```
diff --git a/vendor/github.com/client9/reopen/reopen.go b/vendor/github.com/client9/reopen/reopen.go
new file mode 100644
index 00000000..6032c308
--- /dev/null
+++ b/vendor/github.com/client9/reopen/reopen.go
@@ -0,0 +1,254 @@
+package reopen
+
+import (
+ "bufio"
+ "io"
+ "io/ioutil"
+ "os"
+ "sync"
+ "time"
+)
+
+// Reopener interface defines something that can be reopened
+type Reopener interface {
+ Reopen() error
+}
+
+// Writer is a writer that also can be reopened
+type Writer interface {
+ Reopener
+ io.Writer
+}
+
+// WriteCloser is a io.WriteCloser that can also be reopened
+type WriteCloser interface {
+ Reopener
+ io.WriteCloser
+}
+
+// FileWriter that can also be reopened
+type FileWriter struct {
+ mu sync.Mutex // ensures close / reopen / write are not called at the same time, protects f
+ f *os.File
+ mode os.FileMode
+ name string
+}
+
+// Close calls the underlyding File.Close()
+func (f *FileWriter) Close() error {
+ f.mu.Lock()
+ err := f.f.Close()
+ f.mu.Unlock()
+ return err
+}
+
+// mutex free version
+func (f *FileWriter) reopen() error {
+ if f.f != nil {
+ f.f.Close()
+ f.f = nil
+ }
+ newf, err := os.OpenFile(f.name, os.O_WRONLY|os.O_APPEND|os.O_CREATE, f.mode)
+ if err != nil {
+ f.f = nil
+ return err
+ }
+ f.f = newf
+
+ return nil
+}
+
+// Reopen the file
+func (f *FileWriter) Reopen() error {
+ f.mu.Lock()
+ err := f.reopen()
+ f.mu.Unlock()
+ return err
+}
+
+// Write implements the stander io.Writer interface
+func (f *FileWriter) Write(p []byte) (int, error) {
+ f.mu.Lock()
+ n, err := f.f.Write(p)
+ f.mu.Unlock()
+ return n, err
+}
+
+// NewFileWriter opens a file for appending and writing and can be reopened.
+// it is a ReopenWriteCloser...
+func NewFileWriter(name string) (*FileWriter, error) {
+ // Standard default mode
+ return NewFileWriterMode(name, 0666)
+}
+
+// NewFileWriterMode opens a Reopener file with a specific permission
+func NewFileWriterMode(name string, mode os.FileMode) (*FileWriter, error) {
+ writer := FileWriter{
+ f: nil,
+ name: name,
+ mode: mode,
+ }
+ err := writer.reopen()
+ if err != nil {
+ return nil, err
+ }
+ return &writer, nil
+}
+
+// BufferedFileWriter is buffer writer than can be reopned
+type BufferedFileWriter struct {
+ mu sync.Mutex
+ quitChan chan bool
+ done bool
+ origWriter *FileWriter
+ bufWriter *bufio.Writer
+}
+
+// Reopen implement Reopener
+func (bw *BufferedFileWriter) Reopen() error {
+ bw.mu.Lock()
+ bw.bufWriter.Flush()
+
+ // use non-mutex version since we are using this one
+ err := bw.origWriter.reopen()
+
+ bw.bufWriter.Reset(io.Writer(bw.origWriter))
+ bw.mu.Unlock()
+
+ return err
+}
+
+// Close flushes the internal buffer and closes the destination file
+func (bw *BufferedFileWriter) Close() error {
+ bw.quitChan <- true
+ bw.mu.Lock()
+ bw.done = true
+ bw.bufWriter.Flush()
+ bw.origWriter.f.Close()
+ bw.mu.Unlock()
+ return nil
+}
+
+// Write implements io.Writer (and reopen.Writer)
+func (bw *BufferedFileWriter) Write(p []byte) (int, error) {
+ bw.mu.Lock()
+ n, err := bw.bufWriter.Write(p)
+
+ // Special Case... if the used space in the buffer is LESS than
+ // the input, then we did a flush in the middle of the line
+ // and the full log line was not sent on its way.
+ if bw.bufWriter.Buffered() < len(p) {
+ bw.bufWriter.Flush()
+ }
+
+ bw.mu.Unlock()
+ return n, err
+}
+
+// Flush flushes the buffer.
+func (bw *BufferedFileWriter) Flush() {
+ bw.mu.Lock()
+ // could add check if bw.done already
+ // should never happen
+ bw.bufWriter.Flush()
+ bw.origWriter.f.Sync()
+ bw.mu.Unlock()
+}
+
+// flushDaemon periodically flushes the log file buffers.
+func (bw *BufferedFileWriter) flushDaemon(interval time.Duration) {
+ ticker := time.NewTicker(interval)
+ for {
+ select {
+ case <-bw.quitChan:
+ ticker.Stop()
+ return
+ case <-ticker.C:
+ bw.Flush()
+ }
+ }
+}
+
+const bufferSize = 256 * 1024
+const flushInterval = 30 * time.Second
+
+// NewBufferedFileWriter opens a buffered file that is periodically
+// flushed.
+func NewBufferedFileWriter(w *FileWriter) *BufferedFileWriter {
+ return NewBufferedFileWriterSize(w, bufferSize, flushInterval)
+}
+
+// NewBufferedFileWriterSize opens a buffered file with the given size that is periodically
+// flushed on the given interval.
+func NewBufferedFileWriterSize(w *FileWriter, size int, flush time.Duration) *BufferedFileWriter {
+ bw := BufferedFileWriter{
+ quitChan: make(chan bool, 1),
+ origWriter: w,
+ bufWriter: bufio.NewWriterSize(w, size),
+ }
+ go bw.flushDaemon(flush)
+ return &bw
+}
+
+type multiReopenWriter struct {
+ writers []Writer
+}
+
+// Reopen reopens all child Reopeners
+func (t *multiReopenWriter) Reopen() error {
+ for _, w := range t.writers {
+ err := w.Reopen()
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// Write implements standard io.Write and reopen.Write
+func (t *multiReopenWriter) Write(p []byte) (int, error) {
+ for _, w := range t.writers {
+ n, err := w.Write(p)
+ if err != nil {
+ return n, err
+ }
+ if n != len(p) {
+ return n, io.ErrShortWrite
+ }
+ }
+ return len(p), nil
+}
+
+// MultiWriter creates a writer that duplicates its writes to all the
+// provided writers, similar to the Unix tee(1) command.
+// Also allow reopen
+func MultiWriter(writers ...Writer) Writer {
+ w := make([]Writer, len(writers))
+ copy(w, writers)
+ return &multiReopenWriter{w}
+}
+
+type nopReopenWriteCloser struct {
+ io.Writer
+}
+
+func (nopReopenWriteCloser) Reopen() error {
+ return nil
+}
+
+func (nopReopenWriteCloser) Close() error {
+ return nil
+}
+
+// NopWriter turns a normal writer into a ReopenWriter
+// by doing a NOP on Reopen. See https://en.wikipedia.org/wiki/NOP
+func NopWriter(w io.Writer) WriteCloser {
+ return nopReopenWriteCloser{w}
+}
+
+// Reopenable versions of os.Stdout, os.Stderr, /dev/null (reopen does nothing)
+var (
+ Stdout = NopWriter(os.Stdout)
+ Stderr = NopWriter(os.Stderr)
+ Discard = NopWriter(ioutil.Discard)
+)
diff --git a/vendor/github.com/konsorten/go-windows-terminal-sequences/LICENSE b/vendor/github.com/konsorten/go-windows-terminal-sequences/LICENSE
new file mode 100644
index 00000000..14127cd8
--- /dev/null
+++ b/vendor/github.com/konsorten/go-windows-terminal-sequences/LICENSE
@@ -0,0 +1,9 @@
+(The MIT License)
+
+Copyright (c) 2017 marvin + konsorten GmbH (open-source@konsorten.de)
+
+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/konsorten/go-windows-terminal-sequences/README.md b/vendor/github.com/konsorten/go-windows-terminal-sequences/README.md
new file mode 100644
index 00000000..195333e5
--- /dev/null
+++ b/vendor/github.com/konsorten/go-windows-terminal-sequences/README.md
@@ -0,0 +1,41 @@
+# Windows Terminal Sequences
+
+This library allow for enabling Windows terminal color support for Go.
+
+See [Console Virtual Terminal Sequences](https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences) for details.
+
+## Usage
+
+```go
+import (
+ "syscall"
+
+ sequences "github.com/konsorten/go-windows-terminal-sequences"
+)
+
+func main() {
+ sequences.EnableVirtualTerminalProcessing(syscall.Stdout, true)
+}
+
+```
+
+## Authors
+
+The tool is sponsored by the [marvin + konsorten GmbH](http://www.konsorten.de).
+
+We thank all the authors who provided code to this library:
+
+* Felix Kollmann
+* Nicolas Perraut
+
+## License
+
+(The MIT License)
+
+Copyright (c) 2018 marvin + konsorten GmbH (open-source@konsorten.de)
+
+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/konsorten/go-windows-terminal-sequences/go.mod b/vendor/github.com/konsorten/go-windows-terminal-sequences/go.mod
new file mode 100644
index 00000000..716c6131
--- /dev/null
+++ b/vendor/github.com/konsorten/go-windows-terminal-sequences/go.mod
@@ -0,0 +1 @@
+module github.com/konsorten/go-windows-terminal-sequences
diff --git a/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences.go b/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences.go
new file mode 100644
index 00000000..ef18d8f9
--- /dev/null
+++ b/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences.go
@@ -0,0 +1,36 @@
+// +build windows
+
+package sequences
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+var (
+ kernel32Dll *syscall.LazyDLL = syscall.NewLazyDLL("Kernel32.dll")
+ setConsoleMode *syscall.LazyProc = kernel32Dll.NewProc("SetConsoleMode")
+)
+
+func EnableVirtualTerminalProcessing(stream syscall.Handle, enable bool) error {
+ const ENABLE_VIRTUAL_TERMINAL_PROCESSING uint32 = 0x4
+
+ var mode uint32
+ err := syscall.GetConsoleMode(syscall.Stdout, &mode)
+ if err != nil {
+ return err
+ }
+
+ if enable {
+ mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING
+ } else {
+ mode &^= ENABLE_VIRTUAL_TERMINAL_PROCESSING
+ }
+
+ ret, _, err := setConsoleMode.Call(uintptr(unsafe.Pointer(stream)), uintptr(mode))
+ if ret == 0 {
+ return err
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences_dummy.go b/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences_dummy.go
new file mode 100644
index 00000000..df61a6f2
--- /dev/null
+++ b/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences_dummy.go
@@ -0,0 +1,11 @@
+// +build linux darwin
+
+package sequences
+
+import (
+ "fmt"
+)
+
+func EnableVirtualTerminalProcessing(stream uintptr, enable bool) error {
+ return fmt.Errorf("windows only package")
+}
diff --git a/vendor/github.com/sebest/xff/LICENSE b/vendor/github.com/sebest/xff/LICENSE
new file mode 100644
index 00000000..4d15f4e3
--- /dev/null
+++ b/vendor/github.com/sebest/xff/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2015 Sebastien Estienne (sebastien.estienne@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/sebest/xff/README.md b/vendor/github.com/sebest/xff/README.md
new file mode 100644
index 00000000..41a518ab
--- /dev/null
+++ b/vendor/github.com/sebest/xff/README.md
@@ -0,0 +1,44 @@
+# X-Forwarded-For middleware fo Go [![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/sebest/xff) [![Build Status](https://travis-ci.org/sebest/xff.svg?branch=master)](https://travis-ci.org/sebest/xff)
+
+Package `xff` is a `net/http` middleware/handler to parse [Forwarded HTTP Extension](http://tools.ietf.org/html/rfc7239) in Golang.
+
+## Example usage
+
+Install `xff`:
+
+ go get github.com/sebest/xff
+
+Edit `server.go`:
+
+```go
+package main
+
+import (
+ "net/http"
+
+ "github.com/sebest/xff"
+)
+
+func main() {
+ handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte("hello from " + r.RemoteAddr + "\n"))
+ })
+
+ xffmw, _ := xff.Default()
+ http.ListenAndServe(":8080", xffmw.Handler(handler))
+}
+```
+
+Then run your server:
+
+ go run server.go
+
+The server now runs on `localhost:8080`:
+
+ $ curl -D - -H 'X-Forwarded-For: 42.42.42.42' http://localhost:8080/
+ HTTP/1.1 200 OK
+ Date: Fri, 20 Feb 2015 20:03:02 GMT
+ Content-Length: 29
+ Content-Type: text/plain; charset=utf-8
+
+ hello from 42.42.42.42:52661
diff --git a/vendor/github.com/sebest/xff/xff.go b/vendor/github.com/sebest/xff/xff.go
new file mode 100644
index 00000000..02f6e69f
--- /dev/null
+++ b/vendor/github.com/sebest/xff/xff.go
@@ -0,0 +1,158 @@
+package xff
+
+import (
+ "log"
+ "net"
+ "net/http"
+ "os"
+ "strings"
+)
+
+// list of private subnets
+var privateMasks, _ = toMasks([]string{
+ "127.0.0.0/8",
+ "10.0.0.0/8",
+ "172.16.0.0/12",
+ "192.168.0.0/16",
+ "fc00::/7",
+})
+
+// converts a list of subnets' string to a list of net.IPNet.
+func toMasks(ips []string) (masks []net.IPNet, err error) {
+ for _, cidr := range ips {
+ var network *net.IPNet
+ _, network, err = net.ParseCIDR(cidr)
+ if err != nil {
+ return
+ }
+ masks = append(masks, *network)
+ }
+ return
+}
+
+// checks if a net.IP is in a list of net.IPNet
+func ipInMasks(ip net.IP, masks []net.IPNet) bool {
+ for _, mask := range masks {
+ if mask.Contains(ip) {
+ return true
+ }
+ }
+ return false
+}
+
+// IsPublicIP returns true if the given IP can be routed on the Internet.
+func IsPublicIP(ip net.IP) bool {
+ if !ip.IsGlobalUnicast() {
+ return false
+ }
+ return !ipInMasks(ip, privateMasks)
+}
+
+// Parse parses the value of the X-Forwarded-For Header and returns the IP address.
+func Parse(ipList string) string {
+ for _, ip := range strings.Split(ipList, ",") {
+ ip = strings.TrimSpace(ip)
+ if IP := net.ParseIP(ip); IP != nil && IsPublicIP(IP) {
+ return ip
+ }
+ }
+ return ""
+}
+
+// GetRemoteAddr parses the given request, resolves the X-Forwarded-For header
+// and returns the resolved remote address.
+func GetRemoteAddr(r *http.Request) string {
+ return GetRemoteAddrIfAllowed(r, func(sip string) bool { return true })
+}
+
+// GetRemoteAddrIfAllowed parses the given request, resolves the X-Forwarded-For header
+// and returns the resolved remote address if allowed.
+func GetRemoteAddrIfAllowed(r *http.Request, allowed func(sip string) bool) string {
+ if xffh := r.Header.Get("X-Forwarded-For"); xffh != "" {
+ if sip, sport, err := net.SplitHostPort(r.RemoteAddr); err == nil && sip != "" {
+ if allowed(sip) {
+ if xip := Parse(xffh); xip != "" {
+ return net.JoinHostPort(xip, sport)
+ }
+ }
+ }
+ }
+ return r.RemoteAddr
+}
+
+// Options is a configuration container to setup the XFF middleware.
+type Options struct {
+ // AllowedSubnets is a list of Subnets from which we will accept the
+ // X-Forwarded-For header.
+ // If this list is empty we will accept every Subnets (default).
+ AllowedSubnets []string
+ // Debugging flag adds additional output to debug server side XFF issues.
+ Debug bool
+}
+
+// XFF http handler
+type XFF struct {
+ // Debug logger
+ Log *log.Logger
+ // Set to true if all IPs or Subnets are allowed.
+ allowAll bool
+ // List of IP subnets that are allowed.
+ allowedMasks []net.IPNet
+}
+
+// New creates a new XFF handler with the provided options.
+func New(options Options) (*XFF, error) {
+ allowedMasks, err := toMasks(options.AllowedSubnets)
+ if err != nil {
+ return nil, err
+ }
+ xff := &XFF{
+ allowAll: len(options.AllowedSubnets) == 0,
+ allowedMasks: allowedMasks,
+ }
+ if options.Debug {
+ xff.Log = log.New(os.Stdout, "[xff] ", log.LstdFlags)
+ }
+ return xff, nil
+}
+
+// Default creates a new XFF handler with default options.
+func Default() (*XFF, error) {
+ return New(Options{})
+}
+
+// Handler updates RemoteAdd from X-Fowarded-For Headers.
+func (xff *XFF) Handler(h http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ r.RemoteAddr = GetRemoteAddrIfAllowed(r, xff.allowed)
+ h.ServeHTTP(w, r)
+ })
+}
+
+// Negroni compatible interface
+func (xff *XFF) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
+ r.RemoteAddr = GetRemoteAddrIfAllowed(r, xff.allowed)
+ next(w, r)
+}
+
+// HandlerFunc provides Martini compatible handler
+func (xff *XFF) HandlerFunc(w http.ResponseWriter, r *http.Request) {
+ r.RemoteAddr = GetRemoteAddrIfAllowed(r, xff.allowed)
+}
+
+// checks that the IP is allowed.
+func (xff *XFF) allowed(sip string) bool {
+ if xff.allowAll {
+ return true
+ } else if ip := net.ParseIP(sip); ip != nil && ipInMasks(ip, xff.allowedMasks) {
+ return true
+ }
+ return false
+}
+
+// convenience method. checks if debugging is turned on before printing
+func (xff *XFF) logf(format string, a ...interface{}) {
+ if xff.Log != nil {
+ xff.Log.Printf(format, a...)
+ }
+}
diff --git a/vendor/github.com/sirupsen/logrus/CHANGELOG.md b/vendor/github.com/sirupsen/logrus/CHANGELOG.md
index cc58f645..51a7ab0c 100644
--- a/vendor/github.com/sirupsen/logrus/CHANGELOG.md
+++ b/vendor/github.com/sirupsen/logrus/CHANGELOG.md
@@ -1,3 +1,85 @@
+# 1.4.2
+ * Fixes build break for plan9, nacl, solaris
+# 1.4.1
+This new release introduces:
+ * Enhance TextFormatter to not print caller information when they are empty (#944)
+ * Remove dependency on golang.org/x/crypto (#932, #943)
+
+Fixes:
+ * Fix Entry.WithContext method to return a copy of the initial entry (#941)
+
+# 1.4.0
+This new release introduces:
+ * Add `DeferExitHandler`, similar to `RegisterExitHandler` but prepending the handler to the list of handlers (semantically like `defer`) (#848).
+ * Add `CallerPrettyfier` to `JSONFormatter` and `TextFormatter (#909, #911)
+ * Add `Entry.WithContext()` and `Entry.Context`, to set a context on entries to be used e.g. in hooks (#919).
+
+Fixes:
+ * Fix wrong method calls `Logger.Print` and `Logger.Warningln` (#893).
+ * Update `Entry.Logf` to not do string formatting unless the log level is enabled (#903)
+ * Fix infinite recursion on unknown `Level.String()` (#907)
+ * Fix race condition in `getCaller` (#916).
+
+
+# 1.3.0
+This new release introduces:
+ * Log, Logf, Logln functions for Logger and Entry that take a Level
+
+Fixes:
+ * Building prometheus node_exporter on AIX (#840)
+ * Race condition in TextFormatter (#468)
+ * Travis CI import path (#868)
+ * Remove coloured output on Windows (#862)
+ * Pointer to func as field in JSONFormatter (#870)
+ * Properly marshal Levels (#873)
+
+# 1.2.0
+This new release introduces:
+ * A new method `SetReportCaller` in the `Logger` to enable the file, line and calling function from which the trace has been issued
+ * A new trace level named `Trace` whose level is below `Debug`
+ * A configurable exit function to be called upon a Fatal trace
+ * The `Level` object now implements `encoding.TextUnmarshaler` interface
+
+# 1.1.1
+This is a bug fix release.
+ * fix the build break on Solaris
+ * don't drop a whole trace in JSONFormatter when a field param is a function pointer which can not be serialized
+
+# 1.1.0
+This new release introduces:
+ * several fixes:
+ * a fix for a race condition on entry formatting
+ * proper cleanup of previously used entries before putting them back in the pool
+ * the extra new line at the end of message in text formatter has been removed
+ * a new global public API to check if a level is activated: IsLevelEnabled
+ * the following methods have been added to the Logger object
+ * IsLevelEnabled
+ * SetFormatter
+ * SetOutput
+ * ReplaceHooks
+ * introduction of go module
+ * an indent configuration for the json formatter
+ * output colour support for windows
+ * the field sort function is now configurable for text formatter
+ * the CLICOLOR and CLICOLOR\_FORCE environment variable support in text formater
+
+# 1.0.6
+
+This new release introduces:
+ * a new api WithTime which allows to easily force the time of the log entry
+ which is mostly useful for logger wrapper
+ * a fix reverting the immutability of the entry given as parameter to the hooks
+ a new configuration field of the json formatter in order to put all the fields
+ in a nested dictionnary
+ * a new SetOutput method in the Logger
+ * a new configuration of the textformatter to configure the name of the default keys
+ * a new configuration of the text formatter to disable the level truncation
+
+# 1.0.5
+
+* Fix hooks race (#707)
+* Fix panic deadlock (#695)
+
# 1.0.4
* Fix race when adding hooks (#612)
diff --git a/vendor/github.com/sirupsen/logrus/README.md b/vendor/github.com/sirupsen/logrus/README.md
index 08584b5f..e7450a88 100644
--- a/vendor/github.com/sirupsen/logrus/README.md
+++ b/vendor/github.com/sirupsen/logrus/README.md
@@ -56,8 +56,39 @@ time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased
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
```
+To ensure this behaviour even if a TTY is attached, set your formatter as follows:
+
+```go
+ log.SetFormatter(&log.TextFormatter{
+ DisableColors: true,
+ FullTimestamp: true,
+ })
+```
+
+#### Logging Method Name
+
+If you wish to add the calling method as a field, instruct the logger via:
+```go
+log.SetReportCaller(true)
+```
+This adds the caller as 'method' like so:
+
+```json
+{"animal":"penguin","level":"fatal","method":"github.com/sirupsen/arcticcreatures.migrate","msg":"a penguin swims by",
+"time":"2014-03-10 19:57:38.562543129 -0400 EDT"}
+```
+
+```text
+time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcreatures.migrate msg="a penguin swims by" animal=penguin
+```
+Note that this does add measurable overhead - the cost will depend on the version of Go, but is
+between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your
+environment via benchmarks:
+```
+go test -bench=.*CallerTracing
+```
+
#### Case-sensitivity
@@ -156,7 +187,7 @@ func main() {
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)
+ // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
// if err == nil {
// log.Out = file
// } else {
@@ -220,7 +251,7 @@ 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"
+ "gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "airbrake"
logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
"log/syslog"
)
@@ -241,64 +272,15 @@ 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).
-| Hook | Description |
-| ----- | ----------- |
-| [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. |
-| [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) |
-| [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|
-| [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 |
-| [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/) |
+A list of currently known of service hook can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks)
+
#### Level logging
-Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.
+Logrus has seven logging levels: Trace, Debug, Info, Warning, Error, Fatal and Panic.
```go
+log.Trace("Something very low level.")
log.Debug("Useful debugging information.")
log.Info("Something noteworthy happened!")
log.Warn("You should probably take a look at this.")
@@ -370,6 +352,9 @@ 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`. For Windows, see
[github.com/mattn/go-colorable](https://github.com/mattn/go-colorable).
+ * When colors are enabled, levels are truncated to 4 characters by default. To disable
+ truncation set the `DisableLevelTruncation` field to `true`.
+ * When outputting to a TTY, it's often helpful to visually scan down a column where all the levels are the same width. Setting the `PadLevelText` field to `true` enables this behavior, by adding padding to the level text.
* 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).
@@ -377,9 +362,11 @@ The built-in logging formatters are:
Third party logging formatters:
* [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be parsed by Kubernetes and Google Container Engine.
+* [`GELF`](https://github.com/fabienm/go-logrus-formatters). Formats entries so they comply to Graylog's [GELF 1.1 specification](http://docs.graylog.org/en/2.4/pages/gelf.html).
* [`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̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
+* [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure.
You can define your formatter by implementing the `Formatter` interface,
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
@@ -493,7 +480,7 @@ 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.
+By default, Logger is protected by a mutex for concurrent writes. The mutex is held 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:
diff --git a/vendor/github.com/sirupsen/logrus/alt_exit.go b/vendor/github.com/sirupsen/logrus/alt_exit.go
index 8af90637..8fd189e1 100644
--- a/vendor/github.com/sirupsen/logrus/alt_exit.go
+++ b/vendor/github.com/sirupsen/logrus/alt_exit.go
@@ -51,9 +51,9 @@ func Exit(code int) {
os.Exit(code)
}
-// RegisterExitHandler adds a Logrus Exit handler, call logrus.Exit to invoke
-// all handlers. The handlers will also be invoked when any Fatal log entry is
-// made.
+// RegisterExitHandler appends a Logrus Exit handler to the list of handlers,
+// call logrus.Exit to invoke all handlers. The handlers will also be invoked when
+// any Fatal log entry is made.
//
// This method is useful when a caller wishes to use logrus to log a fatal
// message but also needs to gracefully shutdown. An example usecase could be
@@ -62,3 +62,15 @@ func Exit(code int) {
func RegisterExitHandler(handler func()) {
handlers = append(handlers, handler)
}
+
+// DeferExitHandler prepends a Logrus Exit handler to the list of handlers,
+// call logrus.Exit to invoke all handlers. The handlers will also be invoked when
+// any Fatal log entry is made.
+//
+// This method is useful when a caller wishes to use logrus to log a fatal
+// message but also needs to gracefully shutdown. An example usecase could be
+// closing database connections, or sending a alert that the application is
+// closing.
+func DeferExitHandler(handler func()) {
+ handlers = append([]func(){handler}, handlers...)
+}
diff --git a/vendor/github.com/sirupsen/logrus/entry.go b/vendor/github.com/sirupsen/logrus/entry.go
index 1fad45e0..63e25583 100644
--- a/vendor/github.com/sirupsen/logrus/entry.go
+++ b/vendor/github.com/sirupsen/logrus/entry.go
@@ -2,13 +2,33 @@ package logrus
import (
"bytes"
+ "context"
"fmt"
"os"
+ "reflect"
+ "runtime"
+ "strings"
"sync"
"time"
)
-var bufferPool *sync.Pool
+var (
+ bufferPool *sync.Pool
+
+ // qualified package name, cached at first use
+ logrusPackage string
+
+ // Positions in the call stack when tracing to report the calling method
+ minimumCallerDepth int
+
+ // Used for caller information initialisation
+ callerInitOnce sync.Once
+)
+
+const (
+ maximumCallerDepth int = 25
+ knownLogrusFrames int = 4
+)
func init() {
bufferPool = &sync.Pool{
@@ -16,15 +36,18 @@ func init() {
return new(bytes.Buffer)
},
}
+
+ // start at the bottom of the stack before the package-name cache is primed
+ minimumCallerDepth = 1
}
// 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
-// passed around as much as you wish to avoid field duplication.
+// the fields passed with WithField{,s}. It's finally logged when Trace, Debug,
+// Info, Warn, Error, Fatal or Panic is called on it. These objects can be
+// reused and passed around as much as you wish to avoid field duplication.
type Entry struct {
Logger *Logger
@@ -34,22 +57,31 @@ type Entry struct {
// Time at which the log entry was created
Time time.Time
- // Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic
+ // Level the log entry was logged at: Trace, 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
+ // Calling method, with package name
+ Caller *runtime.Frame
+
+ // Message passed to Trace, Debug, Info, Warn, Error, Fatal or Panic
Message string
- // When formatter is called in entry.log(), an Buffer may be set to entry
+ // When formatter is called in entry.log(), a Buffer may be set to entry
Buffer *bytes.Buffer
+
+ // Contains the context set by the user. Useful for hook processing etc.
+ Context context.Context
+
+ // err may contain a field formatting error
+ err string
}
func NewEntry(logger *Logger) *Entry {
return &Entry{
Logger: logger,
- // Default is three fields, give a little extra room
- Data: make(Fields, 5),
+ // Default is three fields, plus one optional. Give a little extra room.
+ Data: make(Fields, 6),
}
}
@@ -69,6 +101,11 @@ func (entry *Entry) WithError(err error) *Entry {
return entry.WithField(ErrorKey, err)
}
+// Add a context to the Entry.
+func (entry *Entry) WithContext(ctx context.Context) *Entry {
+ return &Entry{Logger: entry.Logger, Data: entry.Data, Time: entry.Time, err: entry.err, Context: ctx}
+}
+
// Add a single field to the Entry.
func (entry *Entry) WithField(key string, value interface{}) *Entry {
return entry.WithFields(Fields{key: value})
@@ -80,59 +117,164 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
for k, v := range entry.Data {
data[k] = v
}
+ fieldErr := entry.err
for k, v := range fields {
- data[k] = v
+ isErrField := false
+ if t := reflect.TypeOf(v); t != nil {
+ switch t.Kind() {
+ case reflect.Func:
+ isErrField = true
+ case reflect.Ptr:
+ isErrField = t.Elem().Kind() == reflect.Func
+ }
+ }
+ if isErrField {
+ tmp := fmt.Sprintf("can not add field %q", k)
+ if fieldErr != "" {
+ fieldErr = entry.err + ", " + tmp
+ } else {
+ fieldErr = tmp
+ }
+ } else {
+ data[k] = v
+ }
+ }
+ return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context}
+}
+
+// Overrides the time of the Entry.
+func (entry *Entry) WithTime(t time.Time) *Entry {
+ return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t, err: entry.err, Context: entry.Context}
+}
+
+// getPackageName reduces a fully qualified function name to the package name
+// There really ought to be to be a better way...
+func getPackageName(f string) string {
+ for {
+ lastPeriod := strings.LastIndex(f, ".")
+ lastSlash := strings.LastIndex(f, "/")
+ if lastPeriod > lastSlash {
+ f = f[:lastPeriod]
+ } else {
+ break
+ }
}
- return &Entry{Logger: entry.Logger, Data: data}
+
+ return f
+}
+
+// getCaller retrieves the name of the first non-logrus calling function
+func getCaller() *runtime.Frame {
+
+ // cache this package's fully-qualified name
+ callerInitOnce.Do(func() {
+ pcs := make([]uintptr, 2)
+ _ = runtime.Callers(0, pcs)
+ logrusPackage = getPackageName(runtime.FuncForPC(pcs[1]).Name())
+
+ // now that we have the cache, we can skip a minimum count of known-logrus functions
+ // XXX this is dubious, the number of frames may vary
+ minimumCallerDepth = knownLogrusFrames
+ })
+
+ // Restrict the lookback frames to avoid runaway lookups
+ pcs := make([]uintptr, maximumCallerDepth)
+ depth := runtime.Callers(minimumCallerDepth, pcs)
+ frames := runtime.CallersFrames(pcs[:depth])
+
+ for f, again := frames.Next(); again; f, again = frames.Next() {
+ pkg := getPackageName(f.Function)
+
+ // If the caller isn't part of this package, we're done
+ if pkg != logrusPackage {
+ return &f
+ }
+ }
+
+ // if we got here, we failed to find the caller's context
+ return nil
+}
+
+func (entry Entry) HasCaller() (has bool) {
+ return entry.Logger != nil &&
+ entry.Logger.ReportCaller &&
+ entry.Caller != nil
}
// 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()
+
+ // Default to now, but allow users to override if they want.
+ //
+ // We don't have to worry about polluting future calls to Entry#log()
+ // with this assignment because this function is declared with a
+ // non-pointer receiver.
+ if entry.Time.IsZero() {
+ entry.Time = time.Now()
+ }
+
entry.Level = level
entry.Message = msg
-
- entry.Logger.mu.Lock()
- err := entry.Logger.Hooks.Fire(level, &entry)
- entry.Logger.mu.Unlock()
- if err != nil {
- entry.Logger.mu.Lock()
- fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
- entry.Logger.mu.Unlock()
+ if entry.Logger.ReportCaller {
+ entry.Caller = getCaller()
}
+
+ entry.fireHooks()
+
buffer = bufferPool.Get().(*bytes.Buffer)
buffer.Reset()
defer bufferPool.Put(buffer)
entry.Buffer = buffer
- serialized, err := entry.Logger.Formatter.Format(&entry)
+
+ entry.write()
+
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
+ // directly here.
+ if level <= PanicLevel {
+ panic(&entry)
+ }
+}
+
+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() {
+ entry.Logger.mu.Lock()
+ defer entry.Logger.mu.Unlock()
+ serialized, err := entry.Logger.Formatter.Format(entry)
if err != nil {
- entry.Logger.mu.Lock()
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
- entry.Logger.mu.Unlock()
} else {
- entry.Logger.mu.Lock()
_, err = entry.Logger.Out.Write(serialized)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
}
- entry.Logger.mu.Unlock()
}
+}
- // To avoid Entry#log() returning a value that only would make sense for
- // panic() to use in Entry#Panic(), we avoid the allocation by checking
- // directly here.
- if level <= PanicLevel {
- panic(&entry)
+func (entry *Entry) Log(level Level, args ...interface{}) {
+ if entry.Logger.IsLevelEnabled(level) {
+ entry.log(level, fmt.Sprint(args...))
}
}
+func (entry *Entry) Trace(args ...interface{}) {
+ entry.Log(TraceLevel, args...)
+}
+
func (entry *Entry) Debug(args ...interface{}) {
- if entry.Logger.level() >= DebugLevel {
- entry.log(DebugLevel, fmt.Sprint(args...))
- }
+ entry.Log(DebugLevel, args...)
}
func (entry *Entry) Print(args ...interface{}) {
@@ -140,15 +282,11 @@ func (entry *Entry) Print(args ...interface{}) {
}
func (entry *Entry) Info(args ...interface{}) {
- if entry.Logger.level() >= InfoLevel {
- entry.log(InfoLevel, fmt.Sprint(args...))
- }
+ entry.Log(InfoLevel, args...)
}
func (entry *Entry) Warn(args ...interface{}) {
- if entry.Logger.level() >= WarnLevel {
- entry.log(WarnLevel, fmt.Sprint(args...))
- }
+ entry.Log(WarnLevel, args...)
}
func (entry *Entry) Warning(args ...interface{}) {
@@ -156,37 +294,37 @@ func (entry *Entry) Warning(args ...interface{}) {
}
func (entry *Entry) Error(args ...interface{}) {
- if entry.Logger.level() >= ErrorLevel {
- entry.log(ErrorLevel, fmt.Sprint(args...))
- }
+ entry.Log(ErrorLevel, args...)
}
func (entry *Entry) Fatal(args ...interface{}) {
- if entry.Logger.level() >= FatalLevel {
- entry.log(FatalLevel, fmt.Sprint(args...))
- }
- Exit(1)
+ entry.Log(FatalLevel, args...)
+ entry.Logger.Exit(1)
}
func (entry *Entry) Panic(args ...interface{}) {
- if entry.Logger.level() >= PanicLevel {
- entry.log(PanicLevel, fmt.Sprint(args...))
- }
+ entry.Log(PanicLevel, args...)
panic(fmt.Sprint(args...))
}
// Entry Printf family functions
-func (entry *Entry) Debugf(format string, args ...interface{}) {
- if entry.Logger.level() >= DebugLevel {
- entry.Debug(fmt.Sprintf(format, args...))
+func (entry *Entry) Logf(level Level, format string, args ...interface{}) {
+ if entry.Logger.IsLevelEnabled(level) {
+ entry.Log(level, fmt.Sprintf(format, args...))
}
}
+func (entry *Entry) Tracef(format string, args ...interface{}) {
+ entry.Logf(TraceLevel, format, args...)
+}
+
+func (entry *Entry) Debugf(format string, args ...interface{}) {
+ entry.Logf(DebugLevel, format, args...)
+}
+
func (entry *Entry) Infof(format string, args ...interface{}) {
- if entry.Logger.level() >= InfoLevel {
- entry.Info(fmt.Sprintf(format, args...))
- }
+ entry.Logf(InfoLevel, format, args...)
}
func (entry *Entry) Printf(format string, args ...interface{}) {
@@ -194,9 +332,7 @@ func (entry *Entry) Printf(format string, args ...interface{}) {
}
func (entry *Entry) Warnf(format string, args ...interface{}) {
- if entry.Logger.level() >= WarnLevel {
- entry.Warn(fmt.Sprintf(format, args...))
- }
+ entry.Logf(WarnLevel, format, args...)
}
func (entry *Entry) Warningf(format string, args ...interface{}) {
@@ -204,36 +340,36 @@ func (entry *Entry) Warningf(format string, args ...interface{}) {
}
func (entry *Entry) Errorf(format string, args ...interface{}) {
- if entry.Logger.level() >= ErrorLevel {
- entry.Error(fmt.Sprintf(format, args...))
- }
+ entry.Logf(ErrorLevel, format, args...)
}
func (entry *Entry) Fatalf(format string, args ...interface{}) {
- if entry.Logger.level() >= FatalLevel {
- entry.Fatal(fmt.Sprintf(format, args...))
- }
- Exit(1)
+ entry.Logf(FatalLevel, format, args...)
+ entry.Logger.Exit(1)
}
func (entry *Entry) Panicf(format string, args ...interface{}) {
- if entry.Logger.level() >= PanicLevel {
- entry.Panic(fmt.Sprintf(format, args...))
- }
+ entry.Logf(PanicLevel, format, args...)
}
// Entry Println family functions
-func (entry *Entry) Debugln(args ...interface{}) {
- if entry.Logger.level() >= DebugLevel {
- entry.Debug(entry.sprintlnn(args...))
+func (entry *Entry) Logln(level Level, args ...interface{}) {
+ if entry.Logger.IsLevelEnabled(level) {
+ entry.Log(level, entry.sprintlnn(args...))
}
}
+func (entry *Entry) Traceln(args ...interface{}) {
+ entry.Logln(TraceLevel, args...)
+}
+
+func (entry *Entry) Debugln(args ...interface{}) {
+ entry.Logln(DebugLevel, args...)
+}
+
func (entry *Entry) Infoln(args ...interface{}) {
- if entry.Logger.level() >= InfoLevel {
- entry.Info(entry.sprintlnn(args...))
- }
+ entry.Logln(InfoLevel, args...)
}
func (entry *Entry) Println(args ...interface{}) {
@@ -241,9 +377,7 @@ func (entry *Entry) Println(args ...interface{}) {
}
func (entry *Entry) Warnln(args ...interface{}) {
- if entry.Logger.level() >= WarnLevel {
- entry.Warn(entry.sprintlnn(args...))
- }
+ entry.Logln(WarnLevel, args...)
}
func (entry *Entry) Warningln(args ...interface{}) {
@@ -251,22 +385,16 @@ func (entry *Entry) Warningln(args ...interface{}) {
}
func (entry *Entry) Errorln(args ...interface{}) {
- if entry.Logger.level() >= ErrorLevel {
- entry.Error(entry.sprintlnn(args...))
- }
+ entry.Logln(ErrorLevel, args...)
}
func (entry *Entry) Fatalln(args ...interface{}) {
- if entry.Logger.level() >= FatalLevel {
- entry.Fatal(entry.sprintlnn(args...))
- }
- Exit(1)
+ entry.Logln(FatalLevel, args...)
+ entry.Logger.Exit(1)
}
func (entry *Entry) Panicln(args ...interface{}) {
- if entry.Logger.level() >= PanicLevel {
- entry.Panic(entry.sprintlnn(args...))
- }
+ entry.Logln(PanicLevel, args...)
}
// Sprintlnn => Sprint no newline. This is to get the behavior of how
diff --git a/vendor/github.com/sirupsen/logrus/exported.go b/vendor/github.com/sirupsen/logrus/exported.go
index 013183ed..62fc2f21 100644
--- a/vendor/github.com/sirupsen/logrus/exported.go
+++ b/vendor/github.com/sirupsen/logrus/exported.go
@@ -1,7 +1,9 @@
package logrus
import (
+ "context"
"io"
+ "time"
)
var (
@@ -15,37 +17,38 @@ func StandardLogger() *Logger {
// SetOutput sets the standard logger output.
func SetOutput(out io.Writer) {
- std.mu.Lock()
- defer std.mu.Unlock()
- std.Out = out
+ std.SetOutput(out)
}
// SetFormatter sets the standard logger formatter.
func SetFormatter(formatter Formatter) {
- std.mu.Lock()
- defer std.mu.Unlock()
- std.Formatter = formatter
+ std.SetFormatter(formatter)
+}
+
+// SetReportCaller sets whether the standard logger will include the calling
+// method as a field.
+func SetReportCaller(include bool) {
+ std.SetReportCaller(include)
}
// SetLevel sets the standard logger level.
func SetLevel(level Level) {
- std.mu.Lock()
- defer std.mu.Unlock()
std.SetLevel(level)
}
// GetLevel returns the standard logger level.
func GetLevel() Level {
- std.mu.Lock()
- defer std.mu.Unlock()
- return std.level()
+ return std.GetLevel()
+}
+
+// IsLevelEnabled checks if the log level of the standard logger is greater than the level param
+func IsLevelEnabled(level Level) bool {
+ return std.IsLevelEnabled(level)
}
// AddHook adds a hook to the standard logger hooks.
func AddHook(hook Hook) {
- std.mu.Lock()
- defer std.mu.Unlock()
- std.Hooks.Add(hook)
+ std.AddHook(hook)
}
// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key.
@@ -53,6 +56,11 @@ func WithError(err error) *Entry {
return std.WithField(ErrorKey, err)
}
+// WithContext creates an entry from the standard logger and adds a context to it.
+func WithContext(ctx context.Context) *Entry {
+ return std.WithContext(ctx)
+}
+
// WithField creates an entry from the standard logger and adds a field to
// it. If you want multiple fields, use `WithFields`.
//
@@ -72,6 +80,20 @@ func WithFields(fields Fields) *Entry {
return std.WithFields(fields)
}
+// WithTime creats an entry from the standard logger and overrides the time of
+// logs generated with it.
+//
+// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
+// or Panic on the Entry it returns.
+func WithTime(t time.Time) *Entry {
+ return std.WithTime(t)
+}
+
+// Trace logs a message at level Trace on the standard logger.
+func Trace(args ...interface{}) {
+ std.Trace(args...)
+}
+
// Debug logs a message at level Debug on the standard logger.
func Debug(args ...interface{}) {
std.Debug(args...)
@@ -107,11 +129,16 @@ func Panic(args ...interface{}) {
std.Panic(args...)
}
-// Fatal logs a message at level Fatal on the standard logger.
+// Fatal logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
func Fatal(args ...interface{}) {
std.Fatal(args...)
}
+// Tracef logs a message at level Trace on the standard logger.
+func Tracef(format string, args ...interface{}) {
+ std.Tracef(format, args...)
+}
+
// Debugf logs a message at level Debug on the standard logger.
func Debugf(format string, args ...interface{}) {
std.Debugf(format, args...)
@@ -147,11 +174,16 @@ func Panicf(format string, args ...interface{}) {
std.Panicf(format, args...)
}
-// Fatalf logs a message at level Fatal on the standard logger.
+// Fatalf logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
func Fatalf(format string, args ...interface{}) {
std.Fatalf(format, args...)
}
+// Traceln logs a message at level Trace on the standard logger.
+func Traceln(args ...interface{}) {
+ std.Traceln(args...)
+}
+
// Debugln logs a message at level Debug on the standard logger.
func Debugln(args ...interface{}) {
std.Debugln(args...)
@@ -187,7 +219,7 @@ func Panicln(args ...interface{}) {
std.Panicln(args...)
}
-// Fatalln logs a message at level Fatal on the standard logger.
+// Fatalln logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
func Fatalln(args ...interface{}) {
std.Fatalln(args...)
}
diff --git a/vendor/github.com/sirupsen/logrus/formatter.go b/vendor/github.com/sirupsen/logrus/formatter.go
index b183ff5b..40888377 100644
--- a/vendor/github.com/sirupsen/logrus/formatter.go
+++ b/vendor/github.com/sirupsen/logrus/formatter.go
@@ -2,7 +2,16 @@ package logrus
import "time"
-const defaultTimestampFormat = time.RFC3339
+// Default key names for the default fields
+const (
+ defaultTimestampFormat = time.RFC3339
+ FieldKeyMsg = "msg"
+ FieldKeyLevel = "level"
+ FieldKeyTime = "time"
+ FieldKeyLogrusError = "logrus_error"
+ FieldKeyFunc = "func"
+ FieldKeyFile = "file"
+)
// The Formatter interface is used to implement a custom Formatter. It takes an
// `Entry`. It exposes all the fields, including the default ones:
@@ -18,7 +27,7 @@ type Formatter interface {
Format(*Entry) ([]byte, error)
}
-// This is to not silently overwrite `time`, `msg` and `level` fields when
+// This is to not silently overwrite `time`, `msg`, `func` and `level` fields when
// dumping it. If this code wasn't there doing:
//
// logrus.WithField("level", 1).Info("hello")
@@ -30,16 +39,40 @@ type Formatter interface {
//
// It's not exported because it's still using Data in an opinionated way. It's to
// avoid code duplication between the two default formatters.
-func prefixFieldClashes(data Fields) {
- if t, ok := data["time"]; ok {
- data["fields.time"] = t
+func prefixFieldClashes(data Fields, fieldMap FieldMap, reportCaller bool) {
+ timeKey := fieldMap.resolve(FieldKeyTime)
+ if t, ok := data[timeKey]; ok {
+ data["fields."+timeKey] = t
+ delete(data, timeKey)
}
- if m, ok := data["msg"]; ok {
- data["fields.msg"] = m
+ msgKey := fieldMap.resolve(FieldKeyMsg)
+ if m, ok := data[msgKey]; ok {
+ data["fields."+msgKey] = m
+ delete(data, msgKey)
}
- if l, ok := data["level"]; ok {
- data["fields.level"] = l
+ levelKey := fieldMap.resolve(FieldKeyLevel)
+ if l, ok := data[levelKey]; ok {
+ data["fields."+levelKey] = l
+ delete(data, levelKey)
+ }
+
+ logrusErrKey := fieldMap.resolve(FieldKeyLogrusError)
+ if l, ok := data[logrusErrKey]; ok {
+ data["fields."+logrusErrKey] = l
+ delete(data, logrusErrKey)
+ }
+
+ // If reportCaller is not set, 'func' will not conflict.
+ if reportCaller {
+ funcKey := fieldMap.resolve(FieldKeyFunc)
+ if l, ok := data[funcKey]; ok {
+ data["fields."+funcKey] = l
+ }
+ fileKey := fieldMap.resolve(FieldKeyFile)
+ if l, ok := data[fileKey]; ok {
+ data["fields."+fileKey] = l
+ }
}
}
diff --git a/vendor/github.com/sirupsen/logrus/go.mod b/vendor/github.com/sirupsen/logrus/go.mod
new file mode 100644
index 00000000..12fdf989
--- /dev/null
+++ b/vendor/github.com/sirupsen/logrus/go.mod
@@ -0,0 +1,10 @@
+module github.com/sirupsen/logrus
+
+require (
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/konsorten/go-windows-terminal-sequences v1.0.1
+ github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/stretchr/objx v0.1.1 // indirect
+ github.com/stretchr/testify v1.2.2
+ golang.org/x/sys v0.0.0-20190422165155-953cdadca894
+)
diff --git a/vendor/github.com/sirupsen/logrus/go.sum b/vendor/github.com/sirupsen/logrus/go.sum
new file mode 100644
index 00000000..596c318b
--- /dev/null
+++ b/vendor/github.com/sirupsen/logrus/go.sum
@@ -0,0 +1,16 @@
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe h1:CHRGQ8V7OlCYtwaKPJi3iA7J+YdNKdo8j7nG5IgDhjs=
+github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
diff --git a/vendor/github.com/sirupsen/logrus/json_formatter.go b/vendor/github.com/sirupsen/logrus/json_formatter.go
index fb01c1b1..098a21a0 100644
--- a/vendor/github.com/sirupsen/logrus/json_formatter.go
+++ b/vendor/github.com/sirupsen/logrus/json_formatter.go
@@ -1,8 +1,10 @@
package logrus
import (
+ "bytes"
"encoding/json"
"fmt"
+ "runtime"
)
type fieldKey string
@@ -10,13 +12,6 @@ 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
@@ -33,21 +28,34 @@ type JSONFormatter struct {
// DisableTimestamp allows disabling automatic timestamps in output
DisableTimestamp bool
+ // DataKey allows users to put all the log entry parameters into a nested dictionary at a given key.
+ DataKey string
+
// FieldMap allows users to customize the names of keys for default fields.
// As an example:
// formatter := &JSONFormatter{
// FieldMap: FieldMap{
- // FieldKeyTime: "@timestamp",
+ // FieldKeyTime: "@timestamp",
// FieldKeyLevel: "@level",
- // FieldKeyMsg: "@message",
+ // FieldKeyMsg: "@message",
+ // FieldKeyFunc: "@caller",
// },
// }
FieldMap FieldMap
+
+ // CallerPrettyfier can be set by the user to modify the content
+ // of the function and file keys in the json data when ReportCaller is
+ // activated. If any of the returned value is the empty string the
+ // corresponding key will be removed from json fields.
+ CallerPrettyfier func(*runtime.Frame) (function string, file string)
+
+ // PrettyPrint will indent all json logs
+ PrettyPrint bool
}
// Format renders a single log entry
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
- data := make(Fields, len(entry.Data)+3)
+ data := make(Fields, len(entry.Data)+4)
for k, v := range entry.Data {
switch v := v.(type) {
case error:
@@ -58,22 +66,56 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
data[k] = v
}
}
- prefixFieldClashes(data)
+
+ if f.DataKey != "" {
+ newData := make(Fields, 4)
+ newData[f.DataKey] = data
+ data = newData
+ }
+
+ prefixFieldClashes(data, f.FieldMap, entry.HasCaller())
timestampFormat := f.TimestampFormat
if timestampFormat == "" {
timestampFormat = defaultTimestampFormat
}
+ if entry.err != "" {
+ data[f.FieldMap.resolve(FieldKeyLogrusError)] = entry.err
+ }
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()
+ if entry.HasCaller() {
+ funcVal := entry.Caller.Function
+ fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
+ if f.CallerPrettyfier != nil {
+ funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
+ }
+ if funcVal != "" {
+ data[f.FieldMap.resolve(FieldKeyFunc)] = funcVal
+ }
+ if fileVal != "" {
+ data[f.FieldMap.resolve(FieldKeyFile)] = fileVal
+ }
+ }
- serialized, err := json.Marshal(data)
- if err != nil {
- return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
+ var b *bytes.Buffer
+ if entry.Buffer != nil {
+ b = entry.Buffer
+ } else {
+ b = &bytes.Buffer{}
}
- return append(serialized, '\n'), nil
+
+ encoder := json.NewEncoder(b)
+ if f.PrettyPrint {
+ encoder.SetIndent("", " ")
+ }
+ if err := encoder.Encode(data); err != nil {
+ return nil, fmt.Errorf("failed to marshal fields to JSON, %v", err)
+ }
+
+ return b.Bytes(), nil
}
diff --git a/vendor/github.com/sirupsen/logrus/logger.go b/vendor/github.com/sirupsen/logrus/logger.go
index fdaf8a65..c0c0b1e5 100644
--- a/vendor/github.com/sirupsen/logrus/logger.go
+++ b/vendor/github.com/sirupsen/logrus/logger.go
@@ -1,16 +1,18 @@
package logrus
import (
+ "context"
"io"
"os"
"sync"
"sync/atomic"
+ "time"
)
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.Stderr`. You can also set this to
- // something more adventorous, such as logging to Kafka.
+ // something more adventurous, 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
@@ -23,6 +25,10 @@ type Logger struct {
// own that implements the `Formatter` interface, see the `README` or included
// formatters for examples.
Formatter Formatter
+
+ // Flag for whether to log caller info (off by default)
+ ReportCaller bool
+
// 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.
@@ -31,8 +37,12 @@ type Logger struct {
mu MutexWrap
// Reusable empty entry
entryPool sync.Pool
+ // Function to exit the application, defaults to `os.Exit()`
+ ExitFunc exitFunc
}
+type exitFunc func(int)
+
type MutexWrap struct {
lock sync.Mutex
disabled bool
@@ -68,10 +78,12 @@ func (mw *MutexWrap) Disable() {
// It's recommended to make this a global instance called `log`.
func New() *Logger {
return &Logger{
- Out: os.Stderr,
- Formatter: new(TextFormatter),
- Hooks: make(LevelHooks),
- Level: InfoLevel,
+ Out: os.Stderr,
+ Formatter: new(TextFormatter),
+ Hooks: make(LevelHooks),
+ Level: InfoLevel,
+ ExitFunc: os.Exit,
+ ReportCaller: false,
}
}
@@ -84,11 +96,12 @@ func (logger *Logger) newEntry() *Entry {
}
func (logger *Logger) releaseEntry(entry *Entry) {
+ entry.Data = map[string]interface{}{}
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.
+// Debug, Print, Info, Warn, Error, 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 {
entry := logger.newEntry()
@@ -112,20 +125,38 @@ func (logger *Logger) WithError(err error) *Entry {
return entry.WithError(err)
}
-func (logger *Logger) Debugf(format string, args ...interface{}) {
- if logger.level() >= DebugLevel {
+// Add a context to the log entry.
+func (logger *Logger) WithContext(ctx context.Context) *Entry {
+ entry := logger.newEntry()
+ defer logger.releaseEntry(entry)
+ return entry.WithContext(ctx)
+}
+
+// Overrides the time of the log entry.
+func (logger *Logger) WithTime(t time.Time) *Entry {
+ entry := logger.newEntry()
+ defer logger.releaseEntry(entry)
+ return entry.WithTime(t)
+}
+
+func (logger *Logger) Logf(level Level, format string, args ...interface{}) {
+ if logger.IsLevelEnabled(level) {
entry := logger.newEntry()
- entry.Debugf(format, args...)
+ entry.Logf(level, format, args...)
logger.releaseEntry(entry)
}
}
+func (logger *Logger) Tracef(format string, args ...interface{}) {
+ logger.Logf(TraceLevel, format, args...)
+}
+
+func (logger *Logger) Debugf(format string, args ...interface{}) {
+ logger.Logf(DebugLevel, format, args...)
+}
+
func (logger *Logger) Infof(format string, args ...interface{}) {
- if logger.level() >= InfoLevel {
- entry := logger.newEntry()
- entry.Infof(format, args...)
- logger.releaseEntry(entry)
- }
+ logger.Logf(InfoLevel, format, args...)
}
func (logger *Logger) Printf(format string, args ...interface{}) {
@@ -135,123 +166,91 @@ func (logger *Logger) Printf(format string, args ...interface{}) {
}
func (logger *Logger) Warnf(format string, args ...interface{}) {
- if logger.level() >= WarnLevel {
- entry := logger.newEntry()
- entry.Warnf(format, args...)
- logger.releaseEntry(entry)
- }
+ logger.Logf(WarnLevel, format, args...)
}
func (logger *Logger) Warningf(format string, args ...interface{}) {
- if logger.level() >= WarnLevel {
- entry := logger.newEntry()
- entry.Warnf(format, args...)
- logger.releaseEntry(entry)
- }
+ logger.Warnf(format, args...)
}
func (logger *Logger) Errorf(format string, args ...interface{}) {
- if logger.level() >= ErrorLevel {
- entry := logger.newEntry()
- entry.Errorf(format, args...)
- logger.releaseEntry(entry)
- }
+ logger.Logf(ErrorLevel, format, args...)
}
func (logger *Logger) Fatalf(format string, args ...interface{}) {
- if logger.level() >= FatalLevel {
- entry := logger.newEntry()
- entry.Fatalf(format, args...)
- logger.releaseEntry(entry)
- }
- Exit(1)
+ logger.Logf(FatalLevel, format, args...)
+ logger.Exit(1)
}
func (logger *Logger) Panicf(format string, args ...interface{}) {
- if logger.level() >= PanicLevel {
+ logger.Logf(PanicLevel, format, args...)
+}
+
+func (logger *Logger) Log(level Level, args ...interface{}) {
+ if logger.IsLevelEnabled(level) {
entry := logger.newEntry()
- entry.Panicf(format, args...)
+ entry.Log(level, args...)
logger.releaseEntry(entry)
}
}
+func (logger *Logger) Trace(args ...interface{}) {
+ logger.Log(TraceLevel, args...)
+}
+
func (logger *Logger) Debug(args ...interface{}) {
- if logger.level() >= DebugLevel {
- entry := logger.newEntry()
- entry.Debug(args...)
- logger.releaseEntry(entry)
- }
+ logger.Log(DebugLevel, args...)
}
func (logger *Logger) Info(args ...interface{}) {
- if logger.level() >= InfoLevel {
- entry := logger.newEntry()
- entry.Info(args...)
- logger.releaseEntry(entry)
- }
+ logger.Log(InfoLevel, args...)
}
func (logger *Logger) Print(args ...interface{}) {
entry := logger.newEntry()
- entry.Info(args...)
+ entry.Print(args...)
logger.releaseEntry(entry)
}
func (logger *Logger) Warn(args ...interface{}) {
- if logger.level() >= WarnLevel {
- entry := logger.newEntry()
- entry.Warn(args...)
- logger.releaseEntry(entry)
- }
+ logger.Log(WarnLevel, args...)
}
func (logger *Logger) Warning(args ...interface{}) {
- if logger.level() >= WarnLevel {
- entry := logger.newEntry()
- entry.Warn(args...)
- logger.releaseEntry(entry)
- }
+ logger.Warn(args...)
}
func (logger *Logger) Error(args ...interface{}) {
- if logger.level() >= ErrorLevel {
- entry := logger.newEntry()
- entry.Error(args...)
- logger.releaseEntry(entry)
- }
+ logger.Log(ErrorLevel, args...)
}
func (logger *Logger) Fatal(args ...interface{}) {
- if logger.level() >= FatalLevel {
- entry := logger.newEntry()
- entry.Fatal(args...)
- logger.releaseEntry(entry)
- }
- Exit(1)
+ logger.Log(FatalLevel, args...)
+ logger.Exit(1)
}
func (logger *Logger) Panic(args ...interface{}) {
- if logger.level() >= PanicLevel {
+ logger.Log(PanicLevel, args...)
+}
+
+func (logger *Logger) Logln(level Level, args ...interface{}) {
+ if logger.IsLevelEnabled(level) {
entry := logger.newEntry()
- entry.Panic(args...)
+ entry.Logln(level, args...)
logger.releaseEntry(entry)
}
}
+func (logger *Logger) Traceln(args ...interface{}) {
+ logger.Logln(TraceLevel, args...)
+}
+
func (logger *Logger) Debugln(args ...interface{}) {
- if logger.level() >= DebugLevel {
- entry := logger.newEntry()
- entry.Debugln(args...)
- logger.releaseEntry(entry)
- }
+ logger.Logln(DebugLevel, args...)
}
func (logger *Logger) Infoln(args ...interface{}) {
- if logger.level() >= InfoLevel {
- entry := logger.newEntry()
- entry.Infoln(args...)
- logger.releaseEntry(entry)
- }
+ logger.Logln(InfoLevel, args...)
}
func (logger *Logger) Println(args ...interface{}) {
@@ -261,44 +260,32 @@ func (logger *Logger) Println(args ...interface{}) {
}
func (logger *Logger) Warnln(args ...interface{}) {
- if logger.level() >= WarnLevel {
- entry := logger.newEntry()
- entry.Warnln(args...)
- logger.releaseEntry(entry)
- }
+ logger.Logln(WarnLevel, args...)
}
func (logger *Logger) Warningln(args ...interface{}) {
- if logger.level() >= WarnLevel {
- entry := logger.newEntry()
- entry.Warnln(args...)
- logger.releaseEntry(entry)
- }
+ logger.Warnln(args...)
}
func (logger *Logger) Errorln(args ...interface{}) {
- if logger.level() >= ErrorLevel {
- entry := logger.newEntry()
- entry.Errorln(args...)
- logger.releaseEntry(entry)
- }
+ logger.Logln(ErrorLevel, args...)
}
func (logger *Logger) Fatalln(args ...interface{}) {
- if logger.level() >= FatalLevel {
- entry := logger.newEntry()
- entry.Fatalln(args...)
- logger.releaseEntry(entry)
- }
- Exit(1)
+ logger.Logln(FatalLevel, args...)
+ logger.Exit(1)
}
func (logger *Logger) Panicln(args ...interface{}) {
- if logger.level() >= PanicLevel {
- entry := logger.newEntry()
- entry.Panicln(args...)
- logger.releaseEntry(entry)
+ logger.Logln(PanicLevel, args...)
+}
+
+func (logger *Logger) Exit(code int) {
+ runHandlers()
+ if logger.ExitFunc == nil {
+ logger.ExitFunc = os.Exit
}
+ logger.ExitFunc(code)
}
//When file is opened with appending mode, it's safe to
@@ -312,12 +299,53 @@ func (logger *Logger) level() Level {
return Level(atomic.LoadUint32((*uint32)(&logger.Level)))
}
+// SetLevel sets the logger level.
func (logger *Logger) SetLevel(level Level) {
atomic.StoreUint32((*uint32)(&logger.Level), uint32(level))
}
+// GetLevel returns the logger level.
+func (logger *Logger) GetLevel() Level {
+ return logger.level()
+}
+
+// AddHook adds a hook to the logger hooks.
func (logger *Logger) AddHook(hook Hook) {
logger.mu.Lock()
defer logger.mu.Unlock()
logger.Hooks.Add(hook)
}
+
+// IsLevelEnabled checks if the log level of the logger is greater than the level param
+func (logger *Logger) IsLevelEnabled(level Level) bool {
+ return logger.level() >= level
+}
+
+// SetFormatter sets the logger formatter.
+func (logger *Logger) SetFormatter(formatter Formatter) {
+ logger.mu.Lock()
+ defer logger.mu.Unlock()
+ logger.Formatter = formatter
+}
+
+// SetOutput sets the logger output.
+func (logger *Logger) SetOutput(output io.Writer) {
+ logger.mu.Lock()
+ defer logger.mu.Unlock()
+ logger.Out = output
+}
+
+func (logger *Logger) SetReportCaller(reportCaller bool) {
+ logger.mu.Lock()
+ defer logger.mu.Unlock()
+ logger.ReportCaller = reportCaller
+}
+
+// ReplaceHooks replaces the logger hooks and returns the old ones
+func (logger *Logger) ReplaceHooks(hooks LevelHooks) LevelHooks {
+ logger.mu.Lock()
+ oldHooks := logger.Hooks
+ logger.Hooks = hooks
+ logger.mu.Unlock()
+ return oldHooks
+}
diff --git a/vendor/github.com/sirupsen/logrus/logrus.go b/vendor/github.com/sirupsen/logrus/logrus.go
index dd389997..8644761f 100644
--- a/vendor/github.com/sirupsen/logrus/logrus.go
+++ b/vendor/github.com/sirupsen/logrus/logrus.go
@@ -14,22 +14,11 @@ type Level uint32
// Convert the Level to a string. E.g. PanicLevel becomes "panic".
func (level Level) String() string {
- switch level {
- case DebugLevel:
- return "debug"
- case InfoLevel:
- return "info"
- case WarnLevel:
- return "warning"
- case ErrorLevel:
- return "error"
- case FatalLevel:
- return "fatal"
- case PanicLevel:
- return "panic"
+ if b, err := level.MarshalText(); err == nil {
+ return string(b)
+ } else {
+ return "unknown"
}
-
- return "unknown"
}
// ParseLevel takes a string level and returns the Logrus log level constant.
@@ -47,12 +36,47 @@ func ParseLevel(lvl string) (Level, error) {
return InfoLevel, nil
case "debug":
return DebugLevel, nil
+ case "trace":
+ return TraceLevel, nil
}
var l Level
return l, fmt.Errorf("not a valid logrus Level: %q", lvl)
}
+// UnmarshalText implements encoding.TextUnmarshaler.
+func (level *Level) UnmarshalText(text []byte) error {
+ l, err := ParseLevel(string(text))
+ if err != nil {
+ return err
+ }
+
+ *level = Level(l)
+
+ return nil
+}
+
+func (level Level) MarshalText() ([]byte, error) {
+ switch level {
+ case TraceLevel:
+ return []byte("trace"), nil
+ case DebugLevel:
+ return []byte("debug"), nil
+ case InfoLevel:
+ return []byte("info"), nil
+ case WarnLevel:
+ return []byte("warning"), nil
+ case ErrorLevel:
+ return []byte("error"), nil
+ case FatalLevel:
+ return []byte("fatal"), nil
+ case PanicLevel:
+ return []byte("panic"), nil
+ }
+
+ return nil, fmt.Errorf("not a valid logrus level %d", level)
+}
+
// A constant exposing all logging levels
var AllLevels = []Level{
PanicLevel,
@@ -61,6 +85,7 @@ var AllLevels = []Level{
WarnLevel,
InfoLevel,
DebugLevel,
+ TraceLevel,
}
// These are the different logging levels. You can set the logging level to log
@@ -69,7 +94,7 @@ const (
// PanicLevel level, highest level of severity. Logs and then calls panic with the
// message passed to Debug, Info, ...
PanicLevel Level = iota
- // FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the
+ // FatalLevel level. Logs and then calls `logger.Exit(1)`. It will exit even if the
// logging level is set to Panic.
FatalLevel
// ErrorLevel level. Logs. Used for errors that should definitely be noted.
@@ -82,6 +107,8 @@ const (
InfoLevel
// DebugLevel level. Usually only enabled when debugging. Very verbose logging.
DebugLevel
+ // TraceLevel level. Designates finer-grained informational events than the Debug.
+ TraceLevel
)
// Won't compile if StdLogger can't be realized by a log.Logger
@@ -140,4 +167,20 @@ type FieldLogger interface {
Errorln(args ...interface{})
Fatalln(args ...interface{})
Panicln(args ...interface{})
+
+ // IsDebugEnabled() bool
+ // IsInfoEnabled() bool
+ // IsWarnEnabled() bool
+ // IsErrorEnabled() bool
+ // IsFatalEnabled() bool
+ // IsPanicEnabled() bool
+}
+
+// Ext1FieldLogger (the first extension to FieldLogger) is superfluous, it is
+// here for consistancy. Do not use. Use Logger or Entry instead.
+type Ext1FieldLogger interface {
+ FieldLogger
+ Tracef(format string, args ...interface{})
+ Trace(args ...interface{})
+ Traceln(args ...interface{})
}
diff --git a/vendor/github.com/sirupsen/logrus/terminal_bsd.go b/vendor/github.com/sirupsen/logrus/terminal_bsd.go
deleted file mode 100644
index d7b3893f..00000000
--- a/vendor/github.com/sirupsen/logrus/terminal_bsd.go
+++ /dev/null
@@ -1,10 +0,0 @@
-// +build darwin freebsd openbsd netbsd dragonfly
-// +build !appengine
-
-package logrus
-
-import "golang.org/x/sys/unix"
-
-const ioctlReadTermios = unix.TIOCGETA
-
-type Termios unix.Termios
diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go b/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go
new file mode 100644
index 00000000..3c4f43f9
--- /dev/null
+++ b/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go
@@ -0,0 +1,13 @@
+// +build darwin dragonfly freebsd netbsd openbsd
+
+package logrus
+
+import "golang.org/x/sys/unix"
+
+const ioctlReadTermios = unix.TIOCGETA
+
+func isTerminal(fd int) bool {
+ _, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
+ return err == nil
+}
+
diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go b/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go
new file mode 100644
index 00000000..97af92c6
--- /dev/null
+++ b/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go
@@ -0,0 +1,11 @@
+// +build js nacl plan9
+
+package logrus
+
+import (
+ "io"
+)
+
+func checkIfTerminal(w io.Writer) bool {
+ return false
+}
diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go b/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go
index 116bcb4e..3293fb3c 100644
--- a/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go
+++ b/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go
@@ -1,18 +1,16 @@
-// +build !appengine
+// +build !appengine,!js,!windows,!nacl,!plan9
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()))
+ return isTerminal(int(v.Fd()))
default:
return false
}
diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go b/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go
new file mode 100644
index 00000000..f6710b3b
--- /dev/null
+++ b/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go
@@ -0,0 +1,11 @@
+package logrus
+
+import (
+ "golang.org/x/sys/unix"
+)
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+func isTerminal(fd int) bool {
+ _, err := unix.IoctlGetTermio(fd, unix.TCGETA)
+ return err == nil
+}
diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_unix.go b/vendor/github.com/sirupsen/logrus/terminal_check_unix.go
new file mode 100644
index 00000000..355dc966
--- /dev/null
+++ b/vendor/github.com/sirupsen/logrus/terminal_check_unix.go
@@ -0,0 +1,13 @@
+// +build linux aix
+
+package logrus
+
+import "golang.org/x/sys/unix"
+
+const ioctlReadTermios = unix.TCGETS
+
+func isTerminal(fd int) bool {
+ _, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
+ return err == nil
+}
+
diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_windows.go b/vendor/github.com/sirupsen/logrus/terminal_check_windows.go
new file mode 100644
index 00000000..572889db
--- /dev/null
+++ b/vendor/github.com/sirupsen/logrus/terminal_check_windows.go
@@ -0,0 +1,34 @@
+// +build !appengine,!js,windows
+
+package logrus
+
+import (
+ "io"
+ "os"
+ "syscall"
+
+ sequences "github.com/konsorten/go-windows-terminal-sequences"
+)
+
+func initTerminal(w io.Writer) {
+ switch v := w.(type) {
+ case *os.File:
+ sequences.EnableVirtualTerminalProcessing(syscall.Handle(v.Fd()), true)
+ }
+}
+
+func checkIfTerminal(w io.Writer) bool {
+ var ret bool
+ switch v := w.(type) {
+ case *os.File:
+ var mode uint32
+ err := syscall.GetConsoleMode(syscall.Handle(v.Fd()), &mode)
+ ret = (err == nil)
+ default:
+ ret = false
+ }
+ if ret {
+ initTerminal(w)
+ }
+ return ret
+}
diff --git a/vendor/github.com/sirupsen/logrus/terminal_linux.go b/vendor/github.com/sirupsen/logrus/terminal_linux.go
deleted file mode 100644
index 88d7298e..00000000
--- a/vendor/github.com/sirupsen/logrus/terminal_linux.go
+++ /dev/null
@@ -1,14 +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.
-
-// +build !appengine
-
-package logrus
-
-import "golang.org/x/sys/unix"
-
-const ioctlReadTermios = unix.TCGETS
-
-type Termios unix.Termios
diff --git a/vendor/github.com/sirupsen/logrus/text_formatter.go b/vendor/github.com/sirupsen/logrus/text_formatter.go
index 61b21cae..f08563e4 100644
--- a/vendor/github.com/sirupsen/logrus/text_formatter.go
+++ b/vendor/github.com/sirupsen/logrus/text_formatter.go
@@ -3,24 +3,24 @@ package logrus
import (
"bytes"
"fmt"
+ "os"
+ "runtime"
"sort"
+ "strconv"
"strings"
"sync"
"time"
+ "unicode/utf8"
)
const (
- nocolor = 0
- red = 31
- green = 32
- yellow = 33
- blue = 36
- gray = 37
+ red = 31
+ yellow = 33
+ blue = 36
+ gray = 37
)
-var (
- baseTimestamp time.Time
-)
+var baseTimestamp time.Time
func init() {
baseTimestamp = time.Now()
@@ -34,6 +34,9 @@ type TextFormatter struct {
// Force disabling colors.
DisableColors bool
+ // Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/
+ EnvironmentOverrideColors bool
+
// Disable timestamp logging. useful when output is redirected to logging
// system that already adds timestamps.
DisableTimestamp bool
@@ -50,60 +53,165 @@ type TextFormatter struct {
// be desired.
DisableSorting bool
+ // The keys sorting function, when uninitialized it uses sort.Strings.
+ SortingFunc func([]string)
+
+ // Disables the truncation of the level text to 4 characters.
+ DisableLevelTruncation bool
+
+ // PadLevelText Adds padding the level text so that all the levels output at the same length
+ // PadLevelText is a superset of the DisableLevelTruncation option
+ PadLevelText 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
+ // FieldMap allows users to customize the names of keys for default fields.
+ // As an example:
+ // formatter := &TextFormatter{
+ // FieldMap: FieldMap{
+ // FieldKeyTime: "@timestamp",
+ // FieldKeyLevel: "@level",
+ // FieldKeyMsg: "@message"}}
+ FieldMap FieldMap
+
+ // CallerPrettyfier can be set by the user to modify the content
+ // of the function and file keys in the data when ReportCaller is
+ // activated. If any of the returned value is the empty string the
+ // corresponding key will be removed from fields.
+ CallerPrettyfier func(*runtime.Frame) (function string, file string)
+
+ terminalInitOnce sync.Once
+
+ // The max length of the level text, generated dynamically on init
+ levelTextMaxLength int
}
func (f *TextFormatter) init(entry *Entry) {
if entry.Logger != nil {
f.isTerminal = checkIfTerminal(entry.Logger.Out)
}
+ // Get the max length of the level text
+ for _, level := range AllLevels {
+ levelTextLength := utf8.RuneCount([]byte(level.String()))
+ if levelTextLength > f.levelTextMaxLength {
+ f.levelTextMaxLength = levelTextLength
+ }
+ }
+}
+
+func (f *TextFormatter) isColored() bool {
+ isColored := f.ForceColors || (f.isTerminal && (runtime.GOOS != "windows"))
+
+ if f.EnvironmentOverrideColors {
+ if force, ok := os.LookupEnv("CLICOLOR_FORCE"); ok && force != "0" {
+ isColored = true
+ } else if ok && force == "0" {
+ isColored = false
+ } else if os.Getenv("CLICOLOR") == "0" {
+ isColored = false
+ }
+ }
+
+ return isColored && !f.DisableColors
}
// Format renders a single log entry
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
- var b *bytes.Buffer
- keys := make([]string, 0, len(entry.Data))
- for k := range entry.Data {
+ data := make(Fields)
+ for k, v := range entry.Data {
+ data[k] = v
+ }
+ prefixFieldClashes(data, f.FieldMap, entry.HasCaller())
+ keys := make([]string, 0, len(data))
+ for k := range data {
keys = append(keys, k)
}
+ var funcVal, fileVal string
+
+ fixedKeys := make([]string, 0, 4+len(data))
+ if !f.DisableTimestamp {
+ fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime))
+ }
+ fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLevel))
+ if entry.Message != "" {
+ fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyMsg))
+ }
+ if entry.err != "" {
+ fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLogrusError))
+ }
+ if entry.HasCaller() {
+ if f.CallerPrettyfier != nil {
+ funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
+ } else {
+ funcVal = entry.Caller.Function
+ fileVal = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
+ }
+
+ if funcVal != "" {
+ fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFunc))
+ }
+ if fileVal != "" {
+ fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFile))
+ }
+ }
+
if !f.DisableSorting {
- sort.Strings(keys)
+ if f.SortingFunc == nil {
+ sort.Strings(keys)
+ fixedKeys = append(fixedKeys, keys...)
+ } else {
+ if !f.isColored() {
+ fixedKeys = append(fixedKeys, keys...)
+ f.SortingFunc(fixedKeys)
+ } else {
+ f.SortingFunc(keys)
+ }
+ }
+ } else {
+ fixedKeys = append(fixedKeys, keys...)
}
+
+ var b *bytes.Buffer
if entry.Buffer != nil {
b = entry.Buffer
} else {
b = &bytes.Buffer{}
}
- prefixFieldClashes(entry.Data)
-
- f.Do(func() { f.init(entry) })
-
- isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors
+ f.terminalInitOnce.Do(func() { f.init(entry) })
timestampFormat := f.TimestampFormat
if timestampFormat == "" {
timestampFormat = defaultTimestampFormat
}
- if isColored {
- f.printColored(b, entry, keys, timestampFormat)
+ if f.isColored() {
+ f.printColored(b, entry, keys, data, timestampFormat)
} else {
- if !f.DisableTimestamp {
- f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat))
- }
- f.appendKeyValue(b, "level", entry.Level.String())
- if entry.Message != "" {
- f.appendKeyValue(b, "msg", entry.Message)
- }
- for _, key := range keys {
- f.appendKeyValue(b, key, entry.Data[key])
+
+ for _, key := range fixedKeys {
+ var value interface{}
+ switch {
+ case key == f.FieldMap.resolve(FieldKeyTime):
+ value = entry.Time.Format(timestampFormat)
+ case key == f.FieldMap.resolve(FieldKeyLevel):
+ value = entry.Level.String()
+ case key == f.FieldMap.resolve(FieldKeyMsg):
+ value = entry.Message
+ case key == f.FieldMap.resolve(FieldKeyLogrusError):
+ value = entry.err
+ case key == f.FieldMap.resolve(FieldKeyFunc) && entry.HasCaller():
+ value = funcVal
+ case key == f.FieldMap.resolve(FieldKeyFile) && entry.HasCaller():
+ value = fileVal
+ default:
+ value = data[key]
+ }
+ f.appendKeyValue(b, key, value)
}
}
@@ -111,10 +219,10 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
return b.Bytes(), nil
}
-func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) {
+func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, data Fields, timestampFormat string) {
var levelColor int
switch entry.Level {
- case DebugLevel:
+ case DebugLevel, TraceLevel:
levelColor = gray
case WarnLevel:
levelColor = yellow
@@ -124,17 +232,51 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
levelColor = blue
}
- levelText := strings.ToUpper(entry.Level.String())[0:4]
+ levelText := strings.ToUpper(entry.Level.String())
+ if !f.DisableLevelTruncation && !f.PadLevelText {
+ levelText = levelText[0:4]
+ }
+ if f.PadLevelText {
+ // Generates the format string used in the next line, for example "%-6s" or "%-7s".
+ // Based on the max level text length.
+ formatString := "%-" + strconv.Itoa(f.levelTextMaxLength) + "s"
+ // Formats the level text by appending spaces up to the max length, for example:
+ // - "INFO "
+ // - "WARNING"
+ levelText = fmt.Sprintf(formatString, levelText)
+ }
+
+ // Remove a single newline if it already exists in the message to keep
+ // the behavior of logrus text_formatter the same as the stdlib log package
+ entry.Message = strings.TrimSuffix(entry.Message, "\n")
+
+ caller := ""
+ if entry.HasCaller() {
+ funcVal := fmt.Sprintf("%s()", entry.Caller.Function)
+ fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
+
+ if f.CallerPrettyfier != nil {
+ funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
+ }
+
+ if fileVal == "" {
+ caller = funcVal
+ } else if funcVal == "" {
+ caller = fileVal
+ } else {
+ caller = fileVal + " " + funcVal
+ }
+ }
if f.DisableTimestamp {
- fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m %-44s ", levelColor, levelText, entry.Message)
+ fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m%s %-44s ", levelColor, levelText, caller, 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)
+ fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d]%s %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), caller, entry.Message)
} else {
- fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message)
+ fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message)
}
for _, k := range keys {
- v := entry.Data[k]
+ v := data[k]
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
f.appendValue(b, v)
}
diff --git a/vendor/github.com/sirupsen/logrus/writer.go b/vendor/github.com/sirupsen/logrus/writer.go
index 7bdebedc..9e1f7513 100644
--- a/vendor/github.com/sirupsen/logrus/writer.go
+++ b/vendor/github.com/sirupsen/logrus/writer.go
@@ -24,6 +24,8 @@ func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
var printFunc func(args ...interface{})
switch level {
+ case TraceLevel:
+ printFunc = entry.Trace
case DebugLevel:
printFunc = entry.Debug
case InfoLevel:
diff --git a/vendor/gitlab.com/gitlab-org/labkit/log/access_logger.go b/vendor/gitlab.com/gitlab-org/labkit/log/access_logger.go
new file mode 100644
index 00000000..e4e3d41f
--- /dev/null
+++ b/vendor/gitlab.com/gitlab-org/labkit/log/access_logger.go
@@ -0,0 +1,171 @@
+package log
+
+import (
+ "bufio"
+ "net"
+ "net/http"
+ "time"
+
+ "github.com/sebest/xff"
+ "github.com/sirupsen/logrus"
+ "gitlab.com/gitlab-org/labkit/correlation"
+ "gitlab.com/gitlab-org/labkit/mask"
+)
+
+// AccessLogger will generate access logs for the service.
+func AccessLogger(h http.Handler, opts ...AccessLoggerOption) http.Handler {
+ config := applyAccessLoggerOptions(opts)
+
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ lrw := newLoggingResponseWriter(w, &config)
+ defer lrw.requestFinished(r)
+
+ h.ServeHTTP(lrw, r)
+ })
+}
+
+func newLoggingResponseWriter(rw http.ResponseWriter, config *accessLoggerConfig) notifiableResponseWriter {
+ out := loggingResponseWriter{
+ rw: rw,
+ started: time.Now(),
+ config: config,
+ }
+
+ // If the underlying response writer supports hijacking,
+ // we need to ensure that we do too
+ if _, ok := rw.(http.Hijacker); ok {
+ return &hijackingResponseWriter{out}
+ }
+
+ return &out
+}
+
+// notifiableResponseWriter is a response writer that can be notified when the request is complete,
+// via the requestFinished method
+type notifiableResponseWriter interface {
+ http.ResponseWriter
+
+ // requestFinished is called by the middleware when the request has completed
+ requestFinished(r *http.Request)
+}
+
+type loggingResponseWriter struct {
+ rw http.ResponseWriter
+ status int
+ wroteHeader bool
+ written int64
+ started time.Time
+ config *accessLoggerConfig
+}
+
+func (l *loggingResponseWriter) Header() http.Header {
+ return l.rw.Header()
+}
+
+func (l *loggingResponseWriter) Write(data []byte) (int, error) {
+ if !l.wroteHeader {
+ l.WriteHeader(http.StatusOK)
+ }
+ n, err := l.rw.Write(data)
+
+ l.written += int64(n)
+ return n, err
+}
+
+func (l *loggingResponseWriter) WriteHeader(status int) {
+ if l.wroteHeader {
+ return
+ }
+ l.wroteHeader = true
+ l.status = status
+
+ l.rw.WriteHeader(status)
+}
+
+func (l *loggingResponseWriter) accessLogFields(r *http.Request) logrus.Fields {
+ duration := time.Since(l.started)
+
+ fields := l.config.extraFields(r)
+ fieldsBitMask := l.config.fields
+
+ // Optionally add built in fields
+ if fieldsBitMask&CorrelationID != 0 {
+ fields[correlation.FieldName] = correlation.ExtractFromContext(r.Context())
+ }
+
+ if fieldsBitMask&HTTPHost != 0 {
+ fields[httpHostField] = r.Host
+ }
+
+ if fieldsBitMask&HTTPRemoteIP != 0 {
+ fields[httpRemoteIPField] = getRemoteIP(r)
+ }
+
+ if fieldsBitMask&HTTPRemoteAddr != 0 {
+ fields[httpRemoteAddrField] = r.RemoteAddr
+ }
+
+ if fieldsBitMask&HTTPRequestMethod != 0 {
+ fields[httpRequestMethodField] = r.Method
+ }
+
+ if fieldsBitMask&HTTPURI != 0 {
+ fields[httpURIField] = mask.URL(r.RequestURI)
+ }
+
+ if fieldsBitMask&HTTPProto != 0 {
+ fields[httpProtoField] = r.Proto
+ }
+
+ if fieldsBitMask&HTTPResponseStatusCode != 0 {
+ fields[httpResponseStatusCodeField] = l.status
+ }
+
+ if fieldsBitMask&HTTPResponseSize != 0 {
+ fields[httpResponseSizeField] = l.written
+ }
+
+ if fieldsBitMask&HTTPRequestReferrer != 0 {
+ fields[httpRequestReferrerField] = mask.URL(r.Referer())
+ }
+
+ if fieldsBitMask&HTTPUserAgent != 0 {
+ fields[httpUserAgentField] = r.UserAgent()
+ }
+
+ if fieldsBitMask&RequestDuration != 0 {
+ fields[requestDurationField] = int64(duration / time.Millisecond)
+ }
+
+ if fieldsBitMask&System != 0 {
+ fields[systemField] = "http"
+ }
+
+ return fields
+}
+
+func (l *loggingResponseWriter) requestFinished(r *http.Request) {
+ l.config.logger.WithFields(l.accessLogFields(r)).Info("access")
+}
+
+// hijackingResponseWriter is like a loggingResponseWriter that supports the http.Hijacker interface
+type hijackingResponseWriter struct {
+ loggingResponseWriter
+}
+
+func (l *hijackingResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
+ // The only way to get here is through NewStatsCollectingResponseWriter(), which
+ // checks that this cast will be valid.
+ hijacker := l.rw.(http.Hijacker)
+ return hijacker.Hijack()
+}
+
+func getRemoteIP(r *http.Request) string {
+ remoteAddr := xff.GetRemoteAddr(r)
+ host, _, err := net.SplitHostPort(remoteAddr)
+ if err != nil {
+ return r.RemoteAddr
+ }
+
+ return host
+}
diff --git a/vendor/gitlab.com/gitlab-org/labkit/log/access_logger_fields.go b/vendor/gitlab.com/gitlab-org/labkit/log/access_logger_fields.go
new file mode 100644
index 00000000..ae86b748
--- /dev/null
+++ b/vendor/gitlab.com/gitlab-org/labkit/log/access_logger_fields.go
@@ -0,0 +1,64 @@
+package log
+
+// AccessLogField is used to select which fields are recorded in the access log. See WithoutFields.
+type AccessLogField uint16
+
+const (
+ // CorrelationID field will record the Correlation-ID in the access log.
+ CorrelationID AccessLogField = 1 << iota
+
+ // HTTPHost field will record the Host Header in the access log.
+ HTTPHost
+
+ // HTTPRemoteIP field will record the remote caller in the access log, taking Real-IP and X-Forwarded-For headers into account.
+ HTTPRemoteIP
+
+ // HTTPRemoteAddr field will record the remote socket endpoint in the access log.
+ HTTPRemoteAddr
+
+ // HTTPRequestMethod field will record the HTTP method in the access log.
+ HTTPRequestMethod
+
+ // HTTPURI field will record the URI, including parameters.
+ HTTPURI
+
+ // HTTPProto field will record the protocol used to make the request in the access log.
+ HTTPProto
+
+ // HTTPResponseStatusCode field will record the response HTTP status code in the access log.
+ HTTPResponseStatusCode
+
+ // HTTPResponseSize field will record the response size, in bytes, in the access log.
+ HTTPResponseSize
+
+ // HTTPRequestReferrer field will record the referer header in the access log.
+ HTTPRequestReferrer
+
+ // HTTPUserAgent field will record the useragent header in the access log.
+ HTTPUserAgent
+
+ // RequestDuration field will record the request duration in the access log.
+ RequestDuration
+
+ // System field will record the system for the log entry.
+ System
+)
+
+const defaultEnabledFields = ^AccessLogField(0) // By default, all fields are enabled
+
+// For field definitions, consult the Elastic Common Schema field reference
+// https://www.elastic.co/guide/en/ecs/current/ecs-field-reference.html.
+const (
+ httpHostField = "host" // ESC: url.domain
+ httpRemoteIPField = "remote_ip" // ESC: client.ip
+ httpRemoteAddrField = "remote_addr" // ESC: client.address
+ httpRequestMethodField = "method" // ESC: http.request.method
+ httpURIField = "uri" // ESC url.path + `?` + url.query
+ httpProtoField = "proto" // ESC: url.scheme
+ httpResponseStatusCodeField = "status" // ESC: http.response.status_code
+ httpResponseSizeField = "written_bytes" // ESC: http.response.body.bytes
+ httpRequestReferrerField = "referrer" // ESC: http.request.referrer
+ httpUserAgentField = "user_agent" // ESC: user_agent.original
+ requestDurationField = "duration_ms" // ESC: no mapping
+ systemField = "system" // ESC: no mapping
+)
diff --git a/vendor/gitlab.com/gitlab-org/labkit/log/access_logger_options.go b/vendor/gitlab.com/gitlab-org/labkit/log/access_logger_options.go
new file mode 100644
index 00000000..762797f3
--- /dev/null
+++ b/vendor/gitlab.com/gitlab-org/labkit/log/access_logger_options.go
@@ -0,0 +1,58 @@
+package log
+
+import (
+ "net/http"
+
+ "github.com/sirupsen/logrus"
+)
+
+// ExtraFieldsGeneratorFunc allows extra fields to be included in the access log.
+type ExtraFieldsGeneratorFunc func(r *http.Request) Fields
+
+// The configuration for an access logger
+type accessLoggerConfig struct {
+ logger *logrus.Logger
+ extraFields ExtraFieldsGeneratorFunc
+ fields AccessLogField
+}
+
+func nullExtraFieldsGenerator(r *http.Request) Fields {
+ return Fields{}
+}
+
+// AccessLoggerOption will configure a access logger handler.
+type AccessLoggerOption func(*accessLoggerConfig)
+
+func applyAccessLoggerOptions(opts []AccessLoggerOption) accessLoggerConfig {
+ config := accessLoggerConfig{
+ logger: logger,
+ extraFields: nullExtraFieldsGenerator,
+ fields: defaultEnabledFields,
+ }
+ for _, v := range opts {
+ v(&config)
+ }
+
+ return config
+}
+
+// WithExtraFields allows extra fields to be passed into the access logger, based on the request.
+func WithExtraFields(f ExtraFieldsGeneratorFunc) AccessLoggerOption {
+ return func(config *accessLoggerConfig) {
+ config.extraFields = f
+ }
+}
+
+// WithFieldsExcluded allows fields to be excluded from the access log. For example, backend services may not require the referer or user agent fields.
+func WithFieldsExcluded(fields AccessLogField) AccessLoggerOption {
+ return func(config *accessLoggerConfig) {
+ config.fields = config.fields &^ fields
+ }
+}
+
+// WithAccessLogger configures the logger to be used with the access logger.
+func WithAccessLogger(logger *logrus.Logger) AccessLoggerOption {
+ return func(config *accessLoggerConfig) {
+ config.logger = logger
+ }
+}
diff --git a/vendor/gitlab.com/gitlab-org/labkit/log/clock.go b/vendor/gitlab.com/gitlab-org/labkit/log/clock.go
new file mode 100644
index 00000000..8a4b0f9f
--- /dev/null
+++ b/vendor/gitlab.com/gitlab-org/labkit/log/clock.go
@@ -0,0 +1,24 @@
+package log
+
+import (
+ "time"
+)
+
+// Clock interface provides the time
+type clock interface {
+ Now() time.Time
+}
+
+// realClock is the default time implementation
+type realClock struct{}
+
+// Now returns the time
+func (realClock) Now() time.Time { return time.Now() }
+
+// stubClock is the default time implementation
+type stubClock struct {
+ StubTime time.Time
+}
+
+// Now returns a stub time
+func (c *stubClock) Now() time.Time { return c.StubTime }
diff --git a/vendor/gitlab.com/gitlab-org/labkit/log/combined_formatter.go b/vendor/gitlab.com/gitlab-org/labkit/log/combined_formatter.go
new file mode 100644
index 00000000..518cb8c8
--- /dev/null
+++ b/vendor/gitlab.com/gitlab-org/labkit/log/combined_formatter.go
@@ -0,0 +1,53 @@
+package log
+
+import (
+ "bytes"
+ "fmt"
+
+ log "github.com/sirupsen/logrus"
+)
+
+func forNil(v interface{}, otherwise interface{}) interface{} {
+ if v == nil {
+ return otherwise
+ }
+
+ return v
+}
+
+// combinedAccessLogFormatter formats logs into a format similar to the combined access log format
+// See https://httpd.apache.org/docs/1.3/logs.html#combined
+type combinedAccessLogFormatter struct {
+ clock clock
+}
+
+// Format renders a single log entry
+func (f *combinedAccessLogFormatter) Format(entry *log.Entry) ([]byte, error) {
+ host := forNil(entry.Data[httpHostField], "-")
+ remoteAddr := forNil(entry.Data[httpRemoteIPField], "")
+ method := forNil(entry.Data[httpRequestMethodField], "")
+ uri := forNil(entry.Data[httpURIField], "")
+ proto := forNil(entry.Data[httpProtoField], "")
+ status := forNil(entry.Data[httpResponseStatusCodeField], 0)
+ written := forNil(entry.Data[httpResponseSizeField], 0)
+ referer := forNil(entry.Data[httpRequestReferrerField], "")
+ userAgent := forNil(entry.Data[httpUserAgentField], "")
+ duration := forNil(entry.Data[requestDurationField], 0.0)
+
+ now := f.clock.Now().Format("2006/01/02:15:04:05 -0700")
+
+ requestLine := fmt.Sprintf("%s %s %s", method, uri, proto)
+
+ buf := &bytes.Buffer{}
+ _, err := fmt.Fprintf(buf, "%s %s - - [%s] %q %d %d %q %q %.3f\n",
+ host, remoteAddr, now, requestLine,
+ status, written, referer, userAgent, duration,
+ )
+
+ return buf.Bytes(), err
+}
+
+// newCombinedcombinedAccessLogFormatter returns a new formatter for combined access logs
+func newCombinedcombinedAccessLogFormatter() log.Formatter {
+ return &combinedAccessLogFormatter{clock: &realClock{}}
+}
diff --git a/vendor/gitlab.com/gitlab-org/labkit/log/doc.go b/vendor/gitlab.com/gitlab-org/labkit/log/doc.go
new file mode 100644
index 00000000..b3c7c276
--- /dev/null
+++ b/vendor/gitlab.com/gitlab-org/labkit/log/doc.go
@@ -0,0 +1,16 @@
+/*
+Package log provides sensible logging defaults for Labkit.
+
+Labkit uses Logrus for logging.
+
+Applications that use Labkit should rely directly on Labkit.
+
+This package provides some utility methods for working with Labkit, including:
+
+1. Initialize Logrus in a consistent manner
+1. Using GitLab's defaults with logrus
+1. Passing correlation-ids between contexts and logrus
+
+Please review the examples for more details of how to use this package.
+*/
+package log
diff --git a/vendor/gitlab.com/gitlab-org/labkit/log/initialization.go b/vendor/gitlab.com/gitlab-org/labkit/log/initialization.go
new file mode 100644
index 00000000..b53ee42c
--- /dev/null
+++ b/vendor/gitlab.com/gitlab-org/labkit/log/initialization.go
@@ -0,0 +1,82 @@
+package log
+
+import (
+ "io"
+ "log"
+ "os"
+ "os/signal"
+ "syscall"
+
+ "github.com/client9/reopen"
+ "github.com/sirupsen/logrus"
+)
+
+type nopCloser struct{}
+
+func (nopCloser) Close() error { return nil }
+
+// Initialize will configure the logger based on the options passed. It will
+// validate the options and if validation fails drop to the defaults while
+// logging a message to STDERR.
+func Initialize(opts ...LoggerOption) (io.Closer, error) {
+ conf := applyLoggerOptions(opts)
+
+ // Being unable to open the output file will cause an error
+ writer, closer, err := getOutputWriter(conf)
+ if err != nil {
+ return closer, err
+ }
+
+ conf.logger.SetFormatter(conf.formatter)
+ conf.logger.SetLevel(conf.level)
+ conf.logger.SetOutput(writer)
+
+ // Only output the warnings _after_ the logger has been configured
+ for _, warning := range conf.warnings {
+ conf.logger.Warn(warning)
+ }
+
+ return closer, nil
+}
+
+func getOutputWriter(conf *loggerConfig) (io.Writer, io.Closer, error) {
+ if conf.writer != nil {
+ return conf.writer, nopCloser{}, nil
+ }
+
+ // When writing to a file, use `reopen` so that we can
+ // reopen the file on SIGHUP signals
+ f, err := reopen.NewFileWriterMode(conf.outputPath, 0644)
+ if err != nil {
+ return f, nopCloser{}, err
+ }
+
+ isMainLogger := conf.logger == logger
+
+ sighup := make(chan os.Signal, 1)
+ signal.Notify(sighup, syscall.SIGHUP)
+ go listenForSignalHangup(f, isMainLogger, conf.outputPath, sighup)
+
+ return f, f, nil
+}
+
+// Will listen for SIGHUP signals and reopen the underlying file
+func listenForSignalHangup(l reopen.WriteCloser, isMainLogger bool, logFilePath string, sighup chan os.Signal) {
+ for v := range sighup {
+ // Specifically, do _not_ write to the log that is being reopened,
+ // but log this to the _main_ log file instead as the actual log
+ // might be specialised, eg: an access logger leading to an incorrect entry
+ logger.WithFields(logrus.Fields{"signal": v, "path": logFilePath}).Print("Reopening log file on signal")
+
+ err := l.Reopen()
+ if err != nil {
+ if isMainLogger {
+ // Main logger failed to reopen, last ditch effort to notify the user, but don't
+ // do this for auxillary loggers, since we may get double-logs
+ log.Printf("Unable to reopen log file '%s' after %v. Error %v", logFilePath, v, err)
+ } else {
+ logger.WithError(err).WithFields(logrus.Fields{"signal": v, "path": logFilePath}).Print("Failed to reopen log file")
+ }
+ }
+ }
+}
diff --git a/vendor/gitlab.com/gitlab-org/labkit/log/logger.go b/vendor/gitlab.com/gitlab-org/labkit/log/logger.go
new file mode 100644
index 00000000..665d7d6f
--- /dev/null
+++ b/vendor/gitlab.com/gitlab-org/labkit/log/logger.go
@@ -0,0 +1,36 @@
+package log
+
+import (
+ "context"
+ "os"
+
+ "github.com/sirupsen/logrus"
+ "gitlab.com/gitlab-org/labkit/correlation"
+)
+
+var logger = logrus.StandardLogger()
+
+func init() {
+ // Enfore our own defaults on the logrus stdlogger
+ logger.Out = os.Stderr
+ logger.Formatter = &logrus.TextFormatter{}
+ logger.Level = logrus.InfoLevel
+}
+
+// ContextLogger will set the correlation id in the logger based on the context.
+// This reference should not be held outside of the scope of the context
+func ContextLogger(ctx context.Context) *logrus.Entry {
+ return logger.WithFields(ContextFields(ctx))
+}
+
+// WithContextFields is a utility method for logging with context and fields
+func WithContextFields(ctx context.Context, fields Fields) *logrus.Entry {
+ return logger.WithFields(ContextFields(ctx)).WithFields(fields)
+}
+
+// ContextFields a logrus fields structure with the CorrelationID set
+func ContextFields(ctx context.Context) Fields {
+ correlationID := correlation.ExtractFromContext(ctx)
+
+ return logrus.Fields{correlation.FieldName: correlationID}
+}
diff --git a/vendor/gitlab.com/gitlab-org/labkit/log/logger_options.go b/vendor/gitlab.com/gitlab-org/labkit/log/logger_options.go
new file mode 100644
index 00000000..2263d1f2
--- /dev/null
+++ b/vendor/gitlab.com/gitlab-org/labkit/log/logger_options.go
@@ -0,0 +1,105 @@
+package log
+
+import (
+ "fmt"
+ "io"
+ "os"
+
+ "github.com/sirupsen/logrus"
+)
+
+type loggerConfig struct {
+ logger *logrus.Logger
+ level logrus.Level
+ formatter logrus.Formatter
+ outputPath string
+ writer io.Writer
+
+ // A list of warnings that will be emitted once the logger is configured
+ warnings []string
+}
+
+// LoggerOption will configure a new logrus Logger
+type LoggerOption func(*loggerConfig)
+
+func applyLoggerOptions(opts []LoggerOption) *loggerConfig {
+ conf := loggerConfig{
+ logger: logger,
+ level: logrus.InfoLevel,
+ formatter: &logrus.TextFormatter{},
+ writer: os.Stdout,
+ }
+
+ for _, v := range opts {
+ v(&conf)
+ }
+
+ return &conf
+}
+
+// WithFormatter allows setting the format to `text`, `json`, `color` or `combined`. In case
+// the input is not recognized it defaults to text with a warning.
+// More details of these formats:
+// * `text` - human readable.
+// * `json` - computer readable, new-line delimited JSON.
+// * `color` - human readable, in color. Useful for development.
+// * `combined` - httpd access logs. Good for legacy access log parsers.
+func WithFormatter(format string) LoggerOption {
+ return func(conf *loggerConfig) {
+ switch format {
+ case "text":
+ conf.formatter = &logrus.TextFormatter{}
+ case "color":
+ conf.formatter = &logrus.TextFormatter{ForceColors: true, EnvironmentOverrideColors: true}
+ case "json":
+ conf.formatter = &logrus.JSONFormatter{}
+ case "combined":
+ conf.formatter = newCombinedcombinedAccessLogFormatter()
+ default:
+ conf.warnings = append(conf.warnings, fmt.Sprintf("unknown logging format %s, ignoring option", format))
+ }
+ }
+}
+
+// WithLogLevel is used to set the log level when defaulting to `info` is not
+// wanted. Other options are: `debug`, `warn`, `error`, `fatal`, and `panic`.
+func WithLogLevel(level string) LoggerOption {
+ return func(conf *loggerConfig) {
+ logrusLevel, err := logrus.ParseLevel(level)
+ if err != nil {
+ conf.warnings = append(conf.warnings, fmt.Sprintf("unknown log level, ignoring option: %v", err))
+ } else {
+ conf.level = logrusLevel
+ }
+ }
+}
+
+// WithOutputName allows customization of the sink of the logger. Output is either:
+// `stdout`, `stderr`, or a path to a file.
+func WithOutputName(outputName string) LoggerOption {
+ return func(conf *loggerConfig) {
+ switch outputName {
+ case "stdout":
+ conf.writer = os.Stdout
+ case "stderr":
+ conf.writer = os.Stderr
+ default:
+ conf.writer = nil
+ conf.outputPath = outputName
+ }
+ }
+}
+
+// WithWriter allows the writer to be customized. The application is responsible for closing the writer manually.
+func WithWriter(writer io.Writer) LoggerOption {
+ return func(conf *loggerConfig) {
+ conf.writer = writer
+ }
+}
+
+// WithLogger allows you to configure a proprietary logger using the `Initialize` method
+func WithLogger(logger *logrus.Logger) LoggerOption {
+ return func(conf *loggerConfig) {
+ conf.logger = logger
+ }
+}
diff --git a/vendor/gitlab.com/gitlab-org/labkit/log/logrus.go b/vendor/gitlab.com/gitlab-org/labkit/log/logrus.go
new file mode 100644
index 00000000..18d271fd
--- /dev/null
+++ b/vendor/gitlab.com/gitlab-org/labkit/log/logrus.go
@@ -0,0 +1,40 @@
+package log
+
+import (
+ "github.com/sirupsen/logrus"
+)
+
+// Note that we specifically discourage the use of Fatal, Error by excluding them from the API.
+// Since we prefer structured logging with `.WithError(err)`
+
+// Fields is an alias for the underlying logger Fields type
+// Using this alias saves clients from having to import
+// two distinct logging packages, which can be confusing
+type Fields = logrus.Fields
+
+// New is a delegator method for logrus.New
+func New() *logrus.Logger {
+ return logrus.New()
+}
+
+// Info is a delegator method for logrus.Info
+// Info is an exception to our rule about discouraging non-structured use, as there are valid
+// reasons for simply emitting a single log line.
+func Info(args ...interface{}) {
+ logger.Info(args...)
+}
+
+// WithField is a delegator method for logrus.WithField
+func WithField(key string, value interface{}) *logrus.Entry {
+ return logger.WithField(key, value)
+}
+
+// WithFields is a delegator method for logrus.WithFields
+func WithFields(fields Fields) *logrus.Entry {
+ return logger.WithFields(fields)
+}
+
+// WithError is a delegator method for logrus.WithError
+func WithError(err error) *logrus.Entry {
+ return logger.WithError(err)
+}
diff --git a/vendor/vendor.json b/vendor/vendor.json
index 2b2f1420..a4506b9f 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -15,6 +15,12 @@
"revisionTime": "2019-04-14T21:10:42Z"
},
{
+ "checksumSHA1": "Se+tHrDK92BXj6/YoYuCXOH5jz0=",
+ "path": "github.com/client9/reopen",
+ "revision": "dbabf56e0beda57421ccdf6c7431a75a7d8a1562",
+ "revisionTime": "2018-03-16T01:46:14Z"
+ },
+ {
"checksumSHA1": "DDb/mPtFh1OfbUXdXMaLxio0g48=",
"path": "github.com/getsentry/raven-go",
"revision": "5c24d5110e0e198d9ae16f1f3465366085001d92",
@@ -186,6 +192,12 @@
"versionExact": "v1.5.0"
},
{
+ "checksumSHA1": "Aulh7C5SVOA4Jzt5eHNH6197Mbk=",
+ "path": "github.com/konsorten/go-windows-terminal-sequences",
+ "revision": "f55edac94c9bbba5d6182a4be46d86a2c9b5b50e",
+ "revisionTime": "2019-02-26T22:47:05Z"
+ },
+ {
"checksumSHA1": "bKMZjd2wPw13VwoE7mBeSv5djFA=",
"comment": "v1.0.0-2-gc12348c",
"path": "github.com/matttproud/golang_protobuf_extensions/pbutil",
@@ -285,12 +297,18 @@
"revision": "8dd4211afb5d08dbb39a533b9bb9e4b486351df6"
},
{
- "checksumSHA1": "ySaT8G3I3y4MmnoXOYAAX0rC+p8=",
+ "checksumSHA1": "bQ+Wb430AXpen54AYtrR1Igfh18=",
+ "path": "github.com/sebest/xff",
+ "revision": "6c115e0ffa35d6a2e3f7a9e797c9cf07f0da4b9f",
+ "revisionTime": "2016-09-10T04:38:05Z"
+ },
+ {
+ "checksumSHA1": "+enshrlE9AS7iFQTrtHNWj4wAFw=",
"path": "github.com/sirupsen/logrus",
- "revision": "d682213848ed68c0a260ca37d6dd5ace8423f5ba",
- "revisionTime": "2017-12-05T20:32:29Z",
- "version": "v1.0.4",
- "versionExact": "v1.0.4"
+ "revision": "07a84ee7412e7a28663d92930a1d46f81b124ee1",
+ "revisionTime": "2019-07-01T14:35:06Z",
+ "version": "master",
+ "versionExact": "master"
},
{
"checksumSHA1": "4uEmekuD2eHuhPbwKVh/X9Y/ESo=",
@@ -311,20 +329,34 @@
{
"checksumSHA1": "86VccaJApU9EEq4KKen8DX9gMUM=",
"path": "gitlab.com/gitlab-org/labkit/correlation",
- "revision": "ed6d5f223a90a4e9bb63f414a212e547edfd8d84",
- "revisionTime": "2019-07-10T10:24:15Z"
+ "revision": "503c162155664d5a8d6d84a7dac51960dc0119ec",
+ "revisionTime": "2019-07-12T13:09:19Z",
+ "version": "master",
+ "versionExact": "master"
},
{
"checksumSHA1": "XKPA3adbywKGnRFOPl1sHc0oUmM=",
"path": "gitlab.com/gitlab-org/labkit/errortracking",
- "revision": "ed6d5f223a90a4e9bb63f414a212e547edfd8d84",
- "revisionTime": "2019-07-10T10:24:15Z"
+ "revision": "503c162155664d5a8d6d84a7dac51960dc0119ec",
+ "revisionTime": "2019-07-12T13:09:19Z",
+ "version": "master",
+ "versionExact": "master"
+ },
+ {
+ "checksumSHA1": "3/0keVU4UZOoFSNajbtnexV6Ou4=",
+ "path": "gitlab.com/gitlab-org/labkit/log",
+ "revision": "503c162155664d5a8d6d84a7dac51960dc0119ec",
+ "revisionTime": "2019-07-12T13:09:19Z",
+ "version": "master",
+ "versionExact": "master"
},
{
"checksumSHA1": "yWZietMQ0YjAXj4NEVrQAypOIBU=",
"path": "gitlab.com/gitlab-org/labkit/mask",
- "revision": "ed6d5f223a90a4e9bb63f414a212e547edfd8d84",
- "revisionTime": "2019-07-10T10:24:15Z"
+ "revision": "503c162155664d5a8d6d84a7dac51960dc0119ec",
+ "revisionTime": "2019-07-12T13:09:19Z",
+ "version": "master",
+ "versionExact": "master"
},
{
"checksumSHA1": "UxFypaeBgKNoj1+mF/OwdtgfrWk=",