Add a structured logger and collector so runs can be traced across processes.
ci / vet-and-test (push) Successful in 4s
ci / docker-checkout-hostgateway (push) Successful in 2s

Sync and async sinks, redaction, and a queryable HTTP service keep concurrent logs tied to a trace id.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
weichen
2026-09-29 13:51:16 +00:00
co-authored by Cursor
commit bed0dbf076
31 changed files with 3078 additions and 0 deletions
+61
View File
@@ -0,0 +1,61 @@
package logsys
import (
"bytes"
"encoding/json"
"fmt"
"sort"
"strings"
"time"
)
// FormatJSON 把条目编码为一行 JSON(含换行)。
func FormatJSON(e Entry) ([]byte, error) {
buf := &bytes.Buffer{}
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(false)
if err := enc.Encode(e); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// FormatHuman 把条目格式化成单行文本(含换行)。
func FormatHuman(e Entry) string {
ts := e.Time.UTC().Format(time.RFC3339Nano)
var b strings.Builder
fmt.Fprintf(&b, "%s %s", ts, strings.ToUpper(e.Level))
if e.Service != "" || e.Module != "" {
b.WriteByte(' ')
b.WriteString(e.Service)
if e.Module != "" {
if e.Service != "" {
b.WriteByte('/')
}
b.WriteString(e.Module)
}
}
b.WriteByte(' ')
b.WriteString(e.Message)
if e.Code != "" {
fmt.Fprintf(&b, " code=%s", e.Code)
}
if e.TraceID != "" {
fmt.Fprintf(&b, " trace=%s", e.TraceID)
}
if e.Error != "" {
fmt.Fprintf(&b, " error=%q", e.Error)
}
if len(e.Fields) > 0 {
keys := make([]string, 0, len(e.Fields))
for k := range e.Fields {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
fmt.Fprintf(&b, " %s=%v", k, e.Fields[k])
}
}
b.WriteByte('\n')
return b.String()
}