Peep (peep v6.0.0)

View Source

Telemetry.Metrics reporter supporting Statsd and Prometheus.

To use it, start the reporter with start_link/1, providing a list of Telemetry.Metrics metric definitions in a Keyword list matching the schema specified in Peep.Options:

import Telemetry.Metrics

Peep.start_link([
  name: :my_peep,
  metrics: [
    counter("http.request.count"),
    sum("http.request.payload_size"),
    last_value("vm.memory.total"),
    distribution("http.request.latency")
  ]
])

To emit Statsd metrics, Peep supports both UDP and Unix Domain Sockets.

Why another Telemetry.Metrics library?

Both TelemetryMetricsStatsd and TelemetryMetrics.Prometheus are great choices for emitting telemetry. However, Peep makes several different choices that may not be as general-purpose as either of those libraries.

No sampled metrics

Sampling is a popular approach to reduce the amount of Statsd data flowing out of a service, but naive sampling dramatically reduces visibility into the shapes of distributions. Peep represents distributions using histograms, using a small exponential function by default. This sacrifices some granularity on individual samples, but one usually doesn't mind too much if a sample value of 95 is rounded to 100, or if 950 is rounded to 1000. These histograms are emitted to statsd using the optional sample rate of 1/$count.

Peep uses :atomics stored in :ets for performance. New :atomics arrays are created when a metric with a new set of tags is observed, so there is a slight overhead when handling the first telemetry event with a distinct set of tags. Peep reporter processes are not involved in the handling of any :telemetry events, so there's no chance of a single process becoming a bottleneck.

Distributions are aggregated immediately

This is a consequence of choosing to represent distributions as histograms. There is no step in Peep's processing where samples are aggregated in large batches. This leads to a flatter performance profile when scaling up.

Statsd packets are stuffed

This is something that is not (at the time of writing) supported by TelemetryMetricsStatsd, but the need for stuffing packets became pretty clear when handling tens of thousands of telemetry events every second. During each reporting period, metrics collected by a Peep reporter will be collected into a minimum number of packets. Users can set the maximum packet size in Peep.Options.

Supported :reporter_options

:prometheus_type

When using Telemetry.Metrics.sum/2 or Telemetry.Metrics.last_value/2, you can use this option to define the Prometheus type used by the metric. By default, sum/2 uses counter and last_value/2 uses gauge. It can be useful when some values are already precomputed, for example presummed socket stats.

:peep_export_conversion_factor

A positive number that scales the values of a Telemetry.Metrics.sum/2, Telemetry.Metrics.last_value/2 or Telemetry.Metrics.distribution/2 when they are exported.

For example, to store durations as nanoseconds and report them as seconds:

distribution("http.request.duration",
  unit: :nanosecond,
  reporter_options: [
    max_value: 60_000_000_000,
    peep_export_conversion_factor: 1.0e-9
  ]
)

Note: Unlike this option, setting :unit in a Telemetry.Metrics struct does affect Peep's storage of distributions. The recommendation is to store distribution sample measurements in a granularity fine enough to spread most of the distribution over several buckets, then to set :peep_export_conversion_factor.

Summary

Functions

Returns a specification to start this module under a supervisor.

Fetches all metrics from the worker. Called when preparing Prometheus or StatsD data.

Removes metrics whose metadata contains the specified tag patterns.

Sends StatsD metrics now, without waiting for the next flush interval.

Returns measurements about the size of a running Peep's storage, in number of ETS elements and in bytes of memory.

Types

metric_id()

@type metric_id() :: non_neg_integer()

Functions

allow_metric?(metric)

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

get_all_metrics(name)

Fetches all metrics from the worker. Called when preparing Prometheus or StatsD data.

prune_tags(name, tags_patterns)

Removes metrics whose metadata contains the specified tag patterns.

Example inputs:

  • [%{foo: :bar}, %{baz: :quux}] removes metrics with foo == :bar OR baz == :quux
  • [%{foo: :bar, baz: :quux}] removes metrics with foo == :bar AND baz == :quux
  • [%{foo: :one}, %{foo: :two}] removes metrics with foo == :one OR foo == :two

start_link(options)

statsd_flush(peep)

@spec statsd_flush(GenServer.server()) :: :ok

Sends StatsD metrics now, without waiting for the next flush interval.

Returns :ok once the packets are sent. Does nothing if the worker has no :statsd options.

storage_size(name)

@spec storage_size(atom()) :: %{size: non_neg_integer(), memory: non_neg_integer()}

Returns measurements about the size of a running Peep's storage, in number of ETS elements and in bytes of memory.