Documentation
¶
Overview ¶
Package analytics provides terminal escape code extraction and analysis
Index ¶
- func DescribeSGR(params string) string
- func DescribeSimpleEscape(secondByte byte) string
- func GetDECPrivateModeDescription(mode string) string
- func GetEraseInDisplayDescription(params string) string
- func GetEraseInLineDescription(params string) string
- func GetGlobalEscapeWriterDroppedCount() int64
- func GetOSCDescription(cmd string) string
- func SetGlobalEscapeWriter(w EscapeEventWriter)
- type EscapeCategory
- type EscapeCodeEntry
- type EscapeCodeParser
- func (p *EscapeCodeParser) GetStats() ParserStats
- func (p *EscapeCodeParser) IsEnabled() bool
- func (p *EscapeCodeParser) Parse(data []byte, sessionSeq int64) []byte
- func (p *EscapeCodeParser) ParseStage2(data []byte, sessionSeq int64)
- func (p *EscapeCodeParser) RunCorrelatorEviction(ctx context.Context)
- func (p *EscapeCodeParser) SetCorrelator(c *MangleCorrelator)
- func (p *EscapeCodeParser) SetEnabled(enabled bool)
- func (p *EscapeCodeParser) SetEventWriter(w EscapeEventWriter, captureLevel string, redactOSC bool, samplingRate float64)
- func (p *EscapeCodeParser) SetProjectPath(path string)
- func (p *EscapeCodeParser) SetStableSessionID(id string)
- type EscapeCodeStats
- type EscapeCodeStore
- func (s *EscapeCodeStore) Clear()
- func (s *EscapeCodeStore) Export() ([]byte, error)
- func (s *EscapeCodeStore) GetAll() []EscapeCodeEntry
- func (s *EscapeCodeStore) GetByCategory(category EscapeCategory) []EscapeCodeEntry
- func (s *EscapeCodeStore) GetBySession(sessionID string) []EscapeCodeEntry
- func (s *EscapeCodeStore) GetStats() EscapeCodeStats
- func (s *EscapeCodeStore) IsEnabled() bool
- func (s *EscapeCodeStore) Record(sessionID string, rawBytes []byte, category EscapeCategory, description string)
- func (s *EscapeCodeStore) SetEnabled(enabled bool)
- type EscapeEventRecord
- type EscapeEventWriter
- type EscapeEventWriterStats
- type MangleCorrelator
- func (c *MangleCorrelator) CheckStage2(sessionID, sequenceType, hash string, byteLen int) (bool, string)
- func (c *MangleCorrelator) EvictExpired(ctx context.Context, writer EscapeEventWriter)
- func (c *MangleCorrelator) ObserveTransport(sessionID string)
- func (c *MangleCorrelator) PendingCount() int
- func (c *MangleCorrelator) PruneStaleOrdinals()
- func (c *MangleCorrelator) RecordStage1(sessionID, sequenceType, hash string, byteLen int)
- func (c *MangleCorrelator) RecordStage1WithMetadata(sessionID, projectPath, sequenceType, sequenceSignature, hash string, ...)
- func (c *MangleCorrelator) StartEviction(ctx context.Context, writer EscapeEventWriter)
- type NoopEscapeEventWriter
- type ParsedEscapeCode
- type ParserStats
- type Stage
- type Stage1Observation
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DescribeSGR ¶
DescribeSGR returns a human-readable description of SGR parameters
func DescribeSimpleEscape ¶
DescribeSimpleEscape returns a description for a simple 2-byte escape
func GetDECPrivateModeDescription ¶
GetDECPrivateModeDescription returns a human-readable description for a DEC private mode
func GetEraseInDisplayDescription ¶
Erase in Display descriptions
func GetEraseInLineDescription ¶
Erase in Line descriptions
func GetGlobalEscapeWriterDroppedCount ¶ added in v1.54.0
func GetGlobalEscapeWriterDroppedCount() int64
GetGlobalEscapeWriterDroppedCount reports events lost to writer backpressure.
func GetOSCDescription ¶
GetOSCDescription returns a human-readable description for an OSC command
func SetGlobalEscapeWriter ¶
func SetGlobalEscapeWriter(w EscapeEventWriter)
SetGlobalEscapeWriter replaces the process-wide escape event writer. Called once at server startup after the batch writer is created.
Types ¶
type EscapeCategory ¶
type EscapeCategory string
EscapeCategory represents the type of escape sequence
const ( CategoryCSI EscapeCategory = "CSI" // Control Sequence Introducer \x1b[ CategoryOSC EscapeCategory = "OSC" // Operating System Command \x1b] CategoryDCS EscapeCategory = "DCS" // Device Control String \x1bP CategoryPM EscapeCategory = "PM" // Privacy Message \x1b^ CategoryAPC EscapeCategory = "APC" // Application Program Command \x1b_ CategorySOS EscapeCategory = "SOS" // Start of String \x1bX CategoryC1 EscapeCategory = "C1" // C1 control codes CategorySimple EscapeCategory = "Simple" // Simple 2-char escapes CategoryDECPriv EscapeCategory = "DECPriv" // DEC Private modes \x1b[? CategorySGR EscapeCategory = "SGR" // Select Graphic Rendition (colors/styles) CategoryCursor EscapeCategory = "Cursor" // Cursor positioning CategoryErase EscapeCategory = "Erase" // Screen/line erase CategoryScroll EscapeCategory = "Scroll" // Scroll region CategoryCharset EscapeCategory = "Charset" // Character set selection CategoryUnknown EscapeCategory = "Unknown" // Unknown sequence )
type EscapeCodeEntry ¶
type EscapeCodeEntry struct {
Code string `json:"code"` // Hex-encoded sequence
HumanReadable string `json:"humanReadable"` // Description
Category EscapeCategory `json:"category"` // Type of sequence
Count int64 `json:"count"` // How many times seen
FirstSeen time.Time `json:"firstSeen"` // First occurrence
LastSeen time.Time `json:"lastSeen"` // Most recent occurrence
SessionIDs []string `json:"sessionIds"` // Sessions that produced this code
}
EscapeCodeEntry represents a tracked escape sequence
type EscapeCodeParser ¶
type EscapeCodeParser struct {
// contains filtered or unexported fields
}
EscapeCodeParser extracts escape sequences from terminal output
func NewEscapeCodeParser ¶
func NewEscapeCodeParser(store *EscapeCodeStore, sessionID string) *EscapeCodeParser
NewEscapeCodeParser creates a new parser with the given store and session ID
func (*EscapeCodeParser) GetStats ¶
func (p *EscapeCodeParser) GetStats() ParserStats
GetStats returns lifetime counters for this parser.
func (*EscapeCodeParser) IsEnabled ¶
func (p *EscapeCodeParser) IsEnabled() bool
IsEnabled returns whether parsing is enabled
func (*EscapeCodeParser) Parse ¶
func (p *EscapeCodeParser) Parse(data []byte, sessionSeq int64) []byte
Parse extracts all escape sequences from data and records them to the store. sessionSeq is the cumulative PTY byte offset at the start of this chunk. Returns the original data unchanged (passthrough).
func (*EscapeCodeParser) ParseStage2 ¶
func (p *EscapeCodeParser) ParseStage2(data []byte, sessionSeq int64)
ParseStage2 performs a secondary parse pass over data (a coalesced transport frame) and emits EscapeEventRecord entries with Stage=StageTransport. sessionSeq is the cumulative transport byte offset at the start of this frame. ParseStage2 uses its own independent chunk counter (stage2ChunkSeqNum) for sampling so that calling it independently does not interfere with the Stage 1 counter.
func (*EscapeCodeParser) RunCorrelatorEviction ¶
func (p *EscapeCodeParser) RunCorrelatorEviction(ctx context.Context)
RunCorrelatorEviction runs the attached correlator's eviction loop until ctx is cancelled. No-op if no correlator or writer is configured (e.g. capture_level=off). Blocks the calling goroutine — callers that need this to run in the background must launch it themselves (e.g. `go p.RunCorrelatorEviction(ctx)`, ideally tracked with the same WaitGroup used for their other background work so a "Stop" method can block until this has actually exited too). Recovers from panics so a bug here can't take down the whole process.
func (*EscapeCodeParser) SetCorrelator ¶
func (p *EscapeCodeParser) SetCorrelator(c *MangleCorrelator)
SetCorrelator attaches a MangleCorrelator to this parser for Stage 1/2 mangle detection.
func (*EscapeCodeParser) SetEnabled ¶
func (p *EscapeCodeParser) SetEnabled(enabled bool)
SetEnabled enables or disables escape code parsing
func (*EscapeCodeParser) SetEventWriter ¶
func (p *EscapeCodeParser) SetEventWriter(w EscapeEventWriter, captureLevel string, redactOSC bool, samplingRate float64)
SetEventWriter configures the event writer and capture settings.
func (*EscapeCodeParser) SetProjectPath ¶ added in v1.54.0
func (p *EscapeCodeParser) SetProjectPath(path string)
SetProjectPath records the canonical project path alongside future events.
func (*EscapeCodeParser) SetStableSessionID ¶
func (p *EscapeCodeParser) SetStableSessionID(id string)
SetStableSessionID overrides the session identifier recorded on emitted events. Used to switch from the tmux session name (used at construction time) to the stable session UUID once it is known, so escape_event rows can be correlated with the same session identifier the rest of the app uses. Safe to call at any time, including concurrently with Parse/ParseStage2 on a running stream — the name matches ResponseStream.SetStableSessionID, which wraps it, so both ends of this wiring are textually distinguishable from tmux-name-keyed setters (like detection.StatusDetector.SetSessionID) called nearby at the same call site.
type EscapeCodeStats ¶
type EscapeCodeStats struct {
Enabled bool `json:"enabled"`
TotalCodes int64 `json:"totalCodes"` // Total codes recorded
UniqueCodes int `json:"uniqueCodes"` // Unique sequences
CategoryCounts map[EscapeCategory]int64 `json:"categoryCounts"` // Count by category
TopCodes []EscapeCodeEntry `json:"topCodes"` // Most frequent codes
RecentCodes []EscapeCodeEntry `json:"recentCodes"` // Recently seen codes
}
EscapeCodeStats provides aggregated statistics
type EscapeCodeStore ¶
type EscapeCodeStore struct {
// contains filtered or unexported fields
}
EscapeCodeStore provides thread-safe storage for escape code analytics
func GetGlobalStore ¶
func GetGlobalStore() *EscapeCodeStore
GetGlobalStore returns the singleton escape code store
func NewEscapeCodeStore ¶
func NewEscapeCodeStore() *EscapeCodeStore
NewEscapeCodeStore creates a new store with default settings
func (*EscapeCodeStore) Export ¶
func (s *EscapeCodeStore) Export() ([]byte, error)
Export returns all data as JSON
func (*EscapeCodeStore) GetAll ¶
func (s *EscapeCodeStore) GetAll() []EscapeCodeEntry
GetAll returns all entries
func (*EscapeCodeStore) GetByCategory ¶
func (s *EscapeCodeStore) GetByCategory(category EscapeCategory) []EscapeCodeEntry
GetByCategory returns entries for a specific category
func (*EscapeCodeStore) GetBySession ¶
func (s *EscapeCodeStore) GetBySession(sessionID string) []EscapeCodeEntry
GetBySession returns entries for a specific session
func (*EscapeCodeStore) GetStats ¶
func (s *EscapeCodeStore) GetStats() EscapeCodeStats
GetStats returns aggregated statistics
func (*EscapeCodeStore) IsEnabled ¶
func (s *EscapeCodeStore) IsEnabled() bool
IsEnabled returns whether tracking is enabled
func (*EscapeCodeStore) Record ¶
func (s *EscapeCodeStore) Record(sessionID string, rawBytes []byte, category EscapeCategory, description string)
Record adds or updates an escape code entry
func (*EscapeCodeStore) SetEnabled ¶
func (s *EscapeCodeStore) SetEnabled(enabled bool)
SetEnabled enables or disables tracking
type EscapeEventRecord ¶
type EscapeEventRecord struct {
SessionID string
ProjectPath string
Stage Stage
SequenceType string // "CSI", "OSC", "DCS", etc.
SequenceSubtype string // e.g. "SGR", "cursor-up", "clipboard"
SequenceSignature string // privacy-safe normalized command, e.g. "CSI:?25h" or "OSC:52"
ByteLen int
PayloadHash string // SHA-256 hex prefix, empty if redacted
RawBytes []byte // nil unless capture_level=full
Mangled bool
MangleType string // "truncated", "mutated", "stripped"
WallTime time.Time
SessionSeq int64 // cumulative PTY byte offset at start of chunk
}
EscapeEventRecord holds a single observed escape sequence event.
type EscapeEventWriter ¶
type EscapeEventWriter interface {
WriteEscapeEvent(ctx context.Context, event EscapeEventRecord)
}
EscapeEventWriter is the interface for persisting escape events.
func GetGlobalEscapeWriter ¶
func GetGlobalEscapeWriter() EscapeEventWriter
GetGlobalEscapeWriter returns the process-wide escape event writer.
type EscapeEventWriterStats ¶ added in v1.54.0
type EscapeEventWriterStats interface {
DroppedCount() int64
}
EscapeEventWriterStats is implemented by writers that expose capture loss.
type MangleCorrelator ¶
type MangleCorrelator struct {
// contains filtered or unexported fields
}
func NewMangleCorrelator ¶
func NewMangleCorrelator(maxAge time.Duration, maxSize int) *MangleCorrelator
NewMangleCorrelator creates a correlator with the given TTL and max pending size.
func (*MangleCorrelator) CheckStage2 ¶
func (c *MangleCorrelator) CheckStage2(sessionID, sequenceType, hash string, byteLen int) (bool, string)
CheckStage2 checks whether the next Stage 2 observation for this (sessionID, sequenceType) pair — the next ordinal — matches its corresponding Stage 1 observation. Returns (mangled bool, mangleType string). mangleType is one of: "", "truncated", "mutated". If no Stage 1 observation is found at that ordinal, returns (false, "") — absence is not definitive; the eviction pass handles the "never arrived" (stripped) case.
func (*MangleCorrelator) EvictExpired ¶
func (c *MangleCorrelator) EvictExpired(ctx context.Context, writer EscapeEventWriter)
EvictExpired removes observations older than maxAge and emits them as "stripped" escape events. Call this periodically (e.g., every maxAge/2).
Deliberately scans only c.pending, not the ordinal-counter maps (see PruneStaleOrdinals) — this runs on StartEviction's frequent maxAge/2 ticker (2.5s for the production TTL), so its critical section stays as short as live pprof/Pyroscope evidence showed matters: on a production instance running ~50-100 concurrent sessions, mutex-contention profiling attributed 1,229 events / 224.6s of waiter delay to this function — ~183ms average per event, far too long for a map scan this small to explain on its own. Cross-referencing the same window's CPU profile (17% in runtime.gcBgMarkWorker/scanSpan) and `uptime`'s load average (32 runnable threads on an 18-core machine) pins the real cause on scheduler/GC preemption while the lock is held, not this critical section's own cost — so the fix here is to minimize exposure (shrink what runs under the frequent lock), not to restructure the lock itself; the systemic fix is reducing allocation/goroutine pressure elsewhere.
func (*MangleCorrelator) ObserveTransport ¶ added in v1.54.0
func (c *MangleCorrelator) ObserveTransport(sessionID string)
ObserveTransport records that a transport is actively carrying output for the session. This prevents sessions with no consumer from being reported as stripped merely because Stage 2 was never expected.
func (*MangleCorrelator) PendingCount ¶
func (c *MangleCorrelator) PendingCount() int
PendingCount returns the number of unmatched Stage 1 observations (for monitoring).
func (*MangleCorrelator) PruneStaleOrdinals ¶ added in v1.54.0
func (c *MangleCorrelator) PruneStaleOrdinals()
PruneStaleOrdinals removes stage1Ordinals/stage2Ordinals/ordinalLastSeen entries for (session, type) pairs that have gone quiet for longer than maxAge: a session that ended (or a sequence type it stopped emitting) leaves these with no further writer to ever clean them up otherwise. Split out of EvictExpired (see its doc comment) so this second map scan doesn't add to the lock hold time of the frequent maxAge/2 eviction tick — StartEviction calls this on its own, much less frequent, ordinalPruneEveryNTicks cadence instead, since staleness here is only ever session-idle-scale, not sequence-TTL-scale.
func (*MangleCorrelator) RecordStage1 ¶
func (c *MangleCorrelator) RecordStage1(sessionID, sequenceType, hash string, byteLen int)
RecordStage1 records a Stage 1 observation for later correlation. It is assigned the next ordinal for this (sessionID, sequenceType) pair.
func (*MangleCorrelator) RecordStage1WithMetadata ¶ added in v1.54.0
func (c *MangleCorrelator) RecordStage1WithMetadata(sessionID, projectPath, sequenceType, sequenceSignature, hash string, byteLen int)
RecordStage1WithMetadata preserves project and normalized command attribution when an unmatched observation is later emitted as stripped.
func (*MangleCorrelator) StartEviction ¶
func (c *MangleCorrelator) StartEviction(ctx context.Context, writer EscapeEventWriter)
StartEviction starts a background goroutine that calls EvictExpired periodically, and PruneStaleOrdinals every ordinalPruneEveryNTicks-th tick. Returns when ctx is cancelled.
type NoopEscapeEventWriter ¶
type NoopEscapeEventWriter struct{}
NoopEscapeEventWriter discards all events (used when capture_level=off).
func (NoopEscapeEventWriter) WriteEscapeEvent ¶
func (n NoopEscapeEventWriter) WriteEscapeEvent(_ context.Context, _ EscapeEventRecord)
type ParsedEscapeCode ¶
type ParsedEscapeCode struct {
RawBytes []byte // Original bytes
Category EscapeCategory // Type of sequence
Description string // Human-readable description
StartOffset int // Position in original data
EndOffset int // End position in original data
}
ParsedEscapeCode represents a single parsed escape sequence
type ParserStats ¶
ParserStats holds lifetime counters for a parser session.