aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/sirupsen/logrus/text_formatter.go
diff options
context:
space:
mode:
authorLibravatar Rutger Broekhoff2023-12-29 21:31:53 +0100
committerLibravatar Rutger Broekhoff2023-12-29 21:31:53 +0100
commit404aeae4545d2426c089a5f8d5e82dae56f5212b (patch)
tree2d84e00af272b39fc04f3795ae06bc48970e57b5 /vendor/github.com/sirupsen/logrus/text_formatter.go
parent209d8b0187ed025dec9ac149ebcced3462877bff (diff)
downloadgitolfs3-404aeae4545d2426c089a5f8d5e82dae56f5212b.tar.gz
gitolfs3-404aeae4545d2426c089a5f8d5e82dae56f5212b.zip
Make Nix builds work
Diffstat (limited to 'vendor/github.com/sirupsen/logrus/text_formatter.go')
-rw-r--r--vendor/github.com/sirupsen/logrus/text_formatter.go339
1 files changed, 339 insertions, 0 deletions
diff --git a/vendor/github.com/sirupsen/logrus/text_formatter.go b/vendor/github.com/sirupsen/logrus/text_formatter.go
new file mode 100644
index 0000000..be2c6ef
--- /dev/null
+++ b/vendor/github.com/sirupsen/logrus/text_formatter.go
@@ -0,0 +1,339 @@
1package logrus
2
3import (
4 "bytes"
5 "fmt"
6 "os"
7 "runtime"
8 "sort"
9 "strconv"
10 "strings"
11 "sync"
12 "time"
13 "unicode/utf8"
14)
15
16const (
17 red = 31
18 yellow = 33
19 blue = 36
20 gray = 37
21)
22
23var baseTimestamp time.Time
24
25func init() {
26 baseTimestamp = time.Now()
27}
28
29// TextFormatter formats logs into text
30type TextFormatter struct {
31 // Set to true to bypass checking for a TTY before outputting colors.
32 ForceColors bool
33
34 // Force disabling colors.
35 DisableColors bool
36
37 // Force quoting of all values
38 ForceQuote bool
39
40 // DisableQuote disables quoting for all values.
41 // DisableQuote will have a lower priority than ForceQuote.
42 // If both of them are set to true, quote will be forced on all values.
43 DisableQuote bool
44
45 // Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/
46 EnvironmentOverrideColors bool
47
48 // Disable timestamp logging. useful when output is redirected to logging
49 // system that already adds timestamps.
50 DisableTimestamp bool
51
52 // Enable logging the full timestamp when a TTY is attached instead of just
53 // the time passed since beginning of execution.
54 FullTimestamp bool
55
56 // TimestampFormat to use for display when a full timestamp is printed.
57 // The format to use is the same than for time.Format or time.Parse from the standard
58 // library.
59 // The standard Library already provides a set of predefined format.
60 TimestampFormat string
61
62 // The fields are sorted by default for a consistent output. For applications
63 // that log extremely frequently and don't use the JSON formatter this may not
64 // be desired.
65 DisableSorting bool
66
67 // The keys sorting function, when uninitialized it uses sort.Strings.
68 SortingFunc func([]string)
69
70 // Disables the truncation of the level text to 4 characters.
71 DisableLevelTruncation bool
72
73 // PadLevelText Adds padding the level text so that all the levels output at the same length
74 // PadLevelText is a superset of the DisableLevelTruncation option
75 PadLevelText bool
76
77 // QuoteEmptyFields will wrap empty fields in quotes if true
78 QuoteEmptyFields bool
79
80 // Whether the logger's out is to a terminal
81 isTerminal bool
82
83 // FieldMap allows users to customize the names of keys for default fields.
84 // As an example:
85 // formatter := &TextFormatter{
86 // FieldMap: FieldMap{
87 // FieldKeyTime: "@timestamp",
88 // FieldKeyLevel: "@level",
89 // FieldKeyMsg: "@message"}}
90 FieldMap FieldMap
91
92 // CallerPrettyfier can be set by the user to modify the content
93 // of the function and file keys in the data when ReportCaller is
94 // activated. If any of the returned value is the empty string the
95 // corresponding key will be removed from fields.
96 CallerPrettyfier func(*runtime.Frame) (function string, file string)
97
98 terminalInitOnce sync.Once
99
100 // The max length of the level text, generated dynamically on init
101 levelTextMaxLength int
102}
103
104func (f *TextFormatter) init(entry *Entry) {
105 if entry.Logger != nil {
106 f.isTerminal = checkIfTerminal(entry.Logger.Out)
107 }
108 // Get the max length of the level text
109 for _, level := range AllLevels {
110 levelTextLength := utf8.RuneCount([]byte(level.String()))
111 if levelTextLength > f.levelTextMaxLength {
112 f.levelTextMaxLength = levelTextLength
113 }
114 }
115}
116
117func (f *TextFormatter) isColored() bool {
118 isColored := f.ForceColors || (f.isTerminal && (runtime.GOOS != "windows"))
119
120 if f.EnvironmentOverrideColors {
121 switch force, ok := os.LookupEnv("CLICOLOR_FORCE"); {
122 case ok && force != "0":
123 isColored = true
124 case ok && force == "0", os.Getenv("CLICOLOR") == "0":
125 isColored = false
126 }
127 }
128
129 return isColored && !f.DisableColors
130}
131
132// Format renders a single log entry
133func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
134 data := make(Fields)
135 for k, v := range entry.Data {
136 data[k] = v
137 }
138 prefixFieldClashes(data, f.FieldMap, entry.HasCaller())
139 keys := make([]string, 0, len(data))
140 for k := range data {
141 keys = append(keys, k)
142 }
143
144 var funcVal, fileVal string
145
146 fixedKeys := make([]string, 0, 4+len(data))
147 if !f.DisableTimestamp {
148 fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime))
149 }
150 fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLevel))
151 if entry.Message != "" {
152 fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyMsg))
153 }
154 if entry.err != "" {
155 fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLogrusError))
156 }
157 if entry.HasCaller() {
158 if f.CallerPrettyfier != nil {
159 funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
160 } else {
161 funcVal = entry.Caller.Function
162 fileVal = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
163 }
164
165 if funcVal != "" {
166 fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFunc))
167 }
168 if fileVal != "" {
169 fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFile))
170 }
171 }
172
173 if !f.DisableSorting {
174 if f.SortingFunc == nil {
175 sort.Strings(keys)
176 fixedKeys = append(fixedKeys, keys...)
177 } else {
178 if !f.isColored() {
179 fixedKeys = append(fixedKeys, keys...)
180 f.SortingFunc(fixedKeys)
181 } else {
182 f.SortingFunc(keys)
183 }
184 }
185 } else {
186 fixedKeys = append(fixedKeys, keys...)
187 }
188
189 var b *bytes.Buffer
190 if entry.Buffer != nil {
191 b = entry.Buffer
192 } else {
193 b = &bytes.Buffer{}
194 }
195
196 f.terminalInitOnce.Do(func() { f.init(entry) })
197
198 timestampFormat := f.TimestampFormat
199 if timestampFormat == "" {
200 timestampFormat = defaultTimestampFormat
201 }
202 if f.isColored() {
203 f.printColored(b, entry, keys, data, timestampFormat)
204 } else {
205
206 for _, key := range fixedKeys {
207 var value interface{}
208 switch {
209 case key == f.FieldMap.resolve(FieldKeyTime):
210 value = entry.Time.Format(timestampFormat)
211 case key == f.FieldMap.resolve(FieldKeyLevel):
212 value = entry.Level.String()
213 case key == f.FieldMap.resolve(FieldKeyMsg):
214 value = entry.Message
215 case key == f.FieldMap.resolve(FieldKeyLogrusError):
216 value = entry.err
217 case key == f.FieldMap.resolve(FieldKeyFunc) && entry.HasCaller():
218 value = funcVal
219 case key == f.FieldMap.resolve(FieldKeyFile) && entry.HasCaller():
220 value = fileVal
221 default:
222 value = data[key]
223 }
224 f.appendKeyValue(b, key, value)
225 }
226 }
227
228 b.WriteByte('\n')
229 return b.Bytes(), nil
230}
231
232func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, data Fields, timestampFormat string) {
233 var levelColor int
234 switch entry.Level {
235 case DebugLevel, TraceLevel:
236 levelColor = gray
237 case WarnLevel:
238 levelColor = yellow
239 case ErrorLevel, FatalLevel, PanicLevel:
240 levelColor = red
241 case InfoLevel:
242 levelColor = blue
243 default:
244 levelColor = blue
245 }
246
247 levelText := strings.ToUpper(entry.Level.String())
248 if !f.DisableLevelTruncation && !f.PadLevelText {
249 levelText = levelText[0:4]
250 }
251 if f.PadLevelText {
252 // Generates the format string used in the next line, for example "%-6s" or "%-7s".
253 // Based on the max level text length.
254 formatString := "%-" + strconv.Itoa(f.levelTextMaxLength) + "s"
255 // Formats the level text by appending spaces up to the max length, for example:
256 // - "INFO "
257 // - "WARNING"
258 levelText = fmt.Sprintf(formatString, levelText)
259 }
260
261 // Remove a single newline if it already exists in the message to keep
262 // the behavior of logrus text_formatter the same as the stdlib log package
263 entry.Message = strings.TrimSuffix(entry.Message, "\n")
264
265 caller := ""
266 if entry.HasCaller() {
267 funcVal := fmt.Sprintf("%s()", entry.Caller.Function)
268 fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
269
270 if f.CallerPrettyfier != nil {
271 funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
272 }
273
274 if fileVal == "" {
275 caller = funcVal
276 } else if funcVal == "" {
277 caller = fileVal
278 } else {
279 caller = fileVal + " " + funcVal
280 }
281 }
282
283 switch {
284 case f.DisableTimestamp:
285 fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m%s %-44s ", levelColor, levelText, caller, entry.Message)
286 case !f.FullTimestamp:
287 fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d]%s %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), caller, entry.Message)
288 default:
289 fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message)
290 }
291 for _, k := range keys {
292 v := data[k]
293 fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
294 f.appendValue(b, v)
295 }
296}
297
298func (f *TextFormatter) needsQuoting(text string) bool {
299 if f.ForceQuote {
300 return true
301 }
302 if f.QuoteEmptyFields && len(text) == 0 {
303 return true
304 }
305 if f.DisableQuote {
306 return false
307 }
308 for _, ch := range text {
309 if !((ch >= 'a' && ch <= 'z') ||
310 (ch >= 'A' && ch <= 'Z') ||
311 (ch >= '0' && ch <= '9') ||
312 ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '@' || ch == '^' || ch == '+') {
313 return true
314 }
315 }
316 return false
317}
318
319func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
320 if b.Len() > 0 {
321 b.WriteByte(' ')
322 }
323 b.WriteString(key)
324 b.WriteByte('=')
325 f.appendValue(b, value)
326}
327
328func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
329 stringVal, ok := value.(string)
330 if !ok {
331 stringVal = fmt.Sprint(value)
332 }
333
334 if !f.needsQuoting(stringVal) {
335 b.WriteString(stringVal)
336 } else {
337 b.WriteString(fmt.Sprintf("%q", stringVal))
338 }
339}