github.com/TBD54566975/ftl@v0.219.0/internal/log/plain.go (about)

     1  package log
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  	"time"
     8  
     9  	"github.com/mattn/go-isatty"
    10  )
    11  
    12  var colours = map[Level]string{
    13  	Trace: "\x1b[90m", // Dark gray
    14  	Debug: "\x1b[34m", // Blue
    15  	Info:  "\x1b[37m", // White
    16  	Warn:  "\x1b[33m", // Yellow
    17  	Error: "\x1b[31m", // Red
    18  }
    19  
    20  var _ Sink = (*plainSink)(nil)
    21  
    22  func newPlainSink(w io.Writer, logTime bool, alwaysColor bool) *plainSink {
    23  	var isaTTY bool
    24  	if alwaysColor {
    25  		isaTTY = true
    26  	} else if f, ok := w.(*os.File); ok {
    27  		isaTTY = isatty.IsTerminal(f.Fd())
    28  	}
    29  	return &plainSink{
    30  		isaTTY:  isaTTY,
    31  		w:       w,
    32  		logTime: logTime,
    33  	}
    34  }
    35  
    36  type plainSink struct {
    37  	isaTTY  bool
    38  	w       io.Writer
    39  	logTime bool
    40  }
    41  
    42  // Log implements Sink
    43  func (t *plainSink) Log(entry Entry) error {
    44  	var prefix string
    45  
    46  	// Add timestamp if required
    47  	if t.logTime {
    48  		prefix += entry.Time.Format(time.TimeOnly) + " "
    49  	}
    50  
    51  	// Add scope if required
    52  	scope, exists := entry.Attributes[scopeKey]
    53  	if exists {
    54  		prefix += entry.Level.String() + ":" + scope + ": "
    55  	} else {
    56  		prefix += entry.Level.String() + ": "
    57  	}
    58  
    59  	// Print
    60  	var err error
    61  	if t.isaTTY {
    62  		_, err = fmt.Fprintf(t.w, "%s%s%s\x1b[0m\n", colours[entry.Level], prefix, entry.Message)
    63  	} else {
    64  		_, err = fmt.Fprintf(t.w, "%s%s\n", prefix, entry.Message)
    65  	}
    66  	if err != nil {
    67  		return err
    68  	}
    69  	return nil
    70  }