telemetry

package
v0.59.0 Latest Latest
Warning

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

Go to latest
Published: Nov 3, 2026 License: MIT Imports: 14 Imported by: 0

README

telemetry

import "github.com/kitsunium/sdk/framework/telemetry"

Package telemetry — the exporter: a bounded ring producers never wait on, drained to the clients attached to a private socket, which it never reads.

Package telemetry — the compile-time proof that Exporter is an Emitter.

Package telemetry is the port a running product reports what it does on, and the exporter that hands it to a tool attached to the product (ADR 0149).

It is not the trace domain. pkg/v1/observe/trace builds spans a backend stores and carries its context through context.WithValue; this port carries nothing through a context, builds nothing a caller keeps, and costs a producer one fixed-size copy into a bounded ring — zero allocations, pinned by a test (testing.AllocsPerRun) and measured in BENCH.md:

emitter.Emit(&telemetry.Event{Kind: telemetry.KindSpan, Op: telemetry.OpRequest,
	Node: ref, Start: start, Duration: d, Outcome: telemetry.OutcomeOK})
A closed schema

An Event holds numbers only: enumerations (Kind, Op, Outcome), a dotted-quad error code, instants, identifiers a trace already carries, and NODE REFERENCES — indexes into the table of node IDs the exporter sends once, in its handshake, each one checked against the ID grammar (framework/model.ParseID). There is no free text, so there is no field a person's name, an address or a request body could travel in: the schema itself is what keeps personal data out, in every environment.

Inert unless configured

Nop is the emitter a product holds by default: Emit returns at once. An Exporter exists only when the product was started with one, and it only EMITS — it never reads what a client sends, so nothing reaches the product through it. It listens on a private socket (pkg/v1/proc/ipc, ADR 0148): the product's account and the UIDs and GIDs the deployment names, under the runtime directory; a stale socket is replaced, a live one refused. A client connects, reads one handshake line — product, binary, role, instance, revision, model version, design digest, the node table, the first sequence number — then fixed-size records (RecordSize bytes each) for as long as it reads. A slow client loses records, counted; the product never waits.

Package telemetry — the interfaces: what a producer emits to, and what the exporter needs of its listener and of a client.

Index

Constants

const CodeMisconfigured errs.Code = 262913

CodeMisconfigured identifies an exporter configuration that cannot be honoured: no socket path, a buffer outside its bounds, an ID outside the grammar in the node table.

Code 0.4.3.1

const CodeRunning errs.Code = 262914

CodeRunning identifies a Start on an exporter already started.

Code 0.4.3.2

const DefaultBuffer int = 4096

The ring's bounds and default, in events.

const KindBreaker Kind = 4

KindBreaker is a port's breaker changing phase: Node is the port, Op the phase it enters — closed, open, half-open.

const KindGaveUp Kind = 6

KindGaveUp is a supervised loop the framework stopped restarting, its failures over their bound: Node is its node, Code the give-up's. The product is no longer live from then on.

const KindPhase Kind = 2

KindPhase is the process changing phase: starting, serving, draining, stopped, failed — Op carries it.

const KindRestart Kind = 5

KindRestart is a supervised loop restarting after a failure — a consumer, a declared or a hand-written loop —: Node is its node, Code the failure's, Duration the wait before it runs again. A reader counts a node's restarts by counting these.

const KindRetry Kind = 3

KindRetry is a port's call about to be tried again: Node is the port, Code the failure's, Duration the wait before the next attempt.

const KindSpan Kind = 1

KindSpan is one unit of work that ended on a node.

const MaxBuffer int = 1048576

The ring's bounds and default, in events.

const MinBuffer int = 64

The ring's bounds and default, in events.

const OpAsk Op = 14

The operations, in the order of their names in OpNames; zero is none.

const OpAuth Op = 9

The operations, in the order of their names in OpNames; zero is none.

const OpCLI Op = 17

The operations, in the order of their names in OpNames; zero is none.

const OpCall Op = 2

The operations, in the order of their names in OpNames; zero is none.

const OpClosed Op = 23

The breaker's phases, for KindBreaker.

const OpConnect Op = 16

The operations, in the order of their names in OpNames; zero is none.

const OpDeliver Op = 6

The operations, in the order of their names in OpNames; zero is none.

