-
Notifications
You must be signed in to change notification settings - Fork 616
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue #151: Add support for Circonus metrics
This patch adds support for sending metrics to Circonus (http://circonus.com). The code is squashed from PR #150 with minor edits and was written by @maier.
- Loading branch information
1 parent
add14ed
commit 4a94696
Showing
8 changed files
with
307 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
package metrics | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"log" | ||
"os" | ||
"sync" | ||
"time" | ||
|
||
cgm "github.com/circonus-labs/circonus-gometrics" | ||
) | ||
|
||
var ( | ||
circonus *cgmRegistry | ||
once sync.Once | ||
) | ||
|
||
const serviceName = "fabio" | ||
|
||
// circonusRegistry returns a provider that reports to Circonus. | ||
func circonusRegistry(prefix string, | ||
circKey string, | ||
circApp string, | ||
circURL string, | ||
circBrokerID string, | ||
circCheckID string, | ||
interval time.Duration) (Registry, error) { | ||
|
||
var initError error | ||
|
||
once.Do(func() { | ||
if circKey == "" { | ||
initError = errors.New("metrics: Circonus API token key") | ||
return | ||
} | ||
|
||
if circApp == "" { | ||
circApp = serviceName | ||
} | ||
|
||
host, err := os.Hostname() | ||
if err != nil { | ||
initError = fmt.Errorf("metrics: unable to initialize Circonus %s", err) | ||
return | ||
} | ||
|
||
cfg := &cgm.Config{} | ||
|
||
cfg.CheckManager.API.TokenKey = circKey | ||
cfg.CheckManager.API.TokenApp = circApp | ||
cfg.CheckManager.API.URL = circURL | ||
cfg.CheckManager.Check.ID = circCheckID | ||
cfg.CheckManager.Broker.ID = circBrokerID | ||
cfg.Interval = fmt.Sprintf("%.0fs", interval.Seconds()) | ||
cfg.CheckManager.Check.InstanceID = host | ||
cfg.CheckManager.Check.DisplayName = fmt.Sprintf("%s /%s", host, serviceName) | ||
cfg.CheckManager.Check.SearchTag = fmt.Sprintf("service:%s", serviceName) | ||
|
||
metrics, err := cgm.NewCirconusMetrics(cfg) | ||
if err != nil { | ||
initError = fmt.Errorf("metrics: unable to initialize Circonus %s", err) | ||
return | ||
} | ||
|
||
circonus = &cgmRegistry{metrics, prefix} | ||
|
||
metrics.Start() | ||
|
||
log.Print("[INFO] Sending metrics to Circonus") | ||
}) | ||
|
||
return circonus, initError | ||
} | ||
|
||
type cgmRegistry struct { | ||
metrics *cgm.CirconusMetrics | ||
prefix string | ||
} | ||
|
||
// Names is not supported by Circonus. | ||
func (m *cgmRegistry) Names() []string { return nil } | ||
|
||
// Unregister is implicitly supported by Circonus, | ||
// stop submitting the metric and it stops being sent to Circonus. | ||
func (m *cgmRegistry) Unregister(name string) {} | ||
|
||
// UnregisterAll is implicitly supported by Circonus, | ||
// stop submitting metrics and they will no longer be sent to Circonus. | ||
func (m *cgmRegistry) UnregisterAll() {} | ||
|
||
// GetCounter returns a counter for the given metric name. | ||
func (m *cgmRegistry) GetCounter(name string) Counter { | ||
metricName := fmt.Sprintf("%s`%s", m.prefix, name) | ||
return &cgmCounter{m.metrics, metricName} | ||
} | ||
|
||
// GetTimer returns a timer for the given metric name. | ||
func (m *cgmRegistry) GetTimer(name string) Timer { | ||
metricName := fmt.Sprintf("%s`%s", m.prefix, name) | ||
return &cgmTimer{m.metrics, metricName} | ||
} | ||
|
||
type cgmCounter struct { | ||
metrics *cgm.CirconusMetrics | ||
name string | ||
} | ||
|
||
// Inc increases the counter by n. | ||
func (c *cgmCounter) Inc(n int64) { | ||
c.metrics.IncrementByValue(c.name, uint64(n)) | ||
} | ||
|
||
type cgmTimer struct { | ||
metrics *cgm.CirconusMetrics | ||
name string | ||
} | ||
|
||
// Percentile is not supported by Circonus. | ||
func (t *cgmTimer) Percentile(nth float64) float64 { return 0 } | ||
|
||
// Rate1 is not supported by Circonus. | ||
func (t *cgmTimer) Rate1() float64 { return 0 } | ||
|
||
// UpdateSince adds delta between start and current time as | ||
// a sample to a histogram. The histogram is created if it | ||
// does not already exist. | ||
func (t *cgmTimer) UpdateSince(start time.Time) { | ||
t.metrics.Timing(t.name, float64(time.Since(start))) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package metrics | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
"time" | ||
) | ||
|
||
func TestRegistry(t *testing.T) { | ||
t.Log("Testing registry interface") | ||
|
||
p := &cgmRegistry{} | ||
|
||
t.Log("\tNames()") | ||
names := p.Names() | ||
if names != nil { | ||
t.Errorf("Expected nil got '%+v'", names) | ||
} | ||
|
||
t.Log("\tUnregister()") | ||
p.Unregister("foo") | ||
|
||
t.Log("\tUnregisterAll()") | ||
p.UnregisterAll() | ||
|
||
t.Log("\tGetTimer()") | ||
timer := p.GetTimer("foo") | ||
if timer == nil { | ||
t.Error("Expected a timer, got nil") | ||
} | ||
} | ||
|
||
func TestTimer(t *testing.T) { | ||
t.Log("Testing timer interface") | ||
|
||
timer := &cgmTimer{} | ||
|
||
t.Log("\tPercentile()") | ||
pct := timer.Percentile(99.9) | ||
if pct != 0 { | ||
t.Errorf("Expected 0 got '%+v'", pct) | ||
} | ||
|
||
t.Log("\tRate1()") | ||
rate := timer.Rate1() | ||
if rate != 0 { | ||
t.Errorf("Expected 0 got '%+v'", rate) | ||
} | ||
} | ||
|
||
func TestAll(t *testing.T) { | ||
start := time.Now() | ||
|
||
if os.Getenv("CIRCONUS_API_TOKEN") == "" { | ||
t.Skip("skipping test; $CIRCONUS_API_TOKEN not set") | ||
} | ||
|
||
t.Log("Testing cgm functionality -- this *will* create/use a check") | ||
|
||
apiKey := os.Getenv("CIRCONUS_API_TOKEN") | ||
apiApp := os.Getenv("CIRCONUS_API_APP") | ||
apiURL := os.Getenv("CIRCONUS_API_URL") | ||
brokerID := os.Getenv("CIRCONUS_BROKER_ID") | ||
checkID := os.Getenv("CIRCONUS_CHECK_ID") | ||
|
||
interval, err := time.ParseDuration("60s") | ||
if err != nil { | ||
t.Fatalf("Unable to parse interval %+v", err) | ||
} | ||
|
||
circ, err := circonusRegistry("test", apiKey, apiApp, apiURL, brokerID, checkID, interval) | ||
if err != nil { | ||
t.Fatalf("Unable to initialize Circonus +%v", err) | ||
} | ||
|
||
counter := circ.GetCounter("fooCounter") | ||
counter.Inc(3) | ||
|
||
timer := circ.GetTimer("fooTimer") | ||
timer.UpdateSince(start) | ||
|
||
circonus.metrics.Flush() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters