github.com/securityscorecard/go-stats

Metrics and Instrumentation


Keywords
datadog, influxdb, library, metrics, signals-collection, stats
License
MIT
Install
go get github.com/securityscorecard/go-stats

Documentation

go-stats

Exposes a Statser interface.

// Statser can submit metrics
type Statser interface {
  // Timing submits a timing metric
  Timing(name string, dur time.Duration, tags ...Tags)
  // Count submits a count metric
  Count(name string, count int64, tags ...Tags)

  WithPrefix(prefix string) Statser
  WithTags(tags Tags) Statser
  WithTag(tagKey, tagValue string) Statser
}

Usage

There are several New functions that will take an existing metrics collection client and output a Statser, meaning this repo doesn't create the initial client for you.

Datadog Statser

func main() {
  statser := stats.NewDatadogStatser(c)
  ...

  statser.Timing("requests", dur)
}

Also available: stats.NewInfluxDBStatser

Global Statser

We can pass around a statser, but we can also set a global default Statser, which can later be retrieved using stats.DefaultStatser().

func main() {
  // Set the default statser
  statser := stats.NewDatadogStatser(c)
  stats.SetDefaultStatser(statser)
  ...

  // Use the default statser
  stats.Timing("requests", dur)
  stats.Count("events", 10)

  // Can also get the default statser
  statser = stats.DefaultStatser()
  statser.Timing("requests", dur)
}

Applying Prefixes/Tags

We can also extend a Statser with prefixes and tags. WithPrefix, WithTag and WithTags all return a new Statser.

Prefixes

statser.WithPrefix("sub-worker").Count("domains", 1000)

Tags

// Single Tag
statser.WithTag("status", "success").Count("request", 1)

// Multiple Tags
statser.WithTags(stats.Tags{
    "status": "failure",
    "code": "400",
}).Count("request", 1)

Sampling

When a Statser is called many times in a small period of time, UDP overhead can become significant enough to warrant sampling. Use a sampled Statser to lower the number of outgoing requests.

// Set a Sample Rate of 1%
sampledStatser := stats.WithSample(statser, 0.01)

// Timing will only be called once every 100 calls
sampledStatser.Timing("request", time.Since(start))

// Count will only be called once every 100 calls
// the count will be adjusted (1 -> 100)
// according to the sample rate
sampledStatser.Count("request", 1)

Example Usage

ddStatsd, _ := statsd.New(ddEndpoint)
statser := stats.NewDatadogStatser(ddStatsd)
statser = statser.WithPrefix("subdomain-discovery").WithTag("worker", workerType)
stats.SetDefaultStatser(statser)

type Worker struct {
    statser stats.Statser
}

func NewWorker() *Worker {
    return &Worker{
        statser: stats.DefaultStatser().WithPrefix("discoverer"),
    }
}

func (w *Worker) DoWork() {
    startTime := time.Now()
    ...
    w.statser.WithTag("status", workStatus).Timing("work", time.Since(startTime))
}

Utilities - Timer/Counter

Timer

func main() {
  statser := stats.NewDatadogStatser(c)
  ...

  // Create a new timer
  timer := stats.NewTimer("requests", tags)
  defer timer.Finish(statser) // use `nil` for default statser
  ...
}

Counter

func main() {
  statser := stats.NewDatadogStatser(c)
  ...

  // Create a new counter
  counter := stats.NewCounter("events", tags)

  // Do stuff that increment count
  counter.Inc(3)
  ...

  counter.Finish(statser) // use `nil` for default statser
}