telemetry

package
v0.2.19 Latest Latest
Warning

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

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

Documentation

Overview

Package telemetry processes tenant-owned business metrics.

Application stacks EMIT metric intents into the request envelope at `_txc.telemetry.metrics` (an array of {name, kind, value, unit, attrs} objects; MergeJSON's array-append semantics accumulate contributions from multiple rules). After the request completes — off the response path — the chassis validates, redacts, and enriches the intents, then hands them to the configured Exporter. Export is best-effort: a failure can drop metrics (counted via DropFunc) but never affects the request.

Enablement is per tenant, by convention: a tenant that sets the SecretEndpointName secret (`txco secrets set TELEMETRY_ENDPOINT`) has telemetry on; without it, intents are dropped. There is no config table — the endpoint and its auth headers ARE the configuration, and they are credentials, so the tenant secret store is where they live.

Known v1 gaps, accepted deliberately: requests completed on the continuation/deferred paths bypass the request-end seam; intents are visible in trace artifacts like any other envelope field; only the tenant-wide secret scope is consulted (no per-stack override).

Index

Constants

View Source
const (
	SecretEndpointName = "TELEMETRY_ENDPOINT"
	SecretHeadersName  = "TELEMETRY_HEADERS"
)

Conventional tenant secret names — the tenant-facing enablement contract. SecretEndpointName holds the destination URL (https required, plain http allowed for loopback); its presence turns the feature on for the tenant. SecretHeadersName is optional and holds request headers in the OTel env-var format: "k1=v1,k2=v2".

Variables

View Source
var ErrSecretNotFound = errors.New("telemetry: secret not found")

ErrSecretNotFound is the seam's own not-found sentinel so backends never import the secret store; the server-side SecretSource adapter maps the store's not-found error to this one.

Functions

func HasMetrics

func HasMetrics(payload []byte) bool

HasMetrics reports whether the payload carries any metric intents — the zero-cost fast path for the overwhelmingly common case of a request that emits none.

func Register

func Register(name string, c Constructor)

Register adds an exporter constructor. Called from a backend package's init().

Types

type Constructor

type Constructor func(ExporterConfig) (Exporter, error)

Constructor builds an Exporter from resolved config. Called by Open.

type DropFunc

type DropFunc func(tenantSlug, reason string, n int64)

DropFunc counts metric intents that were dropped rather than exported, tagged with a small fixed reason vocabulary. May be nil — invoke it through Drop.

func (DropFunc) Drop

func (d DropFunc) Drop(tenantSlug, reason string, n int64)

Drop is the nil-safe way to invoke a DropFunc.

type Exporter

type Exporter interface {
	Name() string
	Record(ctx context.Context, tenant string, events []MetricEvent)
	Close(ctx context.Context) error
}

Exporter delivers validated metric events for one tenant at a time. Record must be best-effort and quick: buffer/aggregate in memory and ship in the background; never block on the network. Close flushes whatever is pending, bounded by the caller's context.

func Open

func Open(name string, cfg ExporterConfig) (Exporter, error)

Open constructs the named exporter. Unknown name is a startup error listing what is available (sorted for a stable message).

type ExporterConfig

type ExporterConfig struct {
	// NodeID is a stable identity for this chassis (FQDN-ish), for
	// attribution when many nodes export the same tenant's metrics.
	NodeID string
	// Environment is the chassis environment (dev/stage/prod).
	Environment string
	// Logger is the chassis logger; a backend may emit observability
	// lines but must never log secret values.
	Logger *zap.Logger
	// HTTPClient is the egress-guarded outbound client a network
	// backend must use for tenant-supplied destinations.
	HTTPClient *http.Client
	// Secrets resolves per-tenant configuration secrets.
	Secrets SecretSource
	// Dropped counts metric intents dropped instead of exported.
	Dropped DropFunc
}

ExporterConfig carries the node/runtime context a backend may need, resolved from chassis config. It is deliberately generic — the same posture as usage.SinkConfig — so the seam stays unopinionated; a backend reads any backend-specific settings in its constructor.

type MetricEvent

type MetricEvent struct {
	Tenant string
	Stack  string
	Src    string

	Name  string
	Kind  string // "counter" | "histogram"
	Value float64
	Unit  string

	// Attrs values are string, float64, or bool only (schema.go
	// enforces this before an event is built).
	Attrs map[string]any
	Time  time.Time
}

MetricEvent is one validated, enriched metric ready for export.

func ParseAndValidate

func ParseAndValidate(payload []byte, tenant, stack, src string, now time.Time, dropped DropFunc) []MetricEvent

ParseAndValidate extracts `_txc.telemetry.metrics` from the final request envelope and returns the events that survive validation, enriched with the trusted request context (tenant/stack/src/time). Invalid entries are dropped (counted via dropped), never fatal.

type Processor

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

Processor is the request-end dispatcher: it reads metric intents off the final envelope, validates them, and hands survivors to the exporter. Construct once at server start; call Process from the request-completion path (after the response is written) and Close on shutdown. All methods are nil-receiver-safe so callers can hold a nil *Processor when the feature is off.

func NewProcessor

func NewProcessor(exp Exporter, logger *zap.Logger, dropped DropFunc) *Processor

NewProcessor builds a Processor around an opened Exporter. logger and dropped may be nil.

func (*Processor) Close

func (p *Processor) Close(ctx context.Context) error

Close flushes the exporter, bounded by ctx.

func (*Processor) Process

func (p *Processor) Process(ctx context.Context, finalPayload []byte, tenant, stack, src string)

Process handles one completed request. finalPayload is the final merged envelope; tenant must be the TRUSTED pinned slug ("" when the pipeline never pinned one — never the author-rewritable envelope field). Best-effort: drops are counted, nothing propagates back.

type SecretSource

type SecretSource func(ctx context.Context, tenantSlug, name string) ([]byte, error)

SecretSource resolves one named tenant secret to its cleartext. Returns ErrSecretNotFound when the tenant hasn't set it. The returned slice is caller-owned; copy what you need and zero it.

Directories

Path Synopsis
Package log is the bundled "log" telemetry exporter: every validated metric event becomes one structured chassis log line.
Package log is the bundled "log" telemetry exporter: every validated metric event becomes one structured chassis log line.
Package otlp is the bundled "otlp" telemetry exporter: each tenant's validated metric events feed a tenant-private OTel MeterProvider whose periodic reader ships OTLP/HTTP batches to the endpoint named by the tenant's TELEMETRY_ENDPOINT secret (auth headers from the optional TELEMETRY_HEADERS secret, OTel "k1=v1,k2=v2" format).
Package otlp is the bundled "otlp" telemetry exporter: each tenant's validated metric events feed a tenant-private OTel MeterProvider whose periodic reader ships OTLP/HTTP batches to the endpoint named by the tenant's TELEMETRY_ENDPOINT secret (auth headers from the optional TELEMETRY_HEADERS secret, OTel "k1=v1,k2=v2" format).

Jump to

Keyboard shortcuts

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