package storage import ( "context" "encoding/json" "git.tornberg.me/go-otel/internal/websocket" "time" ) // SearchOptions narrows a Search call beyond the free-text query. type SearchOptions struct { // Query is a case-insensitive substring match against the raw entry JSON. Query string // Type restricts results to a single telemetry type (traces, metrics, logs). // Empty matches any type. Type string // Since restricts results to entries with Timestamp >= Since (unix nanoseconds). // Zero means no lower bound. Since int64 // Until restricts results to entries with Timestamp <= Until (unix nanoseconds). // Zero means no upper bound. Until int64 } // Store interface for telemetry history persistence. type Store interface { Append(ctx context.Context, entry websocket.HistoryEntry) error GetHistory(ctx context.Context, limit int) ([]websocket.HistoryEntry, error) Search(ctx context.Context, opts SearchOptions, limit int) ([]websocket.HistoryEntry, error) } // StatsQuerier is implemented by stores that can answer aggregate // time-series questions (currently ClickHouseStore). The stats HTTP // endpoints are only enabled when the configured store implements it. type StatsQuerier interface { // ErrorStats returns hourly/daily total and error entry counts. ErrorStats(ctx context.Context, service string, hours int, daily bool) ([]StatsPoint, error) // ReleaseSeries returns per-release error-count time series. ReleaseSeries(ctx context.Context, service string, hours int) (map[string][]StatsPoint, error) // Services lists distinct services seen in the rollup. Services(ctx context.Context) ([]string, error) // EndpointStats groups HTTP spans by normalized request identity and safe // browser metadata. SpanCount exposes proxy/instrumentation duplication; // TraceCount is the better approximation of logical requests. EndpointStats(ctx context.Context, host, service string, hours, limit int) ([]EndpointStat, error) } // EndpointStat is a bounded, privacy-conscious HTTP traffic aggregate. Origin // and referer are host-only values; paths and query strings are normalized by // the query so secrets are not returned as part of endpoint diagnostics. type EndpointStat struct { Service string `json:"service"` Host string `json:"host"` Method string `json:"method"` Path string `json:"path"` Route string `json:"route,omitempty"` StatusCode uint16 `json:"statusCode"` UserAgent string `json:"userAgent,omitempty"` OriginHost string `json:"originHost,omitempty"` RefererHost string `json:"refererHost,omitempty"` FetchSite string `json:"fetchSite,omitempty"` FetchMode string `json:"fetchMode,omitempty"` SpanCount uint64 `json:"spanCount"` TraceCount uint64 `json:"traceCount"` FirstSeen time.Time `json:"firstSeen"` LastSeen time.Time `json:"lastSeen"` } // TraceSummary is the per-trace rollup shown in overview tables: everything // needed to render a row without reading any span payloads. Times are unix // nanoseconds, matching the rest of the API. type TraceSummary struct { ID string `json:"id"` Name string `json:"name"` StartTime int64 `json:"startTime"` EndTime int64 `json:"endTime"` Duration int64 `json:"duration"` SpanCount int `json:"spanCount"` ErrorCount int `json:"errorCount"` Services []string `json:"services"` } // TraceQuerier is implemented by stores that can look a trace up by id // without scanning payloads, and can summarise many traces cheaply // (ClickHouseStore). type TraceQuerier interface { // TraceEntries returns every stored entry that contains the trace, with // each payload trimmed to that trace's spans/log records. Newest first. TraceEntries(ctx context.Context, traceID string, limit int) ([]websocket.HistoryEntry, error) // TraceSummaries returns summaries keyed by trace id; unknown ids are absent. TraceSummaries(ctx context.Context, ids []string) (map[string]TraceSummary, error) } // Occurrence is one persisted occurrence of an issue. Sample is the issue // sample as JSON, so its shape can evolve without a schema change. type Occurrence struct { IssueID string Kind string Service string Timestamp int64 TraceID string SpanID string Sample json.RawMessage } // IssueTrace is one trace an issue occurred in. type IssueTrace struct { TraceID string `json:"traceId"` Hits int `json:"hits"` FirstSeen int64 `json:"firstSeen"` LastSeen int64 `json:"lastSeen"` Summary *TraceSummary `json:"summary,omitempty"` Samples []json.RawMessage `json:"-"` } // IssueTracesOptions pages through an issue's traces, most recently hit first. // Before is a LastSeen boundary (exclusive); zero starts from the newest. type IssueTracesOptions struct { Limit int Before int64 } // IssueTracesPage is one page of an issue's traces plus totals. Untraced holds // occurrences that carried no trace id (only on the first page). type IssueTracesPage struct { Traces []IssueTrace Untraced []json.RawMessage TotalOccurrences int HasMore bool } // OccurrenceStore persists every issue occurrence and answers "which traces // did this issue happen in" in one query (ClickHouseStore). type OccurrenceStore interface { AppendOccurrence(ctx context.Context, occ Occurrence) error IssueTraces(ctx context.Context, issueID string, opts IssueTracesOptions) (IssueTracesPage, error) }