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 }