Documentation
¶
Overview ¶
Package storage 为 tracing 采集记录提供本地分段存储、查询和健康投影。
连接采集循环 ¶
Engine 实现 tracing.Handler。通常由 Run 同时运行 tracing 的批量采集循环和本存储配置的 自动清理:
engine, err := storage.Open(ctx, "data/tracing")
if err != nil {
return err
}
defer engine.Close()
go func() {
if err := engine.Run(ctx); err != nil && !errors.Is(err, context.Canceled) {
reportError(err)
}
}()
业务代码只依赖 tracing 并产生 Trace、Span、Event 和 Log,不需要依赖 storage。需要自行组合 消费者时,也可以把 Engine 直接传给 tracing.RunHandler。
打开与写入 ¶
Open 接收存储目录。一个目录同一时刻只能由一个 Engine 打开;Engine 的公开方法可以由多个 goroutine 并发调用:
engine, err := storage.Open(
ctx,
"data/tracing",
storage.WithCapacity(2<<30, 0.90, 0.75),
)
if err != nil {
return err
}
defer engine.Close()
_, err = engine.Store(ctx, storage.Batch{
Traces: []storage.Trace{{
ID: traceID,
CreatedAt: time.Now(),
}},
})
一次 Batch 被编码为一个带 CRC32C 校验的追加帧。Store 会先校验记录、父级和 Span 树,再写入 完整帧,因此同一个 Batch 内的 Trace、Span、Event、Log 和 SpanEnd 具有共同的追加边界。 同一类记录按 ID 使用“第一次成功写入生效”语义;在原始记录或对应清理标记仍保留期间, 重复写入不会重复更新健康统计。健康快照只保存自然日聚合和有限排查样本,不保存逐 Log ID 集合,因此其大小由服务数、保留天数和样本上限决定,而不是由全部 Log 数量决定。
索引与恢复 ¶
原始记录保存在只追加的分段文件中;Engine 使用 google/btree 和 ID 映射维护内存索引,查询 负载按记录引用从分段读取。MANIFEST 和健康快照通过临时文件、fsync 和 rename 原子替换。 Open 会重放 MANIFEST 中的分段;活动分段末尾未写完或校验失败的最后一帧会被截断,已封存 分段中的损坏则返回 ErrCorrupt,避免静默接受中间数据损坏。
默认 Store 不为每个 Batch 执行 fsync,以减少高频观测写入对业务 I/O 的影响。进程正常退出时 Close 会同步活动分段和健康快照;需要 Store 返回即具备更强落盘保证时,使用 WithSyncWrites(true)。该保证仍不等同于跨机器复制或业务事务。
容量与清理 ¶
Engine 同时受时间和物理容量约束。默认策略如下:
- 原始 Trace 超过 7 天后整条删除;
- 未达到高水位时,7 天内的原始数据不因级别而提前清理;
- 达到高水位后,超过 3、4、5、6 天的 Debug、Info、Warn、Error Log 依次具备清理资格;
- Critical Log 在原始 Trace 保留期内不按级别提前清理;
- 超过硬容量时,先清理较旧 Log,仍不足时才删除最旧 Trace;
- 服务健康投影默认独立保留 180 天,原始父记录删除后样本仍可查询,并返回 ParentsAvailable=false。
Cleanup 生成有界逻辑删除记录,查询结果立即反映清理。达到容量高水位或失效记录占比达到 阈值后,Engine 才把仍可查询的原始记录压缩到新分段,避免少量过期记录触发整库重写;也可 调用 Compact 主动压缩。替换 MANIFEST 前会先持久化健康快照,因此压缩不会依赖已被替换的 原始 Log 重新计算健康统计。WithRetention 的 health=0 表示不自动清理健康投影。
使用边界 ¶
Engine 用于允许过载丢失、按容量退化的非业务观测数据。它不提供外键、跨进程并发写、远程 复制、分布式一致性、备份恢复或业务事务语义。应用应在 Handler 停止并排空后调用 Close,且 不应直接修改存储目录中的文件。Observe 只发布成功提交后的轻量 ID 和健康自然日变化,供 查询适配层失效缓存或生成增量;它不保存 HTTP 连接、展示 DTO 或前端状态。
Index ¶
- Constants
- Variables
- type Attr
- type Batch
- type Changes
- type CleanupResult
- type Engine
- func (engine *Engine) Cleanup(ctx context.Context) (CleanupResult, error)
- func (engine *Engine) CleanupAt(ctx context.Context, now time.Time) (CleanupResult, error)
- func (engine *Engine) Close() error
- func (engine *Engine) Compact(ctx context.Context) error
- func (engine *Engine) Handle(ctx context.Context, batch tracing.Batch) error
- func (engine *Engine) NeedsCleanup() bool
- func (engine *Engine) Observe(observer Observer) (revision uint64, unsubscribe func(), err error)
- func (engine *Engine) QueryEvent(ctx context.Context, eventID string) (*Event, error)
- func (engine *Engine) QueryServiceSamples(ctx context.Context, service string, day string) ([]ServiceSample, error)
- func (engine *Engine) QueryServices(ctx context.Context, query ServiceQuery) ([]ServiceView, error)
- func (engine *Engine) QuerySpans(ctx context.Context, traceID string, query SpanQuery) (SpanPage, error)
- func (engine *Engine) QueryTimeline(ctx context.Context, traceID string, query TimelineQuery) (TimelinePage, error)
- func (engine *Engine) QueryTrace(ctx context.Context, traceID string) (*TraceView, error)
- func (engine *Engine) QueryTraces(ctx context.Context, query TraceQuery) (TracePage, error)
- func (engine *Engine) Run(ctx context.Context) error
- func (engine *Engine) RunWithReady(ctx context.Context, ready chan<- struct{}) error
- func (engine *Engine) Stats() (Stats, error)
- func (engine *Engine) Store(ctx context.Context, batch Batch) (Changes, error)
- func (engine *Engine) Sync(ctx context.Context) error
- type Event
- type HealthDay
- type Level
- type LevelCounts
- type Log
- type Observer
- type Option
- func WithCapacity(maxBytes int64, highWatermark, lowWatermark float64) Option
- func WithCleanup(interval time.Duration, batchSize int) Option
- func WithCleanupBatchSize(size int) Option
- func WithLevelAges(debug, info, warn, errorAge time.Duration) Option
- func WithLocation(location *time.Location) Option
- func WithRetention(raw, health time.Duration) Option
- func WithSegmentBytes(size int64) Option
- func WithSyncWrites(enabled bool) Option
- type ServiceCurrent
- type ServiceDay
- type ServiceQuery
- type ServiceSample
- type ServiceStatus
- type ServiceView
- type Span
- type SpanEnd
- type SpanPage
- type SpanQuery
- type Stats
- type TimelineItem
- type TimelineKind
- type TimelinePage
- type TimelineQuery
- type Trace
- type TracePage
- type TraceQuery
- type TraceSummary
- type TraceView
- type Update
Constants ¶
const ( DefaultSegmentBytes int64 = 64 << 20 DefaultMaxBytes int64 = 512 << 20 DefaultHighWatermark = 0.90 DefaultLowWatermark = 0.75 DefaultRawRetention = 7 * 24 * time.Hour DefaultHealthRetention = 180 * 24 * time.Hour DefaultCleanupInterval = time.Hour DefaultCleanupBatchSize = 5000 DefaultQueryLimit = 100 MaxQueryLimit = 500 )
Variables ¶
var ( // ErrClosed 表示 Engine 已关闭。 ErrClosed = errors.New("tracing storage: closed") // ErrLocked 表示存储目录正被另一个 Engine 实例占用。 ErrLocked = errors.New("tracing storage: directory locked") // ErrCorrupt 表示持久化文件损坏且无法安全恢复。 ErrCorrupt = errors.New("tracing storage: corrupt data") // ErrInvalidRecord 表示 Batch 包含缺少标识、时间或父记录的记录。 ErrInvalidRecord = errors.New("tracing storage: invalid record") // ErrNotFound 表示指定记录不存在或已经被清理。 ErrNotFound = errors.New("tracing storage: record not found") // ErrInvalidCursor 表示查询游标无效或不属于当前查询。 ErrInvalidCursor = errors.New("tracing storage: invalid cursor") // ErrInvalidQuery 表示查询参数无效。 ErrInvalidQuery = errors.New("tracing storage: invalid query") )
Functions ¶
This section is empty.
Types ¶
type Attr ¶
type Attr struct {
Key string `json:"k"`
Kind string `json:"t"`
Value json.RawMessage `json:"v"`
}
Attr 是已经完成类型校验的属性编码。
Value 必须保存一个合法 JSON 值。storage 不解释 Kind;上层负责按 Kind 恢复具体 Go 类型。
type Batch ¶
type Batch struct {
Traces []Trace `json:"traces,omitempty"`
Spans []Span `json:"spans,omitempty"`
Events []Event `json:"events,omitempty"`
Logs []Log `json:"logs,omitempty"`
SpanEnds []SpanEnd `json:"span_ends,omitempty"`
}
Batch 是一次原子追加的记录集合。
type Changes ¶
type Changes struct {
TraceIDs []string `json:"trace_ids,omitempty"`
SpanIDs []string `json:"span_ids,omitempty"`
EventIDs []string `json:"event_ids,omitempty"`
LogIDs []string `json:"log_ids,omitempty"`
Services []string `json:"services,omitempty"`
}
Changes 描述一次 Store 实际新增或修改的记录。
type CleanupResult ¶
type CleanupResult struct {
PrunedLogs int `json:"pruned_logs"`
RemovedTraces int `json:"removed_traces"`
RemovedSpans int `json:"removed_spans"`
RemovedEvents int `json:"removed_events"`
RemovedLogs int `json:"removed_logs"`
RemovedHealthDay int `json:"removed_health_days"`
CompactedBytes int64 `json:"compacted_bytes"`
BeforeBytes int64 `json:"before_bytes"`
AfterBytes int64 `json:"after_bytes"`
TraceIDs []string `json:"trace_ids,omitempty"`
HasMore bool `json:"has_more"`
}
CleanupResult 描述一次清理和压缩结果。
func (CleanupResult) Changed ¶
func (result CleanupResult) Changed() bool
Changed 报告清理是否改变了可查询数据或物理文件。
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine 是单目录、单进程写入的 tracing 分段存储引擎。
Engine 的公开方法可以由多个 goroutine 并发调用。一个目录同一时刻只能被一个 Engine 打开;Close 会同步活动分段和健康快照并释放目录锁。
func (*Engine) Cleanup ¶
func (engine *Engine) Cleanup(ctx context.Context) (CleanupResult, error)
Cleanup 按当前时间执行一次有界分级清理,并按需回收失效分段空间。
func (*Engine) Observe ¶
func (engine *Engine) Observe(observer Observer) ( revision uint64, unsubscribe func(), err error, )
Observe 注册一个存储变化观察者。
revision 是注册完成时的最新修订号;unsubscribe 可安全重复调用。observer 不能为 nil。
func (*Engine) QueryEvent ¶
QueryEvent 按 Event ID 返回仍保留的 Event。
func (*Engine) QueryServiceSamples ¶
func (engine *Engine) QueryServiceSamples( ctx context.Context, service string, day string, ) ([]ServiceSample, error)
QueryServiceSamples 返回服务指定自然日内的有限样本。
func (*Engine) QueryServices ¶
func (engine *Engine) QueryServices(ctx context.Context, query ServiceQuery) ([]ServiceView, error)
QueryServices 返回服务健康投影。原始 Trace 清理不会减少历史计数。
func (*Engine) QuerySpans ¶
func (engine *Engine) QuerySpans( ctx context.Context, traceID string, query SpanQuery, ) (SpanPage, error)
QuerySpans 返回一条 Trace 的 Span,顺序为 started_at、span_id 正序。
func (*Engine) QueryTimeline ¶
func (engine *Engine) QueryTimeline( ctx context.Context, traceID string, query TimelineQuery, ) (TimelinePage, error)
QueryTimeline 返回一条 Trace 的 Event 和当前仍保留的 Log,顺序为时间倒序。
func (*Engine) QueryTrace ¶
QueryTrace 返回一条仍保留原始数据的 Trace。
func (*Engine) QueryTraces ¶
QueryTraces 返回按 last seen、Trace ID 倒序排列的一页。
func (*Engine) Run ¶
Run 运行 tracing 采集循环,并按配置执行自动清理。
同一进程同一时间只能运行一个 tracing 采集循环。ctx 取消后,Run 会等待采集队列尽量排空 并停止自动清理,然后返回 ctx.Err()。
func (*Engine) RunWithReady ¶
RunWithReady 与 Engine.Run 相同,但会在 tracing 采集循环开始接收记录后通知 ready。
ready 不能为 nil。该方法主要供应用生命周期编排使用;普通调用方应继续使用 Run。
type Event ¶
type Event struct {
ID string `json:"id"`
TraceID string `json:"trace_id"`
SpanID string `json:"span_id"`
Name string `json:"name"`
Timestamp time.Time `json:"timestamp"`
Attrs []Attr `json:"attrs,omitempty"`
}
Event 是 Span 内发生的一项结构化事实。
type Log ¶
type Log struct {
ID string `json:"id"`
TraceID string `json:"trace_id"`
SpanID string `json:"span_id"`
Service string `json:"service,omitempty"`
Level Level `json:"level"`
Timestamp time.Time `json:"timestamp"`
What string `json:"what"`
Detail string `json:"detail"`
Attrs []Attr `json:"attrs,omitempty"`
}
Log 是 Span 内的一条业务观测记录。
type Observer ¶
type Observer func(Update)
Observer 接收 Engine 成功提交后的轻量变化通知。
Observer 在 Store、Cleanup 或 Close 的调用 goroutine 中执行,必须快速返回,不能执行 I/O 或长时间阻塞。需要查询或聚合时,应只把通知转交给自己的队列。
type Option ¶
type Option func(*options)
Option 配置 Engine。
func WithCapacity ¶
WithCapacity 设置物理容量、高水位和压缩目标水位。
func WithCleanupBatchSize ¶
WithCleanupBatchSize 设置一次 Cleanup 最多生成的清理操作数。
func WithLevelAges ¶
WithLevelAges 设置各级 Log 进入可清理状态的年龄。
四个值必须非递减且不能超过原始 Trace 保留时间。Critical 跟随原始 Trace 保留时间, 不会按级别提前清理。最终约束在全部 Option 应用后统一校验,因此 Option 顺序不影响结果。
func WithRetention ¶
WithRetention 设置原始 Trace 和健康投影的保留时间。
raw 必须大于 0。health 小于 0 时无效,等于 0 时不自动清理健康投影。
func WithSyncWrites ¶
WithSyncWrites 控制 Store 返回前是否对活动分段执行 fsync。
type ServiceCurrent ¶
type ServiceCurrent struct {
Service string `json:"service"`
Status ServiceStatus `json:"status"`
FirstSeenAt time.Time `json:"first_seen_at"`
LastSeenAt time.Time `json:"last_seen_at"`
LastLogID string `json:"last_log_id"`
LastTraceID string `json:"last_trace_id"`
LastSpanID string `json:"last_span_id"`
LastLevel Level `json:"last_level"`
LastWhat string `json:"last_what"`
LastDetail string `json:"last_detail"`
UpdatedAt time.Time `json:"updated_at"`
}
ServiceCurrent 是服务最近一条 Log 派生的状态。
type ServiceDay ¶
type ServiceDay struct {
Day string `json:"day"`
StartedAt time.Time `json:"started_at"`
EndedAt time.Time `json:"ended_at"`
Status ServiceStatus `json:"status"`
Counts LevelCounts `json:"counts"`
FirstSeenAt *time.Time `json:"first_seen_at,omitempty"`
LastSeenAt *time.Time `json:"last_seen_at,omitempty"`
LastLogID string `json:"last_log_id,omitempty"`
LastLevel *Level `json:"last_level,omitempty"`
Preview *ServiceSample `json:"preview,omitempty"`
}
ServiceDay 是一个服务自然日内的精确级别计数。
type ServiceQuery ¶
ServiceQuery 描述服务健康查询。
type ServiceSample ¶
type ServiceSample struct {
LogID string `json:"log_id"`
Service string `json:"service"`
Day string `json:"day"`
Level Level `json:"level"`
TraceID string `json:"trace_id"`
SpanID string `json:"span_id"`
What string `json:"what"`
Detail string `json:"detail"`
Attrs []Attr `json:"attrs,omitempty"`
SeenAt time.Time `json:"seen_at"`
ParentsAvailable bool `json:"parents_available"`
}
ServiceSample 是独立于原始 Trace 保留的一条排查样本。
type ServiceStatus ¶
type ServiceStatus string
ServiceStatus 是服务健康展示状态。
const ( ServiceUnknown ServiceStatus = "unknown" ServiceOK ServiceStatus = "ok" ServiceWarn ServiceStatus = "warn" ServiceError ServiceStatus = "error" ServiceCritical ServiceStatus = "critical" )
type ServiceView ¶
type ServiceView struct {
Current ServiceCurrent `json:"current"`
Availability float64 `json:"availability"`
ErrorRate float64 `json:"error_rate"`
HistoryLogCount uint64 `json:"history_log_count"`
History []ServiceDay `json:"history"`
}
ServiceView 是服务当前状态和连续自然日历史。
type Span ¶
type Span struct {
ID string `json:"id"`
TraceID string `json:"trace_id"`
ParentID string `json:"parent_id,omitempty"`
Name string `json:"name"`
StartedAt time.Time `json:"started_at"`
EndedAt *time.Time `json:"ended_at,omitempty"`
Attrs []Attr `json:"attrs,omitempty"`
}
Span 是 Span 创建时的不可变记录。EndedAt 由 SpanEnd 首次补齐。
type SpanEnd ¶
type SpanEnd struct {
TraceID string `json:"trace_id"`
SpanID string `json:"span_id"`
EndedAt time.Time `json:"ended_at"`
}
SpanEnd 记录 Span 第一次结束的时间。
type SpanPage ¶
type SpanPage struct {
Spans []Span `json:"spans"`
HasMore bool `json:"has_more"`
NextCursor string `json:"next_cursor,omitempty"`
}
SpanPage 是按 started_at、span_id 正序排列的 Span 页。
type Stats ¶
type Stats struct {
PhysicalBytes int64 `json:"physical_bytes"`
LiveBytes int64 `json:"live_bytes"`
DeadBytes int64 `json:"dead_bytes"`
Segments int `json:"segments"`
Traces int `json:"traces"`
Spans int `json:"spans"`
Events int `json:"events"`
Logs int `json:"logs"`
Services int `json:"services"`
}
Stats 是 Engine 的轻量运行状态。
type TimelineItem ¶
type TimelineItem struct {
Kind TimelineKind `json:"kind"`
Event *Event `json:"event,omitempty"`
Log *Log `json:"log,omitempty"`
}
TimelineItem 是统一时间线中的一项。
type TimelineKind ¶
type TimelineKind uint8
TimelineKind 区分 Event 与 Log。
const ( TimelineEvent TimelineKind = iota + 1 TimelineLog )
type TimelinePage ¶
type TimelinePage struct {
Items []TimelineItem `json:"items"`
HasMore bool `json:"has_more"`
NextCursor string `json:"next_cursor,omitempty"`
}
TimelinePage 是时间线游标页。
type TimelineQuery ¶
TimelineQuery 描述按时间倒序的 Event/Log 查询。
type Trace ¶
type Trace struct {
ID string `json:"id"`
TriggerEventID string `json:"trigger_event_id,omitempty"`
CreatedAt time.Time `json:"created_at"`
Attrs []Attr `json:"attrs,omitempty"`
}
Trace 是 Trace 创建时的不可变记录。
type TracePage ¶
type TracePage struct {
Traces []TraceSummary `json:"traces"`
HasMore bool `json:"has_more"`
NextCursor string `json:"next_cursor,omitempty"`
}
TracePage 是 Trace 游标页。
type TraceQuery ¶
type TraceQuery struct {
Cursor string
Limit int
From time.Time
To time.Time
MinLevel *Level
TriggerEventID string
Service string
Search string
}
TraceQuery 描述按 last seen 倒序的 Trace 游标查询。
type TraceSummary ¶
type TraceSummary struct {
TraceID string `json:"trace_id"`
TriggerEventID string `json:"trigger_event_id,omitempty"`
CreatedAt time.Time `json:"created_at"`
FirstSeenAt time.Time `json:"first_seen_at"`
LastSeenAt time.Time `json:"last_seen_at"`
HighestLevel *Level `json:"highest_level,omitempty"`
SpanCount int `json:"span_count"`
EventCount int `json:"event_count"`
LogCount int `json:"log_count"`
ObservedLogCount int `json:"observed_log_count"`
ObservedLevels LevelCounts `json:"observed_levels"`
StoredLevels LevelCounts `json:"stored_levels"`
RootSpanName string `json:"root_span_name,omitempty"`
Pruned bool `json:"pruned"`
}
TraceSummary 是不读取属性负载即可返回的 Trace 列表项。
type TraceView ¶
type TraceView struct {
TraceSummary
Attrs []Attr `json:"attrs,omitempty"`
}
TraceView 是带属性的单条 Trace。