package pipelinex import ( "encoding/json" "fmt" "sort" "strings" "time" ) type Entry struct { Time time.Time LevelName string LoggerName string Thread string File string Line int Func string Message string Error string Fields map[string]interface{} } type TextOptions struct { IncludeTimestamp bool IncludeLevel bool IncludeSource bool IncludeThread bool IncludeLogger bool } func cloneFields(fields map[string]interface{}) map[string]interface{} { if len(fields) == 0 { return nil } cloned := make(map[string]interface{}, len(fields)) for key, value := range fields { cloned[key] = value } return cloned } func renderFields(fields map[string]interface{}) string { if len(fields) == 0 { return "" } keys := make([]string, 0, len(fields)) for key := range fields { keys = append(keys, key) } sort.Strings(keys) pairs := make([]string, 0, len(keys)) for _, key := range keys { pairs = append(pairs, fmt.Sprintf("%s=%v", key, fields[key])) } return strings.Join(pairs, " ") } func FormatText(entry Entry, options TextOptions) ([]byte, error) { parts := make([]string, 0, 6) if options.IncludeTimestamp { if !entry.Time.IsZero() { parts = append(parts, entry.Time.Format("2006-01-02 15:04:05.000000")) } } if options.IncludeSource { source := "" if entry.File != "" { source = fmt.Sprintf("%s:%d", entry.File, entry.Line) } if entry.Func != "" { if source != "" { source += " " } source += "<" + entry.Func + ">" } if source != "" { parts = append(parts, source) } } if options.IncludeThread && entry.Thread != "" { parts = append(parts, "|"+entry.Thread+"|") } if options.IncludeLevel { if entry.LevelName != "" { parts = append(parts, "["+entry.LevelName+"]") } } if options.IncludeLogger && entry.LoggerName != "" { parts = append(parts, "logger="+entry.LoggerName) } messageParts := make([]string, 0, 3) if entry.Message != "" { messageParts = append(messageParts, entry.Message) } if entry.Error != "" { messageParts = append(messageParts, "error="+entry.Error) } fieldText := renderFields(entry.Fields) if fieldText != "" { messageParts = append(messageParts, fieldText) } if len(messageParts) > 0 { parts = append(parts, strings.Join(messageParts, " ")) } return []byte(strings.Join(parts, " ")), nil } func FormatJSON(entry Entry, pretty bool) ([]byte, error) { payload := map[string]interface{}{ "time": entry.Time.Format(time.RFC3339Nano), "level": entry.LevelName, "msg": entry.Message, "logger": entry.LoggerName, "thread": entry.Thread, } if entry.File != "" { payload["file"] = entry.File } if entry.Line > 0 { payload["line"] = entry.Line } if entry.Func != "" { payload["func"] = entry.Func } if entry.Error != "" { payload["error"] = entry.Error } if len(entry.Fields) > 0 { payload["fields"] = cloneFields(entry.Fields) } if pretty { return json.MarshalIndent(payload, "", " ") } return json.Marshal(payload) }