vinxi / metrics

Extensible metrics instrumentation with multi-backend reporting
MIT License
2 stars 1 forks source link

metrics Build Status GoDoc Coverage Status Go Report Card

Simple and extensible metrics instrumentation for your proxies. Collects useful and versatile metrics based on the analysis of duplex HTTP traffic and Go runtime stats.

Supports counters, gauges and histogram with 50, 75, 90, 95, 99 and 99.9 percentiles.

Uses codahale/metrics under the hood.

Reporters

Reporters are pluggable components that reads metric reports and tipically sends it to an data ingestor provider.

You can write and plug in your own reporter. See how to write reporters section.

Built-in supported reporters:

Meters

Meters are simple functions that reads HTTP request/response info and generates further counters, gauges or histograms based on it.

metrics package allows you to easily extend meter function in order to measure custom or new properies of the HTTP flow to cover your specific needs. See how to write meters section.

Default provided meters (listed as: description, measure type, metric name):

Installation

go get -u gopkg.in/vinxi/metrics.v0

API

See godoc reference.

Examples

Report metrics to InfluxDB

package main

import (
  "fmt"
  "gopkg.in/vinxi/metrics.v0"
  "gopkg.in/vinxi/metrics.v0/reporters/influx"
  "gopkg.in/vinxi/vinxi.v0"
)

const port = 3100

func main() {
  // Create a new vinxi proxy
  vs := vinxi.NewServer(vinxi.ServerOptions{Port: port})

  // Attach the metrics middleware
  config := influx.Config{
    URL:      "http://localhost:8086",
    Username: "root",
    Password: "root",
    Database: "metrics",
  }
  vs.Use(metrics.New(influx.New(config)))

  // Target server to forward
  vs.Forward("http://httpbin.org")

  fmt.Printf("Server listening on port: %d\n", port)
  err := vs.Listen()
  if err != nil {
    fmt.Errorf("Error: %s\n", err)
  }
}

Report metrics only for certain scenarios via multiplexer

package main

import (
  "fmt"
  "gopkg.in/vinxi/metrics.v0"
  "gopkg.in/vinxi/metrics.v0/reporters/influx"
  "gopkg.in/vinxi/mux.v0"
  "gopkg.in/vinxi/vinxi.v0"
)

const port = 3100

func main() {
  // Create a new vinxi proxy
  vs := vinxi.NewServer(vinxi.ServerOptions{Port: port})

  // InfluxDB reporter config
  config := influx.Config{
    URL:      "http://localhost:8086",
    Username: "root",
    Password: "root",
    Database: "metrics",
  }

  // Attach the metrics middleware via muxer
  mx := mux.If(mux.Method("GET", "POST"), mux.Path("/"))
  mx.Use(metrics.New(influx.New(config)))
  vs.Use(mx)

  // Target server to forward
  vs.Forward("http://httpbin.org")

  fmt.Printf("Server listening on port: %d\n", port)
  err := vs.Listen()
  if err != nil {
    fmt.Errorf("Error: %s\n", err)
  }
}

Writting reporters

metrics package allows you to write and plug in custom reporters in order to send data to third-party metrics and storage providers.

Reporters must implement the Reporter interface, which consists is a single method:

type Reporter interface {
  Report(metrics.Report) error
}

The metrics publisher will call the Report method passing the Report struct, which exports the fields Counters and Gauges.

Reporter example

import (
  "fmt"
  "gopkg.in/vinxi/metrics.v0"  
)

type MyCustomReporter struct {
  // reporter specific fields  
}

func (m *MyCustomReporter) Report(r metrics.Report) {
  // Print maps
  fmt.Printf("Counters: %#v \n", r.Counters)
  fmt.Printf("Gauges: %#v \n", r.Gauges)

  // Here you should usually map and transform the metrics report
  // into reporter specific data structures.
  data := mapReport(r)

  // Finally send the metrics, tipically via network to another server
  reporterClient.Send(data)
}

Writting meters

Meters are simple functions implementing the following function signature:

type MeterFunc func(*metrics.Info, *metrics.Metrics)

Meter example

package main

import (
  "fmt"
  "gopkg.in/vinxi/metrics.v0"
  "gopkg.in/vinxi/vinxi.v0"
)

const port = 3100

func main() {
  // Create a new vinxi proxy
  vs := vinxi.NewServer(vinxi.ServerOptions{Port: port})

  // Create a custom meter function the increases a counter
  // when the response status is 200
  myMeter := func(i *metrics.Info, m *metrics.Metrics) {
    if i.Status == 200 {
      m.Counter("res.success.total").Add()
    }
  }

  // Create a new metrics middleware
  m := metrics.New(reporter(collect))
  // Add the custom meter
  m.AddMeter(myMeter)
  // Attach the metrics middleware
  vs.Use(m)

  // Target server to forward
  vs.Forward("http://httpbin.org")

  fmt.Printf("Server listening on port: %d\n", port)
  err := vs.Listen()
  if err != nil {
    fmt.Errorf("Error: %s\n", err)
  }
}

// Simple stub reporter
type reporter func(metrics.Report) error

func (c reporter) Report(r metrics.Report) error {
  return c(r)
}

func collect(r metrics.Report) error {
  fmt.Printf("Gaudes: %#v\n", r.Gauges)
  fmt.Printf("Counters: %#v\n", r.Counters)
  return nil
}

License

MIT