From f41d3adad2989c43d4b8dba23d16c489a7e690be Mon Sep 17 00:00:00 2001 From: Ben Johnson Date: Thu, 6 Oct 2016 15:11:33 -0600 Subject: [PATCH] Add StatsClient with DataDog implementation. --- datadog/datadog.go | 113 ++++++++++++++++++++++++++++++++++++++++ datadog/datadog_test.go | 29 +++++++++++ stats.go | 38 ++++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 datadog/datadog.go create mode 100644 datadog/datadog_test.go create mode 100644 stats.go diff --git a/datadog/datadog.go b/datadog/datadog.go new file mode 100644 index 000000000..24e756c3f --- /dev/null +++ b/datadog/datadog.go @@ -0,0 +1,113 @@ +package datadog + +import ( + "sort" + "time" + + "github.com/DataDog/datadog-go/statsd" + "github.com/umbel/pilosa" +) + +const ( + // Rate represents a metric rate of 1/sec. + Rate = 1 + + // Client buffer size. + BufferLen = 256 +) + +// Ensure client implements interface. +var _ pilosa.StatsClient = &StatsClient{} + +// StatsClient represents a DataDog implementation of pilosa.StatsClient. +type StatsClient struct { + client *statsd.Client + tags []string +} + +// NewStatsClient returns a new instance of StatsClient. +func NewStatsClient() (*StatsClient, error) { + c, err := statsd.NewBuffered("127.0.0.1:8125", BufferLen) + if err != nil { + return nil, err + } + return &StatsClient{client: c}, nil +} + +// Close closes the connection to the agent. +func (c *StatsClient) Close() error { + return c.client.Close() +} + +// Tags returns a sorted list of tags on the client. +func (c *StatsClient) Tags() []string { + return c.tags +} + +// WithTags returns a new client with additional tags appended. +func (c *StatsClient) WithTags(tags ...string) pilosa.StatsClient { + return &StatsClient{ + client: c.client, + tags: unionTags(c.tags, tags), + } +} + +// Count tracks the number of times something occurs per second. +func (c *StatsClient) Count(name string, value int64) error { + return c.client.Count(name, value, c.tags, Rate) +} + +// Gauge sets the value of a metric. +func (c *StatsClient) Gauge(name string, value float64) error { + return c.client.Gauge(name, value, c.tags, Rate) +} + +// Histogram tracks statistical distribution of a metric. +func (c *StatsClient) Histogram(name string, value float64) error { + return c.client.Histogram(name, value, c.tags, Rate) +} + +// Set tracks number of unique elements. +func (c *StatsClient) Set(name string, value string) error { + return c.client.Set(name, value, c.tags, Rate) +} + +// Timing tracks timing information for a metric. +func (c *StatsClient) Timing(name string, value time.Duration) error { + return c.client.Timing(name, value, c.tags, Rate) +} + +// unionTags returns a sorted set of tags which combine a & b. +func unionTags(a, b []string) []string { + // Sort both sets first. + sort.Strings(a) + sort.Strings(b) + + // Find size of largest slice. + n := len(a) + if len(b) > n { + n = len(b) + } + + // Exit if both sets are empty. + if n == 0 { + return nil + } + + // Iterate over both in order and merge. + other := make([]string, 0, n) + for len(a) > 0 || len(b) > 0 { + if len(a) == 0 { + other, b = append(other, b[0]), b[1:] + } else if len(b) == 0 { + other, a = append(other, a[0]), a[1:] + } else if a[0] < b[0] { + other, a = append(other, a[0]), a[1:] + } else if b[0] < a[0] { + other, b = append(other, b[0]), b[1:] + } else { + other, a, b = append(other, a[0]), a[1:], b[1:] + } + } + return other +} diff --git a/datadog/datadog_test.go b/datadog/datadog_test.go new file mode 100644 index 000000000..fcfb6b6ae --- /dev/null +++ b/datadog/datadog_test.go @@ -0,0 +1,29 @@ +package datadog_test + +import ( + "reflect" + "testing" + + "github.com/umbel/pilosa/datadog" +) + +func TestStatsClient_WithTags(t *testing.T) { + // Create a new client. + c, err := datadog.NewStatsClient() + if err != nil { + t.Fatal(err) + } + defer c.Close() + + // Create a new client with additional tags. + c1 := c.WithTags("foo", "bar") + if tags := c1.Tags(); !reflect.DeepEqual(tags, []string{"bar", "foo"}) { + t.Fatalf("unexpected tags: %+v", tags) + } + + // Create a new client from the clone with more tags. + c2 := c1.WithTags("bar", "baz") + if tags := c2.Tags(); !reflect.DeepEqual(tags, []string{"bar", "baz", "foo"}) { + t.Fatalf("unexpected tags: %+v", tags) + } +} diff --git a/stats.go b/stats.go new file mode 100644 index 000000000..82cffa38c --- /dev/null +++ b/stats.go @@ -0,0 +1,38 @@ +package pilosa + +import "time" + +// StatsClient represents a client to a stats server. +type StatsClient interface { + // Returns a sorted list of tags on the client. + Tags() []string + + // Returns a new client with additional tags appended. + WithTags(tags ...string) StatsClient + + // Tracks the number of times something occurs per second. + Count(name string, value int64) error + + // Sets the value of a metric. + Gauge(name string, value float64) error + + // Tracks statistical distribution of a metric. + Histogram(name string, value float64) error + + // Tracks number of unique elements. + Set(name string, value string) error + + // Tracks timing information for a metric. + Timing(name string, value time.Duration) error +} + +// NopStatsClient represents a client that doesn't do anything. +type NopStatsClient struct{} + +func (c *NopStatsClient) Tags() []string { return nil } +func (c *NopStatsClient) WithTags(tags ...string) StatsClient { return c } +func (c *NopStatsClient) Count(name string, value int64) error { return nil } +func (c *NopStatsClient) Gauge(name string, value float64) error { return nil } +func (c *NopStatsClient) Histogram(name string, value float64) error { return nil } +func (c *NopStatsClient) Set(name string, value string) error { return nil } +func (c *NopStatsClient) Timing(name string, value time.Duration) error { return nil }