Files
Cyrene_v2/internal/services/gateway/observability_trace.go
T
AskaEth 074ffc4ebf
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
docs(gateway): gateway 剩余文件注释改中文(逐行 1:1,代码零字节改动)
主人 2026-10-03 拍板「注释一律中文」;gateway 至此整包注释中文化完成(本会话共 3 批:
已推库 16 个文件 + 本批剩余文件)。
校验:机械校验(非注释改动行)= 空;剥注释后代码文本逐行比对全等(不一致文件数 0);
全树 go test ./... 58 包 ok / 0 FAIL;交叉编译绿。
2026-10-03 22:28:25 +08:00

138 lines
3.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package gateway
import (
"sync"
"time"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/trace"
)
// defaultTraceBufCap 是环形缓冲在逐出最旧 span 之前持有的已
// 完成 span 数。编译期常量。
const defaultTraceBufCap = 512
// AdminTraceSpan 是一个已追踪 span 的驼峰式线上结构。它把
// 前端与 trace 包蛇形命名的 SpanRecord 解耦。
type AdminTraceSpan struct {
ID string `json:"id"`
ParentID string `json:"parentId,omitempty"`
Name string `json:"name"`
Kind string `json:"kind"`
Status string `json:"status"`
StartedAt string `json:"startedAt"`
EndedAt string `json:"endedAt,omitempty"`
DurationNs int64 `json:"durationNs"`
Props map[string]any `json:"props,omitempty"`
Events []trace.Event `json:"events,omitempty"`
}
// RingBuffer 是已完成的 trace span 的并发安全有界缓冲。缓冲
// 满时逐出最旧 span,因此管理面板总能看到
// 最近的工作而不会无界增长。
type RingBuffer struct {
mu sync.RWMutex
buf []trace.SpanRecord
next int
full bool
cap int
}
// newRingBuffer 构建至多容纳 cap 个 span 的环形缓冲(cap<=0 时用默认值)。
func newRingBuffer(cap int) *RingBuffer {
if cap <= 0 {
cap = defaultTraceBufCap
}
return &RingBuffer{buf: make([]trace.SpanRecord, cap), cap: cap}
}
// Append 存储 rec,缓冲满时逐出最旧的。
func (r *RingBuffer) Append(rec trace.SpanRecord) {
r.mu.Lock()
defer r.mu.Unlock()
r.buf[r.next] = rec
r.next = (r.next + 1) % r.cap
if r.next == 0 {
r.full = true
}
}
// Snapshot 返回至多 n 条最近 span,最新优先。非正的 n
// 选用 defaultObsLimit;n 被夹取到 maxObsLimit。
func (r *RingBuffer) Snapshot(n int) []trace.SpanRecord {
if n <= 0 {
n = defaultObsLimit
}
if n > maxObsLimit {
n = maxObsLimit
}
r.mu.RLock()
defer r.mu.RUnlock()
count := r.next
if r.full {
count = r.cap
}
if count > n {
count = n
}
out := make([]trace.SpanRecord, 0, count)
for i := 0; i < count; i++ {
idx := (r.next - 1 - i + r.cap*2) % r.cap
out = append(out, r.buf[idx])
}
return out
}
// traceSink 把 *RingBuffer 适配为 trace.Writer,使已完成的 span 被
// 追加进网关的 trace 缓冲。
type traceSink struct{ b *RingBuffer }
// WriteSpan 把 rec 追加进环形缓冲。
func (s traceSink) WriteSpan(rec trace.SpanRecord) error {
if s.b == nil {
return nil
}
s.b.Append(rec)
return nil
}
// MultiTraceWriter 把已完成的 span 扇出到多个 trace.Writer,使一个
// tracer 能一趟同时记录到网关的环形缓冲与一个 JSONL 文件。nil 的
// Writer 被跳过。它是主会话用来把 loop tracer 的输出引入
// 网关而不丢掉 stderr 的组合根辅助件。
type MultiTraceWriter struct {
// Writers 是 span 被写入的汇聚点。nil 的 Writer 被跳过。
Writers []trace.Writer
}
// WriteSpan 把 rec 写入每一个已配置的 Writer。
func (m MultiTraceWriter) WriteSpan(rec trace.SpanRecord) error {
for _, w := range m.Writers {
if w == nil {
continue
}
if err := w.WriteSpan(rec); err != nil {
return err
}
}
return nil
}
// adminSpanOf 把 trace.SpanRecord 映射为驼峰式管理线上结构。
func adminSpanOf(rec trace.SpanRecord) AdminTraceSpan {
sp := AdminTraceSpan{
ID: rec.ID,
ParentID: rec.ParentID,
Name: rec.Name,
Kind: string(rec.Kind),
Status: string(rec.Status),
StartedAt: rec.StartedAt.UTC().Format(time.RFC3339),
DurationNs: rec.Duration.Nanoseconds(),
Props: rec.Props,
Events: rec.Events,
}
if !rec.EndedAt.IsZero() {
sp.EndedAt = rec.EndedAt.UTC().Format(time.RFC3339)
}
return sp
}