emf

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Aug 28, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

Documentation

Overview

Package emf implements metrics.Recorder on top of the CloudWatch Embedded Metric Format.

Why EMF rather than PutMetricData: EMF metrics are extracted from structured JSON written to stdout, so a worker needs no CloudWatch API permissions, no SDK client, and no network call on the hot path. Both deployment shapes Murmur targets already ship stdout to CloudWatch Logs — Lambda natively, ECS via the awslogs driver — so wiring this up is a one-line change with no IAM edit.

rec := emf.New(emf.Config{Namespace: "Murmur"})
defer rec.Close()
handler, _ := kinesis.NewHandler(pipe, dec, kinesis.WithMetrics(rec))

Aggregation

The Recorder contract asks for nanosecond-cost on the hot path, and a pipeline running at even modest throughput would be ruinous to log per-event: one EMF document per record is one CloudWatch Logs ingestion charge per record, and CloudWatch bills ingestion by the byte.

So calls only touch an in-memory aggregate under a sharded lock, and a background goroutine emits one document per flush interval carrying counters as sums and latencies as EMF StatisticSets (Max/Min/Sum/SampleCount, which CloudWatch expands into averages and percentile-capable statistics). At the default 60s interval a pipeline emits 1440 documents a day regardless of whether it processed a hundred records or a hundred million.

Sub-event names

Murmur runtimes encode sub-events by suffixing the pipeline name, e.g. `RecordEvent("orders:dedup_skip")`. Emitting those verbatim would create a separate Pipeline dimension value per sub-event and fragment the dashboards. This recorder splits on the last colon instead, so `orders:dedup_skip` becomes the metric `DedupSkip` on Pipeline=orders — which is what makes dedup_skip, dedup_release, and dedup_release_failed visible at all.

Index

Constants

View Source
const DefaultFlushInterval = 60 * time.Second

DefaultFlushInterval is how often aggregates are emitted when Config.FlushInterval is zero.

View Source
const DefaultNamespace = "Murmur"

DefaultNamespace is the CloudWatch namespace used when Config.Namespace is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	// Namespace is the CloudWatch namespace. Defaults to DefaultNamespace.
	Namespace string

	// FlushInterval is how often aggregated metrics are written. Defaults to
	// DefaultFlushInterval. Shorter intervals cost proportionally more in
	// CloudWatch Logs ingestion for no extra resolution below one minute,
	// which is CloudWatch's own floor for standard metrics.
	FlushInterval time.Duration

	// Out is where EMF documents are written. Defaults to os.Stdout, which is
	// what both Lambda and the ECS awslogs driver forward to CloudWatch Logs.
	Out io.Writer

	// Dimensions are extra dimensions attached to every metric, e.g.
	// {"Env": "soak"}. Keep this small: every distinct combination of
	// dimension values is a separate CloudWatch custom metric, and they are
	// billed individually.
	Dimensions map[string]string
}

Config configures a Recorder.

type Recorder

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

Recorder is a metrics.Recorder that emits CloudWatch EMF documents.

Safe for concurrent use. Call Close to flush pending aggregates and stop the background goroutine; without it, up to one flush interval of metrics is lost when the process exits.

func New

func New(cfg Config) *Recorder

New constructs a Recorder and starts its flush goroutine.

func (*Recorder) Close

func (r *Recorder) Close() error

Close flushes any pending aggregates and stops the background goroutine. It is safe to call more than once.

func (*Recorder) Flush

func (r *Recorder) Flush()

Flush writes one EMF document per pipeline with everything aggregated since the last flush, and resets the aggregates. Called automatically on the flush interval and by Close; exported so callers can force a flush (e.g. a Lambda handler that wants metrics out before the freeze).

func (*Recorder) RecordBatch

func (r *Recorder) RecordBatch(pipeline, mode string, n int, d time.Duration)

RecordBatch implements metrics.Recorder.

func (*Recorder) RecordError

func (r *Recorder) RecordError(pipeline string, _ error)

RecordError implements metrics.Recorder.

func (*Recorder) RecordEvent

func (r *Recorder) RecordEvent(pipeline string)

RecordEvent implements metrics.Recorder.

func (*Recorder) RecordLatency

func (r *Recorder) RecordLatency(pipeline, op string, d time.Duration)

RecordLatency implements metrics.Recorder.

Jump to

Keyboard shortcuts

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