summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/matterbridge/logrus-prefixed-formatter/formatter.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/matterbridge/logrus-prefixed-formatter/formatter.go')
-rw-r--r--vendor/github.com/matterbridge/logrus-prefixed-formatter/formatter.go382
1 files changed, 382 insertions, 0 deletions
diff --git a/vendor/github.com/matterbridge/logrus-prefixed-formatter/formatter.go b/vendor/github.com/matterbridge/logrus-prefixed-formatter/formatter.go
new file mode 100644
index 00000000..7cb807da
--- /dev/null
+++ b/vendor/github.com/matterbridge/logrus-prefixed-formatter/formatter.go
@@ -0,0 +1,382 @@
+package prefixed
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+ "regexp"
+ "sort"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/mgutz/ansi"
+ "github.com/sirupsen/logrus"
+ "golang.org/x/crypto/ssh/terminal"
+)
+
+const defaultTimestampFormat = time.RFC3339
+
+var (
+ baseTimestamp time.Time = time.Now()
+ defaultColorScheme *ColorScheme = &ColorScheme{
+ InfoLevelStyle: "green",
+ WarnLevelStyle: "yellow",
+ ErrorLevelStyle: "red",
+ FatalLevelStyle: "red",
+ PanicLevelStyle: "red",
+ DebugLevelStyle: "blue",
+ PrefixStyle: "cyan",
+ TimestampStyle: "black+h",
+ }
+ noColorsColorScheme *compiledColorScheme = &compiledColorScheme{
+ InfoLevelColor: ansi.ColorFunc(""),
+ WarnLevelColor: ansi.ColorFunc(""),
+ ErrorLevelColor: ansi.ColorFunc(""),
+ FatalLevelColor: ansi.ColorFunc(""),
+ PanicLevelColor: ansi.ColorFunc(""),
+ DebugLevelColor: ansi.ColorFunc(""),
+ PrefixColor: ansi.ColorFunc(""),
+ TimestampColor: ansi.ColorFunc(""),
+ }
+ defaultCompiledColorScheme *compiledColorScheme = compileColorScheme(defaultColorScheme)
+)
+
+func miniTS() int {
+ return int(time.Since(baseTimestamp) / time.Second)
+}
+
+type ColorScheme struct {
+ InfoLevelStyle string
+ WarnLevelStyle string
+ ErrorLevelStyle string
+ FatalLevelStyle string
+ PanicLevelStyle string
+ DebugLevelStyle string
+ PrefixStyle string
+ TimestampStyle string
+}
+
+type compiledColorScheme struct {
+ InfoLevelColor func(string) string
+ WarnLevelColor func(string) string
+ ErrorLevelColor func(string) string
+ FatalLevelColor func(string) string
+ PanicLevelColor func(string) string
+ DebugLevelColor func(string) string
+ PrefixColor func(string) string
+ TimestampColor func(string) string
+}
+
+type TextFormatter struct {
+ // Set to true to bypass checking for a TTY before outputting colors.
+ ForceColors bool
+
+ // Force disabling colors. For a TTY colors are enabled by default.
+ DisableColors bool
+
+ // Force formatted layout, even for non-TTY output.
+ ForceFormatting bool
+
+ // Disable timestamp logging. useful when output is redirected to logging
+ // system that already adds timestamps.
+ DisableTimestamp bool
+
+ // Disable the conversion of the log levels to uppercase
+ DisableUppercase bool
+
+ // Enable logging the full timestamp when a TTY is attached instead of just
+ // the time passed since beginning of execution.
+ FullTimestamp bool
+
+ // Timestamp format to use for display when a full timestamp is printed.
+ TimestampFormat string
+
+ // The fields are sorted by default for a consistent output. For applications
+ // that log extremely frequently and don't use the JSON formatter this may not
+ // be desired.
+ DisableSorting bool
+
+ // Wrap empty fields in quotes if true.
+ QuoteEmptyFields bool
+
+ // Can be set to the override the default quoting character "
+ // with something else. For example: ', or `.
+ QuoteCharacter string
+
+ // Pad msg field with spaces on the right for display.
+ // The value for this parameter will be the size of padding.
+ // Its default value is zero, which means no padding will be applied for msg.
+ SpacePadding int
+
+ // Pad prefix field with spaces on the right for display.
+ // The value for this parameter will be the size of padding.
+ // Its default value is zero, which means no padding will be applied for prefix.
+ PrefixPadding int
+
+ // Color scheme to use.
+ colorScheme *compiledColorScheme
+
+ // Whether the logger's out is to a terminal.
+ isTerminal bool
+
+ sync.Once
+}
+
+func getCompiledColor(main string, fallback string) func(string) string {
+ var style string
+ if main != "" {
+ style = main
+ } else {
+ style = fallback
+ }
+ return ansi.ColorFunc(style)
+}
+
+func compileColorScheme(s *ColorScheme) *compiledColorScheme {
+ return &compiledColorScheme{
+ InfoLevelColor: getCompiledColor(s.InfoLevelStyle, defaultColorScheme.InfoLevelStyle),
+ WarnLevelColor: getCompiledColor(s.WarnLevelStyle, defaultColorScheme.WarnLevelStyle),
+ ErrorLevelColor: getCompiledColor(s.ErrorLevelStyle, defaultColorScheme.ErrorLevelStyle),
+ FatalLevelColor: getCompiledColor(s.FatalLevelStyle, defaultColorScheme.FatalLevelStyle),
+ PanicLevelColor: getCompiledColor(s.PanicLevelStyle, defaultColorScheme.PanicLevelStyle),
+ DebugLevelColor: getCompiledColor(s.DebugLevelStyle, defaultColorScheme.DebugLevelStyle),
+ PrefixColor: getCompiledColor(s.PrefixStyle, defaultColorScheme.PrefixStyle),
+ TimestampColor: getCompiledColor(s.TimestampStyle, defaultColorScheme.TimestampStyle),
+ }
+}
+
+func (f *TextFormatter) init(entry *logrus.Entry) {
+ if len(f.QuoteCharacter) == 0 {
+ f.QuoteCharacter = "\""
+ }
+ if entry.Logger != nil {
+ f.isTerminal = f.checkIfTerminal(entry.Logger.Out)
+ }
+}
+
+func (f *TextFormatter) checkIfTerminal(w io.Writer) bool {
+ switch v := w.(type) {
+ case *os.File:
+ return terminal.IsTerminal(int(v.Fd()))
+ default:
+ return false
+ }
+}
+
+func (f *TextFormatter) SetColorScheme(colorScheme *ColorScheme) {
+ f.colorScheme = compileColorScheme(colorScheme)
+}
+
+func (f *TextFormatter) Format(entry *logrus.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)
+ }
+ lastKeyIdx := len(keys) - 1
+
+ if !f.DisableSorting {
+ sort.Strings(keys)
+ }
+ if entry.Buffer != nil {
+ b = entry.Buffer
+ } else {
+ b = &bytes.Buffer{}
+ }
+
+ prefixFieldClashes(entry.Data)
+
+ f.Do(func() { f.init(entry) })
+
+ isFormatted := f.ForceFormatting || f.isTerminal
+
+ timestampFormat := f.TimestampFormat
+ if timestampFormat == "" {
+ timestampFormat = defaultTimestampFormat
+ }
+ if isFormatted {
+ isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors
+ var colorScheme *compiledColorScheme
+ if isColored {
+ if f.colorScheme == nil {
+ colorScheme = defaultCompiledColorScheme
+ } else {
+ colorScheme = f.colorScheme
+ }
+ } else {
+ colorScheme = noColorsColorScheme
+ }
+ f.printColored(b, entry, keys, timestampFormat, colorScheme)
+ } else {
+ if !f.DisableTimestamp {
+ f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat), true)
+ }
+ f.appendKeyValue(b, "level", entry.Level.String(), true)
+ if entry.Message != "" {
+ f.appendKeyValue(b, "msg", entry.Message, lastKeyIdx >= 0)
+ }
+ for i, key := range keys {
+ f.appendKeyValue(b, key, entry.Data[key], lastKeyIdx != i)
+ }
+ }
+
+ b.WriteByte('\n')
+ return b.Bytes(), nil
+}
+
+func (f *TextFormatter) printColored(b *bytes.Buffer, entry *logrus.Entry, keys []string, timestampFormat string, colorScheme *compiledColorScheme) {
+ var levelColor func(string) string
+ var levelText string
+ switch entry.Level {
+ case logrus.InfoLevel:
+ levelColor = colorScheme.InfoLevelColor
+ case logrus.WarnLevel:
+ levelColor = colorScheme.WarnLevelColor
+ case logrus.ErrorLevel:
+ levelColor = colorScheme.ErrorLevelColor
+ case logrus.FatalLevel:
+ levelColor = colorScheme.FatalLevelColor
+ case logrus.PanicLevel:
+ levelColor = colorScheme.PanicLevelColor
+ default:
+ levelColor = colorScheme.DebugLevelColor
+ }
+
+ if entry.Level != logrus.WarnLevel {
+ levelText = entry.Level.String()
+ } else {
+ levelText = "warn"
+ }
+
+ if !f.DisableUppercase {
+ levelText = strings.ToUpper(levelText)
+ }
+
+ level := levelColor(fmt.Sprintf("%5s", levelText))
+ prefix := ""
+ message := entry.Message
+
+ adjustedPrefixPadding := f.PrefixPadding //compensate for ANSI color sequences
+
+ if prefixValue, ok := entry.Data["prefix"]; ok {
+ rawPrefixLength := len(prefixValue.(string))
+ prefix = colorScheme.PrefixColor(" " + prefixValue.(string) + ":")
+ adjustedPrefixPadding = f.PrefixPadding + (len(prefix) - rawPrefixLength - 1)
+ } else {
+ prefixValue, trimmedMsg := extractPrefix(entry.Message)
+ rawPrefixLength := len(prefixValue)
+ if len(prefixValue) > 0 {
+ prefix = colorScheme.PrefixColor(" " + prefixValue + ":")
+ message = trimmedMsg
+ }
+ adjustedPrefixPadding = f.PrefixPadding + (len(prefix) - rawPrefixLength - 1)
+ }
+
+ prefixFormat := "%s"
+ if f.PrefixPadding != 0 {
+ prefixFormat = fmt.Sprintf("%%-%ds", adjustedPrefixPadding)
+ }
+
+ messageFormat := "%s"
+ if f.SpacePadding != 0 {
+ messageFormat = fmt.Sprintf("%%-%ds", f.SpacePadding)
+ }
+
+ if f.DisableTimestamp {
+ fmt.Fprintf(b, "%s"+prefixFormat+" "+messageFormat, level, prefix, message)
+ } else {
+ var timestamp string
+ if !f.FullTimestamp {
+ timestamp = fmt.Sprintf("[%04d]", miniTS())
+ } else {
+ timestamp = fmt.Sprintf("[%s]", entry.Time.Format(timestampFormat))
+ }
+ fmt.Fprintf(b, "%s %s"+prefixFormat+" "+messageFormat, colorScheme.TimestampColor(timestamp), level, prefix, message)
+ }
+ for _, k := range keys {
+ if k != "prefix" {
+ v := entry.Data[k]
+ fmt.Fprintf(b, " %s=%+v", levelColor(k), v)
+ }
+ }
+}
+
+func (f *TextFormatter) needsQuoting(text string) bool {
+ if f.QuoteEmptyFields && len(text) == 0 {
+ return true
+ }
+ for _, ch := range text {
+ if !((ch >= 'a' && ch <= 'z') ||
+ (ch >= 'A' && ch <= 'Z') ||
+ (ch >= '0' && ch <= '9') ||
+ ch == '-' || ch == '.') {
+ return true
+ }
+ }
+ return false
+}
+
+func extractPrefix(msg string) (string, string) {
+ prefix := ""
+ regex := regexp.MustCompile("^\\[(.*?)\\]")
+ if regex.MatchString(msg) {
+ match := regex.FindString(msg)
+ prefix, msg = match[1:len(match)-1], strings.TrimSpace(msg[len(match):])
+ }
+ return prefix, msg
+}
+
+func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}, appendSpace bool) {
+ b.WriteString(key)
+ b.WriteByte('=')
+ f.appendValue(b, value)
+
+ if appendSpace {
+ b.WriteByte(' ')
+ }
+}
+
+func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
+ switch value := value.(type) {
+ case string:
+ if !f.needsQuoting(value) {
+ b.WriteString(value)
+ } else {
+ fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, value, f.QuoteCharacter)
+ }
+ case error:
+ errmsg := value.Error()
+ if !f.needsQuoting(errmsg) {
+ b.WriteString(errmsg)
+ } else {
+ fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, errmsg, f.QuoteCharacter)
+ }
+ default:
+ fmt.Fprint(b, value)
+ }
+}
+
+// This is to not silently overwrite `time`, `msg` and `level` fields when
+// dumping it. If this code wasn't there doing:
+//
+// logrus.WithField("level", 1).Info("hello")
+//
+// would just silently drop the user provided level. Instead with this code
+// it'll be logged as:
+//
+// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
+func prefixFieldClashes(data logrus.Fields) {
+ if t, ok := data["time"]; ok {
+ data["fields.time"] = t
+ }
+
+ if m, ok := data["msg"]; ok {
+ data["fields.msg"] = m
+ }
+
+ if l, ok := data["level"]; ok {
+ data["fields.level"] = l
+ }
+}