metrics

package
v1.10.0 Latest Latest
Warning

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

Go to latest
Published: Feb 23, 2023 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package metrics provides a generic interface for sending metrics from your script, application, or service. Metrics consist of a name, a numeric value, and optionally a number of tags that are made up of key/value pairs.

Multiple implementations are provided to allow for production, local development, and testing use cases. Generally you will instantiate one of these clients and then write metrics to it:

import "github.com/istreamlabs/go-metrics/metrics"

client := metrics.NewDataDogClient("127.0.0.1:8125", "myprefix")

// Simple incrementing counter
client.Incr("requests.count")

// Tagging with counters
client.WithTags(map[string]string{
  "tag": "value"
}).Incr("requests.count")

// Sample rate for high-throughput applications
client.WithRate(0.01).Incr("requests.count")

Also provided are useful clients for testing, both for when you want to assert that certain metrics are emitted and a `NullClient` for when you want to ignore them.

func TestFoo(t *testing.T) {
  client := metrics.NewRecorderClient().WithTest(t)

  client.WithTags(map[string]string{
    "tag": "value",
  }).Count("requests.count", 1)

  // Now, assert that the metric was emitted.
  client.
    Expect("requests.count").
    Value(1).
    Tag("tag", "value")
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Call

type Call fmt.Stringer

Call describes either a metrics or event call. You can cast it to either `MetricCall` or `EventCall` to get at type-specific fields for custom checks. Conversion to a string results in a serialized representation that looks like one of the following, where the (RATE) field is only shown when not equal to 1.0:

// Serialized metric
NAME:VALUE(RATE)[TAG_NAME:TAG_VALUE TAG_NAME:TAG_VALUE ...]

// Serialized event
TITLE:TEXT[TAG_NAME:TAG_VALUE TAG_NAME:TAG_VALUE ...]

// Example of casting to get additional data
value := call.(*MetricCall).Value
title := call.(*EventCall).Event.Title

type Client

type Client interface {
	// WithTags returns a new client with the given tags.
	WithTags(tags map[string]string) Client

	// WithRate returns a new client with the given sample rate.
	WithRate(rate float64) Client

	// Count/Incr/Decr set a numeric integer value.
	Count(name string, value int64)
	Incr(name string)
	Decr(name string)

	// Gauge sets a numeric floating point value.
	Gauge(name string, value float64)

	// Event creates a new event, which allows additional information to be
	// included when something worth calling out happens.
	Event(e *statsd.Event)

	// Timing creates a histogram of a duration.
	Timing(name string, value time.Duration)

	// Historgram creates a numeric floating point metric with min/max/avg/p95/etc.
	Histogram(name string, value float64)

	// Distribution tracks the statistical distribution of a set of values.
	Distribution(name string, value float64)

	// Close closes all client connections and flushes any buffered data.
	Close() error
}

Client provides a generic interface to log metrics and events

type DataDogClient

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

DataDogClient is a dogstatsd metrics client implementation.

Example
package main

import (
	"github.com/DataDog/datadog-go/v5/statsd"
	"github.com/istreamlabs/go-metrics/metrics"
)

func main() {
	// Create a new DataDog metrics client.
	datadog := metrics.NewDataDogClient("127.0.0.1:8125", "myprefix")
	datadog.WithTags(map[string]string{
		"tag": "value",
	}).Incr("requests.count")

	// Create a DataDog metrics client with a custom configured statsd.
	client, err := statsd.New("127.0.0.1:8125", statsd.WithNamespace("myprefix"))
	if err != nil {
		panic(err)
	}
	custom := metrics.NewDataDogClient("", "", metrics.WithStatsd(client))
	custom.WithTags(map[string]string{
		"tag": "value",
	}).Incr("requests.count")
}
Output:

func NewDataDogClient

func NewDataDogClient(address string, namespace string, options ...Option) *DataDogClient

NewDataDogClient creates a new dogstatsd client pointing to `address` with the metrics prefix of `namespace`. For example, given a namespace of `foo.bar`, a call to `Incr('baz')` would emit a metric with the full name `foo.bar.baz` (note the period between the namespace and metric name).

func (*DataDogClient) Close added in v1.5.0

func (c *DataDogClient) Close() error

Close closes all client connections and flushes any buffered data.

func (*DataDogClient) Count

func (c *DataDogClient) Count(name string, value int64)

Count adds some integer value to a metric.

func (*DataDogClient) Decr

func (c *DataDogClient) Decr(name string)

Decr subtracts one from a metric.

func (*DataDogClient) Distribution added in v1.5.0

func (c *DataDogClient) Distribution(name string, value float64)

Distribution tracks the statistical distribution of a set of values.

func (*DataDogClient) Event

func (c *DataDogClient) Event(e *statsd.Event)

Event tracks an event that may be relevant to other metrics.

func (*DataDogClient) Gauge

func (c *DataDogClient) Gauge(name string, value float64)

Gauge sets a numeric value.

func (*DataDogClient) Histogram

func (c *DataDogClient) Histogram(name string, value float64)

Histogram sets a numeric value while tracking min/max/avg/p95/etc.

func (*DataDogClient) Incr

func (c *DataDogClient) Incr(name string)

Incr adds one to a metric.

func (*DataDogClient) Timing

func (c *DataDogClient) Timing(name string, value time.Duration)

Timing tracks a duration.

func (*DataDogClient) WithRate added in v1.2.0

func (c *DataDogClient) WithRate(rate float64) Client

WithRate clones this client with a new sample rate.

func (*DataDogClient) WithTags

func (c *DataDogClient) WithTags(tags map[string]string) Client

WithTags clones this client with additional tags. Duplicate tags overwrite the existing value.

func (*DataDogClient) WithoutTelemetry added in v1.6.0

func (c *DataDogClient) WithoutTelemetry() Client

WithoutTelemetry clones this client with telemetry stats turned off. Underlying DataDog statsd client only supports turning off telemetry, which is on by default.

type EventCall

type EventCall struct {
	Event  *statsd.Event
	TagMap map[string]string
}

EventCall tracks a single event call and tags.

func (*EventCall) String

func (e *EventCall) String() string

String returns a serialized representation of the event.

type InfoLogger

type InfoLogger interface {
	Printf(format string, args ...interface{})
}

InfoLogger provides a method for logging info messages and is implemented by the standard `log` package as well as various other packages.

type LoggerClient

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

LoggerClient simple dumps metrics into the log. Useful when running locally for testing. Can be used with multiple different logging systems.

Example
package main

import (
	"github.com/istreamlabs/go-metrics/metrics"
)

func main() {
	client := metrics.NewLoggerClient(nil)
	client.WithTags(map[string]string{
		"tag1": "value1",
	}).Incr("requests.count")
}
Output:

Count requests.count:1 map[tag1:value1]

func NewLoggerClient

func NewLoggerClient(logger InfoLogger) *LoggerClient

NewLoggerClient creates a new logging client. If `logger` is `nil` then it defaults to stdout using the built-in `log` package. It is equivalent to the following with added auto-detection for colorized output:

metrics.NewLoggerClient(log.New(os.Stdout, "", 0))

You can use your own logger and enable colorized output manually via:

metrics.NewLoggerClient(myLog).Colorized()

func (*LoggerClient) Close added in v1.5.0

func (c *LoggerClient) Close() error

Close on LoggerClient is a no-op

func (*LoggerClient) Colorized added in v1.3.0

func (c *LoggerClient) Colorized() *LoggerClient

Colorized enables colored terminal output.

func (*LoggerClient) Count

func (c *LoggerClient) Count(name string, value int64)

Count adds some value to a metric.

func (*LoggerClient) Decr

func (c *LoggerClient) Decr(name string)

Decr subtracts one from a metric.

func (*LoggerClient) Distribution added in v1.5.0

func (c *LoggerClient) Distribution(name string, value float64)

Distribution tracks the statistical distribution of a set of values.

func (*LoggerClient) Event

func (c *LoggerClient) Event(e *statsd.Event)

Event tracks an event that may be relevant to other metrics.

func (*LoggerClient) Gauge

func (c *LoggerClient) Gauge(name string, value float64)

Gauge sets a numeric value.

func (*LoggerClient) Histogram

func (c *LoggerClient) Histogram(name string, value float64)

Histogram sets a numeric value while tracking min/max/avg/p95/etc.

func (*LoggerClient) Incr

func (c *LoggerClient) Incr(name string)

Incr adds one to a metric.

func (*LoggerClient) Timing

func (c *LoggerClient) Timing(name string, value time.Duration)

Timing tracks a duration.

func (*LoggerClient) WithRate added in v1.3.0

func (c *LoggerClient) WithRate(rate float64) Client

WithRate clones this client with a given sample rate. Subsequent calls will be limited to logging metrics at this rate.

func (*LoggerClient) WithTags

func (c *LoggerClient) WithTags(tags map[string]string) Client

WithTags clones this client with additional tags. Duplicate tags overwrite the existing value.

type MetricCall

type MetricCall struct {
	Name   string
	Value  float64
	Rate   float64
	TagMap map[string]string
}

MetricCall tracks a single metrics call, value, and tags. All values are converted to `float64` from the `int`, `float64`, or `time.Duration` inputs.

Example
package main

import (
	"fmt"

	"github.com/istreamlabs/go-metrics/metrics"
)

func main() {
	// First, create a recorder and emit a metric.
	recorder := metrics.NewRecorderClient()
	recorder.Incr("requests.count")

	// Then, print out some info about the `Incr` call.
	call := recorder.GetCalls()[0].(*metrics.MetricCall)
	fmt.Printf("'%s' value is '%v'", call.Name, call.Value)
}
Output:

'requests.count' value is '1'

func (*MetricCall) String

func (m *MetricCall) String() string

String returns a serialized representation of the metric.

type NullClient

type NullClient struct {
}

NullClient does nothing. Useful for tests when you do not care about metrics state or output.

Example
package main

import (
	"github.com/istreamlabs/go-metrics/metrics"
)

func main() {
	client := metrics.NewNullClient()
	client.WithTags(map[string]string{
		"tag1": "value1",
		"tag2": "value2",
	}).Incr("requests.count")
	client.Incr("other")

}
Output:

func NewNullClient

func NewNullClient() *NullClient

NewNullClient creates a new null client.

func (*NullClient) Close added in v1.5.0

func (c *NullClient) Close() error

Close on a NullClient is a no-op

func (*NullClient) Count

func (c *NullClient) Count(name string, value int64)

Count adds some value to a metric.

func (*NullClient) Decr

func (c *NullClient) Decr(name string)

Decr subtracts one from a metric.

func (*NullClient) Distribution added in v1.5.0

func (c *NullClient) Distribution(name string, value float64)

Distribution tracks the statistical distribution of a set of values.

func (*NullClient) Event

func (c *NullClient) Event(event *statsd.Event)

Event tracks an event that may be relevant to other metrics.

func (*NullClient) Gauge

func (c *NullClient) Gauge(name string, value float64)

Gauge sets a numeric value.

func (*NullClient) Histogram

func (c *NullClient) Histogram(name string, value float64)

Histogram sets a numeric value while tracking min/max/avg/p95/etc.

func (*NullClient) Incr

func (c *NullClient) Incr(name string)

Incr adds one to a metric.

func (*NullClient) Timing

func (c *NullClient) Timing(name string, value time.Duration)

Timing tracks a duration.

func (*NullClient) WithRate added in v1.3.0

func (c *NullClient) WithRate(rate float64) Client

WithRate clones this client with a given sample rate.

func (*NullClient) WithTags

func (c *NullClient) WithTags(tags map[string]string) Client

WithTags clones this client with additional tags. Duplicate tags overwrite the existing value.

type Option added in v1.6.0

type Option func(*Options) error

Option is a client option. Can return an error if validation fails.

func WithStatsd added in v1.10.0

func WithStatsd(s *statsd.Client) Option

WithStatsd sets a custom configured statsd client. This lets you set any options that aren't directly supported by the metrics package. When used, this ignores the address and namespace arguments to NewDataDogClient.

func WithoutTelemetry added in v1.6.0

func WithoutTelemetry() Option

WithoutTelemetry turns off sending DataDog telemetry metrics.

type Options added in v1.6.0

type Options struct {
	WithoutTelemetry bool
	Statsd           *statsd.Client
}

Options contains the configuration options for a client.

type Query

type Query interface {
	// Reject fails the test at least the minimum number of items are left.
	// Use this with:
	// `recorder.If("...")...Reject()`
	Reject()

	// Accept fails the test if fewer than the minimum number of items are left.
	// It is the opposite of `Reject` and usually you would use the
	// `recorder.Expect()...` shorthand instead. Use it like so:
	// `recorder.If("...")...Accept()`
	Accept()

	// GetCalls returns the currently matching list of calls. The calls may be
	// cast to `MetricCall` or `EventCall` for further processing.
	GetCalls() []Call

	// MinTimes sets the minimum number of calls that should be left before
	// the query is considered a failure. The default value is `1`, so if you
	// expect five calls you can use `recorder.Expect('my.metric').MinTimes(5)`.
	MinTimes(num int) Query

	// Contains filters out any metric or event that does not contain `component`
	// in the serialized representation of the call.
	Contains(component string) Query

	// ID filters out any metric whose name does not match `id` or event whose
	// title does not match `id`. Using `*` will match any ID.
	ID(name string) Query

	// Value filters out any metric whose numeric value does not match `value`.
	// All events are filtered out.
	Value(value interface{}) Query

	// Text filters out any event whose content text does not match `text`. All
	// metrics are filtered out.
	Text(text string) Query

	// Tag filters out any metric or event that does not contain the given tag
	// `name` and `value`.
	Tag(name, value string) Query

	// TagName filters out any metric or event that does not contain a given
	// tag with name `name`. The value does not matter.
	TagName(name string) Query

	// Rate filters out any metric that does not have the given sample rate.
	Rate(rate float64) Query
}

Query provides a mechanism to filter and test metrics for given chainable constraints. It allows you to write tests to ensure behavior that is independent of metrics fired in upstream or downstream code.

// Assert that a metric with the given numerical value and tag has
// been fired during a test.
recorder.Expect("my.metric").Value(1).Tag("foo", "bar")

// Fail if a metric with the given name and numerical value is found.
recorder.If("my.metric").Value(100).Reject()

Custom checks are also possible via `GetCalls`, which returns a slice of calls that can be processed however you like. The recorder provides a `Fatalf` function you can use that will dump out the current metrics stack if your custom check fails.

type RecorderClient

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

RecorderClient records any metric that is sent, allowing you to make assertions about the metrics flowing out of a service. A shared call context is used that allows cloned clients to all write to the same call info store.

Assertion methods for convenient testing

These methods provide a fast way to write tests while providing useful and consistent output in the event of a test failure. For example:

func MyTest(t *testing.T) {
  recorder := metrics.NewRecorderClient().WithTest(t)
  recorder.Count("my.metric", 1)

  recorder.Expect("my.metric").Value(1)
}

It is possible to assert that a given metric, value, tag, etc does not exist and fail a test if it does:

func MyTest(t *testing.T) {
  recorder := metrics.NewRecorderClient().WithTest(t)
  recorder.Count("my.metric", 5)

  // Since a value of 10 was set, this succeeds.
  recorder.If("my.metric").Value(10).Reject()

  // However, these two would fail the test.
  recorder.If("my.metric").Reject()
  recorder.If("my.metric").Value(5).Reject()
}

Custom Checks

The recorder provides access to individual call information so that custom checks can be written if needed. For example, to check that a given metric name was emitted twice with values in a particular order:

func MyTest(t *testing.T) {
  recorder := metrics.NewRecorderClient().WithTest(t)
  recorder.Count("foo", 1)
  recorder.Count("foo", 2)

  values := make([]float64, 2)
  for _, call := range recorder.Expect("foo").GetCalls() {
    incr = call.(*metrics.MetricCall)
    values = append(values, incr.Value)
  }
  if !reflect.DeepEqual(values, []float64{1.0, 2.0}) {
    recorder.Fatalf("Expected values '1, 2' in order.")
  }
}
Example
package main

import (
	"testing"

	"github.com/istreamlabs/go-metrics/metrics"
)

func main() {
	recorder := metrics.NewRecorderClient()
	recorder.WithTags(map[string]string{
		"tag1": "value1",
		"tag2": "value2",
	}).Incr("requests.count")
	recorder.Incr("other")

	// When run in a unit test, you can invoke assertion methods.
	t := &testing.T{}
	recorder.WithTest(t).
		Expect("requests.count").
		Value(1).
		Tag("tag1", "value1").
		Tag("tag2", "value2")
}
Output:

Example (Output)
package main

import (
	"fmt"

	"github.com/istreamlabs/go-metrics/metrics"
)

func main() {
	recorder := metrics.NewRecorderClient()
	recorder.WithTags(map[string]string{
		"tag1": "value1",
		"tag2": "value2",
	}).Incr("requests.count")
	recorder.Incr("other")

	// Converting calls to a string returns their serialized representation.
	for _, call := range recorder.GetCalls() {
		fmt.Println(call)
	}
}
Output:

requests.count:1[tag1:value1 tag2:value2]
other:1[]

func NewRecorderClient

func NewRecorderClient() *RecorderClient

NewRecorderClient creates a new recording metrics client.

func (*RecorderClient) Close added in v1.5.0

func (c *RecorderClient) Close() error

Close on the RecorderClient is a no-op

func (*RecorderClient) Count

func (c *RecorderClient) Count(name string, value int64)

Count adds some value to a metric.

func (*RecorderClient) Decr

func (c *RecorderClient) Decr(name string)

Decr subtracts one from a metric.

func (*RecorderClient) Distribution added in v1.5.0

func (c *RecorderClient) Distribution(name string, value float64)

Distribution tracks the statistical distribution of a set of values.

func (*RecorderClient) Event

func (c *RecorderClient) Event(e *statsd.Event)

Event tracks an event that may be relevant to other metrics.

func (*RecorderClient) Expect

func (c *RecorderClient) Expect(id string) Query

Expect finds metrics (by name) or events (by title) and returns the matching calls. A wildcard `*` character will match any ID. This method does *not* remove the call from the recorded call list.

// Get a metric by its name.
recorder.Expect("my.metric")

// Get an event by its title.
recorder.Expect("my.event")

func (*RecorderClient) ExpectContains

func (c *RecorderClient) ExpectContains(component string) Query

ExpectContains finds metrics or events that contain the `component` in their serialized representations. It does *not* remove the call from the recorded call list.

recorder.Incr("foo1")
recorder.Incr("foo2")

// The following matches both calls above.
recorder.ExpectContains("foo")

See `Call.String()` for the serialization format.

func (*RecorderClient) ExpectEmpty

func (c *RecorderClient) ExpectEmpty()

ExpectEmpty asserts that no metrics have been emitted.

func (*RecorderClient) Fatalf

func (c *RecorderClient) Fatalf(format string, args ...interface{})

Fatalf fails whatever test is attached to this recorder and additionally appends the current metrics call stack and calling information to the output message to help with debugging.

func (*RecorderClient) Gauge

func (c *RecorderClient) Gauge(name string, value float64)

Gauge sets a numeric value.

func (*RecorderClient) GetCalls

func (c *RecorderClient) GetCalls() []Call

GetCalls returns a slice of all recorded calls.

func (*RecorderClient) Histogram

func (c *RecorderClient) Histogram(name string, value float64)

Histogram sets a numeric value while tracking min/max/avg/p95/etc.

func (*RecorderClient) If

func (c *RecorderClient) If(id string) Query

If acts like `Expect`, but doesn't check for the minimum number of calls after each query operation. It is to be used with query methods like `Accept` and `Reject`. For example:

// Create a metric with the good tag.
recorder.WithTags(map[string]string{"good": "tag"}).Incr("my.metric")

// This will not fail, because the bad tag is not found.
recorder.If("my.metric").Tag("bad", "tag").Reject()

// This will fail because the metric is found.
recorder.If("my.metric").Reject()

// The following are equivalent, but the first is preferred.
recorder.Expect("my.metric")
recorder.If("my.metric").Accept()

func (*RecorderClient) Incr

func (c *RecorderClient) Incr(name string)

Incr adds one to a metric.

func (*RecorderClient) Length

func (c *RecorderClient) Length() int

Length returns the number of calls in the call info context. It is a shorthand for `len(recorder.GetCalls())`.

func (*RecorderClient) Reset

func (c *RecorderClient) Reset()

Reset will clear the call info context, which is useful between test runs.

func (*RecorderClient) Timing

func (c *RecorderClient) Timing(name string, value time.Duration)

Timing tracks a duration.

func (*RecorderClient) WithRate added in v1.3.0

func (c *RecorderClient) WithRate(rate float64) Client

WithRate clones this client with a new sample rate.

func (*RecorderClient) WithTags

func (c *RecorderClient) WithTags(tags map[string]string) Client

WithTags clones this client with additional tags. Duplicate tags overwrite the existing value.

func (*RecorderClient) WithTest

func (c *RecorderClient) WithTest(test TestFailer) *RecorderClient

WithTest returns a recorder client linked with a given test instance.

type TestFailer

type TestFailer interface {
	Fatalf(format string, args ...interface{})
}

TestFailer provides a small subset of methods that can be used to fail a test case. The built-in `testing.T` and `testing.B` structs implement these methods, as do other frameworks, e.g. `GinkgoT()` for Ginkgo.

Jump to

Keyboard shortcuts

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