github.com/blend/go-sdk@v1.20220411.3/logger/text_output_formatter.go (about)

     1  /*
     2  
     3  Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file.
     5  
     6  */
     7  
     8  package logger
     9  
    10  import (
    11  	"context"
    12  	"fmt"
    13  	"io"
    14  	"strings"
    15  	"time"
    16  
    17  	"github.com/blend/go-sdk/ansi"
    18  	"github.com/blend/go-sdk/bufferutil"
    19  )
    20  
    21  var (
    22  	_ WriteFormatter = (*TextOutputFormatter)(nil)
    23  )
    24  
    25  // NewTextOutputFormatter returns a new text writer for a given output.
    26  func NewTextOutputFormatter(options ...TextOutputFormatterOption) *TextOutputFormatter {
    27  	tf := &TextOutputFormatter{
    28  		BufferPool: bufferutil.NewPool(DefaultBufferPoolSize),
    29  		TimeFormat: DefaultTextTimeFormat,
    30  	}
    31  
    32  	for _, option := range options {
    33  		option(tf)
    34  	}
    35  
    36  	return tf
    37  }
    38  
    39  // TextOutputFormatterOption is an option for text formatters.
    40  type TextOutputFormatterOption func(*TextOutputFormatter)
    41  
    42  // OptTextConfig sets the text formatter config.
    43  func OptTextConfig(cfg TextConfig) TextOutputFormatterOption {
    44  	return func(tf *TextOutputFormatter) {
    45  		tf.HideTimestamp = cfg.HideTimestamp
    46  		tf.HideFields = cfg.HideFields
    47  		tf.NoColor = cfg.NoColor
    48  		tf.TimeFormat = cfg.TimeFormatOrDefault()
    49  	}
    50  }
    51  
    52  // OptTextTimeFormat sets the timestamp format.
    53  func OptTextTimeFormat(format string) TextOutputFormatterOption {
    54  	return func(tf *TextOutputFormatter) { tf.TimeFormat = format }
    55  }
    56  
    57  // OptTextHideTimestamp hides the timestamp in output.
    58  func OptTextHideTimestamp() TextOutputFormatterOption {
    59  	return func(tf *TextOutputFormatter) { tf.HideTimestamp = true }
    60  }
    61  
    62  // OptTextHideFields hides the fields in output.
    63  func OptTextHideFields() TextOutputFormatterOption {
    64  	return func(tf *TextOutputFormatter) { tf.HideFields = true }
    65  }
    66  
    67  // OptTextNoColor disables colorizing text output.
    68  func OptTextNoColor() TextOutputFormatterOption {
    69  	return func(tf *TextOutputFormatter) { tf.NoColor = true }
    70  }
    71  
    72  // TextOutputFormatter handles formatting messages as text.
    73  type TextOutputFormatter struct {
    74  	HideTimestamp bool
    75  	HideFields    bool
    76  	NoColor       bool
    77  	TimeFormat    string
    78  
    79  	BufferPool *bufferutil.Pool
    80  }
    81  
    82  // TimeFormatOrDefault returns the time format or a default
    83  func (tf TextOutputFormatter) TimeFormatOrDefault() string {
    84  	if len(tf.TimeFormat) > 0 {
    85  		return tf.TimeFormat
    86  	}
    87  	return DefaultTextTimeFormat
    88  }
    89  
    90  // Colorize (optionally) applies a color to a string.
    91  func (tf TextOutputFormatter) Colorize(value string, color ansi.Color) string {
    92  	if tf.NoColor {
    93  		return value
    94  	}
    95  	return color.Apply(value)
    96  }
    97  
    98  // FormatFlag formats the flag portion of the message.
    99  func (tf TextOutputFormatter) FormatFlag(flag string, color ansi.Color) string {
   100  	return fmt.Sprintf("[%s]", tf.Colorize(string(flag), color))
   101  }
   102  
   103  // FormatTimestamp returns a new timestamp string.
   104  func (tf TextOutputFormatter) FormatTimestamp(ts time.Time) string {
   105  	value := ts.Format(tf.TimeFormatOrDefault())
   106  	return tf.Colorize(fmt.Sprintf("%-30s", value), ansi.ColorLightBlack)
   107  }
   108  
   109  // FormatPath returns the sub-context path section of the message as a string.
   110  func (tf TextOutputFormatter) FormatPath(path ...string) string {
   111  	if len(path) == 0 {
   112  		return ""
   113  	}
   114  	if len(path) == 1 {
   115  		return fmt.Sprintf("[%s]", tf.Colorize(path[0], ansi.ColorBlue))
   116  	}
   117  	if !tf.NoColor {
   118  		var colorized []string
   119  		for _, subPath := range path {
   120  			colorized = append(colorized, tf.Colorize(subPath, ansi.ColorBlue))
   121  		}
   122  		path = colorized
   123  	}
   124  	return fmt.Sprintf("[%s]", strings.Join(path, " > "))
   125  }
   126  
   127  // FormatLabels returns the scope labels section of the message as a string.
   128  func (tf TextOutputFormatter) FormatLabels(labels Labels) string {
   129  	return FormatLabels(tf, ansi.ColorBlue, labels)
   130  }
   131  
   132  // WriteFormat implements write formatter.
   133  func (tf TextOutputFormatter) WriteFormat(ctx context.Context, output io.Writer, e Event) error {
   134  	buffer := tf.BufferPool.Get()
   135  	defer tf.BufferPool.Put(buffer)
   136  
   137  	if !tf.HideTimestamp {
   138  		buffer.WriteString(tf.FormatTimestamp(GetEventTimestamp(ctx, e)))
   139  		buffer.WriteString(Space)
   140  	}
   141  
   142  	scopePath := GetPath(ctx)
   143  	if scopePath != nil {
   144  		buffer.WriteString(tf.FormatPath(scopePath...))
   145  		buffer.WriteString(Space)
   146  	}
   147  
   148  	buffer.WriteString(tf.FormatFlag(e.GetFlag(), FlagTextColor(e.GetFlag())))
   149  	buffer.WriteString(Space)
   150  
   151  	if typed, ok := e.(TextWritable); ok {
   152  		typed.WriteText(tf, buffer)
   153  	} else if stringer, ok := e.(fmt.Stringer); ok {
   154  		buffer.WriteString(stringer.String())
   155  	}
   156  
   157  	if !tf.HideFields {
   158  		labels := GetLabels(ctx)
   159  		if len(labels) > 0 {
   160  			buffer.WriteString("\t")
   161  			buffer.WriteString(tf.FormatLabels(labels))
   162  		}
   163  	}
   164  
   165  	buffer.WriteString(Newline)
   166  	_, err := io.Copy(output, buffer)
   167  	return err
   168  }