Files
Cyrene_v2/internal/runtime/loop/trace.go
T
Aska 44e7a5c2c5
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
feat(flow): 实时全链路流程可视化 P1——trace→graph_update→前端全链路流程页(design/19)
后端: trace/graph.go(RunGraph 图构建器,kind/status 映射,父子优先连线,脱敏白名单+截断);
       loop/flow.go(FlowPublisher 按会话增量发布 ws.graph_update,仅主人web会话); loop span
       start/finish 折入图; run.go StartRound/EndRound; wire.go 组装+注入.
前端: Flow.tsx(全链路流程页:FlowGraphView+会话/节点/开始/耗时卡片+状态计数+节点点开日志);
       FlowGraph 组件加 onNodeClick; types/wsParse 扩展; router/sections/AdminApp 注册 flow;
       api-client.md §3.5 graph_update 契约.
验证: 后端 build/vet/test 全绿+flow_test.go 集成(span→graph→ws.graph_update主干); 前端 build 绿.
红线: 单二进制零新依赖; 工具参数/结果脱敏; 仅主人web会话; 身份靠actor元数据; 只读不虚构.
P2(并行子代理分支/泳道/节点实时日志)后置.
2026-09-07 19:10:57 +08:00

51 lines
1.5 KiB
Go

package loop
import (
"context"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/trace"
)
// span starts a named span of the given kind when a Tracer is configured, else
// returns a nil no-op span. The returned context carries the span as the current
// one so nested spans chain as children. When a FlowPublisher is configured the
// span's session (threaded via ctx) is stamped as a prop and a running placeholder
// node is published immediately, so the flow graph shows work in flight.
func (l *Loop) span(ctx context.Context, name string, kind trace.SpanKind) (context.Context, *trace.Span) {
if l.tracer == nil {
return ctx, nil
}
sctx, sp := l.tracer.StartSpan(ctx, name, kind)
if sid := loopSession(ctx); sid != "" {
_ = sp.SetProp("session", sid)
if l.flow != nil {
l.flow.SpanStarted(sid, sp)
}
}
return sctx, sp
}
// finishSpan closes a span with the given status, ignoring a nil span. After the
// span closes (immutable) the finished record is folded into the flow graph and
// published, flipping the node to its final status.
func (l *Loop) finishSpan(sp *trace.Span, status trace.SpanStatus) {
if sp == nil {
return
}
sp.Finish(status)
if l.flow != nil {
if sid, ok := sp.Props["session"].(string); ok && sid != "" {
l.flow.SpanFinished(sid, sp)
}
}
}
// spanProp attaches a structured property to a span, ignoring a nil span and any
// mutation-after-close error.
func (l *Loop) spanProp(sp *trace.Span, key string, value any) {
if sp == nil {
return
}
_ = sp.SetProp(key, value)
}