Skip to content

Commit

Permalink
store metric units in a fixed tag (hawkular#58)
Browse files Browse the repository at this point in the history
  • Loading branch information
jmazzitelli committed Dec 21, 2016
1 parent 8fcc9de commit 9dfce3f
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 5 deletions.
10 changes: 10 additions & 0 deletions collector/manager/metrics_collector_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,18 @@ func (mcm *MetricsCollectorManager) declareMetricDefinitions(liveMetrics []hmetr
}
}

// the metric tags will consist of the custom tags as well as the fixed tags
metricTags := metric.Tags.ExpandTokens(false, additionalEnv)
units, err := collector.GetMetricUnits(metric.Units)
if err == nil {
if units.Symbol != "" {
metricTags["units"] = units.Symbol
}
} else {
glog.Warningf("Units for metric definition [%v] for endpoint [%v] is invalid. No units will be assigned. err=%v", metric.ID, endpoint.String(), err)
}

// put all the tags together for the full list of tags to be applied to this metric definition
allMetricTags := tags.Tags{}
allMetricTags.AppendTags(globalTags) // global tags are overridden by...
allMetricTags.AppendTags(endpointTags) // endpoint tags which are overridden by...
Expand Down
109 changes: 109 additions & 0 deletions collector/metric_units.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package collector

import (
"fmt"
"strings"
)

type MetricUnits struct {
Symbol string
Custom bool
}

const customMetricUnitsPrefix = "custom:"

type standardMetricUnits []MetricUnits

// standardMetricUnitsList is a list of standard metric units
// See https://en.wikipedia.org/wiki/International_System_of_Units and http://metrics20.org/spec/
var standardMetricUnitsList = standardMetricUnits{
// absolute sizes in bytes
{Symbol: "B"},
{Symbol: "kB"},
{Symbol: "MB"},
{Symbol: "GB"},
{Symbol: "TB"},
{Symbol: "PB"},
{Symbol: "KiB"},
{Symbol: "MiB"},
{Symbol: "GiB"},
{Symbol: "TiB"},
{Symbol: "PiB"},

// absolute sizes in bits
{Symbol: "b"},
{Symbol: "kb"},
{Symbol: "Mb"},
{Symbol: "Gb"},
{Symbol: "Tb"},
{Symbol: "Pb"},
{Symbol: "Kib"},
{Symbol: "Mib"},
{Symbol: "Gib"},
{Symbol: "Tib"},
{Symbol: "Pib"},

// relative time
{Symbol: "jiff"},
{Symbol: "ns"},
{Symbol: "us"},
{Symbol: "ms"},
{Symbol: "s"},
{Symbol: "M"},
{Symbol: "h"},
{Symbol: "d"},
{Symbol: "w"},

// frequency
{Symbol: "Hz"},
{Symbol: "kHz"},
{Symbol: "MHz"},
{Symbol: "GHz"},

// temperature
{Symbol: "C"},
{Symbol: "F"},
{Symbol: "K"},

// current
{Symbol: "uA"},
{Symbol: "mA"},
{Symbol: "A"},
{Symbol: "kA"},
{Symbol: "MA"},
{Symbol: "GA"},

// voltage
{Symbol: "uV"},
{Symbol: "mV"},
{Symbol: "V"},
{Symbol: "kV"},
{Symbol: "MV"},
{Symbol: "GV"},

// percentage
{Symbol: "%"},

// none (no metric units are applicable)
{Symbol: ""},
}

// GetMetricUnits will check to see if the given string is a valid units identifier.
// If it is valid, this returns the units string as a MetricUnits.
// If it is not a valid units identifier, an error is returned.
// If it is a custom units identifier, it is returned minus the custom units prefix.
func GetMetricUnits(u string) (MetricUnits, error) {
if len(u) > len(customMetricUnitsPrefix) && strings.HasPrefix(u, customMetricUnitsPrefix) {
mu := MetricUnits{
Symbol: strings.TrimPrefix(u, customMetricUnitsPrefix),
Custom: true,
}
return mu, nil
}
for _, x := range standardMetricUnitsList {
if x.Symbol == u {
return x, nil
}
}
return MetricUnits{}, fmt.Errorf("invalid metric units: %v", u)
}
51 changes: 51 additions & 0 deletions collector/metric_units_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package collector

import (
"testing"
)

func TestGetMetricUnits(t *testing.T) {
// make sure valid metrics are retrieved successfully
u, e := GetMetricUnits("ms")
if e != nil {
t.Errorf("Should not have failed. Error=%v", e)
}
if u.Symbol != "ms" {
t.Errorf("Should have matched 'ms'. u=%v", u)
}
if u.Custom != false {
t.Errorf("Should not have been custom. u=%v", u)
}

u, e = GetMetricUnits("custom:foobars")
if e != nil {
t.Errorf("Should not have failed. Error=%v", e)
}
if u.Symbol != "foobars" {
t.Errorf("Should have matched 'foobars'. u=%v", u)
}
if u.Custom != true {
t.Errorf("Should have been custom. u=%v", u)
}

u, e = GetMetricUnits("")
if e != nil {
t.Errorf("Should not have failed. Empty units means 'none'. Error=%v", e)
}
if u.Symbol != "" {
t.Errorf("Should have matched the 'none' units (an empty string). u=%v", u)
}
if u.Custom != false {
t.Errorf("Should not have been custom. u=%v", u)
}

// make sure errors are generated properly
u, e = GetMetricUnits("millis")
if e == nil {
t.Errorf("Should have failed - not a standard metric. u=%v", u)
}
u, e = GetMetricUnits("foobars")
if e == nil {
t.Errorf("Should have failed - not a standard metric. u=%v", u)
}
}
15 changes: 10 additions & 5 deletions collector/metrics_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@ const (
// Tags specified here will be attached to the metric when stored to Hawkular Metrics.
// USED FOR YAML
type MonitoredMetric struct {
ID string ",omitempty"
Name string
Type metrics.MetricType
Tags tags.Tags ",omitempty"
ID string ",omitempty"
Name string
Type metrics.MetricType
Units string ",omitempty"
Tags tags.Tags ",omitempty"
}

// Endpoint provides information about how to connect to a particular endpoint in order
Expand All @@ -64,7 +65,7 @@ type Endpoint struct {
}

func (m *MonitoredMetric) String() string {
return fmt.Sprintf("Metric: id=[%v], name=[%v], type=[%v], tags=[%v]", m.ID, m.Name, m.Type, m.Tags)
return fmt.Sprintf("Metric: id=[%v], name=[%v], type=[%v], units=[%v], tags=[%v]", m.ID, m.Name, m.Type, m.Units, m.Tags)
}

func (e *Endpoint) String() string {
Expand Down Expand Up @@ -115,6 +116,10 @@ func (e *Endpoint) ValidateEndpoint() error {
}
}

if _, err := GetMetricUnits(m.Units); err != nil {
return fmt.Errorf("Endpoint [%v] metric [%v] has invalid units [%v]", e.URL, m.Name, m.Units)
}

// if there is no metric ID given, just use the metric name itself
if m.ID == "" {
e.Metrics[i].ID = m.Name
Expand Down

0 comments on commit 9dfce3f

Please sign in to comment.