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" | 	"rsc.io/letsencrypt" | ||||||
| 
 | 
 | ||||||
| 	log "github.com/Sirupsen/logrus" | 	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/bugsnag/bugsnag-go" | ||||||
| 	"github.com/docker/distribution/configuration" | 	"github.com/docker/distribution/configuration" | ||||||
| 	"github.com/docker/distribution/context" | 	"github.com/docker/distribution/context" | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| github.com/Azure/azure-sdk-for-go/storage 0b5fe2abe0271ba07049eacaa65922d67c319543 | 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/aws/aws-sdk-go 90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6 | ||||||
|  | github.com/bshuster-repo/logrus-logstash-hook 5f729f2fb50a301153cae84ff5c58981d51c095a | ||||||
| github.com/bugsnag/bugsnag-go b1d153021fcd90ca3f080db36bec96dc690fb274 | github.com/bugsnag/bugsnag-go b1d153021fcd90ca3f080db36bec96dc690fb274 | ||||||
| github.com/bugsnag/osext 0dd3f918b21bec95ace9dc86c7e70266cfc5c702 | github.com/bugsnag/osext 0dd3f918b21bec95ace9dc86c7e70266cfc5c702 | ||||||
| github.com/bugsnag/panicwrap e2c28503fcd0675329da73bf48b33404db873782 | 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 ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" |  | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"sync" | ||||||
| 	"time" | 	"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
 | // 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,
 | // 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
 | // 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 passed to Debug, Info, Warn, Error, Fatal or Panic
 | ||||||
| 	Message string | 	Message string | ||||||
|  | 
 | ||||||
|  | 	// When formatter is called in entry.log(), an Buffer may be set to entry
 | ||||||