const OpDeliverMail Op = 11

The operations, in the order of their names in OpNames; zero is none.

const OpDispatch Op = 13

The operations, in the order of their names in OpNames; zero is none.

const OpDraining Op = 20

The operations, in the order of their names in OpNames; zero is none.

const OpFailed Op = 22

The operations, in the order of their names in OpNames; zero is none.

const OpHalfOpen Op = 25

The operations, in the order of their names in OpNames; zero is none.

const OpHandle Op = 15

The operations, in the order of their names in OpNames; zero is none.

const OpNone Op = 0

The operations, in the order of their names in OpNames; zero is none.

const OpOpen Op = 24

The operations, in the order of their names in OpNames; zero is none.

const OpPublish Op = 5

The operations, in the order of their names in OpNames; zero is none.

const OpRead Op = 3

The operations, in the order of their names in OpNames; zero is none.

const OpRequest Op = 1

The operations, in the order of their names in OpNames; zero is none.

const OpRun Op = 8

The operations, in the order of their names in OpNames; zero is none.

const OpSecret Op = 12

The operations, in the order of their names in OpNames; zero is none.

const OpSend Op = 10

The operations, in the order of their names in OpNames; zero is none.

const OpServing Op = 19

The operations, in the order of their names in OpNames; zero is none.

const OpStarting Op = 18

The phases, for KindPhase.

const OpStopped Op = 21

The operations, in the order of their names in OpNames; zero is none.

const OpTransition Op = 7

The operations, in the order of their names in OpNames; zero is none.

const OpWrite Op = 4

The operations, in the order of their names in OpNames; zero is none.

const OutcomeError Outcome = 2

The outcomes.

const OutcomeOK Outcome = 1

The outcomes.

const Protocol string = "app-telemetry/v1"

Protocol names the wire this package speaks, in the handshake. Since ADR 0180 step 4 it is app-telemetry/v1, a name the product sends; the wire is unchanged, and a collector accepts kit-telemetry/v1 as the same for two minors.

const RecordSize int = 64

RecordSize is the size of one record on the wire.

Variables

var Misconfigured *errs.Error

Misconfigured refuses an exporter that cannot be built as asked; the rule field says which rule failed.

Code 0.4.3.1 · reason MISCONFIGURED · public "The telemetry exporter is misconfigured"

var Nop Emitter

Nop is the emitter that reports nothing: what a product holds unless it was started with an exporter.

var OpNames [26]string

OpNames are the operations' names, as framework/model spells them, indexed by Op. The handshake sends them, so a reader never hard-codes the table.

var Running *errs.Error

Running refuses a second Start.

Code 0.4.3.2 · reason RUNNING · public "The telemetry exporter is already running"

func Decode

func Decode(b []byte) (e Event, seq uint64, ok bool)

Decode reads one record, as a client does: the event and its sequence number. ok is false for a record of another size or version.

func NewExporter

func NewExporter(cfg *ExporterConfig) (*Exporter, error)

NewExporter checks cfg and builds an exporter that listens nowhere yet: Start opens its socket.

func OpOf

func OpOf(name string) Op

OpOf is the Op named name, or OpNone.

type Emitter

Emitter is where a product reports: the telemetry port. Emit copies e and never keeps it; it must not block and must not allocate.

type Emitter interface {
	Emit(e *Event)
}

func (Emitter) Emit
func (Emitter) Emit(e *Event)

type Event

Event is one report. Its fields are numbers only — see the package comment: there is no field a person's data could travel in, in any environment.

type Event struct {
	Kind     Kind
	Op       Op
	Outcome  Outcome
	// Node is where the work ran; From, the node it came from, if any.
	Node     NodeRef
	// Node is where the work ran; From, the node it came from, if any.
	From     NodeRef
	// Code is the dotted-quad code of the error that ended it, 0 for none.
	Code     uint32
	// Start is when it began, in Unix nanoseconds; Duration, how long it took.
	Start    int64
	// Start is when it began, in Unix nanoseconds; Duration, how long it took.
	Duration int64
	// TraceID and SpanID are the trace's, when there is one.
	TraceID  [16]byte
	SpanID   [8]byte
}

type Exporter

Exporter is an Emitter that hands events to the clients attached to its socket. It never blocks a producer: a full ring drops the event and counts it.

