Update logrus vendor
closes #2125 Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)master
							parent
							
								
									76f514b618
								
							
						
					
					
						commit
						1d6c1a6468
					
				|  | @ -12,7 +12,7 @@ import ( | |||
| 	"rsc.io/letsencrypt" | ||||
| 
 | ||||
| 	log "github.com/Sirupsen/logrus" | ||||
| 	"github.com/Sirupsen/logrus/formatters/logstash" | ||||
| 	logstash "github.com/bshuster-repo/logrus-logstash-hook" | ||||
| 	"github.com/bugsnag/bugsnag-go" | ||||
| 	"github.com/docker/distribution/configuration" | ||||
| 	"github.com/docker/distribution/context" | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| github.com/Azure/azure-sdk-for-go/storage 0b5fe2abe0271ba07049eacaa65922d67c319543 | ||||
| github.com/Sirupsen/logrus 55eb11d21d2a31a3cc93838241d04800f52e823d | ||||
| github.com/Sirupsen/logrus d26492970760ca5d33129d2d799e34be5c4782eb | ||||
| github.com/aws/aws-sdk-go 90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6 | ||||
| github.com/bshuster-repo/logrus-logstash-hook 5f729f2fb50a301153cae84ff5c58981d51c095a | ||||
| github.com/bugsnag/bugsnag-go b1d153021fcd90ca3f080db36bec96dc690fb274 | ||||
| github.com/bugsnag/osext 0dd3f918b21bec95ace9dc86c7e70266cfc5c702 | ||||
| github.com/bugsnag/panicwrap e2c28503fcd0675329da73bf48b33404db873782 | ||||
|  |  | |||
|  | @ -0,0 +1,64 @@ | |||
| package logrus | ||||
| 
 | ||||
| // The following code was sourced and modified from the
 | ||||
| // https://bitbucket.org/tebeka/atexit package governed by the following license:
 | ||||
| //
 | ||||
| // Copyright (c) 2012 Miki Tebeka <miki.tebeka@gmail.com>.
 | ||||
| //
 | ||||
| // Permission is hereby granted, free of charge, to any person obtaining a copy of
 | ||||
| // this software and associated documentation files (the "Software"), to deal in
 | ||||
| // the Software without restriction, including without limitation the rights to
 | ||||
| // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 | ||||
| // the Software, and to permit persons to whom the Software is furnished to do so,
 | ||||
| // subject to the following conditions:
 | ||||
| //
 | ||||
| // The above copyright notice and this permission notice shall be included in all
 | ||||
| // copies or substantial portions of the Software.
 | ||||
| //
 | ||||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | ||||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 | ||||
| // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 | ||||
| // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 | ||||
| // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 | ||||
| // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| ) | ||||
| 
 | ||||
| var handlers = []func(){} | ||||
| 
 | ||||
| func runHandler(handler func()) { | ||||
| 	defer func() { | ||||
| 		if err := recover(); err != nil { | ||||
| 			fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err) | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	handler() | ||||
| } | ||||
| 
 | ||||
| func runHandlers() { | ||||
| 	for _, handler := range handlers { | ||||
| 		runHandler(handler) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code)
 | ||||
| func Exit(code int) { | ||||
| 	runHandlers() | ||||
| 	os.Exit(code) | ||||
| } | ||||
| 
 | ||||
| // RegisterExitHandler adds a Logrus Exit handler, call logrus.Exit to invoke
 | ||||
| // all handlers. The handlers will also be invoked when any Fatal log entry is
 | ||||
| // made.
 | ||||
| //
 | ||||
| // This method is useful when a caller wishes to use logrus to log a fatal
 | ||||
| // message but also needs to gracefully shutdown. An example usecase could be
 | ||||
| // closing database connections, or sending a alert that the application is
 | ||||
| // closing.
 | ||||
| func RegisterExitHandler(handler func()) { | ||||
| 	handlers = append(handlers, handler) | ||||
| } | ||||
|  | @ -0,0 +1,26 @@ | |||
| /* | ||||
| Package logrus is a structured logger for Go, completely API compatible with the standard library logger. | ||||
| 
 | ||||
| 
 | ||||
| The simplest way to use Logrus is simply the package-level exported logger: | ||||
| 
 | ||||
|   package main | ||||
| 
 | ||||
|   import ( | ||||
|     log "github.com/Sirupsen/logrus" | ||||
|   ) | ||||
| 
 | ||||
|   func main() { | ||||
|     log.WithFields(log.Fields{ | ||||
|       "animal": "walrus", | ||||
|       "number": 1, | ||||
|       "size":   10, | ||||
|     }).Info("A walrus appears") | ||||
|   } | ||||
| 
 | ||||
| Output: | ||||
|   time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10 | ||||
| 
 | ||||
| For a full guide visit https://github.com/Sirupsen/logrus
 | ||||
| */ | ||||
| package logrus | ||||
|  | @ -3,11 +3,24 @@ package logrus | |||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| var bufferPool *sync.Pool | ||||
| 
 | ||||
| func init() { | ||||
| 	bufferPool = &sync.Pool{ | ||||
| 		New: func() interface{} { | ||||
| 			return new(bytes.Buffer) | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Defines the key when adding errors using WithError.
 | ||||
| var ErrorKey = "error" | ||||
| 
 | ||||
| // 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
 | ||||
|  | @ -26,6 +39,9 @@ type Entry struct { | |||
| 
 | ||||
| 	// Message passed to Debug, Info, Warn, Error, Fatal or Panic
 | ||||
| 	Message string | ||||
| 
 | ||||
| 	// When formatter is called in entry.log(), an Buffer may be set to entry
 | ||||
| 	Buffer *bytes.Buffer | ||||
| } | ||||
| 
 | ||||
| func NewEntry(logger *Logger) *Entry { | ||||
|  | @ -36,21 +52,20 @@ func NewEntry(logger *Logger) *Entry { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Returns a reader for the entry, which is a proxy to the formatter.
 | ||||
| func (entry *Entry) Reader() (*bytes.Buffer, error) { | ||||
| 	serialized, err := entry.Logger.Formatter.Format(entry) | ||||
| 	return bytes.NewBuffer(serialized), err | ||||
| } | ||||
| 
 | ||||
| // Returns the string representation from the reader and ultimately the
 | ||||
| // formatter.
 | ||||
| func (entry *Entry) String() (string, error) { | ||||
| 	reader, err := entry.Reader() | ||||
| 	serialized, err := entry.Logger.Formatter.Format(entry) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	str := string(serialized) | ||||
| 	return str, nil | ||||
| } | ||||
| 
 | ||||
| 	return reader.String(), err | ||||
| // Add an error as single field (using the key defined in ErrorKey) to the Entry.
 | ||||
| func (entry *Entry) WithError(err error) *Entry { | ||||
| 	return entry.WithField(ErrorKey, err) | ||||
| } | ||||
| 
 | ||||
| // Add a single field to the Entry.
 | ||||
|  | @ -60,7 +75,7 @@ func (entry *Entry) WithField(key string, value interface{}) *Entry { | |||
| 
 | ||||
| // Add a map of fields to the Entry.
 | ||||
| func (entry *Entry) WithFields(fields Fields) *Entry { | ||||
| 	data := Fields{} | ||||
| 	data := make(Fields, len(entry.Data)+len(fields)) | ||||
| 	for k, v := range entry.Data { | ||||
| 		data[k] = v | ||||
| 	} | ||||
|  | @ -70,37 +85,43 @@ func (entry *Entry) WithFields(fields Fields) *Entry { | |||
| 	return &Entry{Logger: entry.Logger, Data: data} | ||||
| } | ||||
| 
 | ||||
| func (entry *Entry) log(level Level, msg string) { | ||||
| // This function is not declared with a pointer value because otherwise
 | ||||
| // race conditions will occur when using multiple goroutines
 | ||||
| func (entry Entry) log(level Level, msg string) { | ||||
| 	var buffer *bytes.Buffer | ||||
| 	entry.Time = time.Now() | ||||
| 	entry.Level = level | ||||
| 	entry.Message = msg | ||||
| 
 | ||||
| 	if err := entry.Logger.Hooks.Fire(level, entry); err != nil { | ||||
| 	if err := entry.Logger.Hooks.Fire(level, &entry); err != nil { | ||||
| 		entry.Logger.mu.Lock() | ||||
| 		fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) | ||||
| 		entry.Logger.mu.Unlock() | ||||
| 	} | ||||
| 
 | ||||
| 	reader, err := entry.Reader() | ||||
| 	buffer = bufferPool.Get().(*bytes.Buffer) | ||||
| 	buffer.Reset() | ||||
| 	defer bufferPool.Put(buffer) | ||||
| 	entry.Buffer = buffer | ||||
| 	serialized, err := entry.Logger.Formatter.Format(&entry) | ||||
| 	entry.Buffer = nil | ||||
| 	if err != nil { | ||||
| 		entry.Logger.mu.Lock() | ||||
| 		fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) | ||||
| 		entry.Logger.mu.Unlock() | ||||
| 	} | ||||
| 
 | ||||
| 	} else { | ||||
| 		entry.Logger.mu.Lock() | ||||
| 	defer entry.Logger.mu.Unlock() | ||||
| 
 | ||||
| 	_, err = io.Copy(entry.Logger.Out, reader) | ||||
| 		_, 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) | ||||
| 		panic(&entry) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -140,7 +161,7 @@ func (entry *Entry) Fatal(args ...interface{}) { | |||
| 	if entry.Logger.Level >= FatalLevel { | ||||
| 		entry.log(FatalLevel, fmt.Sprint(args...)) | ||||
| 	} | ||||
| 	os.Exit(1) | ||||
| 	Exit(1) | ||||
| } | ||||
| 
 | ||||
| func (entry *Entry) Panic(args ...interface{}) { | ||||
|  | @ -188,6 +209,7 @@ func (entry *Entry) Fatalf(format string, args ...interface{}) { | |||
| 	if entry.Logger.Level >= FatalLevel { | ||||
| 		entry.Fatal(fmt.Sprintf(format, args...)) | ||||
| 	} | ||||
| 	Exit(1) | ||||
| } | ||||
| 
 | ||||
| func (entry *Entry) Panicf(format string, args ...interface{}) { | ||||
|  | @ -234,6 +256,7 @@ func (entry *Entry) Fatalln(args ...interface{}) { | |||
| 	if entry.Logger.Level >= FatalLevel { | ||||
| 		entry.Fatal(entry.sprintlnn(args...)) | ||||
| 	} | ||||
| 	Exit(1) | ||||
| } | ||||
| 
 | ||||
| func (entry *Entry) Panicln(args ...interface{}) { | ||||
|  |  | |||
|  | @ -48,6 +48,11 @@ func AddHook(hook Hook) { | |||
| 	std.Hooks.Add(hook) | ||||
| } | ||||
| 
 | ||||
| // WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key.
 | ||||
| func WithError(err error) *Entry { | ||||
| 	return std.WithField(ErrorKey, err) | ||||
| } | ||||
| 
 | ||||
| // WithField creates an entry from the standard logger and adds a field to
 | ||||
| // it. If you want multiple fields, use `WithFields`.
 | ||||
| //
 | ||||
|  |  | |||
|  | @ -31,18 +31,15 @@ type Formatter interface { | |||
| // It's not exported because it's still using Data in an opinionated way. It's to
 | ||||
| // avoid code duplication between the two default formatters.
 | ||||
| func prefixFieldClashes(data Fields) { | ||||
| 	_, ok := data["time"] | ||||
| 	if ok { | ||||
| 		data["fields.time"] = data["time"] | ||||
| 	if t, ok := data["time"]; ok { | ||||
| 		data["fields.time"] = t | ||||
| 	} | ||||
| 
 | ||||
| 	_, ok = data["msg"] | ||||
| 	if ok { | ||||
| 		data["fields.msg"] = data["msg"] | ||||
| 	if m, ok := data["msg"]; ok { | ||||
| 		data["fields.msg"] = m | ||||
| 	} | ||||
| 
 | ||||
| 	_, ok = data["level"] | ||||
| 	if ok { | ||||
| 		data["fields.level"] = data["level"] | ||||
| 	if l, ok := data["level"]; ok { | ||||
| 		data["fields.level"] = l | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -1,56 +0,0 @@ | |||
| package logstash | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"github.com/Sirupsen/logrus" | ||||
| ) | ||||
| 
 | ||||
| // Formatter generates json in logstash format.
 | ||||
| // Logstash site: http://logstash.net/
 | ||||
| type LogstashFormatter struct { | ||||
| 	Type string // if not empty use for logstash type field.
 | ||||
| 
 | ||||
| 	// TimestampFormat sets the format used for timestamps.
 | ||||
| 	TimestampFormat string | ||||
| } | ||||
| 
 | ||||
| func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) { | ||||
| 	entry.Data["@version"] = 1 | ||||
| 
 | ||||
| 	if f.TimestampFormat == "" { | ||||
| 		f.TimestampFormat = logrus.DefaultTimestampFormat | ||||
| 	} | ||||
| 
 | ||||
| 	entry.Data["@timestamp"] = entry.Time.Format(f.TimestampFormat) | ||||
| 
 | ||||
| 	// set message field
 | ||||
| 	v, ok := entry.Data["message"] | ||||
| 	if ok { | ||||
| 		entry.Data["fields.message"] = v | ||||
| 	} | ||||
| 	entry.Data["message"] = entry.Message | ||||
| 
 | ||||
| 	// set level field
 | ||||
| 	v, ok = entry.Data["level"] | ||||
| 	if ok { | ||||
| 		entry.Data["fields.level"] = v | ||||
| 	} | ||||
| 	entry.Data["level"] = entry.Level.String() | ||||
| 
 | ||||
| 	// set type field
 | ||||
| 	if f.Type != "" { | ||||
| 		v, ok = entry.Data["type"] | ||||
| 		if ok { | ||||
| 			entry.Data["fields.type"] = v | ||||
| 		} | ||||
| 		entry.Data["type"] = f.Type | ||||
| 	} | ||||
| 
 | ||||
| 	serialized, err := json.Marshal(entry.Data) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) | ||||
| 	} | ||||
| 	return append(serialized, '\n'), nil | ||||
| } | ||||
|  | @ -11,11 +11,11 @@ type Hook interface { | |||
| } | ||||
| 
 | ||||
| // Internal type for storing the hooks on a logger instance.
 | ||||
| type levelHooks map[Level][]Hook | ||||
| type LevelHooks map[Level][]Hook | ||||
| 
 | ||||
| // Add a hook to an instance of logger. This is called with
 | ||||
| // `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface.
 | ||||
| func (hooks levelHooks) Add(hook Hook) { | ||||
| func (hooks LevelHooks) Add(hook Hook) { | ||||
| 	for _, level := range hook.Levels() { | ||||
| 		hooks[level] = append(hooks[level], hook) | ||||
| 	} | ||||
|  | @ -23,7 +23,7 @@ func (hooks levelHooks) Add(hook Hook) { | |||
| 
 | ||||
| // Fire all the hooks for the passed level. Used by `entry.log` to fire
 | ||||
| // appropriate hooks for a log entry.
 | ||||
| func (hooks levelHooks) Fire(level Level, entry *Entry) error { | ||||
| func (hooks LevelHooks) Fire(level Level, entry *Entry) error { | ||||
| 	for _, hook := range hooks[level] { | ||||
| 		if err := hook.Fire(entry); err != nil { | ||||
| 			return err | ||||
|  |  | |||
|  | @ -24,11 +24,12 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { | |||
| 	} | ||||
| 	prefixFieldClashes(data) | ||||
| 
 | ||||
| 	if f.TimestampFormat == "" { | ||||
| 		f.TimestampFormat = DefaultTimestampFormat | ||||
| 	timestampFormat := f.TimestampFormat | ||||
| 	if timestampFormat == "" { | ||||
| 		timestampFormat = DefaultTimestampFormat | ||||
| 	} | ||||
| 
 | ||||
| 	data["time"] = entry.Time.Format(f.TimestampFormat) | ||||
| 	data["time"] = entry.Time.Format(timestampFormat) | ||||
| 	data["msg"] = entry.Message | ||||
| 	data["level"] = entry.Level.String() | ||||
| 
 | ||||
|  |  | |||
|  | @ -8,13 +8,13 @@ import ( | |||
| 
 | ||||
| type Logger struct { | ||||
| 	// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
 | ||||
| 	// file, or leave it default which is `os.Stdout`. You can also set this to
 | ||||
| 	// file, or leave it default which is `os.Stderr`. You can also set this to
 | ||||
| 	// something more adventorous, such as logging to Kafka.
 | ||||
| 	Out io.Writer | ||||
| 	// Hooks for the logger instance. These allow firing events based on logging
 | ||||
| 	// levels and log entries. For example, to send errors to an error tracking
 | ||||
| 	// service, log to StatsD or dump the core on fatal errors.
 | ||||
| 	Hooks levelHooks | ||||
| 	Hooks LevelHooks | ||||
| 	// All log entries pass through the formatter before logged to Out. The
 | ||||
| 	// included formatters are `TextFormatter` and `JSONFormatter` for which
 | ||||
| 	// TextFormatter is the default. In development (when a TTY is attached) it
 | ||||
|  | @ -26,8 +26,31 @@ type Logger struct { | |||
| 	// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
 | ||||
| 	// logged. `logrus.Debug` is useful in
 | ||||
| 	Level Level | ||||
| 	// Used to sync writing to the log.
 | ||||
| 	mu sync.Mutex | ||||
| 	// Used to sync writing to the log. Locking is enabled by Default
 | ||||
| 	mu MutexWrap | ||||
| 	// Reusable empty entry
 | ||||
| 	entryPool sync.Pool | ||||
| } | ||||
| 
 | ||||
| type MutexWrap struct { | ||||
| 	lock     sync.Mutex | ||||
| 	disabled bool | ||||
| } | ||||
| 
 | ||||
| func (mw *MutexWrap) Lock() { | ||||
| 	if !mw.disabled { | ||||
| 		mw.lock.Lock() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (mw *MutexWrap) Unlock() { | ||||
| 	if !mw.disabled { | ||||
| 		mw.lock.Unlock() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (mw *MutexWrap) Disable() { | ||||
| 	mw.disabled = true | ||||
| } | ||||
| 
 | ||||
| // Creates a new logger. Configuration should be set by changing `Formatter`,
 | ||||
|  | @ -37,167 +60,249 @@ type Logger struct { | |||
| //    var log = &Logger{
 | ||||
| //      Out: os.Stderr,
 | ||||
| //      Formatter: new(JSONFormatter),
 | ||||
| //      Hooks: make(levelHooks),
 | ||||
| //      Hooks: make(LevelHooks),
 | ||||
| //      Level: logrus.DebugLevel,
 | ||||
| //    }
 | ||||
| //
 | ||||
| // It's recommended to make this a global instance called `log`.
 | ||||
| func New() *Logger { | ||||
| 	return &Logger{ | ||||
| 		Out:       os.Stdout, | ||||
| 		Out:       os.Stderr, | ||||
| 		Formatter: new(TextFormatter), | ||||
| 		Hooks:     make(levelHooks), | ||||
| 		Hooks:     make(LevelHooks), | ||||
| 		Level:     InfoLevel, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Adds a field to the log entry, note that you it doesn't log until you call
 | ||||
| func (logger *Logger) newEntry() *Entry { | ||||
| 	entry, ok := logger.entryPool.Get().(*Entry) | ||||
| 	if ok { | ||||
| 		return entry | ||||
| 	} | ||||
| 	return NewEntry(logger) | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) releaseEntry(entry *Entry) { | ||||
| 	logger.entryPool.Put(entry) | ||||
| } | ||||
| 
 | ||||
| // Adds a field to the log entry, note that it doesn't log until you call
 | ||||
| // Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
 | ||||
| // Ff you want multiple fields, use `WithFields`.
 | ||||
| // If you want multiple fields, use `WithFields`.
 | ||||
| func (logger *Logger) WithField(key string, value interface{}) *Entry { | ||||
| 	return NewEntry(logger).WithField(key, value) | ||||
| 	entry := logger.newEntry() | ||||
| 	defer logger.releaseEntry(entry) | ||||
| 	return entry.WithField(key, value) | ||||
| } | ||||
| 
 | ||||
| // Adds a struct of fields to the log entry. All it does is call `WithField` for
 | ||||
| // each `Field`.
 | ||||
| func (logger *Logger) WithFields(fields Fields) *Entry { | ||||
| 	return NewEntry(logger).WithFields(fields) | ||||
| 	entry := logger.newEntry() | ||||
| 	defer logger.releaseEntry(entry) | ||||
| 	return entry.WithFields(fields) | ||||
| } | ||||
| 
 | ||||
| // Add an error as single field to the log entry.  All it does is call
 | ||||
| // `WithError` for the given `error`.
 | ||||
| func (logger *Logger) WithError(err error) *Entry { | ||||
| 	entry := logger.newEntry() | ||||
| 	defer logger.releaseEntry(entry) | ||||
| 	return entry.WithError(err) | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Debugf(format string, args ...interface{}) { | ||||
| 	if logger.Level >= DebugLevel { | ||||
| 		NewEntry(logger).Debugf(format, args...) | ||||
| 		entry := logger.newEntry() | ||||
| 		entry.Debugf(format, args...) | ||||
| 		logger.releaseEntry(entry) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Infof(format string, args ...interface{}) { | ||||
| 	if logger.Level >= InfoLevel { | ||||
| 		NewEntry(logger).Infof(format, args...) | ||||
| 		entry := logger.newEntry() | ||||
| 		entry.Infof(format, args...) | ||||
| 		logger.releaseEntry(entry) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Printf(format string, args ...interface{}) { | ||||
| 	NewEntry(logger).Printf(format, args...) | ||||
| 	entry := logger.newEntry() | ||||
| 	entry.Printf(format, args...) | ||||
| 	logger.releaseEntry(entry) | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Warnf(format string, args ...interface{}) { | ||||
| 	if logger.Level >= WarnLevel { | ||||
| 		NewEntry(logger).Warnf(format, args...) | ||||
| 		entry := logger.newEntry() | ||||
| 		entry.Warnf(format, args...) | ||||
| 		logger.releaseEntry(entry) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Warningf(format string, args ...interface{}) { | ||||
| 	if logger.Level >= WarnLevel { | ||||
| 		NewEntry(logger).Warnf(format, args...) | ||||
| 		entry := logger.newEntry() | ||||
| 		entry.Warnf(format, args...) | ||||
| 		logger.releaseEntry(entry) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Errorf(format string, args ...interface{}) { | ||||
| 	if logger.Level >= ErrorLevel { | ||||
| 		NewEntry(logger).Errorf(format, args...) | ||||
| 		entry := logger.newEntry() | ||||
| 		entry.Errorf(format, args...) | ||||
| 		logger.releaseEntry(entry) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Fatalf(format string, args ...interface{}) { | ||||
| 	if logger.Level >= FatalLevel { | ||||
| 		NewEntry(logger).Fatalf(format, args...) | ||||
| 		entry := logger.newEntry() | ||||
| 		entry.Fatalf(format, args...) | ||||
| 		logger.releaseEntry(entry) | ||||
| 	} | ||||
| 	Exit(1) | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Panicf(format string, args ...interface{}) { | ||||
| 	if logger.Level >= PanicLevel { | ||||
| 		NewEntry(logger).Panicf(format, args...) | ||||
| 		entry := logger.newEntry() | ||||
| 		entry.Panicf(format, args...) | ||||
| 		logger.releaseEntry(entry) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Debug(args ...interface{}) { | ||||
| 	if logger.Level >= DebugLevel { | ||||
| 		NewEntry(logger).Debug(args...) | ||||
| 		entry := logger.newEntry() | ||||
| 		entry.Debug(args...) | ||||
| 		logger.releaseEntry(entry) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Info(args ...interface{}) { | ||||
| 	if logger.Level >= InfoLevel { | ||||
| 		NewEntry(logger).Info(args...) | ||||
| 		entry := logger.newEntry() | ||||
| 		entry.Info(args...) | ||||
| 		logger.releaseEntry(entry) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Print(args ...interface{}) { | ||||
| 	NewEntry(logger).Info(args...) | ||||
| 	entry := logger.newEntry() | ||||
| 	entry.Info(args...) | ||||
| 	logger.releaseEntry(entry) | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Warn(args ...interface{}) { | ||||
| 	if logger.Level >= WarnLevel { | ||||
| 		NewEntry(logger).Warn(args...) | ||||
| 		entry := logger.newEntry() | ||||
| 		entry.Warn(args...) | ||||
| 		logger.releaseEntry(entry) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Warning(args ...interface{}) { | ||||
| 	if logger.Level >= WarnLevel { | ||||
| 		NewEntry(logger).Warn(args...) | ||||
| 		entry := logger.newEntry() | ||||
| 		entry.Warn(args...) | ||||
| 		logger.releaseEntry(entry) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Error(args ...interface{}) { | ||||
| 	if logger.Level >= ErrorLevel { | ||||
| 		NewEntry(logger).Error(args...) | ||||
| 		entry := logger.newEntry() | ||||
| 		entry.Error(args...) | ||||
| 		logger.releaseEntry(entry) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Fatal(args ...interface{}) { | ||||
| 	if logger.Level >= FatalLevel { | ||||
| 		NewEntry(logger).Fatal(args...) | ||||
| 		entry := logger.newEntry() | ||||
| 		entry.Fatal(args...) | ||||
| 		logger.releaseEntry(entry) | ||||
| 	} | ||||
| 	Exit(1) | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Panic(args ...interface{}) { | ||||
| 	if logger.Level >= PanicLevel { | ||||
| 		NewEntry(logger).Panic(args...) | ||||
| 		entry := logger.newEntry() | ||||
| 		entry.Panic(args...) | ||||
| 		logger.releaseEntry(entry) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Debugln(args ...interface{}) { | ||||
| 	if logger.Level >= DebugLevel { | ||||
| 		NewEntry(logger).Debugln(args...) | ||||
| 		entry := logger.newEntry() | ||||
| 		entry.Debugln(args...) | ||||
| 		logger.releaseEntry(entry) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Infoln(args ...interface{}) { | ||||
| 	if logger.Level >= InfoLevel { | ||||
| 		NewEntry(logger).Infoln(args...) | ||||
| 		entry := logger.newEntry() | ||||
| 		entry.Infoln(args...) | ||||
| 		logger.releaseEntry(entry) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Println(args ...interface{}) { | ||||
| 	NewEntry(logger).Println(args...) | ||||
| 	entry := logger.newEntry() | ||||
| 	entry.Println(args...) | ||||
| 	logger.releaseEntry(entry) | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Warnln(args ...interface{}) { | ||||
| 	if logger.Level >= WarnLevel { | ||||
| 		NewEntry(logger).Warnln(args...) | ||||
| 		entry := logger.newEntry() | ||||
| 		entry.Warnln(args...) | ||||
| 		logger.releaseEntry(entry) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Warningln(args ...interface{}) { | ||||
| 	if logger.Level >= WarnLevel { | ||||
| 		NewEntry(logger).Warnln(args...) | ||||
| 		entry := logger.newEntry() | ||||
| 		entry.Warnln(args...) | ||||
| 		logger.releaseEntry(entry) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Errorln(args ...interface{}) { | ||||
| 	if logger.Level >= ErrorLevel { | ||||
| 		NewEntry(logger).Errorln(args...) | ||||
| 		entry := logger.newEntry() | ||||
| 		entry.Errorln(args...) | ||||
| 		logger.releaseEntry(entry) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Fatalln(args ...interface{}) { | ||||
| 	if logger.Level >= FatalLevel { | ||||
| 		NewEntry(logger).Fatalln(args...) | ||||
| 		entry := logger.newEntry() | ||||
| 		entry.Fatalln(args...) | ||||
| 		logger.releaseEntry(entry) | ||||
| 	} | ||||
| 	Exit(1) | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) Panicln(args ...interface{}) { | ||||
| 	if logger.Level >= PanicLevel { | ||||
| 		NewEntry(logger).Panicln(args...) | ||||
| 		entry := logger.newEntry() | ||||
| 		entry.Panicln(args...) | ||||
| 		logger.releaseEntry(entry) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| //When file is opened with appending mode, it's safe to
 | ||||
| //write concurrently to a file (within 4k message on Linux).
 | ||||
| //In these cases user can choose to disable the lock.
 | ||||
| func (logger *Logger) SetNoLock() { | ||||
| 	logger.mu.Disable() | ||||
| } | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ package logrus | |||
| import ( | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // Fields type, used to pass to `WithFields`.
 | ||||
|  | @ -33,7 +34,7 @@ func (level Level) String() string { | |||
| 
 | ||||
| // ParseLevel takes a string level and returns the Logrus log level constant.
 | ||||
| func ParseLevel(lvl string) (Level, error) { | ||||
| 	switch lvl { | ||||
| 	switch strings.ToLower(lvl) { | ||||
| 	case "panic": | ||||
| 		return PanicLevel, nil | ||||
| 	case "fatal": | ||||
|  | @ -52,6 +53,16 @@ func ParseLevel(lvl string) (Level, error) { | |||
| 	return l, fmt.Errorf("not a valid logrus Level: %q", lvl) | ||||
| } | ||||
| 
 | ||||
| // A constant exposing all logging levels
 | ||||
| var AllLevels = []Level{ | ||||
| 	PanicLevel, | ||||
| 	FatalLevel, | ||||
| 	ErrorLevel, | ||||
| 	WarnLevel, | ||||
| 	InfoLevel, | ||||
| 	DebugLevel, | ||||
| } | ||||
| 
 | ||||
| // These are the different logging levels. You can set the logging level to log
 | ||||
| // on your instance of logger, obtained with `logrus.New()`.
 | ||||
| const ( | ||||
|  | @ -74,7 +85,11 @@ const ( | |||
| ) | ||||
| 
 | ||||
| // Won't compile if StdLogger can't be realized by a log.Logger
 | ||||
| var _ StdLogger = &log.Logger{} | ||||
| var ( | ||||
| 	_ StdLogger = &log.Logger{} | ||||
| 	_ StdLogger = &Entry{} | ||||
| 	_ StdLogger = &Logger{} | ||||
| ) | ||||
| 
 | ||||
| // StdLogger is what your logrus-enabled library should take, that way
 | ||||
| // it'll accept a stdlib logger and a logrus logger. There's no standard
 | ||||
|  | @ -92,3 +107,37 @@ type StdLogger interface { | |||
| 	Panicf(string, ...interface{}) | ||||
| 	Panicln(...interface{}) | ||||
| } | ||||
| 
 | ||||
| // The FieldLogger interface generalizes the Entry and Logger types
 | ||||
| type FieldLogger interface { | ||||
| 	WithField(key string, value interface{}) *Entry | ||||
| 	WithFields(fields Fields) *Entry | ||||
| 	WithError(err error) *Entry | ||||
| 
 | ||||
| 	Debugf(format string, args ...interface{}) | ||||
| 	Infof(format string, args ...interface{}) | ||||
| 	Printf(format string, args ...interface{}) | ||||
| 	Warnf(format string, args ...interface{}) | ||||
| 	Warningf(format string, args ...interface{}) | ||||
| 	Errorf(format string, args ...interface{}) | ||||
| 	Fatalf(format string, args ...interface{}) | ||||
| 	Panicf(format string, args ...interface{}) | ||||
| 
 | ||||
| 	Debug(args ...interface{}) | ||||
| 	Info(args ...interface{}) | ||||
| 	Print(args ...interface{}) | ||||
| 	Warn(args ...interface{}) | ||||
| 	Warning(args ...interface{}) | ||||
| 	Error(args ...interface{}) | ||||
| 	Fatal(args ...interface{}) | ||||
| 	Panic(args ...interface{}) | ||||
| 
 | ||||
| 	Debugln(args ...interface{}) | ||||
| 	Infoln(args ...interface{}) | ||||
| 	Println(args ...interface{}) | ||||
| 	Warnln(args ...interface{}) | ||||
| 	Warningln(args ...interface{}) | ||||
| 	Errorln(args ...interface{}) | ||||
| 	Fatalln(args ...interface{}) | ||||
| 	Panicln(args ...interface{}) | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,8 @@ | |||
| // +build appengine
 | ||||
| 
 | ||||
| package logrus | ||||
| 
 | ||||
| // IsTerminal returns true if stderr's file descriptor is a terminal.
 | ||||
| func IsTerminal() bool { | ||||
| 	return true | ||||
| } | ||||
|  | @ -1,3 +1,6 @@ | |||
| // +build darwin freebsd openbsd netbsd dragonfly
 | ||||
| // +build !appengine
 | ||||
| 
 | ||||
| package logrus | ||||
| 
 | ||||
| import "syscall" | ||||
|  | @ -1,12 +0,0 @@ | |||
| // Based on ssh/terminal:
 | ||||
| // Copyright 2013 The Go Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package logrus | ||||
| 
 | ||||
| import "syscall" | ||||
| 
 | ||||
| const ioctlReadTermios = syscall.TIOCGETA | ||||
| 
 | ||||
| type Termios syscall.Termios | ||||
|  | @ -1,20 +0,0 @@ | |||
| /* | ||||
|   Go 1.2 doesn't include Termios for FreeBSD. This should be added in 1.3 and this could be merged with terminal_darwin. | ||||
| */ | ||||
| package logrus | ||||
| 
 | ||||
| import ( | ||||
| 	"syscall" | ||||
| ) | ||||
| 
 | ||||
| const ioctlReadTermios = syscall.TIOCGETA | ||||
| 
 | ||||
| type Termios struct { | ||||
| 	Iflag  uint32 | ||||
| 	Oflag  uint32 | ||||
| 	Cflag  uint32 | ||||
| 	Lflag  uint32 | ||||
| 	Cc     [20]uint8 | ||||
| 	Ispeed uint32 | ||||
| 	Ospeed uint32 | ||||
| } | ||||
|  | @ -3,6 +3,8 @@ | |||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| // +build !appengine
 | ||||
| 
 | ||||
| package logrus | ||||
| 
 | ||||
| import "syscall" | ||||
|  |  | |||
|  | @ -3,7 +3,8 @@ | |||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| // +build linux darwin freebsd openbsd
 | ||||
| // +build linux darwin freebsd openbsd netbsd dragonfly
 | ||||
| // +build !appengine
 | ||||
| 
 | ||||
| package logrus | ||||
| 
 | ||||
|  | @ -12,9 +13,9 @@ import ( | |||
| 	"unsafe" | ||||
| ) | ||||
| 
 | ||||
| // IsTerminal returns true if the given file descriptor is a terminal.
 | ||||
| // IsTerminal returns true if stderr's file descriptor is a terminal.
 | ||||
| func IsTerminal() bool { | ||||
| 	fd := syscall.Stdout | ||||
| 	fd := syscall.Stderr | ||||
| 	var termios Termios | ||||
| 	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) | ||||
| 	return err == 0 | ||||
|  |  | |||
|  | @ -0,0 +1,15 @@ | |||
| // +build solaris,!appengine
 | ||||
| 
 | ||||
| package logrus | ||||
| 
 | ||||
| import ( | ||||
| 	"os" | ||||
| 
 | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
| 
 | ||||
| // IsTerminal returns true if the given file descriptor is a terminal.
 | ||||
| func IsTerminal() bool { | ||||
| 	_, err := unix.IoctlGetTermios(int(os.Stdout.Fd()), unix.TCGETA) | ||||
| 	return err == nil | ||||
| } | ||||
|  | @ -3,7 +3,7 @@ | |||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| // +build windows
 | ||||
| // +build windows,!appengine
 | ||||
| 
 | ||||
| package logrus | ||||
| 
 | ||||
|  | @ -18,9 +18,9 @@ var ( | |||
| 	procGetConsoleMode = kernel32.NewProc("GetConsoleMode") | ||||
| ) | ||||
| 
 | ||||
| // IsTerminal returns true if the given file descriptor is a terminal.
 | ||||
| // IsTerminal returns true if stderr's file descriptor is a terminal.
 | ||||
| func IsTerminal() bool { | ||||
| 	fd := syscall.Stdout | ||||
| 	fd := syscall.Stderr | ||||
| 	var st uint32 | ||||
| 	r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) | ||||
| 	return r != 0 && e == 0 | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ package logrus | |||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"runtime" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | @ -56,6 +57,7 @@ type TextFormatter struct { | |||
| } | ||||
| 
 | ||||
| func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { | ||||
| 	var b *bytes.Buffer | ||||
| 	var keys []string = make([]string, 0, len(entry.Data)) | ||||
| 	for k := range entry.Data { | ||||
| 		keys = append(keys, k) | ||||
|  | @ -64,24 +66,31 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { | |||
| 	if !f.DisableSorting { | ||||
| 		sort.Strings(keys) | ||||
| 	} | ||||
| 
 | ||||
| 	b := &bytes.Buffer{} | ||||
| 	if entry.Buffer != nil { | ||||
| 		b = entry.Buffer | ||||
| 	} else { | ||||
| 		b = &bytes.Buffer{} | ||||
| 	} | ||||
| 
 | ||||
| 	prefixFieldClashes(entry.Data) | ||||
| 
 | ||||
| 	isColored := (f.ForceColors || isTerminal) && !f.DisableColors | ||||
| 	isColorTerminal := isTerminal && (runtime.GOOS != "windows") | ||||
| 	isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors | ||||
| 
 | ||||
| 	if f.TimestampFormat == "" { | ||||
| 		f.TimestampFormat = DefaultTimestampFormat | ||||
| 	timestampFormat := f.TimestampFormat | ||||
| 	if timestampFormat == "" { | ||||
| 		timestampFormat = DefaultTimestampFormat | ||||
| 	} | ||||
| 	if isColored { | ||||
| 		f.printColored(b, entry, keys) | ||||
| 		f.printColored(b, entry, keys, timestampFormat) | ||||
| 	} else { | ||||
| 		if !f.DisableTimestamp { | ||||
| 			f.appendKeyValue(b, "time", entry.Time.Format(f.TimestampFormat)) | ||||
| 			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]) | ||||
| 		} | ||||
|  | @ -91,7 +100,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { | |||
| 	return b.Bytes(), nil | ||||
| } | ||||
| 
 | ||||
| func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string) { | ||||
| func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) { | ||||
| 	var levelColor int | ||||
| 	switch entry.Level { | ||||
| 	case DebugLevel: | ||||
|  | @ -109,11 +118,12 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin | |||
| 	if !f.FullTimestamp { | ||||
| 		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message) | ||||
| 	} else { | ||||
| 		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(f.TimestampFormat), entry.Message) | ||||
| 		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message) | ||||
| 	} | ||||
| 	for _, k := range keys { | ||||
| 		v := entry.Data[k] | ||||
| 		fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%v", levelColor, k, v) | ||||
| 		fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k) | ||||
| 		f.appendValue(b, v) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -123,27 +133,36 @@ func needsQuoting(text string) bool { | |||
| 			(ch >= 'A' && ch <= 'Z') || | ||||
| 			(ch >= '0' && ch <= '9') || | ||||
| 			ch == '-' || ch == '.') { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key, value interface{}) { | ||||
| 	switch value.(type) { | ||||
| func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) { | ||||
| 
 | ||||
| 	b.WriteString(key) | ||||
| 	b.WriteByte('=') | ||||
| 	f.appendValue(b, value) | ||||
| 	b.WriteByte(' ') | ||||
| } | ||||
| 
 | ||||
| func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { | ||||
| 	switch value := value.(type) { | ||||
| 	case string: | ||||
| 		if needsQuoting(value.(string)) { | ||||
| 			fmt.Fprintf(b, "%v=%s ", key, value) | ||||
| 		if !needsQuoting(value) { | ||||
| 			b.WriteString(value) | ||||
| 		} else { | ||||
| 			fmt.Fprintf(b, "%v=%q ", key, value) | ||||
| 			fmt.Fprintf(b, "%q", value) | ||||
| 		} | ||||
| 	case error: | ||||
| 		if needsQuoting(value.(error).Error()) { | ||||
| 			fmt.Fprintf(b, "%v=%s ", key, value) | ||||
| 		errmsg := value.Error() | ||||
| 		if !needsQuoting(errmsg) { | ||||
| 			b.WriteString(errmsg) | ||||
| 		} else { | ||||
| 			fmt.Fprintf(b, "%v=%q ", key, value) | ||||
| 			fmt.Fprintf(b, "%q", errmsg) | ||||
| 		} | ||||
| 	default: | ||||
| 		fmt.Fprintf(b, "%v=%v ", key, value) | ||||
| 		fmt.Fprint(b, value) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -7,18 +7,40 @@ import ( | |||
| ) | ||||
| 
 | ||||
| func (logger *Logger) Writer() *io.PipeWriter { | ||||
| 	return logger.WriterLevel(InfoLevel) | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) WriterLevel(level Level) *io.PipeWriter { | ||||
| 	reader, writer := io.Pipe() | ||||
| 
 | ||||
| 	go logger.writerScanner(reader) | ||||
| 	var printFunc func(args ...interface{}) | ||||
| 	switch level { | ||||
| 	case DebugLevel: | ||||
| 		printFunc = logger.Debug | ||||
| 	case InfoLevel: | ||||
| 		printFunc = logger.Info | ||||
| 	case WarnLevel: | ||||
| 		printFunc = logger.Warn | ||||
| 	case ErrorLevel: | ||||
| 		printFunc = logger.Error | ||||
| 	case FatalLevel: | ||||
| 		printFunc = logger.Fatal | ||||
| 	case PanicLevel: | ||||
| 		printFunc = logger.Panic | ||||
| 	default: | ||||
| 		printFunc = logger.Print | ||||
| 	} | ||||
| 
 | ||||
| 	go logger.writerScanner(reader, printFunc) | ||||
| 	runtime.SetFinalizer(writer, writerFinalizer) | ||||
| 
 | ||||
| 	return writer | ||||
| } | ||||
| 
 | ||||
| func (logger *Logger) writerScanner(reader *io.PipeReader) { | ||||
| func (logger *Logger) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) { | ||||
| 	scanner := bufio.NewScanner(reader) | ||||
| 	for scanner.Scan() { | ||||
| 		logger.Print(scanner.Text()) | ||||
| 		printFunc(scanner.Text()) | ||||
| 	} | ||||
| 	if err := scanner.Err(); err != nil { | ||||
| 		logger.Errorf("Error while reading from Writer: %s", err) | ||||
|  |  | |||
|  | @ -0,0 +1,21 @@ | |||
| The MIT License (MIT) | ||||
| 
 | ||||
| Copyright (c) 2016 Boaz Shuster | ||||
| 
 | ||||
| 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. | ||||
|  | @ -0,0 +1,129 @@ | |||
| package logrus_logstash | ||||
| 
 | ||||
| import ( | ||||
| 	"net" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/Sirupsen/logrus" | ||||
| ) | ||||
| 
 | ||||
| // Hook represents a connection to a Logstash instance
 | ||||
| type Hook struct { | ||||
| 	conn             net.Conn | ||||
| 	appName          string | ||||
| 	alwaysSentFields logrus.Fields | ||||
| 	hookOnlyPrefix   string | ||||
| } | ||||
| 
 | ||||
| // NewHook creates a new hook to a Logstash instance, which listens on
 | ||||
| // `protocol`://`address`.
 | ||||
| func NewHook(protocol, address, appName string) (*Hook, error) { | ||||
| 	return NewHookWithFields(protocol, address, appName, make(logrus.Fields)) | ||||
| } | ||||
| 
 | ||||
| // NewHookWithConn creates a new hook to a Logstash instance, using the supplied connection
 | ||||
| func NewHookWithConn(conn net.Conn, appName string) (*Hook, error) { | ||||
| 	return NewHookWithFieldsAndConn(conn, appName, make(logrus.Fields)) | ||||
| } | ||||
| 
 | ||||
| // NewHookWithFields creates a new hook to a Logstash instance, which listens on
 | ||||
| // `protocol`://`address`. alwaysSentFields will be sent with every log entry.
 | ||||
| func NewHookWithFields(protocol, address, appName string, alwaysSentFields logrus.Fields) (*Hook, error) { | ||||
| 	return NewHookWithFieldsAndPrefix(protocol, address, appName, alwaysSentFields, "") | ||||
| } | ||||
| 
 | ||||
| // NewHookWithFieldsAndPrefix creates a new hook to a Logstash instance, which listens on
 | ||||
| // `protocol`://`address`. alwaysSentFields will be sent with every log entry. prefix is used to select fields to filter
 | ||||
| func NewHookWithFieldsAndPrefix(protocol, address, appName string, alwaysSentFields logrus.Fields, prefix string) (*Hook, error) { | ||||
| 	conn, err := net.Dial(protocol, address) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return NewHookWithFieldsAndConnAndPrefix(conn, appName, alwaysSentFields, prefix) | ||||
| } | ||||
| 
 | ||||
| // NewHookWithFieldsAndConn creates a new hook to a Logstash instance using the supplied connection
 | ||||
| func NewHookWithFieldsAndConn(conn net.Conn, appName string, alwaysSentFields logrus.Fields) (*Hook, error) { | ||||
| 	return NewHookWithFieldsAndConnAndPrefix(conn, appName, alwaysSentFields, "") | ||||
| } | ||||
| 
 | ||||
| //NewHookWithFieldsAndConnAndPrefix creates a new hook to a Logstash instance using the suppolied connection and prefix
 | ||||
| func NewHookWithFieldsAndConnAndPrefix(conn net.Conn, appName string, alwaysSentFields logrus.Fields, prefix string) (*Hook, error) { | ||||
| 	return &Hook{conn: conn, appName: appName, alwaysSentFields: alwaysSentFields, hookOnlyPrefix: prefix}, nil | ||||
| } | ||||
| 
 | ||||
| //NewFilterHook makes a new hook which does not forward to logstash, but simply enforces the prefix rules
 | ||||
| func NewFilterHook() *Hook { | ||||
| 	return NewFilterHookWithPrefix("") | ||||
| } | ||||
| 
 | ||||
| //NewFilterHookWithPrefix make a new hook which does not forward to logstash, but simply enforces the specified prefix
 | ||||
| func NewFilterHookWithPrefix(prefix string) *Hook { | ||||
| 	return &Hook{conn: nil, appName: "", alwaysSentFields: make(logrus.Fields), hookOnlyPrefix: prefix} | ||||
| } | ||||
| 
 | ||||
| func (h *Hook) filterHookOnly(entry *logrus.Entry) { | ||||
| 	if h.hookOnlyPrefix != "" { | ||||
| 		for key := range entry.Data { | ||||
| 			if strings.HasPrefix(key, h.hookOnlyPrefix) { | ||||
| 				delete(entry.Data, key) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| //WithPrefix sets a prefix filter to use in all subsequent logging
 | ||||
| func (h *Hook) WithPrefix(prefix string) { | ||||
| 	h.hookOnlyPrefix = prefix | ||||
| } | ||||
| 
 | ||||
| func (h *Hook) WithField(key string, value interface{}) { | ||||
| 	h.alwaysSentFields[key] = value | ||||
| } | ||||
| 
 | ||||
| func (h *Hook) WithFields(fields logrus.Fields) { | ||||
| 	//Add all the new fields to the 'alwaysSentFields', possibly overwriting exising fields
 | ||||
| 	for key, value := range fields { | ||||
| 		h.alwaysSentFields[key] = value | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (h *Hook) Fire(entry *logrus.Entry) error { | ||||
| 	//make sure we always clear the hookonly fields from the entry
 | ||||
| 	defer h.filterHookOnly(entry) | ||||
| 
 | ||||
| 	// Add in the alwaysSentFields. We don't override fields that are already set.
 | ||||
| 	for k, v := range h.alwaysSentFields { | ||||
| 		if _, inMap := entry.Data[k]; !inMap { | ||||
| 			entry.Data[k] = v | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	//For a filteringHook, stop here
 | ||||
| 	if h.conn == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	formatter := LogstashFormatter{Type: h.appName} | ||||
| 
 | ||||
| 	dataBytes, err := formatter.FormatWithPrefix(entry, h.hookOnlyPrefix) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if _, err = h.conn.Write(dataBytes); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (h *Hook) Levels() []logrus.Level { | ||||
| 	return []logrus.Level{ | ||||
| 		logrus.PanicLevel, | ||||
| 		logrus.FatalLevel, | ||||
| 		logrus.ErrorLevel, | ||||
| 		logrus.WarnLevel, | ||||
| 		logrus.InfoLevel, | ||||
| 		logrus.DebugLevel, | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										81
									
								
								vendor/github.com/bshuster-repo/logrus-logstash-hook/logstash_formatter.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										81
									
								
								vendor/github.com/bshuster-repo/logrus-logstash-hook/logstash_formatter.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							|  | @ -0,0 +1,81 @@ | |||
| package logrus_logstash | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/Sirupsen/logrus" | ||||
| ) | ||||
| 
 | ||||
| // Formatter generates json in logstash format.
 | ||||
| // Logstash site: http://logstash.net/
 | ||||
| type LogstashFormatter struct { | ||||
| 	Type string // if not empty use for logstash type field.
 | ||||
| 
 | ||||
| 	// TimestampFormat sets the format used for timestamps.
 | ||||
| 	TimestampFormat string | ||||
| } | ||||
| 
 | ||||
| func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) { | ||||
| 	return f.FormatWithPrefix(entry, "") | ||||
| } | ||||
| 
 | ||||
| func (f *LogstashFormatter) FormatWithPrefix(entry *logrus.Entry, prefix string) ([]byte, error) { | ||||
| 	fields := make(logrus.Fields) | ||||
| 	for k, v := range entry.Data { | ||||
| 		//remvove the prefix when sending the fields to logstash
 | ||||
| 		if prefix != "" && strings.HasPrefix(k, prefix) { | ||||
| 			k = strings.TrimPrefix(k, prefix) | ||||
| 		} | ||||
| 
 | ||||
| 		switch v := v.(type) { | ||||
| 		case error: | ||||
| 			// Otherwise errors are ignored by `encoding/json`
 | ||||
| 			// https://github.com/Sirupsen/logrus/issues/377
 | ||||
| 			fields[k] = v.Error() | ||||
| 		default: | ||||
| 			fields[k] = v | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fields["@version"] = "1" | ||||
| 
 | ||||
| 	timeStampFormat := f.TimestampFormat | ||||
| 
 | ||||
| 	if timeStampFormat == "" { | ||||
| 		//timeStampFormat = logrus.DefaultTimestampFormat
 | ||||
| 		timeStampFormat = "2006-01-02 15:04:05.000" | ||||
| 	} | ||||
| 
 | ||||
| 	fields["@timestamp"] = entry.Time.Format(timeStampFormat) | ||||
| 
 | ||||
| 	// set message field
 | ||||
| 	v, ok := entry.Data["message"] | ||||
| 	if ok { | ||||
| 		fields["fields.message"] = v | ||||
| 	} | ||||
| 	fields["message"] = entry.Message | ||||
| 
 | ||||
| 	// set level field
 | ||||
| 	v, ok = entry.Data["level"] | ||||
| 	if ok { | ||||
| 		fields["fields.level"] = v | ||||
| 	} | ||||
| 	fields["level"] = entry.Level.String() | ||||
| 
 | ||||
| 	// set type field
 | ||||
| 	if f.Type != "" { | ||||
| 		v, ok = entry.Data["type"] | ||||
| 		if ok { | ||||
| 			fields["fields.type"] = v | ||||
| 		} | ||||
| 		fields["type"] = f.Type | ||||
| 	} | ||||
| 
 | ||||
| 	serialized, err := json.Marshal(fields) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) | ||||
| 	} | ||||
| 	return append(serialized, '\n'), nil | ||||
| } | ||||
		Loading…
	
		Reference in New Issue