github.com/fedir/buffalo@v0.11.1/logger_formatter.go (about)

     1  package buffalo
     2  
     3  // I really don't want to have this, but until (if) https://github.com/sirupsen/logrus/pull/606 is merged we're stuck with all this code. And yes, this is ALL needed just to remove some blank space in the logs
     4  
     5  import (
     6  	"bytes"
     7  	"fmt"
     8  	"io"
     9  	"os"
    10  	"regexp"
    11  	"sort"
    12  	"strings"
    13  	"sync"
    14  	"time"
    15  
    16  	"github.com/sirupsen/logrus"
    17  	"golang.org/x/crypto/ssh/terminal"
    18  )
    19  
    20  const (
    21  	nocolor = 0
    22  	red     = 31
    23  	green   = 32
    24  	yellow  = 33
    25  	blue    = 36
    26  	gray    = 37
    27  )
    28  
    29  // textFormatter formats logs into text
    30  type textFormatter struct {
    31  	ForceColors bool
    32  	isTerminal  bool
    33  	sync.Once
    34  }
    35  
    36  func (f *textFormatter) init(entry *logrus.Entry) {
    37  	if entry.Logger != nil {
    38  		f.isTerminal = f.checkIfTerminal(entry.Logger.Out)
    39  	}
    40  }
    41  
    42  func (f *textFormatter) checkIfTerminal(w io.Writer) bool {
    43  	switch v := w.(type) {
    44  	case *os.File:
    45  		return terminal.IsTerminal(int(v.Fd()))
    46  	default:
    47  		return false
    48  	}
    49  }
    50  
    51  const defaultTimestampFormat = time.RFC3339
    52  
    53  // Format renders a single log entry
    54  func (f *textFormatter) Format(entry *logrus.Entry) ([]byte, error) {
    55  	var b *bytes.Buffer
    56  	keys := make([]string, 0, len(entry.Data))
    57  	for k := range entry.Data {
    58  		keys = append(keys, k)
    59  	}
    60  
    61  	sort.Strings(keys)
    62  	if entry.Buffer != nil {
    63  		b = entry.Buffer
    64  	} else {
    65  		b = &bytes.Buffer{}
    66  	}
    67  
    68  	prefixFieldClashes(entry.Data)
    69  
    70  	f.Do(func() { f.init(entry) })
    71  
    72  	isColored := (f.ForceColors || f.isTerminal)
    73  
    74  	if isColored {
    75  		f.printColored(b, entry, keys)
    76  	} else {
    77  		f.appendKeyValue(b, "level", entry.Level.String())
    78  		if entry.Message != "" {
    79  			f.appendKeyValue(b, "msg", entry.Message)
    80  		}
    81  		for _, key := range keys {
    82  			f.appendKeyValue(b, key, entry.Data[key])
    83  		}
    84  	}
    85  
    86  	b.WriteByte('\n')
    87  	return b.Bytes(), nil
    88  }
    89  
    90  func (f *textFormatter) printColored(b *bytes.Buffer, entry *logrus.Entry, keys []string) {
    91  	var levelColor int
    92  	switch entry.Level {
    93  	case logrus.DebugLevel:
    94  		levelColor = gray
    95  	case logrus.WarnLevel:
    96  		levelColor = yellow
    97  	case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel:
    98  		levelColor = red
    99  	default:
   100  		levelColor = blue
   101  	}
   102  
   103  	levelText := strings.ToUpper(entry.Level.String())[0:4]
   104  
   105  	fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]", levelColor, levelText, entry.Time.Format(defaultTimestampFormat))
   106  
   107  	if entry.Message != "" {
   108  		fmt.Fprintf(b, " %s", entry.Message)
   109  	}
   110  
   111  	for _, k := range keys {
   112  		v := entry.Data[k]
   113  		fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
   114  		f.appendValue(b, v)
   115  	}
   116  }
   117  
   118  func (f *textFormatter) needsQuoting(text string) bool {
   119  	if len(text) == 0 {
   120  		return true
   121  	}
   122  	matched, err := regexp.MatchString("[^a-zA-Z\\-\\._\\/@\\^\\+]", text)
   123  	if err != nil {
   124  		return false
   125  	}
   126  	return matched
   127  }
   128  
   129  func (f *textFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
   130  	if b.Len() > 0 {
   131  		b.WriteByte(' ')
   132  	}
   133  	b.WriteString(key)
   134  	b.WriteByte('=')
   135  	f.appendValue(b, value)
   136  }
   137  
   138  func (f *textFormatter) appendValue(b *bytes.Buffer, value interface{}) {
   139  	stringVal, ok := value.(string)
   140  	if !ok {
   141  		stringVal = fmt.Sprint(value)
   142  	}
   143  
   144  	if !f.needsQuoting(stringVal) {
   145  		b.WriteString(stringVal)
   146  	} else {
   147  		b.WriteString(fmt.Sprintf("%q", stringVal))
   148  	}
   149  }
   150  
   151  func prefixFieldClashes(data logrus.Fields) {
   152  	if t, ok := data["time"]; ok {
   153  		data["fields.time"] = t
   154  	}
   155  
   156  	if m, ok := data["msg"]; ok {
   157  		data["fields.msg"] = m
   158  	}
   159  
   160  	if l, ok := data["level"]; ok {
   161  		data["fields.level"] = l
   162  	}
   163  }