pally

package
v1.8.0 Latest Latest
Warning

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

Go to latest
Published: May 1, 2017 License: MIT Imports: 11 Imported by: 0

README ¶

👭 pally

Pally makes Prometheus and Tally pals. Rather than choosing one or the other, take the best of both!

Compared to Prometheus, Pally is fast: modeling metrics as integers instead of floats allows pally to avoid expensive increment-and-compare loops.

Compared to Tally, Pally prioritizes introspection: like expvar and Prometheus, all metrics can be inspected via a simple HTTP endpoint even when central telemetry systems are down.

Pally grew out of the internal metrics libraries built by Uber's software networking team. Its open-source incarnation is incubating in YARPC before potentially migrating into an independent project.

Known to-dos:

  • Histogram support
  • Stopwatches (for convenient timing collection)

Documentation ¶

Overview ¶

Package pally is a simple, atomic-based metrics library. It interoperates seamlessly with both Prometheus and Tally, providing ready-to-use Prometheus text and Protocol Buffer endpoints, differential updates to StatsD- or M3-based systems, and excellent performance along the hot path.

Metric Names ¶

Pally requires that all metric names, label names, and label values be valid both in Tally and in Prometheus. Metric and label names must pass IsValidName. Statically-defined label values must pass IsValidLabelValue, but dynamic label values are automatically scrubbed using ScrubLabelValue. This minimizes magic while still permitting use of label values generated at runtime (e.g., service names).

Counters And Gauges ¶

Pally offers two simple metric types: counters and gauges. Counters represent an ever-accumulating total, like a car's odometer. Gauges represent a point-in-time measurement, like a car's speedometer. In Pally, both counters and gauges must have all their labels specified ahead of time.

Vectors ¶

In many real-world situations, it's impossible to know all the labels for a metric ahead of time. For example, you may want to track the number of requests your server receives by caller; in most cases, you can't list all the possible callers ahead of time. To accomodate these situations, Pally offers vectors.

Vectors represent a collection of metrics that have some constant labels, but some labels assigned at runtime. At vector construction time, you must specify the variable label keys; in our example, we'd supply "caller_name" as the only variable label. At runtime, pass the values for the variable labels to the Get (or MustGet) method on the vector. The number of label values must match the configured number of label keys, and they must be supplied in the same order. Vectors create metrics on demand, caching them for efficient repeated access.

registry := NewRegistry()
requestsByCaller := registry.NewCounterVector(Opts{
  Name: "requests",
  Help: "Total requests by caller name.",
  ConstLabels: Labels{
    "zone": "us-west-1",
    "service": "my_service_name",
  },
  // At runtime, we'll supply the caller name.
  VariableLabels: []string{"caller_name"},
})
// In real-world use, we'd do this in a handler function (and we'd
// probably use the safer Get variant).
vec.MustGet("some_calling_service").Inc()
Example (DependencyInjection) ¶
package main

import (
	"context"
	"net/http"
	"time"

	"go.uber.org/yarpc/internal/pally"

	"github.com/uber-go/tally"
)

// If you'd prefer to use pure dependency injection and scope your metrics
// to a single struct, create a new pally.Registry in your struct's
// constructor. In this case, we're also exporting our metrics to a Tally
// scope, which can report to StatsD- or M3-aware systems.
type Resolver struct {
	registry        *pally.Registry
	watches         pally.Gauge
	resolves        pally.CounterVector
	stopTallyExport context.CancelFunc
}

func NewResolver(scope tally.Scope) (*Resolver, error) {
	reg := pally.NewRegistry()
	stop, err := reg.Push(scope, time.Second)
	if err != nil {
		return nil, err
	}

	watches, err := _reg.NewGauge(pally.Opts{
		Name: "watch_count",
		Help: "Current number of active service name watches.",
		ConstLabels: pally.Labels{
			"foo": "bar",
		},
	})
	if err != nil {
		return nil, err
	}

	resolves, err := _reg.NewCounterVector(pally.Opts{
		Name: "resolve_count",
		Help: "Total name resolves by path.",
		ConstLabels: pally.Labels{
			"foo": "bar",
		},
		VariableLabels: []string{"service"},
	})
	if err != nil {
		return nil, err
	}

	return &Resolver{
		registry:        reg,
		watches:         watches,
		resolves:        resolves,
		stopTallyExport: stop,
	}, nil
}

func (r *Resolver) Watch() {
	r.watches.Inc()
}

func (r *Resolver) Resolve(name string) {
	if c, err := r.resolves.Get(name); err == nil {
		c.Inc()
	}
}

func (r *Resolver) Close() {
	r.stopTallyExport()
}

func (r *Resolver) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	// Our registry can report its own metrics via a Prometheus-compatible HTTP
	// handler.
	r.registry.ServeHTTP(w, req)
}

func main() {
	scope := tally.NewTestScope("testing", nil /* labels */)
	reg, err := NewResolver(scope)
	if err != nil {
		panic(err.Error())
	}
	reg.Watch()
	reg.Resolve("some_service")
}
Output:

Example (GlobalMetrics) ¶
package main

import (
	"go.uber.org/yarpc/internal/pally"

	"github.com/prometheus/client_golang/prometheus"
)

// For expvar-style usage (where all metrics are package-global), create a
// package-global pally.Registry and use the Must* constructors. This
// guarantees that all your metrics are unique.
var (
	_reg = pally.NewRegistry(
		// Also register all metrics with the package-global Prometheus
		// registry.
		pally.Federated(prometheus.DefaultRegisterer),
	)
	_watches = _reg.MustGauge(pally.Opts{
		Name: "watch_count",
		Help: "Current number of active service name watches.",
		ConstLabels: pally.Labels{
			"foo": "bar",
		},
	})
	_resolvesPerName = _reg.MustCounterVector(pally.Opts{
		Name: "resolve_count",
		Help: "Total name resolves by service.",
		ConstLabels: pally.Labels{
			"foo": "bar",
		},
		VariableLabels: []string{"service"},
	})
)

func main() {
	_watches.Store(42)
	_resolvesPerName.MustGet("some_service").Inc()
}
Output:

Index ¶

Examples ¶

Constants ¶

View Source
const DefaultLabelValue = "default"

DefaultLabelValue is used in place of empty label values.

Variables ¶

This section is empty.

Functions ¶

func IsValidLabelValue ¶ added in v1.8.0

func IsValidLabelValue(s string) bool

IsValidLabelValue checks whether the supplied string is a valid label value in both Prometheus and Tally.

Tally allows label values that match the regexp `^[0-9A-z_\-.]+$`. Prometheus allows any valid UTF-8 string.

func IsValidName ¶ added in v1.8.0

func IsValidName(s string) bool

IsValidName checks whether the supplied string is a valid metric and label name in both Prometheus and Tally.

Tally and Prometheus each allow runes that the other doesn't, so Pally can accept only the common subset. For simplicity, we'd also like the rules for metric names and label names to be the same even if that's more restrictive than absolutely necessary.

Tally allows anything matching the regexp `^[0-9A-z_\-]+$`. Prometheus allows the regexp `^[A-z_:][0-9A-z_:]*$` for metric names, and `^[A-z_][0-9A-z_]*$` for label names.

The common subset is `^[A-z_][0-9A-z_]+$`.

func ScrubLabelValue ¶ added in v1.8.0

func ScrubLabelValue(s string) string

ScrubLabelValue replaces any invalid runes in the input string with '-'. If the input is already a valid label value (in both Prometheus and Tally), it's returned unchanged.

Types ¶

type Counter ¶

type Counter interface {
	Inc() int64
	Add(int64) int64
	Load() int64
}

A Counter is a monotonically increasing value, like a car's odometer.

Counters are exported to Prometheus as a snapshot of the current total, and to Tally as a diff since the last export.

func NewNopCounter ¶ added in v1.8.0

func NewNopCounter() Counter

NewNopCounter returns a no-op Counter.

type CounterVector ¶

type CounterVector interface {
	// For a description of Get, MustGet, and vector types in general, see the
	// package-level documentation on vectors.
	Get(...string) (Counter, error)
	MustGet(...string) Counter
}

A CounterVector is a collection of Counters that share a name and some constant labels, but also have an enumerated set of variable labels.

func NewNopCounterVector ¶ added in v1.8.0

func NewNopCounterVector() CounterVector

NewNopCounterVector returns a no-op CounterVector.

type Gauge ¶

type Gauge interface {
	Inc() int64
	Dec() int64
	Add(int64) int64
	Sub(int64) int64
	Store(int64)
	Load() int64
}

A Gauge is a point-in-time measurement, like a car's speedometer.

Gauges are exported to both Prometheus and Tally by simply reporting the current value.

func NewNopGauge ¶ added in v1.8.0

func NewNopGauge() Gauge

NewNopGauge returns a no-op Gauge.

type GaugeVector ¶

type GaugeVector interface {
	// For a description of Get, MustGet, and vector types in general, see the
	// package-level documentation on vectors.
	Get(...string) (Gauge, error)
	MustGet(...string) Gauge
}

A GaugeVector is a collection of Gauges that share a name and some constant labels, but also have an enumerated set of variable labels.

func NewNopGaugeVector ¶ added in v1.8.0

func NewNopGaugeVector() GaugeVector

NewNopGaugeVector returns a no-op GaugeVector.

type Labels ¶

type Labels map[string]string

Labels describe the dimensions of a metric.

type Latencies ¶ added in v1.8.0

type Latencies interface {
	Observe(time.Duration)
}

Latencies approximates a latency distribution with a histogram.

Latencies are exported to both Prometheus and Tally using their native histogram types.

func NewNopLatencies ¶ added in v1.8.0

func NewNopLatencies() Latencies

NewNopLatencies returns a no-op Latencies.

type LatenciesVector ¶ added in v1.8.0