type Exporter struct{}

func (Exporter) Dropped
func (*Exporter) Dropped() uint64

Dropped is how many events a full ring refused.

func (Exporter) Emit
func (*Exporter) Emit(ev *Event)

Emit copies ev into the ring, or drops it when the ring is full. It never blocks and never allocates.

func (Exporter) Instance
func (*Exporter) Instance() string

Instance is the process's instance identifier, in the handshake.

func (Exporter) Ref
func (*Exporter) Ref(id string) NodeRef

Ref is the reference of node id in the handshake's table; zero when the table does not hold it.

func (Exporter) Sent
func (*Exporter) Sent() uint64

Sent is how many records reached at least one client.

func (Exporter) Start
func (*Exporter) Start(ctx context.Context) error

Start opens the socket and starts the two goroutines: one accepts clients and greets them, one drains the ring to them. Both end at Stop.

func (Exporter) Stop
func (*Exporter) Stop(ctx context.Context) error

Stop closes the socket and every client, and waits for the goroutines.

type ExporterConfig

ExporterConfig is where the exporter listens, who may attach, and how much it holds for a slow reader.

type ExporterConfig struct {
	// Path is the private socket's absolute path (pkg/v1/proc/ipc).
	Path      string
	// AllowUIDs and AllowGIDs admit accounts besides the product's own: the
	// on-call group declared at deployment.
	AllowUIDs []int
	// AllowUIDs and AllowGIDs admit accounts besides the product's own: the
	// on-call group declared at deployment.
	AllowGIDs []int
	// Buffer is the ring's size in events, rounded up to a power of two, in
	// [MinBuffer, MaxBuffer]; zero is DefaultBuffer.
	Buffer    int
	// Hello is what the handshake says.
	Hello     HelloValue
}

type HelloValue

HelloValue is what the handshake says about the process, besides the operations and the record size: which product, binary and role run, from which revision and design, and the node table events refer to.

type HelloValue struct {
	// Product, Binary and Role name what runs: the app, the binary and the
	// process role it is (framework/model's IDs).
	Product  string
	// Product, Binary and Role name what runs: the app, the binary and the
	// process role it is (framework/model's IDs).
	Binary   string
	// Product, Binary and Role name what runs: the app, the binary and the
	// process role it is (framework/model's IDs).
	Role     string
	// Revision is the VCS commit the binary was built from (vcs.revision).
	Revision string
	// Digest is the design's digest the code was generated from, when known.
	Digest   string
	// Nodes are the node IDs events refer to: Nodes[i] is NodeRef(i+1).
	Nodes    []string
}

type Kind

Kind says what an event reports.

type Kind uint8

type NodeRef

NodeRef is a node's index in the handshake's node table, from 1; zero is "no node".

type NodeRef uint32

type Op

Op is what the work was: the span operations of framework/model.

type Op uint8

type Outcome

Outcome says how the work ended.

type Outcome uint8

Generated from docs/api by tools/genindex.

Documentation

Overview

Package telemetry — the exporter: a bounded ring producers never wait on, drained to the clients attached to a private socket, which it never reads.

Package telemetry — the compile-time proof that Exporter is an Emitter.

Package telemetry is the port a running product reports what it does on, and the exporter that hands it to a tool attached to the product (ADR 0149).

It is not the trace domain. pkg/v1/observe/trace builds spans a backend stores and carries its context through context.WithValue; this port carries nothing through a context, builds nothing a caller keeps, and costs a producer one fixed-size copy into a bounded ring — zero allocations, pinned by a test (testing.AllocsPerRun) and measured in BENCH.md:

emitter.Emit(&telemetry.Event{Kind: telemetry.KindSpan, Op: telemetry.OpRequest,
	Node: ref, Start: start, Duration: d, Outcome: telemetry.OutcomeOK})

A closed schema

An Event holds numbers only: enumerations (Kind, Op, Outcome), a dotted-quad error code, instants, identifiers a trace already carries, and NODE REFERENCES — indexes into the table of node IDs the exporter sends once, in its handshake, each one checked against the ID grammar (framework/model.ParseID). There is no free text, so there is no field a person's name, an address or a request body could travel in: the schema itself is what keeps personal data out, in every environment.

Inert unless configured

