spectator

package module
v0.2.2 Latest Latest
Warning

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

Go to latest
Published: Feb 16, 2024 License: Apache-2.0 Imports: 24 Imported by: 10

README

Go Reference Snapshot Release

Spectator-go

⚠ Experimental

Simple library for instrumenting code to record dimensional time series.

Description

This implements a basic Spectator library for instrumenting golang applications, sending metrics to an Atlas aggregator service.

Instrumenting Code

package main

import (
	"github.com/Netflix/spectator-go"
	"strconv"
	"time"
)

type Server struct {
	registry       *spectator.Registry
	requestCountId *spectator.Id
	requestLatency *spectator.Timer
	responseSizes  *spectator.DistributionSummary
}

type Request struct {
	country string
}

type Response struct {
	status int
	size   int64
}

func (s *Server) Handle(request *Request) (res *Response) {
	clock := s.registry.Clock()
	start := clock.Now()

	// initialize res
	res = &Response{200, 64}

	// Update the counter id with dimensions based on the request. The
	// counter will then be looked up in the registry which should be
	// fairly cheap, such as lookup of id object in a map
	// However, it is more expensive than having a local variable set
	// to the counter.
	cntId := s.requestCountId.WithTag("country", request.country).WithTag("status", strconv.Itoa(res.status))
	s.registry.CounterWithId(cntId).Increment()

	// ...
	s.requestLatency.Record(clock.Now().Sub(start))
	s.responseSizes.Record(res.size)
	return
}

func newServer(registry *spectator.Registry) *Server {
	return &Server{
		registry,
		registry.NewId("server.requestCount", nil),
		registry.Timer("server.requestLatency", nil),
		registry.DistributionSummary("server.responseSizes", nil),
	}
}

func getNextRequest() *Request {
	// ...
	return &Request{"US"}
}

func main() {
	commonTags := map[string]string{"nf.app": "example", "nf.region": "us-west-1"}
	config := &spectator.Config{Frequency: 5 * time.Second, Timeout: 1 * time.Second,
		Uri: "http://example.org/api/v1/publish", CommonTags: commonTags}
	registry := spectator.NewRegistry(config)

	// optionally set custom logger (it must implement Debugf, Infof, Errorf)
	// registry.SetLogger(logger)
	registry.Start()
	defer registry.Stop()

	// collect memory and file descriptor metrics
	spectator.CollectRuntimeMetrics(registry)

	server := newServer(registry)

	for i := 1; i < 3; i++ {
		// get a request
		req := getNextRequest()
		server.Handle(req)
	}
}

Logging

Logging is implemented with the standard Golang log package. The logger defines interfaces for Debugf, Infof, and Errorf which means that under normal operation, you will see log messages for all of these levels. There are useful messages implemented at the Debug level which can help diagnose the metric publishing workflow. If you do not see any of these messages, then it is an indication that the Registry may not be started.

If you do not wish to see debug log messages from spectator-go, then you should configure a custom logger which implements the Logger interface. A library such as Zap can provide this functionality, which will then allow for log level control at the command line with the --log-level=debug flag.

Debugging Metric Payloads

Set the following environment variable to enumerate the metrics payloads which are sent to the backend. This is useful for debugging metric publishing issues.

export SPECTATOR_DEBUG_PAYLOAD=1

Known Issues

Unable to close body: context canceled

