pally

package
v1.7.0 Latest Latest
Warning

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

Go to latest
Published: Mar 20, 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 and labels be valid both in Tally and in Prometheus. Concretely, this means that metric names, label keys, and label values should match the regular expression `^[a-zA-Z_][a-zA-Z0-9_]*$`.

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 ¶

This section is empty.

Variables ¶

This section is empty.

Functions ¶

This section is empty.

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.

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.

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.

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.

type Labels ¶

type Labels map[string]string

Labels describe the dimensions of a metric.

type Opts ¶

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

Opts configure an individual metric or vector.

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) 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) 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.

Jump to

Keyboard shortcuts

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