|  | 	Buffer *bytes.Buffer | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func NewEntry(logger *Logger) *Entry { | 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
 | // Returns the string representation from the reader and ultimately the
 | ||||||
| // formatter.
 | // formatter.
 | ||||||
| func (entry *Entry) String() (string, error) { | func (entry *Entry) String() (string, error) { | ||||||
| 	reader, err := entry.Reader() | 	serialized, err := entry.Logger.Formatter.Format(entry) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "", err | 		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.
 | // 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.
 | // Add a map of fields to the Entry.
 | ||||||
| func (entry *Entry) WithFields(fields Fields) *Entry { | func (entry *Entry) WithFields(fields Fields) *Entry { | ||||||
| 	data := Fields{} | 	data := make(Fields, len(entry.Data)+len(fields)) | ||||||
| 	for k, v := range entry.Data { | 	for k, v := range entry.Data { | ||||||
| 		data[k] = v | 		data[k] = v | ||||||
| 	} | 	} | ||||||
|  | @ -70,37 +85,43 @@ func (entry *Entry) WithFields(fields Fields) *Entry { | ||||||
| 	return &Entry{Logger: entry.Logger, Data: data} | 	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.Time = time.Now() | ||||||
| 	entry.Level = level | 	entry.Level = level | ||||||
| 	entry.Message = msg | 	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() | 		entry.Logger.mu.Lock() | ||||||
| 		fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) | 		fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) | ||||||
| 		entry.Logger.mu.Unlock() | 		entry.Logger.mu.Unlock() | ||||||
| 	} | 	} | ||||||
| 
 | 	buffer = bufferPool.Get().(*bytes.Buffer) | ||||||
| 	reader, err := entry.Reader() | 	buffer.Reset() | ||||||
|  | 	defer bufferPool.Put(buffer) | ||||||
|  | 	entry.Buffer = buffer | ||||||
|  | 	serialized, err := entry.Logger.Formatter.Format(&entry) | ||||||
|  | 	entry.Buffer = nil | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		entry.Logger.mu.Lock() | 		entry.Logger.mu.Lock() | ||||||
| 		fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) | 		fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) | ||||||
| 		entry.Logger.mu.Unlock() | 		entry.Logger.mu.Unlock() | ||||||
| 	} | 	} else { | ||||||
| 
 | 		entry.Logger.mu.Lock() | ||||||
| 	entry.Logger.mu.Lock() | 		_, err = entry.Logger.Out.Write(serialized) | ||||||
| 	defer entry.Logger.mu.Unlock() | 		if err != nil { | ||||||
| 
 | 			fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) | ||||||
| 	_, err = io.Copy(entry.Logger.Out, reader) | 		} | ||||||
| 	if err != nil { | 		entry.Logger.mu.Unlock() | ||||||
| 		fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// To avoid Entry#log() returning a value that only would make sense for
 | 	// 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
 | 	// panic() to use in Entry#Panic(), we avoid the allocation by checking
 | ||||||
| 	// directly here.
 | 	// directly here.
 | ||||||
| 	if level <= PanicLevel { | 	if level <= PanicLevel { | ||||||
| 		panic(entry) | 		panic(&entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -140,7 +161,7 @@ func (entry *Entry) Fatal(args ...interface{}) { | ||||||
| 	if entry.Logger.Level >= FatalLevel { | 	if entry.Logger.Level >= FatalLevel { | ||||||
| 		entry.log(FatalLevel, fmt.Sprint(args...)) | 		entry.log(FatalLevel, fmt.Sprint(args...)) | ||||||
| 	} | 	} | ||||||
| 	os.Exit(1) | 	Exit(1) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (entry *Entry) Panic(args ...interface{}) { | func (entry *Entry) Panic(args ...interface{}) { | ||||||
|  | @ -188,6 +209,7 @@ func (entry *Entry) Fatalf(format string, args ...interface{}) { | ||||||
| 	if entry.Logger.Level >= FatalLevel { | 	if entry.Logger.Level >= FatalLevel { | ||||||
| 		entry.Fatal(fmt.Sprintf(format, args...)) | 		entry.Fatal(fmt.Sprintf(format, args...)) | ||||||
| 	} | 	} | ||||||
|  | 	Exit(1) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (entry *Entry) Panicf(format string, args ...interface{}) { | func (entry *Entry) Panicf(format string, args ...interface{}) { | ||||||
|  | @ -234,6 +256,7 @@ func (entry *Entry) Fatalln(args ...interface{}) { | ||||||
| 	if entry.Logger.Level >= FatalLevel { | 	if entry.Logger.Level >= FatalLevel { | ||||||
| 		entry.Fatal(entry.sprintlnn(args...)) | 		entry.Fatal(entry.sprintlnn(args...)) | ||||||
| 	} | 	} | ||||||
|  | 	Exit(1) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (entry *Entry) Panicln(args ...interface{}) { | func (entry *Entry) Panicln(args ...interface{}) { | ||||||
|  |  | ||||||
|  | @ -48,6 +48,11 @@ func AddHook(hook Hook) { | ||||||
| 	std.Hooks.Add(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
 | // WithField creates an entry from the standard logger and adds a field to
 | ||||||
| // it. If you want multiple fields, use `WithFields`.
 | // 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
 | // 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.
 | // avoid code duplication between the two default formatters.
 | ||||||
| func prefixFieldClashes(data Fields) { | func prefixFieldClashes(data Fields) { | ||||||
| 	_, ok := data["time"] | 	if t, ok := data["time"]; ok { | ||||||
| 	if ok { | 		data["fields.time"] = t | ||||||
| 		data["fields.time"] = data["time"] |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	_, ok = data["msg"] | 	if m, ok := data["msg"]; ok { | ||||||
| 	if ok { | 		data["fields.msg"] = m | ||||||
| 		data["fields.msg"] = data["msg"] |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	_, ok = data["level"] | 	if l, ok := data["level"]; ok { | ||||||
| 	if ok { | 		data["fields.level"] = l | ||||||
| 		data["fields.level"] = data["level"] |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -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.
 | // 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
 | // Add a hook to an instance of logger. This is called with
 | ||||||
| // `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface.
 | // `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() { | 	for _, level := range hook.Levels() { | ||||||
| 		hooks[level] = append(hooks[level], hook) | 		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
 | // Fire all the hooks for the passed level. Used by `entry.log` to fire
 | ||||||
| // appropriate hooks for a log entry.
 | // 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] { | 	for _, hook := range hooks[level] { | ||||||
| 		if err := hook.Fire(entry); err != nil { | 		if err := hook.Fire(entry); err != nil { | ||||||
| 			return err | 			return err | ||||||
|  |  | ||||||
|  | @ -24,11 +24,12 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { | ||||||
| 	} | 	} | ||||||
| 	prefixFieldClashes(data) | 	prefixFieldClashes(data) | ||||||
| 
 | 
 | ||||||
| 	if f.TimestampFormat == "" { | 	timestampFormat := f.TimestampFormat | ||||||
| 		f.TimestampFormat = DefaultTimestampFormat | 	if timestampFormat == "" { | ||||||
|  | 		timestampFormat = DefaultTimestampFormat | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	data["time"] = entry.Time.Format(f.TimestampFormat) | 	data["time"] = entry.Time.Format(timestampFormat) | ||||||
| 	data["msg"] = entry.Message | 	data["msg"] = entry.Message | ||||||
| 	data["level"] = entry.Level.String() | 	data["level"] = entry.Level.String() | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -8,13 +8,13 @@ import ( | ||||||
| 
 | 
 | ||||||
| type Logger struct { | type Logger struct { | ||||||
| 	// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
 | 	// 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.
 | 	// something more adventorous, such as logging to Kafka.
 | ||||||
| 	Out io.Writer | 	Out io.Writer | ||||||
| 	// Hooks for the logger instance. These allow firing events based on logging
 | 	// 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
 | 	// levels and log entries. For example, to send errors to an error tracking
 | ||||||
| 	// service, log to StatsD or dump the core on fatal errors.
 | 	// 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
 | 	// All log entries pass through the formatter before logged to Out. The
 | ||||||
| 	// included formatters are `TextFormatter` and `JSONFormatter` for which
 | 	// included formatters are `TextFormatter` and `JSONFormatter` for which
 | ||||||
| 	// TextFormatter is the default. In development (when a TTY is attached) it
 | 	// 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
 | 	// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
 | ||||||
| 	// logged. `logrus.Debug` is useful in
 | 	// logged. `logrus.Debug` is useful in
 | ||||||
| 	Level Level | 	Level Level | ||||||
| 	// Used to sync writing to the log.
 | 	// Used to sync writing to the log. Locking is enabled by Default
 | ||||||
| 	mu sync.Mutex | 	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`,
 | // Creates a new logger. Configuration should be set by changing `Formatter`,
 | ||||||
|  | @ -37,167 +60,249 @@ type Logger struct { | ||||||
| //    var log = &Logger{
 | //    var log = &Logger{
 | ||||||
| //      Out: os.Stderr,
 | //      Out: os.Stderr,
 | ||||||
| //      Formatter: new(JSONFormatter),
 | //      Formatter: new(JSONFormatter),
 | ||||||
| //      Hooks: make(levelHooks),
 | //      Hooks: make(LevelHooks),
 | ||||||
| //      Level: logrus.DebugLevel,
 | //      Level: logrus.DebugLevel,
 | ||||||
| //    }
 | //    }
 | ||||||
| //
 | //
 | ||||||
| // It's recommended to make this a global instance called `log`.
 | // It's recommended to make this a global instance called `log`.
 | ||||||
| func New() *Logger { | func New() *Logger { | ||||||
| 	return &Logger{ | 	return &Logger{ | ||||||
| 		Out:       os.Stdout, | 		Out:       os.Stderr, | ||||||
| 		Formatter: new(TextFormatter), | 		Formatter: new(TextFormatter), | ||||||
| 		Hooks:     make(levelHooks), | 		Hooks:     make(LevelHooks), | ||||||
| 		Level:     InfoLevel, | 		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.
 | // 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 { | 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
 | // Adds a struct of fields to the log entry. All it does is call `WithField` for
 | ||||||
| // each `Field`.
 | // each `Field`.
 | ||||||
| func (logger *Logger) WithFields(fields Fields) *Entry { | 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{}) { | func (logger *Logger) Debugf(format string, args ...interface{}) { | ||||||
| 	if logger.Level >= DebugLevel { | 	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{}) { | func (logger *Logger) Infof(format string, args ...interface{}) { | ||||||
| 	if logger.Level >= InfoLevel { | 	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{}) { | 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{}) { | func (logger *Logger) Warnf(format string, args ...interface{}) { | ||||||
| 	if logger.Level >= WarnLevel { | 	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{}) { | func (logger *Logger) Warningf(format string, args ...interface{}) { | ||||||
| 	if logger.Level >= WarnLevel { | 	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{}) { | func (logger *Logger) Errorf(format string, args ...interface{}) { | ||||||
| 	if logger.Level >= ErrorLevel { | 	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{}) { | func (logger *Logger) Fatalf(format string, args ...interface{}) { | ||||||
| 	if logger.Level >= FatalLevel { | 	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{}) { | func (logger *Logger) Panicf(format string, args ...interface{}) { | ||||||
| 	if logger.Level >= PanicLevel { | 	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{}) { | func (logger *Logger) Debug(args ...interface{}) { | ||||||
| 	if logger.Level >= DebugLevel { | 	if logger.Level >= DebugLevel { | ||||||
| 		NewEntry(logger).Debug(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Debug(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (logger *Logger) Info(args ...interface{}) { | func (logger *Logger) Info(args ...interface{}) { | ||||||
| 	if logger.Level >= InfoLevel { | 	if logger.Level >= InfoLevel { | ||||||
| 		NewEntry(logger).Info(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Info(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (logger *Logger) Print(args ...interface{}) { | 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{}) { | func (logger *Logger) Warn(args ...interface{}) { | ||||||
| 	if logger.Level >= WarnLevel { | 	if logger.Level >= WarnLevel { | ||||||
| 		NewEntry(logger).Warn(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Warn(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (logger *Logger) Warning(args ...interface{}) { | func (logger *Logger) Warning(args ...interface{}) { | ||||||
| 	if logger.Level >= WarnLevel { | 	if logger.Level >= WarnLevel { | ||||||
| 		NewEntry(logger).Warn(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Warn(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (logger *Logger) Error(args ...interface{}) { | func (logger *Logger) Error(args ...interface{}) { | ||||||
| 	if logger.Level >= ErrorLevel { | 	if logger.Level >= ErrorLevel { | ||||||
| 		NewEntry(logger).Error(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Error(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (logger *Logger) Fatal(args ...interface{}) { | func (logger *Logger) Fatal(args ...interface{}) { | ||||||
| 	if logger.Level >= FatalLevel { | 	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{}) { | func (logger *Logger) Panic(args ...interface{}) { | ||||||
| 	if logger.Level >= PanicLevel { | 	if logger.Level >= PanicLevel { | ||||||
| 		NewEntry(logger).Panic(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Panic(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (logger *Logger) Debugln(args ...interface{}) { | func (logger *Logger) Debugln(args ...interface{}) { | ||||||
| 	if logger.Level >= DebugLevel { | 	if logger.Level >= DebugLevel { | ||||||
| 		NewEntry(logger).Debugln(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Debugln(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (logger *Logger) Infoln(args ...interface{}) { | func (logger *Logger) Infoln(args ...interface{}) { | ||||||
| 	if logger.Level >= InfoLevel { | 	if logger.Level >= InfoLevel { | ||||||
| 		NewEntry(logger).Infoln(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Infoln(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (logger *Logger) Println(args ...interface{}) { | 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{}) { | func (logger *Logger) Warnln(args ...interface{}) { | ||||||
| 	if logger.Level >= WarnLevel { | 	if logger.Level >= WarnLevel { | ||||||
| 		NewEntry(logger).Warnln(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Warnln(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (logger *Logger) Warningln(args ...interface{}) { | func (logger *Logger) Warningln(args ...interface{}) { | ||||||
| 	if logger.Level >= WarnLevel { | 	if logger.Level >= WarnLevel { | ||||||
| 		NewEntry(logger).Warnln(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Warnln(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (logger *Logger) Errorln(args ...interface{}) { | func (logger *Logger) Errorln(args ...interface{}) { | ||||||
| 	if logger.Level >= ErrorLevel { | 	if logger.Level >= ErrorLevel { | ||||||
| 		NewEntry(logger).Errorln(args...) | 		entry := logger.newEntry() | ||||||
|  | 		entry.Errorln(args...) | ||||||
|  | 		logger.releaseEntry(entry) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (logger *Logger) Fatalln(args ...interface{}) { | func (logger *Logger) Fatalln(args ...interface{}) { | ||||||
| 	if logger.Level >= FatalLevel { | 	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{}) { | func (logger *Logger) Panicln(args ...interface{}) { | ||||||
| 	if logger.Level >= PanicLevel { | 	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 ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"log" | 	"log" | ||||||
|  | 	"strings" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Fields type, used to pass to `WithFields`.
 | // 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.
 | // ParseLevel takes a string level and returns the Logrus log level constant.
 | ||||||
| func ParseLevel(lvl string) (Level, error) { | func ParseLevel(lvl string) (Level, error) { | ||||||
| 	switch lvl { | 	switch strings.ToLower(lvl) { | ||||||
| 	case "panic": | 	case "panic": | ||||||
| 		return PanicLevel, nil | 		return PanicLevel, nil | ||||||
| 	case "fatal": | 	case "fatal": | ||||||
|  | @ -52,6 +53,16 @@ func ParseLevel(lvl string) (Level, error) { | ||||||
| 	return l, fmt.Errorf("not a valid logrus Level: %q", lvl) | 	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
 | // These are the different logging levels. You can set the logging level to log
 | ||||||
| // on your instance of logger, obtained with `logrus.New()`.
 | // on your instance of logger, obtained with `logrus.New()`.
 | ||||||
| const ( | const ( | ||||||
|  | @ -74,7 +85,11 @@ const ( | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Won't compile if StdLogger can't be realized by a log.Logger
 | // 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
 | // 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
 | // it'll accept a stdlib logger and a logrus logger. There's no standard
 | ||||||
|  | @ -92,3 +107,37 @@ type StdLogger interface { | ||||||
| 	Panicf(string, ...interface{}) | 	Panicf(string, ...interface{}) | ||||||
| 	Panicln(...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 | package logrus | ||||||
| 
 | 
 | ||||||
| import "syscall" | 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
 | // Use of this source code is governed by a BSD-style
 | ||||||
| // license that can be found in the LICENSE file.
 | // license that can be found in the LICENSE file.
 | ||||||
| 
 | 
 | ||||||
|  | // +build !appengine
 | ||||||
|  | 
 | ||||||
| package logrus | package logrus | ||||||
| 
 | 
 | ||||||
| import "syscall" | import "syscall" | ||||||
|  |  | ||||||
|  | @ -3,7 +3,8 @@ | ||||||
| // Use of this source code is governed by a BSD-style
 | // Use of this source code is governed by a BSD-style
 | ||||||
| // license that can be found in the LICENSE file.
 | // 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 | package logrus | ||||||
| 
 | 
 | ||||||
|  | @ -12,9 +13,9 @@ import ( | ||||||
| 	"unsafe" | 	"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 { | func IsTerminal() bool { | ||||||
| 	fd := syscall.Stdout | 	fd := syscall.Stderr | ||||||
| 	var termios Termios | 	var termios Termios | ||||||
| 	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) | 	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) | ||||||
| 	return err == 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
 | // Use of this source code is governed by a BSD-style
 | ||||||
| // license that can be found in the LICENSE file.
 | // license that can be found in the LICENSE file.
 | ||||||
| 
 | 
 | ||||||
| // +build windows
 | // +build windows,!appengine
 | ||||||
| 
 | 
 | ||||||
| package logrus | package logrus | ||||||
| 
 | 
 | ||||||
|  | @ -18,9 +18,9 @@ var ( | ||||||
| 	procGetConsoleMode = kernel32.NewProc("GetConsoleMode") | 	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 { | func IsTerminal() bool { | ||||||
| 	fd := syscall.Stdout | 	fd := syscall.Stderr | ||||||
| 	var st uint32 | 	var st uint32 | ||||||
| 	r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) | 	r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) | ||||||
| 	return r != 0 && e == 0 | 	return r != 0 && e == 0 | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ package logrus | ||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"runtime" | ||||||
| 	"sort" | 	"sort" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  | @ -56,6 +57,7 @@ type TextFormatter struct { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { | func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { | ||||||
|  | 	var b *bytes.Buffer | ||||||
| 	var keys []string = make([]string, 0, len(entry.Data)) | 	var keys []string = make([]string, 0, len(entry.Data)) | ||||||
| 	for k := range entry.Data { | 	for k := range entry.Data { | ||||||
| 		keys = append(keys, k) | 		keys = append(keys, k) | ||||||
|  | @ -64,24 +66,31 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { | ||||||
| 	if !f.DisableSorting { | 	if !f.DisableSorting { | ||||||
| 		sort.Strings(keys) | 		sort.Strings(keys) | ||||||
| 	} | 	} | ||||||
| 
 | 	if entry.Buffer != nil { | ||||||
| 	b := &bytes.Buffer{} | 		b = entry.Buffer | ||||||
|  | 	} else { | ||||||
|  | 		b = &bytes.Buffer{} | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	prefixFieldClashes(entry.Data) | 	prefixFieldClashes(entry.Data) | ||||||
| 
 | 
 | ||||||
| 	isColored := (f.ForceColors || isTerminal) && !f.DisableColors | 	isColorTerminal := isTerminal && (runtime.GOOS != "windows") | ||||||
|  | 	isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors | ||||||
| 
 | 
 | ||||||
| 	if f.TimestampFormat == "" { | 	timestampFormat := f.TimestampFormat | ||||||
| 		f.TimestampFormat = DefaultTimestampFormat | 	if timestampFormat == "" { | ||||||
|  | 		timestampFormat = DefaultTimestampFormat | ||||||
| 	} | 	} | ||||||
| 	if isColored { | 	if isColored { | ||||||
| 		f.printColored(b, entry, keys) | 		f.printColored(b, entry, keys, timestampFormat) | ||||||
| 	} else { | 	} else { | ||||||
| 		if !f.DisableTimestamp { | 		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()) | 		f.appendKeyValue(b, "level", entry.Level.String()) | ||||||
| 		f.appendKeyValue(b, "msg", entry.Message) | 		if entry.Message != "" { | ||||||
|  | 			f.appendKeyValue(b, "msg", entry.Message) | ||||||
|  | 		} | ||||||
| 		for _, key := range keys { | 		for _, key := range keys { | ||||||
| 			f.appendKeyValue(b, key, entry.Data[key]) | 			f.appendKeyValue(b, key, entry.Data[key]) | ||||||
| 		} | 		} | ||||||
|  | @ -91,7 +100,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { | ||||||
| 	return b.Bytes(), nil | 	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 | 	var levelColor int | ||||||
| 	switch entry.Level { | 	switch entry.Level { | ||||||
| 	case DebugLevel: | 	case DebugLevel: | ||||||
|  | @ -109,11 +118,12 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin | ||||||
| 	if !f.FullTimestamp { | 	if !f.FullTimestamp { | ||||||
| 		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message) | 		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message) | ||||||
| 	} else { | 	} 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 { | 	for _, k := range keys { | ||||||
| 		v := entry.Data[k] | 		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 >= 'A' && ch <= 'Z') || | ||||||
| 			(ch >= '0' && ch <= '9') || | 			(ch >= '0' && ch <= '9') || | ||||||
| 			ch == '-' || ch == '.') { | 			ch == '-' || ch == '.') { | ||||||
| 			return false | 			return true | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return true | 	return false | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key, value interface{}) { | func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) { | ||||||
| 	switch value.(type) { | 
 | ||||||
|  | 	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: | 	case string: | ||||||
| 		if needsQuoting(value.(string)) { | 		if !needsQuoting(value) { | ||||||
| 			fmt.Fprintf(b, "%v=%s ", key, value) | 			b.WriteString(value) | ||||||
| 		} else { | 		} else { | ||||||
| 			fmt.Fprintf(b, "%v=%q ", key, value) | 			fmt.Fprintf(b, "%q", value) | ||||||
| 		} | 		} | ||||||
| 	case error: | 	case error: | ||||||
| 		if needsQuoting(value.(error).Error()) { | 		errmsg := value.Error() | ||||||
| 			fmt.Fprintf(b, "%v=%s ", key, value) | 		if !needsQuoting(errmsg) { | ||||||
|  | 			b.WriteString(errmsg) | ||||||
| 		} else { | 		} else { | ||||||
| 			fmt.Fprintf(b, "%v=%q ", key, value) | 			fmt.Fprintf(b, "%q", errmsg) | ||||||
| 		} | 		} | ||||||
| 	default: | 	default: | ||||||
| 		fmt.Fprintf(b, "%v=%v ", key, value) | 		fmt.Fprint(b, value) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -7,18 +7,40 @@ import ( | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func (logger *Logger) Writer() *io.PipeWriter { | func (logger *Logger) Writer() *io.PipeWriter { | ||||||
|  | 	return logger.WriterLevel(InfoLevel) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (logger *Logger) WriterLevel(level Level) *io.PipeWriter { | ||||||
| 	reader, writer := io.Pipe() | 	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) | 	runtime.SetFinalizer(writer, writerFinalizer) | ||||||
| 
 | 
 | ||||||
| 	return writer | 	return writer | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (logger *Logger) writerScanner(reader *io.PipeReader) { | func (logger *Logger) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) { | ||||||
| 	scanner := bufio.NewScanner(reader) | 	scanner := bufio.NewScanner(reader) | ||||||
| 	for scanner.Scan() { | 	for scanner.Scan() { | ||||||
| 		logger.Print(scanner.Text()) | 		printFunc(scanner.Text()) | ||||||
| 	} | 	} | ||||||
| 	if err := scanner.Err(); err != nil { | 	if err := scanner.Err(); err != nil { | ||||||
| 		logger.Errorf("Error while reading from Writer: %s", err) | 		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