If you see the following two error messages repeated in your application logs, then you are running version 1.16.10 or 1.17.3 of Golang which introduced a regression (Issue#49366). See PR#59 in this project for related discussion.

level=error msg="Could not POST measurements: HTTP 200 context canceled"
level=error msg="Unable to close body: context canceled"

Documentation

Overview

Package spectator provides a minimal Go implementation of the Netflix Java Spectator library. The goal of this package is to allow Go programs to emit metrics to Atlas.

Please refer to the Java Spectator documentation for information on spectator / Atlas fundamentals: https://netflix.github.io/spectator/en/latest/

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CollectMemStats

func CollectMemStats(registry *Registry)

CollectMemStats collects memory stats

See: https://golang.org/pkg/runtime/#MemStats

func CollectRuntimeMetrics

func CollectRuntimeMetrics(registry *Registry)

CollectRuntimeMetrics starts the collection of memory and file handle metrics

func CollectSysStats

func CollectSysStats(registry *Registry)

CollectSysStats collects system stats: current/max file handles, number of goroutines

Types

type Clock

type Clock interface {
	Now() time.Time
	Nanos() int64
}

Clock is an interface to provide functionality to create timing/latency metrics.

type Config

type Config struct {
	Frequency      time.Duration     `json:"frequency"`
	Timeout        time.Duration     `json:"timeout"`
	Uri            string            `json:"uri"`
	BatchSize      int               `json:"batch_size"`
	CommonTags     map[string]string `json:"common_tags"`
	PublishWorkers int64             `json:"publish_workers"`
	Log            Logger
	IsEnabled      func() bool
	IpcTimerRecord func(registry *Registry, id *Id, duration time.Duration)
}

Config represents the Registry's configuration.

type Counter

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

Counter is used to measure the rate at which some event is occurring. This type is safe for concurrent use.

You can find more about this type by viewing the relevant Java Spectator documentation here:

https://netflix.github.io/spectator/en/latest/intro/counter/

func NewCounter

func NewCounter(id *Id) *Counter

NewCounter generates a new counter, using the provided meter identifier.

func (*Counter) Add

func (c *Counter) Add(delta int64)

Add is to add a specific int64 delta to the current measurement.

func (*Counter) AddFloat

func (c *Counter) AddFloat(delta float64)

AddFloat adds a specific float64 delta to the current measurement.

func (*Counter) Count

func (c *Counter) Count() float64

Count returns the current value for the counter.

func (*Counter) Increment

func (c *Counter) Increment()

Increment increments the counter.

func (*Counter) Measure

func (c *Counter) Measure() []Measurement

Measure returns the list of measurements known by the counter. This will either contain one item, the last value, or no items. This also resets the internal counter to 0.0.

func (*Counter) MeterId

func (c *Counter) MeterId() *Id

MeterId returns the meter identifier.

type DefaultLogger

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

DefaultLogger is a plain text stdout logger.

func (*DefaultLogger) Debugf

func (l *DefaultLogger) Debugf(format string, v ...interface{})

Debugf is for debug level messages. Satisfies Logger interface.

func (*DefaultLogger) Errorf

func (l *DefaultLogger) Errorf(format string, v ...interface{})

Errorf is for error level messages. Satisfies Logger interface.

func (*DefaultLogger) Infof

func (l *DefaultLogger) Infof(format string, v ...interface{})

Infof is for info level messages. Satisfies Logger interface.

type DistributionSummary

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

DistributionSummary is used to track the distribution of events. This is safe for concurrent use.

You can find more about this type by viewing the relevant Java Spectator documentation here:

https://netflix.github.io/spectator/en/latest/intro/dist-summary/

func NewDistributionSummary

func NewDistributionSummary(id *Id) *DistributionSummary

NewDistributionSummary generates a new distribution summary, using the provided meter identifier.

func (*DistributionSummary) Count

func (d *DistributionSummary) Count() int64

Count returns the number of unique events that have been tracked.

func (*DistributionSummary) Measure

func (d *DistributionSummary) Measure() []Measurement

Measure returns the list of measurements known by the counter. This should return 4 measurements in the slice:

count totalAmount totalOfSquares max

func (*DistributionSummary) MeterId

func (d *DistributionSummary) MeterId() *Id

MeterId returns the meter identifier.

func (*DistributionSummary) Record

func (d *DistributionSummary) Record(amount int64)

Record records a new value to track within the distribution.

func (*DistributionSummary) TotalAmount

func (d *DistributionSummary) TotalAmount() int64

TotalAmount is the total of all records summed together.

type Gauge

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

Gauge represents a value that is sampled at a specific point in time. One example might be the pending messages in a queue. This type is safe for concurrent use.

You can find more about this type by viewing the relevant Java Spectator documentation here:

https://netflix.github.io/spectator/en/latest/intro/gauge/

func NewGauge

func NewGauge(id *Id) *Gauge

NewGauge generates a new gauge, using the provided meter identifier.

func (*Gauge) Get

func (g *Gauge) Get() float64

Get retrieves the current value.

func (*Gauge) Measure

func (g *Gauge) Measure() []Measurement

Measure returns the list of measurements known by the gauge. This will either contain one item (the current value). This also resets the internal value to NaN.

func (*Gauge) MeterId

func (g *Gauge) MeterId() *Id

MeterId returns the meter identifier.

func (*Gauge) Set

func (g *Gauge) Set(value float64)

Set records the current value.

type HttpClient

type HttpClient struct {
	Client *http.Client
	// contains filtered or unexported fields
}

HttpClient represents a spectator HTTP client.

func NewHttpClient

func NewHttpClient(registry *Registry, timeout time.Duration, opts ...HttpClientOption) *HttpClient

NewHttpClient generates a new *HttpClient, allowing us to specify the timeout on requests.

func (*HttpClient) PostJson

func (h *HttpClient) PostJson(uri string, jsonBytes []byte) (response HttpResponse, err error)

PostJson attempts to submit JSON to the uri, with a 100 ms delay between failures.

type HttpClientOption added in v0.1.4

type HttpClientOption func(client *HttpClient)

func WithCompressThreshold added in v0.2.0

func WithCompressThreshold(threshold int) HttpClientOption

WithCompressThreshold is an HttpClientOption that specifies the size threshold for compressing payloads. A value of 0 disables compression.

type HttpResponse added in v0.1.2

type HttpResponse struct {
	Status int
	Body   []byte
}

HttpResponse represents a read HTTP response.

type Id

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

Id represents a meter's identifying information and dimensions (tags).

func NewId

func NewId(name string, tags map[string]string) *Id

NewId generates a new *Id from the metric name, and the tags you want to include on your metric.

func (*Id) MapKey added in v0.1.1

func (id *Id) MapKey() string

MapKey computes and saves a key within the struct to be used to uniquely identify this *Id in a map. This does use the information from within the *Id, so it assumes you've not accidentally double-declared this *Id.

func (*Id) Name

func (id *Id) Name() string

Name exposes the internal metric name field.

func (*Id) String

func (id *Id) String() string

func (*Id) Tags

func (id *Id) Tags() map[string]string

Tags directly exposes the internal tags map. This is not a copy of the map, so any modifications to it will be observed by the *Id.

func (*Id) WithDefaultStat

func (id *Id) WithDefaultStat(stat string) *Id

WithDefaultStat is effectively the WithStat() method, except it only creates the deep copy if the "statistic" tag is not set or is set to empty string. If the "statistic" tag is already present, the *Id is returned without being copied.

func (*Id) WithStat

func (id *Id) WithStat(stat string) *Id

WithStat is id.WithTag("statistic", stat). See that method's documentation for more info.

func (*Id) WithTag

func (id *Id) WithTag(key string, value string) *Id

WithTag creates a deep copy of the *Id, adding the requested tag to the internal collection.

func (*Id) WithTags

func (id *Id) WithTags(tags map[string]string) *Id

WithTags takes a map of tags, and returns a deep copy of *Id with the new tags appended to the original ones. Overlapping keys are overwritten. If the input to this method is empty, this does not return a deep copy of *Id.

type LogEntry

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

LogEntry represents a type for logging the information about POST requests to the remote endpoint. It's not really a log, so much as a specific collector for the registry's outbound HTTP requests.

func NewLogEntry

func NewLogEntry(registry *Registry, method string, url string) *LogEntry

NewLogEntry creates a new LogEntry.

func (*LogEntry) Log

func (entry *LogEntry) Log()

Log captures the time it took for the request to be completed, and records it within the registry.

func (*LogEntry) SetAttempt

func (entry *LogEntry) SetAttempt(attemptNumber int, final bool)

SetAttempt sets the ipc.attempt and ipc.attempt.final tags.

func (*LogEntry) SetError

func (entry *LogEntry) SetError(err string)

SetError sets the ipc.result tag to "failure", and the ipc.status tag to the error string.

func (*LogEntry) SetStatusCode

func (entry *LogEntry) SetStatusCode(code int)

SetStatusCode is for setting the http.status tag.

func (*LogEntry) SetSuccess

func (entry *LogEntry) SetSuccess()

SetSuccess sets both ipc.result and ipc.status to "success".

type Logger

type Logger interface {
	Debugf(format string, v ...interface{})
	Infof(format string, v ...interface{})
	Errorf(format string, v ...interface{})
}

Logger represents the shape of the logging dependency that the spectator library expects.

type ManualClock

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

ManualClock satisfies the Clock interface, using provided values as the time source. You need to seed the time with either .SetFromDuration() or .SetNanos().

func (*ManualClock) Nanos

func (c *ManualClock) Nanos() int64

Nanos returns the internal nanoseconds. Satisfies Clock interface.

func (*ManualClock) Now

func (c *ManualClock) Now() time.Time

Now effectively returns time.Unix(0, c.Nanos()). Satisfies Clock interface.

func (*ManualClock) SetFromDuration

func (c *ManualClock) SetFromDuration(duration time.Duration)

SetFromDuration takes a duration, and sets that to the internal nanosecond value. The .Now() probably has no value when this is used to populate the data.

func (*ManualClock) SetNanos

func (c *ManualClock) SetNanos(nanos int64)

SetNanos sets the internal nanoseconds value directly.

type Measurement

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

Measurement represents a single meter's measurement at a given point in time.

func NewMeasurement

func NewMeasurement(id *Id, Value float64) Measurement

NewMeasurement generates a new measurement, using the provided identifier and value.

func (Measurement) Id

func (m Measurement) Id() *Id

Id returns the measurement's identifier.

func (Measurement) String

func (m Measurement) String() string

func (Measurement) Value

func (m Measurement) Value() float64

Value returns the measurement's value.

type Meter

type Meter interface {
	MeterId() *Id
	Measure() []Measurement
}

Meter represents the functionality presented by the individual meter types.

type MeterFactoryFun

type MeterFactoryFun func() Meter

MeterFactoryFun is a type to allow dependency injection of the function used to generate meters.

type MonotonicCounter

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

MonotonicCounter is used track a monotonically increasing counter.

You can find more about this type by viewing the relevant Java Spectator documentation here:

https://netflix.github.io/spectator/en/latest/intro/gauge/#monotonic-counters

func NewMonotonicCounter

func NewMonotonicCounter(registry *Registry, name string, tags map[string]string) *MonotonicCounter

NewMonotonicCounter generates a new monotonic counter, taking the registry so that it can lazy-load the underlying counter once `Set` is called the first time. It generates a new meter identifier from the name and tags.

func NewMonotonicCounterWithId

func NewMonotonicCounterWithId(registry *Registry, id *Id) *MonotonicCounter

NewMonotonicCounterWithId generates a new monotonic counter, using the provided meter identifier.

func (*MonotonicCounter) Count

func (c *MonotonicCounter) Count() int64

Count returns the current counter value.

func (*MonotonicCounter) Set

func (c *MonotonicCounter) Set(amount int64)

Set adds amount to the current counter.

type Registry

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

Registry is the collection of meters being reported.

func NewRegistry

func NewRegistry(config *Config) *Registry

NewRegistry generates a new registry from the config.

If the config.IpcTimerRecord is unset, a default implementation is used.

If config.IsEnabled is unset, it defaults to an implementation that returns true.

If config.Log is unset, it defaults to using the default logger.

func NewRegistryConfiguredBy

func NewRegistryConfiguredBy(filePath string) (*Registry, error)

NewRegistryConfiguredBy loads a new Config JSON file from disk at the path specified.

Please note, when this method is used to load the configuration both Config.Frequency and Config.Timeout are assumed to not be a time.Duration but an int64 with second precision. As such this function multiplies those configuration values by time.Second, to convert them to time.Duration values.

func NewRegistryWithClock

func NewRegistryWithClock(config *Config, clock Clock) *Registry

NewRegistryWithClock returns a new registry with the clock overriden to the one injected here. This function is mostly used for testing.

func (*Registry) Clock

func (r *Registry) Clock() Clock

Clock returns the internal clock.

func (*Registry) Count added in v0.2.1

func (r *Registry) Count() int

Count returns the number of meters in the registry

func (*Registry) Counter

func (r *Registry) Counter(name string, tags map[string]string) *Counter

Counter calls NewId() with the name and tags, and then calls r.CounterWithId() using that *Id.

func (*Registry) CounterWithId

func (r *Registry) CounterWithId(id *Id) *Counter

CounterWithId returns a new *Counter, using the provided meter identifier.

func (*Registry) DistributionSummary

func (r *Registry) DistributionSummary(name string, tags map[string]string) *DistributionSummary

DistributionSummary calls NewId() using the name and tags, and then calls r.DistributionSummaryWithId() using that *Id.

func (*Registry) DistributionSummaryWithId

func (r *Registry) DistributionSummaryWithId(id *Id) *DistributionSummary

DistributionSummaryWithId returns a new *DistributionSummary, using the provided meter identifier.

func (*Registry) Gauge

func (r *Registry) Gauge(name string, tags map[string]string) *Gauge

Gauge calls NewId() with the name and tags, and then calls r.GaugeWithId() using that *Id.

func (*Registry) GaugeWithId

func (r *Registry) GaugeWithId(id *Id) *Gauge

GaugeWithId returns a new *Gauge, using the provided meter identifier.

func (*Registry) GetLogger

func (r *Registry) GetLogger() Logger

GetLogger returns the internal logger.

func (*Registry) Measurements

func (r *Registry) Measurements() []Measurement

Measurements returns the list of internal measurements that should be sent.

func (*Registry) Meters

func (r *Registry) Meters() []Meter

Meters returns all the internal meters.

func (*Registry) NewId

func (r *Registry) NewId(name string, tags map[string]string) *Id

NewId calls spectator.NewId().

func (*Registry) NewMeter

func (r *Registry) NewMeter(id *Id, meterFactory MeterFactoryFun) Meter

NewMeter registers a new meter internally, and then returns it to the caller. The meterFactory is used to generate this meter. If the id.MapKey() is already present in the internal collection, that is returned instead of creating a new one.

func (*Registry) SetLogger

func (r *Registry) SetLogger(logger Logger)

SetLogger overrides the internal logger.

func (*Registry) Start

func (r *Registry) Start() error

Start spins-up the background goroutine(s) for emitting collected metrics.

func (*Registry) Stop

func (r *Registry) Stop()

Stop shuts down the running goroutine(s), and attempts to flush the metrics.

func (*Registry) Timer

func (r *Registry) Timer(name string, tags map[string]string) *Timer

Timer calls NewId() with the name and tags, and then calls r.TimerWithId() using that *Id.

func (*Registry) TimerWithId

func (r *Registry) TimerWithId(id *Id) *Timer

TimerWithId returns a new *Timer, using the provided meter identifier.

type SystemClock

type SystemClock struct{}

SystemClock satisfies the Clock interface, using the system's time as its source.

func (*SystemClock) Nanos

func (c *SystemClock) Nanos() int64

Nanos returns time.Now().UnixNano(). Satisfies Clock interface.

func (*SystemClock) Now

func (c *SystemClock) Now() time.Time

Now returns time.Now(). Satisfies Clock interface.

type Timer

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

Timer is used to measure how long (in seconds) some event is taking. This type is safe for concurrent use.

func NewTimer

func NewTimer(id *Id) *Timer

NewTimer generates a new timer, using the provided meter identifier.

func (*Timer) Count

func (t *Timer) Count() int64

Count returns the number of unique times that have been recorded.

func (*Timer) Measure

func (t *Timer) Measure() []Measurement

Measure returns the list of measurements known by the counter. This should return 4 measurements in the slice:

count totalTime totalOfSquares max

func (*Timer) MeterId

func (t *Timer) MeterId() *Id

MeterId returns the meter identifier.

func (*Timer) Record

func (t *Timer) Record(amount time.Duration)

Record records the duration this specific event took.

func (*Timer) TotalTime

func (t *Timer) TotalTime() time.Duration

TotalTime returns the total duration of all recorded events.

Directories

Path Synopsis
Package histogram provides types and functionality for capturing metrics, and presenting them as percentiles.
Package histogram provides types and functionality for capturing metrics, and presenting them as percentiles.

Jump to

Keyboard shortcuts

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