logstream

package
v0.19.0 Latest Latest
Warning

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

Go to latest
Published: Nov 7, 2026 License: AGPL-3.0 Imports: 10 Imported by: 0

Documentation

Overview

Package logstream shares one live log stream per app part between everyone watching it on a replica (O-51).

The console's Logs tab used to ask the runtime for the last 500 lines every five seconds, from every tab that had an app open. Each ask is a container log read (Docker) or a pod log request (Kubernetes), so the runtime's load grew with the number of people looking rather than with the number of apps. A Hub holds one following runtime.Logs stream per (runtime, app, part) while at least one viewer watches it, keeps the most recent lines in a bounded ring for whoever joins next, and fans each new line out to every viewer. The runtime stream closes a short grace period after the last viewer leaves, so a page reload does not reopen it.

Only what the runtime printed is shared here. Whether a viewer may read it is decided by the caller when the viewer connects, and decided again on an interval while the viewer stays connected (Serve, R-048, design 06 §4.2): a shared stream never carries an authorization decision.

Design notes: docs/design/notes-shared-log-streams-o51.md.

Index

Constants

View Source
const (
	// Backfill is how many recent lines a stream holds for a viewer who joins
	// after it opened, and how many it asks the runtime for when it opens.
	DefaultBackfill = 1000

	// DefaultTail is what a one-shot read returns when no tail is given, and
	// MaxTail is the most it returns whatever is asked for. A larger tail is
	// cut to MaxTail rather than refused: a person asking for "everything" is
	// better served by the most Pando will send than by an error.
	DefaultTail = 200
	MaxTail     = 5000

	// DefaultGrace is how long a stream with no viewers stays open, so a page
	// reload or a switch between parts and back does not reopen it.
	DefaultGrace = 30 * time.Second

	// DefaultBuffer is how many events one viewer may fall behind by before
	// it is disconnected. Large enough to take a full backfill arriving in one
	// burst when a stream opens.
	DefaultBuffer = 2048

	// MaxLineBytes is the longest line passed on; the rest of a longer line is
	// dropped and the line ends with an ellipsis. One unbroken megabyte of
	// output would otherwise be one megabyte per viewer per buffer slot.
	MaxLineBytes = 16 << 10

	// Reconnect backoff after the runtime's stream ends.
	DefaultRetryMin = time.Second
	DefaultRetryMax = 30 * time.Second
)

Defaults. Each is a [P] default recorded in the design note.

View Source
const DefaultReauthEvery = assertion.Lifetime

DefaultReauthEvery is how often a connected viewer's access is checked again. It is assertion.Lifetime itself rather than a copy of its value, as the proxy's long-lived connections are: design 06 §3.1 sets one revocation window, and every long-lived path re-checks on exactly that interval.

Variables

This section is empty.

Functions

func ClampTail

func ClampTail(n int) int

ClampTail is the tail a one-shot read uses: DefaultTail for none (or a nonsensical negative), and never more than MaxTail.

Types

type Authorize

type Authorize func(ctx context.Context) error

Authorize decides again whether the viewer may still read the log. A nil error allows; any error ends the stream, an unknown answer included.

type Ended

type Ended struct {
	Reason  Reason
	Message string
}

Ended is returned by Serve when Pando ended the stream.

func (*Ended) Error

func (e *Ended) Error() string

type Event

type Event struct {
	Kind Kind
	Text string
}

Event is one thing sent to a viewer.

type Hub

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

Hub is one replica's set of shared log streams. Use New.

func New

func New(opts ...Option) *Hub

New returns a Hub with the defaults above.

func (*Hub) Active

func (h *Hub) Active() int

Active reports how many streams the hub holds open, for tests and metrics.

func (*Hub) Serve

func (h *Hub) Serve(ctx context.Context, sub *Subscription, authorize Authorize, v Viewer) error

Serve writes sub's backfill and then its events to v until the viewer goes (ctx ends, or a write fails), the viewer falls too far behind, or authorize stops allowing. It closes sub. It returns nil when the viewer went, and an *Ended when Pando ended the stream.

authorize runs every DefaultReauthEvery: the check that let the viewer in fires once, and without this a log stream would outlive the access that opened it (R-048, design 06 §3.1, §4.2).

func (*Hub) Subscribe

func (h *Hub) Subscribe(ctx context.Context, rt api.RuntimeAdapter, key Key) (*Subscription, error)

Subscribe joins the shared stream for key, opening it on rt if this replica has none. The caller has already authorized the viewer. The open's error — nothing running under that name, the runtime unreachable — is returned here, before anything has been sent, so it can be answered as an error.

The subscription must be closed.

type Key

type Key struct {
	Runtime  string // the spec's runtime adapter ref
	AppID    string
	Workload string
}

Key names one shared stream. The same app on two runtimes is two streams.

type Kind

type Kind uint8

Kind is what an Event carries.

const (
	// KindLine is one line the app printed, without its newline.
	KindLine Kind = iota + 1
	// KindNotice is Pando saying something about the stream itself — that it
	// ended and is being reconnected, or that it has been.
	KindNotice
)

type Option

type Option func(*Hub)

Option configures a Hub.

func WithBackfill

func WithBackfill(n int) Option

WithBackfill sets how many recent lines a stream keeps.

func WithBuffer

func WithBuffer(n int) Option

WithBuffer sets how far one viewer may fall behind.

func WithGrace

func WithGrace(d time.Duration) Option

WithGrace sets how long a stream with no viewers stays open.

func WithLogger

func WithLogger(l *zap.Logger) Option

WithLogger sets where the hub logs a stream it could not reopen.

func WithReauthEvery

func WithReauthEvery(d time.Duration) Option

WithReauthEvery sets how often Serve checks a viewer's access again.

func WithRetry

func WithRetry(lo, hi time.Duration) Option

WithRetry sets the reconnect backoff.

type Reason

type Reason string

Reason says why Pando, rather than the viewer, ended a viewer's stream.

const (
	// ReasonRevoked: the viewer's access no longer holds. A client must not
	// reconnect on its own.
	ReasonRevoked Reason = "revoked"
	// ReasonLagged: the viewer fell DefaultBuffer events behind. Reconnecting
	// is fine and starts again from the backfill.
	ReasonLagged Reason = "lagged"
)

type Subscription

type Subscription struct {
	// Backfill is the most recent lines the stream held when the viewer
	// joined, oldest first. Lines arriving after it are on Events.
	Backfill []string
	// contains filtered or unexported fields
}

Subscription is one viewer of a shared stream.

func (*Subscription) Close

func (s *Subscription) Close()

Close leaves the stream. Safe to call more than once.

func (*Subscription) Ended

func (s *Subscription) Ended() *Ended

Ended is why Pando closed Events, or nil if the viewer closed it.

func (*Subscription) Events

func (s *Subscription) Events() <-chan Event

Events delivers what arrives after the backfill. It is closed when the viewer is closed or disconnected; Ended then says which.

type Viewer

type Viewer interface {
	Line(text string) error
	Notice(text string) error
	// Flush is called when there is nothing more to write for now, so a
	// burst of lines is sent together rather than one network write each.
	Flush() error
}

Viewer is where Serve writes one viewer's stream: the HTTP handler's server-sent events, or plain text for the CLI.

Jump to

Keyboard shortcuts

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