mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-07-26 10:19:11 +00:00
178 lines
3.9 KiB
Go
178 lines
3.9 KiB
Go
package cmd
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/gzip"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
"text/tabwriter"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
func outputFormatIsJSON() bool {
|
|
return strings.EqualFold(strings.TrimSpace(gatewayOutputFormat), "json")
|
|
}
|
|
|
|
func validateTraceOutputFormat() error {
|
|
switch strings.ToLower(strings.TrimSpace(gatewayOutputFormat)) {
|
|
case "", "table", "json":
|
|
return nil
|
|
default:
|
|
return fmt.Errorf("unsupported output format %q; use table or json", gatewayOutputFormat)
|
|
}
|
|
}
|
|
|
|
func printJSON(value any) error {
|
|
data, err := json.MarshalIndent(value, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println(string(data))
|
|
return nil
|
|
}
|
|
|
|
func printTraceList(resp traceListResponse) error {
|
|
if outputFormatIsJSON() {
|
|
return printJSON(resp)
|
|
}
|
|
if len(resp.Traces) == 0 {
|
|
fmt.Println("No traces found.")
|
|
return nil
|
|
}
|
|
tw := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
|
fmt.Fprintln(tw, "TRACE\tSTATUS\tAGENT\tSESSION\tRUN\tTOKENS\tSTARTED")
|
|
for _, tr := range resp.Traces {
|
|
fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%s\t%d/%d\t%s\n",
|
|
shortTraceID(tr.ID.String()),
|
|
tr.Status,
|
|
shortOptionalUUID(tr.AgentID),
|
|
truncateStr(tr.SessionKey, 36),
|
|
truncateStr(tr.RunID, 24),
|
|
tr.TotalInputTokens,
|
|
tr.TotalOutputTokens,
|
|
formatTraceTime(tr.StartTime),
|
|
)
|
|
}
|
|
return tw.Flush()
|
|
}
|
|
|
|
func printTraceDetail(resp traceDetailResponse) error {
|
|
if outputFormatIsJSON() {
|
|
return printJSON(resp)
|
|
}
|
|
tr := resp.Trace
|
|
fmt.Printf("Trace: %s\n", tr.ID)
|
|
fmt.Printf("Status: %s\n", tr.Status)
|
|
fmt.Printf("Agent: %s\n", shortOptionalUUID(tr.AgentID))
|
|
fmt.Printf("Session: %s\n", tr.SessionKey)
|
|
fmt.Printf("Run: %s\n", tr.RunID)
|
|
fmt.Printf("Tokens: %d input / %d output\n", tr.TotalInputTokens, tr.TotalOutputTokens)
|
|
if tr.Error != "" {
|
|
fmt.Printf("Error: %s\n", tr.Error)
|
|
}
|
|
if len(resp.Spans) == 0 {
|
|
fmt.Println("\nNo spans found.")
|
|
return nil
|
|
}
|
|
fmt.Println()
|
|
tw := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
|
fmt.Fprintln(tw, "SPAN\tTYPE\tSTATUS\tPROVIDER\tMODEL\tDURATION\tNAME")
|
|
for _, sp := range resp.Spans {
|
|
fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%s\t%dms\t%s\n",
|
|
shortTraceID(sp.ID.String()),
|
|
sp.SpanType,
|
|
sp.Status,
|
|
sp.Provider,
|
|
sp.Model,
|
|
sp.DurationMS,
|
|
truncateStr(sp.Name, 48),
|
|
)
|
|
}
|
|
return tw.Flush()
|
|
}
|
|
|
|
func printTraceFollow(resp traceFollowResponse) error {
|
|
if outputFormatIsJSON() {
|
|
return printJSON(resp)
|
|
}
|
|
if resp.NextSince != "" {
|
|
fmt.Printf("Next since: %s\n\n", resp.NextSince)
|
|
}
|
|
return printTraceList(traceListResponse{
|
|
Traces: resp.Traces,
|
|
Total: len(resp.Traces),
|
|
Limit: resp.Limit,
|
|
})
|
|
}
|
|
|
|
func printTraceTimeline(resp traceTimelineResponse) error {
|
|
if outputFormatIsJSON() {
|
|
return printJSON(resp)
|
|
}
|
|
if len(resp.Items) == 0 {
|
|
fmt.Println("No timeline items found.")
|
|
return nil
|
|
}
|
|
tw := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
|
fmt.Fprintln(tw, "SEQ\tTYPE\tSTATUS\tTITLE\tPREVIEW\tCREATED")
|
|
for _, item := range resp.Items {
|
|
fmt.Fprintf(tw, "%d\t%s\t%s\t%s\t%s\t%s\n",
|
|
item.Seq,
|
|
item.ItemType,
|
|
item.Status,
|
|
truncateStr(item.Title, 32),
|
|
truncateStr(firstNonEmpty(item.Preview, item.Content), 60),
|
|
formatTraceTime(item.CreatedAt),
|
|
)
|
|
}
|
|
return tw.Flush()
|
|
}
|
|
|
|
func printGzipJSON(raw []byte) error {
|
|
gr, err := gzip.NewReader(bytes.NewReader(raw))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer gr.Close()
|
|
data, err := io.ReadAll(gr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var value any
|
|
if err := json.Unmarshal(data, &value); err != nil {
|
|
return err
|
|
}
|
|
return printJSON(value)
|
|
}
|
|
|
|
func defaultTraceExportPath(traceID string) string {
|
|
short := shortTraceID(traceID)
|
|
return fmt.Sprintf("trace-%s-%s.json.gz", short, time.Now().UTC().Format("20060102"))
|
|
}
|
|
|
|
func shortTraceID(id string) string {
|
|
if len(id) <= 8 {
|
|
return id
|
|
}
|
|
return id[:8]
|
|
}
|
|
|
|
func shortOptionalUUID(id *uuid.UUID) string {
|
|
if id == nil {
|
|
return "-"
|
|
}
|
|
return shortTraceID(id.String())
|
|
}
|
|
|
|
func formatTraceTime(t time.Time) string {
|
|
if t.IsZero() {
|
|
return "-"
|
|
}
|
|
return t.Local().Format(time.DateTime)
|
|
}
|