type LatenciesVector interface {
	// For a description of Get, MustGet, and vector types in general, see the
	// package-level documentation on vectors.
	Get(...string) (Latencies, error)
	MustGet(...string) Latencies
}

A LatenciesVector is a collection of Latencies that share a name and some constant labels, but also have an enumerated set of variable labels.

func NewNopLatenciesVector ¶ added in v1.8.0

func NewNopLatenciesVector() LatenciesVector

NewNopLatenciesVector returns a no-op LatenciesVector.

type LatencyOpts ¶ added in v1.8.0

type LatencyOpts struct {
	Opts

	// Latencies are exported to Prometheus as a simple number, not a duration.
	// Unit specifies the desired granularity for latency observations. For
	// example, an observation of time.Second with a unit of time.Millisecond is
	// exported to Prometheus as 1000. Typically, the unit should also be part
	// of the metric name; in this example, latency_ms is a good name.
	Unit time.Duration
	// Upper bounds for the histogram buckets. A catch-all bucket for large
	// observations is automatically created, if necessary.
	Buckets []time.Duration
}

LatencyOpts configure Latencies and LatenciesVectors.

type Opts ¶

type Opts struct {
	Name           string
	Help           string
	ConstLabels    Labels
	VariableLabels []string // only meaningful for vectors
	DisableTally   bool
}

Opts configure Counters, Gauges, CounterVectors, and GaugeVectors.

type Registry ¶

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

A Registry is a collection of metrics, usually scoped to a single package or object. Each Registry is also its own http.Handler, and can serve Prometheus-flavored text and protocol buffer pages for metrics introspection or scraping.

func NewRegistry ¶

func NewRegistry(opts ...RegistryOption) *Registry

NewRegistry constructs a new Registry.

func (*Registry) MustCounter ¶

func (r *Registry) MustCounter(opts Opts) Counter

MustCounter constructs a new Counter. It panics if it encounters an error.

func (*Registry) MustCounterVector ¶

func (r *Registry) MustCounterVector(opts Opts) CounterVector

MustCounterVector constructs a new CounterVector. It panics if it encounters an error.

func (*Registry) MustGauge ¶

func (r *Registry) MustGauge(opts Opts) Gauge

MustGauge constructs a new Gauge. It panics if it encounters an error.

func (*Registry) MustGaugeVector ¶

func (r *Registry) MustGaugeVector(opts Opts) GaugeVector

MustGaugeVector constructs a new GaugeVector. It panics if it encounters an error.

func (*Registry) MustLatencies ¶ added in v1.8.0

func (r *Registry) MustLatencies(opts LatencyOpts) Latencies

MustLatencies constructs a new Latencies. It panics if it encounters an error.

func (*Registry) MustLatenciesVector ¶ added in v1.8.0

func (r *Registry) MustLatenciesVector(opts LatencyOpts) LatenciesVector

MustLatenciesVector constructs a new LatenciesVector. It panics if it encounters an error.

func (*Registry) NewCounter ¶

func (r *Registry) NewCounter(opts Opts) (Counter, error)

NewCounter constructs a new Counter.

func (*Registry) NewCounterVector ¶

func (r *Registry) NewCounterVector(opts Opts) (CounterVector, error)

NewCounterVector constructs a new CounterVector.

func (*Registry) NewGauge ¶

func (r *Registry) NewGauge(opts Opts) (Gauge, error)

NewGauge constructs a new Gauge.

func (*Registry) NewGaugeVector ¶

func (r *Registry) NewGaugeVector(opts Opts) (GaugeVector, error)

NewGaugeVector constructs a new CounterVector.

func (*Registry) NewLatencies ¶ added in v1.8.0

func (r *Registry) NewLatencies(opts LatencyOpts) (Latencies, error)

NewLatencies constructs a new Latencies.

func (*Registry) NewLatenciesVector ¶ added in v1.8.0

func (r *Registry) NewLatenciesVector(opts LatencyOpts) (LatenciesVector, error)

NewLatenciesVector constructs a new LatenciesVector.

func (*Registry) Push ¶

func (r *Registry) Push(scope tally.Scope, tick time.Duration) (context.CancelFunc, error)

Push starts a goroutine that periodically exports all registered metrics to a Tally scope. Each Registry can only push to a single Scope; calling Push a second time returns an error.

In practice, this isn't a problem because Tally scopes natively support tee'ing to multiple backends.

func (*Registry) ServeHTTP ¶

func (r *Registry) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP implements http.Handler.

type RegistryOption ¶

type RegistryOption func(*Registry)

A RegistryOption configures a Registry.

func Federated ¶

func Federated(prom prometheus.Registerer) RegistryOption

Federated links a pally.Registry with a prometheus.Registerer, so that all metrics created in one also appear in the other.

func Labeled ¶

func Labeled(ls Labels) RegistryOption

Labeled adds constant labels to a Registry. All metrics created by a Registry inherit its constant labels. Labels with invalid names or values are dropped.

Directories ¶

Path Synopsis

Jump to

Keyboard shortcuts

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