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