commentator

package
v0.2.0-alpha.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 5, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

README

pkg/controller/commentator

The EventCommentator subscribes to every event on the EventBus and turns them into structured log lines with domain-specific context. It's the single component that logs system behaviour for humans — everything else emits events and lets the commentator describe them.

Key property: the commentator owns its own ring buffer (separate from the generic pkg/events/ringbuffer used by pkg/controller/debug). The controller-specific ring buffer is indexed by event type and correlation ID, which is what the "insight" functions use to add context like "last reconciliation was 5.2s ago" to a log line.

Usage

import "gitlab.com/haproxy-haptic/haptic/pkg/controller/commentator"

// bufferSize is the ring-buffer capacity (how many recent events
// to keep for correlation). 500 is the controller's default
// (hardcoded in pkg/controller/controller.go; not a CRD field).
c := commentator.NewEventCommentator(bus, logger, 500)
go c.Start(ctx)

Subscribe-before-bus.Start() is handled inside the constructor, so buffered startup events land in the logs too.

What Gets Logged

Every domain event produces one log line. Level mapping (see log_levels.go and insights_config.go for the full rules):

  • Error — anything that ends in *FailedEvent that wasn't recoverable (deployment failures, validation failures, etc.).
  • Warn — invalid states that the controller can recover from (config rejected, credentials rejected).
  • Info — lifecycle and successful completion events (controller started, reconciliation completed, became/lost leader).
  • Debug — operational detail (resource index updates, parser progress, HTTP resource fetches).

Shape of a typical line:

INFO  configuration validated successfully version=12345 templates=3
DEBUG resource index updated type=ingresses created=5 modified=2 deleted=1 initial_sync=false
INFO  reconciliation started trigger=config_change since_last=5.2s
INFO  reconciliation completed duration_ms=1234 resources=22
ERROR deployment failed instance=haproxy-0 error="connection refused"

Correlation

The insight functions use the ring-buffer's queries — FindByTypeInWindow and FindByCorrelationID — to decide what context to attach to each log line. Those methods live on the private ringBuffer field; consumers outside this package should subscribe to the event bus directly and do their own correlation.

Adding a Log Line for a New Event Type

When a new event type lands in pkg/controller/events:

  1. Pick a case in generateInsight (or add one) to build the log message.
  2. Pick a case in determineLogLevel (or add one) so it doesn't fall through to the default Debug.
  3. If the message should reference prior events, use the FindByCorrelationID / FindByTypeInWindow helpers on the ring buffer rather than scanning the slice manually.

Missing a case isn't a hard error — the event is still stored in the ring buffer and shows up in debug surfaces — but it logs at Debug with only the generic event fields, which is usually not what you want for a new domain event.

See Also

  • pkg/controller/events — the catalogue this subscribes to
  • pkg/controller/commentator/CLAUDE.md — insight patterns, when to add correlation, what the level-classification rules are
  • pkg/controller/debug — exposes a different ring buffer over /debug/vars/events; consumers of raw event history should look there

License

Apache-2.0 — see root LICENSE.

Documentation

Overview

Package commentator provides the Event Commentator pattern for domain-aware logging.

The Event Commentator subscribes to all EventBus events and produces insightful log messages that apply domain knowledge to explain what's happening in the system, similar to how a sports commentator adds context and analysis to events.

Index

Constants

View Source
const (
	// ComponentName is the unique identifier for this component.
	ComponentName = "commentator"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type EventCommentator

type EventCommentator struct {
	// contains filtered or unexported fields
}

EventCommentator provides domain-aware logging for all events flowing through the EventBus. It decouples logging from business logic.

func NewEventCommentator

func NewEventCommentator(eventBus *busevents.EventBus, logger *slog.Logger, bufferSize int) *EventCommentator

NewEventCommentator creates a new Event Commentator.

Parameters:

  • eventBus: The EventBus to subscribe to
  • logger: The structured logger to use
  • bufferSize: Ring buffer capacity (recommended: 1000)

Returns:

  • *EventCommentator ready to start

func (*EventCommentator) Start

func (ec *EventCommentator) Start(ctx context.Context) error

Start begins processing events from the EventBus.

This method blocks until the context is canceled. The component is already subscribed to the EventBus (subscription happens in constructor). Returns nil on graceful shutdown.

Example:

go commentator.Start(ctx)

type RingBuffer

type RingBuffer struct {
	// contains filtered or unexported fields
}

RingBuffer is a fixed-capacity circular buffer of recent events used by the commentator for cross-event correlation. Typical capacity: 1000 events.

The Find* queries run a linear scan over the buffer. They're called at most a handful of times per reconciliation-log-line against a buffer capped at ~1000 entries, so the scan cost is negligible and not worth maintaining secondary type/correlation indices (which also have to be cleaned up on wraparound). Old events fall out of every query automatically as the buffer overwrites their slots — there is no separate index to leak.

func NewRingBuffer

func NewRingBuffer(capacity int) *RingBuffer

NewRingBuffer creates a new ring buffer with the specified capacity.

Parameters:

  • capacity: Maximum number of events to store (recommended: 1000)

Returns:

  • *RingBuffer ready for use

func (*RingBuffer) Add

func (rb *RingBuffer) Add(event busevents.Event)

Add appends an event to the buffer.

If the buffer is full, the oldest event is overwritten (circular behavior).

This operation is O(1).

func (*RingBuffer) Capacity

func (rb *RingBuffer) Capacity() int

Capacity returns the maximum capacity of the buffer.

func (*RingBuffer) FindByCorrelationID

func (rb *RingBuffer) FindByCorrelationID(correlationID string, maxCount int) []busevents.Event

FindByCorrelationID returns events with the specified correlation ID, newest first.

Parameters:

  • correlationID: The correlation ID to search for
  • maxCount: Maximum number of events to return (0 = no limit)

Returns:

  • Slice of events matching the correlation ID, newest first

Example:

// Find all events in a reconciliation cycle
events := rb.FindByCorrelationID("550e8400-e29b-41d4-a716-446655440000", 100)

func (*RingBuffer) FindByTypeInWindow

func (rb *RingBuffer) FindByTypeInWindow(eventType string, window time.Duration) []busevents.Event

FindByTypeInWindow returns events of the specified type within the time window, newest first.

Parameters:

  • eventType: The event type to filter by
  • window: Time duration to look back (e.g., 5 * time.Minute)

Returns:

  • Slice of events matching the type and within the window, newest first

Example:

// Find all config validations in the last 5 minutes
events := rb.FindByTypeInWindow("config.validated", 5*time.Minute)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL