Help us shape the future of Go! Take the 2026 Go Developer Survey

logstream

package
v0.11.0 Latest Latest
Warning

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

Go to latest
Published: Oct 20, 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