Nop is the emitter a product holds by default: Emit returns at once. An Exporter exists only when the product was started with one, and it only EMITS — it never reads what a client sends, so nothing reaches the product through it. It listens on a private socket (pkg/v1/proc/ipc, ADR 0148): the product's account and the UIDs and GIDs the deployment names, under the runtime directory; a stale socket is replaced, a live one refused. A client connects, reads one handshake line — product, binary, role, instance, revision, model version, design digest, the node table, the first sequence number — then fixed-size records (RecordSize bytes each) for as long as it reads. A slow client loses records, counted; the product never waits.

Package telemetry — the interfaces: what a producer emits to, and what the exporter needs of its listener and of a client.

Index

Constants

View Source
const (
	MinBuffer     int = 64
	MaxBuffer     int = 1 << 20
	DefaultBuffer int = 4096
)

The ring's bounds and default, in events.

View Source
const CodeMisconfigured errs.Code = 0x00_04_03_01 // 0.4.3.1

CodeMisconfigured identifies an exporter configuration that cannot be honoured: no socket path, a buffer outside its bounds, an ID outside the grammar in the node table.

View Source
const CodeRunning errs.Code = 0x00_04_03_02 // 0.4.3.2

CodeRunning identifies a Start on an exporter already started.

View Source
const Protocol string = "app-telemetry/v1"

Protocol names the wire this package speaks, in the handshake. Since ADR 0180 step 4 it is app-telemetry/v1, a name the product sends; the wire is unchanged, and a collector accepts kit-telemetry/v1 as the same for two minors.

View Source
const RecordSize int = 64

RecordSize is the size of one record on the wire.

Variables

View Source
var (
	// Misconfigured refuses an exporter that cannot be built as asked; the
	// rule field says which rule failed.
	Misconfigured = errs.Static(CodeMisconfigured, "MISCONFIGURED",
		"The telemetry exporter is misconfigured",
		"framework/telemetry: no socket path, a buffer outside [64, 1<<20], or a node ID outside the grammar; the rule field says which",
		0,
		78)

	// Running refuses a second Start.
	Running = errs.Static(CodeRunning, "RUNNING",
		"The telemetry exporter is already running",
		"framework/telemetry: Start called on a started exporter",
		0,
		0)
)

Functions

This section is empty.

Types

type Emitter

type Emitter interface {
	Emit(e *Event)
}

Emitter is where a product reports: the telemetry port. Emit copies e and never keeps it; it must not block and must not allocate.

var (
	// OpNames are the operations' names, as framework/model spells them,
	// indexed by Op. The handshake sends them, so a reader never hard-codes
	// the table.
	OpNames = [opCount]string{
		"", "request", "call", "read", "write", "publish", "deliver", "transition", "run", "auth", "send",
		"deliver-mail", "secret", "dispatch", "ask", "handle", "connect", "cli",
		"starting", "serving", "draining", "stopped", "failed",
		"closed", "open", "half-open",
	}

	// Nop is the emitter that reports nothing: what a product holds unless it
	// was started with an exporter.
	Nop Emitter = nop{}
)

type Event

type Event struct {
	Kind    Kind
	Op      Op
	Outcome Outcome
	// Node is where the work ran; From, the node it came from, if any.
	Node, From NodeRef
	// Code is the dotted-quad code of the error that ended it, 0 for none.
	Code uint32
	// Start is when it began, in Unix nanoseconds; Duration, how long it took.
	Start, Duration int64
	// TraceID and SpanID are the trace's, when there is one.
	TraceID [16]byte
	SpanID  [8]byte
}

Event is one report. Its fields are numbers only — see the package comment: there is no field a person's data could travel in, in any environment.

func Decode

func Decode(b []byte) (e Event, seq uint64, ok bool)

Decode reads one record, as a client does: the event and its sequence number. ok is false for a record of another size or version.

type Exporter

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

Exporter is an Emitter that hands events to the clients attached to its socket. It never blocks a producer: a full ring drops the event and counts it.

func NewExporter

func NewExporter(cfg *ExporterConfig) (*Exporter, error)

NewExporter checks cfg and builds an exporter that listens nowhere yet: Start opens its socket.

func (*Exporter) Dropped

func (e *Exporter) Dropped() uint64

Dropped is how many events a full ring refused.

