github.com/blend/go-sdk@v1.20240719.1/logger/text_output_formatter.go (about) 1 /* 2 3 Copyright (c) 2024 - 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 // FormatAnnotations returns the scope annotations section of the message as a string. 128 func (tf TextOutputFormatter) FormatAnnotations(annotations Annotations) string { 129 return FormatAnnotations(tf, ansi.ColorGreen, annotations) 130 } 131 132 // FormatLabels returns the scope labels section of the message as a string. 133 func (tf TextOutputFormatter) FormatLabels(labels Labels) string { 134 return FormatLabels(tf, ansi.ColorBlue, labels) 135 } 136 137 // WriteFormat implements write formatter. 138 func (tf TextOutputFormatter) WriteFormat(ctx context.Context, output io.Writer, e Event) error { 139 buffer := tf.BufferPool.Get() 140 defer tf.BufferPool.Put(buffer) 141 142 if !tf.HideTimestamp { 143 buffer.WriteString(tf.FormatTimestamp(GetEventTimestamp(ctx, e))) 144 buffer.WriteString(Space) 145 } 146 147 scopePath := GetPath(ctx) 148 if scopePath != nil { 149 buffer.WriteString(tf.FormatPath(scopePath...)) 150 buffer.WriteString(Space) 151 } 152 153 buffer.WriteString(tf.FormatFlag(e.GetFlag(), FlagTextColor(e.GetFlag()))) 154 buffer.WriteString(Space) 155 156 if typed, ok := e.(TextWritable); ok { 157 typed.WriteText(tf, buffer) 158 } else if stringer, ok := e.(fmt.Stringer); ok { 159 buffer.WriteString(stringer.String()) 160 } 161 162 if !tf.HideFields { 163 labels := GetLabels(ctx) 164 if len(labels) > 0 { 165 buffer.WriteString("\t") 166 buffer.WriteString(tf.FormatLabels(labels)) 167 } 168 169 annotations := GetAnnotations(ctx) 170 if len(annotations) > 0 { 171 buffer.WriteString("\t") 172 buffer.WriteString(tf.FormatAnnotations(annotations)) 173 } 174 } 175 176 buffer.WriteString(Newline) 177 _, err := io.Copy(output, buffer) 178 return err 179 }