Files
cli/vendor/github.com/sirupsen/logrus/json_formatter.go
Sebastiaan van Stijn 58d9cdd2d9 vendor: github.com/sirupsen/logrus v1.10.1
Notable changes:

- Add bidirectional `log/slog` integration with a Logrus hook and `slog.Handler`.
- Add minimal, composable logging interfaces for individual log levels.
- Fix reentrant logging deadlocks and improve concurrency safety around formatters, hooks, and entries.
- Fix generic `Log`, `Logf`, `Logln`, and `LogFn` methods unexpectedly panicking at `PanicLevel`.
- Allow `Entry.Caller` to be set explicitly and preserved across derived entries.
- Improve `TextFormatter` performance and reduce allocations significantly.
- Improve common Logger and Entry hot paths and caller-reporting performance.
- Update `TextFormatter` handling for `[]byte`, debug/trace colors, and Windows ANSI terminals.
- Raise the minimum supported Go version to Go 1.23.
- Deprecate `Entry.HasCaller` and `MutexWrap`.

release-notes: https://github.com/sirupsen/logrus/releases/tag/v1.10.0
release-notes: https://github.com/sirupsen/logrus/releases/tag/v1.10.1
full diff: https://github.com/sirupsen/logrus/compare/v1.9.4...v1.10.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-08-19 11:34:53 +02:00

138 lines
3.7 KiB
Go

package logrus
import (
"bytes"
"encoding/json"
"fmt"
"runtime"
"strconv"
)
type fieldKey string
// FieldMap allows customization of the key names for default fields.
type FieldMap map[fieldKey]string
func (f FieldMap) resolve(key fieldKey) string {
if k, ok := f[key]; ok {
return k
}
return string(key)
}
// JSONFormatter formats logs into parsable JSON.
//
// Fields from [Entry.Data] are included in the JSON object together with the
// standard fields derived from the entry. If a field conflicts with a standard
// field, it is prefixed with "fields.". Standard field names can be customized
// through FieldMap. When DataKey is set, fields from [Entry.Data] are nested
// under that key instead.
type JSONFormatter struct {
// TimestampFormat sets the format used for marshaling timestamps.
// The format to use is the same than for time.Format or time.Parse from the standard
// library.
// The standard Library already provides a set of predefined format.
TimestampFormat string
// DisableTimestamp allows disabling automatic timestamps in output
DisableTimestamp bool
// DisableHTMLEscape allows disabling html escaping in output
DisableHTMLEscape bool
// DataKey allows users to put all the log entry parameters into a nested dictionary at a given key.
DataKey string
// FieldMap allows users to customize the names of keys for default fields.
// As an example:
// formatter := &JSONFormatter{
// FieldMap: FieldMap{
// FieldKeyTime: "@timestamp",
// FieldKeyLevel: "@level",
// FieldKeyMsg: "@message",
// FieldKeyFunc: "@caller",
// },
// }
FieldMap FieldMap
// CallerPrettyfier can be set by the user to modify the content
// of the function and file keys in the json data when ReportCaller is
// activated. If any of the returned value is the empty string the
// corresponding key will be removed from json fields.
CallerPrettyfier func(*runtime.Frame) (function string, file string)
// PrettyPrint will indent all json logs
PrettyPrint bool
}
// Format renders a single log entry
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
caller := entry.Caller
data := make(Fields, len(entry.Data)+defaultFields)
for k, v := range entry.Data {
switch v := v.(type) {
case error:
// Otherwise errors are ignored by `encoding/json`
// https://github.com/sirupsen/logrus/issues/137
data[k] = v.Error()
default:
data[k] = v
}
}
if f.DataKey != "" && len(entry.Data) > 0 {
newData := make(Fields, defaultFields+1)
newData[f.DataKey] = data
data = newData
}
hasCaller := caller != nil
prefixFieldClashes(data, f.FieldMap, hasCaller)
timestampFormat := f.TimestampFormat
if timestampFormat == "" {
timestampFormat = defaultTimestampFormat
}
if entry.err != "" {
data[f.FieldMap.resolve(FieldKeyLogrusError)] = entry.err
}
if !f.DisableTimestamp {
data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat)
}
data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message
data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String()
if caller != nil {
var funcVal, fileVal string
if f.CallerPrettyfier != nil {
funcVal, fileVal = f.CallerPrettyfier(caller)
} else {
funcVal = caller.Function
fileVal = caller.File + ":" + strconv.FormatInt(int64(caller.Line), 10)
}
if funcVal != "" {
data[f.FieldMap.resolve(FieldKeyFunc)] = funcVal
}
if fileVal != "" {
data[f.FieldMap.resolve(FieldKeyFile)] = fileVal
}
}
b := entry.Buffer
if b == nil {
b = new(bytes.Buffer)
}
encoder := json.NewEncoder(b)
encoder.SetEscapeHTML(!f.DisableHTMLEscape)
if f.PrettyPrint {
encoder.SetIndent("", " ")
}
if err := encoder.Encode(data); err != nil {
return nil, fmt.Errorf("failed to marshal fields to JSON, %w", err)
}
return b.Bytes(), nil
}