func (*Exporter) Emit

func (e *Exporter) Emit(ev *Event)

Emit copies ev into the ring, or drops it when the ring is full. It never blocks and never allocates.

func (*Exporter) Instance

func (e *Exporter) Instance() string

Instance is the process's instance identifier, in the handshake.

func (*Exporter) Ref

func (e *Exporter) Ref(id string) NodeRef

Ref is the reference of node id in the handshake's table; zero when the table does not hold it.

func (*Exporter) Sent

func (e *Exporter) Sent() uint64

Sent is how many records reached at least one client.

func (*Exporter) Start

func (e *Exporter) Start(ctx context.Context) error

Start opens the socket and starts the two goroutines: one accepts clients and greets them, one drains the ring to them. Both end at Stop.

func (*Exporter) Stop

func (e *Exporter) Stop(ctx context.Context) error

Stop closes the socket and every client, and waits for the goroutines.

type ExporterConfig

type ExporterConfig struct {
	// Path is the private socket's absolute path (pkg/v1/proc/ipc).
	Path string
	// AllowUIDs and AllowGIDs admit accounts besides the product's own: the
	// on-call group declared at deployment.
	AllowUIDs, AllowGIDs []int
	// Buffer is the ring's size in events, rounded up to a power of two, in
	// [MinBuffer, MaxBuffer]; zero is DefaultBuffer.
	Buffer int
	// Hello is what the handshake says.
	Hello HelloValue
}

ExporterConfig is where the exporter listens, who may attach, and how much it holds for a slow reader.

type HelloValue

type HelloValue struct {
	// Product, Binary and Role name what runs: the app, the binary and the
	// process role it is (framework/model's IDs).
	Product, Binary, Role string
	// Revision is the VCS commit the binary was built from (vcs.revision).
	Revision string
	// Digest is the design's digest the code was generated from, when known.
	Digest string
	// Nodes are the node IDs events refer to: Nodes[i] is NodeRef(i+1).
	Nodes []string
}

HelloValue is what the handshake says about the process, besides the operations and the record size: which product, binary and role run, from which revision and design, and the node table events refer to.

type Kind

type Kind uint8

Kind says what an event reports.

const (
	// KindSpan is one unit of work that ended on a node.
	KindSpan Kind = iota + 1
	// KindPhase is the process changing phase: starting, serving, draining,
	// stopped, failed — Op carries it.
	KindPhase
	// KindRetry is a port's call about to be tried again: Node is the port,
	// Code the failure's, Duration the wait before the next attempt.
	KindRetry
	// KindBreaker is a port's breaker changing phase: Node is the port, Op
	// the phase it enters — closed, open, half-open.
	KindBreaker
	// KindRestart is a supervised loop restarting after a failure — a
	// consumer, a declared or a hand-written loop —: Node is its node, Code
	// the failure's, Duration the wait before it runs again. A reader counts
	// a node's restarts by counting these.
	KindRestart
	// KindGaveUp is a supervised loop the framework stopped restarting, its
	// failures over their bound: Node is its node, Code the give-up's. The
	// product is no longer live from then on.
	KindGaveUp
)

The kinds.

type NodeRef

type NodeRef uint32

NodeRef is a node's index in the handshake's node table, from 1; zero is "no node".

type Op

type Op uint8

Op is what the work was: the span operations of framework/model.

const (
	OpNone Op = iota
	OpRequest
	OpCall
	OpRead
	OpWrite
	OpPublish
	OpDeliver
	OpTransition
	OpRun
	OpAuth
	OpSend
	OpDeliverMail
	OpSecret
	OpDispatch
	OpAsk
	OpHandle
	OpConnect
	OpCLI
	// The phases, for KindPhase.
	OpStarting
	OpServing
	OpDraining
	OpStopped
	OpFailed
	// The breaker's phases, for KindBreaker.
	OpClosed
	OpOpen
	OpHalfOpen
)

The operations, in the order of their names in OpNames; zero is none.

func OpOf

func OpOf(name string) Op

OpOf is the Op named name, or OpNone.

type Outcome

type Outcome uint8

Outcome says how the work ended.

const (
	OutcomeOK    Outcome = 1
	OutcomeError Outcome = 2
)

The outcomes.

Jump to

Keyboard shortcuts

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