diff --git a/internal/ccMetric/ccMetric.go b/internal/ccMetric/ccMetric.go index ae7ab1b..20b9786 100644 --- a/internal/ccMetric/ccMetric.go +++ b/internal/ccMetric/ccMetric.go @@ -14,62 +14,73 @@ import ( // // See: https://docs.influxdata.com/influxdb/latest/reference/syntax/line-protocol/ type ccMetric struct { - name string // Measurement name - tags []*lp.Tag // ordered list of of tags - fields []*lp.Field // unordered list of of fields - tm time.Time // timestamp - meta []*lp.Tag // odered list of meta data tags + name string // Measurement name + meta map[string]string // map of meta data tags + tags map[string]string // map of of tags + fields []*lp.Field // unordered list of of fields + tm time.Time // timestamp } // ccmetric access functions type CCMetric interface { - lp.MutableMetric // SetTime, AddTag, AddField - AddMeta(key, value string) // Add a meta data tag - MetaList() []*lp.Tag // Returns the meta data list - RemoveTag(key string) // Remove a tag addressed by its key - GetTag(key string) (string, bool) // Get a tag addressed by its key - GetMeta(key string) (string, bool) // Get a meta data tab addressed by its key + lp.Metric // Time(), Name(), TagList(), FieldList() + + SetTime(t time.Time) + + Meta() map[string]string // Map of meta data tags + MetaList() []*lp.Tag // Ordered list of meta data + AddMeta(key, value string) // Add a meta data tag + GetMeta(key string) (string, bool) // Get a meta data tab addressed by its key + + Tags() map[string]string // Map of tags + AddTag(key, value string) // Add a tag + GetTag(key string) (string, bool) // Get a tag by its key + RemoveTag(key string) // Remove a tag by its key + GetField(key string) (interface{}, bool) // Get a field addressed by its key HasField(key string) bool // Check if a field key is present RemoveField(key string) // Remove a field addressed by its key } -// Meta returns the list of meta data tags as key-value mapping +// Meta returns the meta data tags as key-value mapping func (m *ccMetric) Meta() map[string]string { - meta := make(map[string]string, len(m.meta)) - for _, m := range m.meta { - meta[m.Key] = m.Value - } - return meta + return m.meta } -// MetaList returns the list of meta data tags +// MetaList returns the the list of meta data tags as sorted list of key value tags func (m *ccMetric) MetaList() []*lp.Tag { - return m.meta + + ml := make([]*lp.Tag, 0, len(m.meta)) + for key, value := range m.meta { + ml = append(ml, &lp.Tag{Key: key, Value: value}) + } + sort.Slice(ml, func(i, j int) bool { return ml[i].Key < ml[j].Key }) + return ml } // String implements the stringer interface for data type ccMetric func (m *ccMetric) String() string { - return fmt.Sprintf("%s %v %v %v %d", m.name, m.Tags(), m.Meta(), m.Fields(), m.tm.UnixNano()) + return fmt.Sprintf("%s %v %v %v %d", m.name, m.tags, m.meta, m.Fields(), m.tm.UnixNano()) } -// Name returns the metric name +// Name returns the measurement name func (m *ccMetric) Name() string { return m.name } // Tags returns the the list of tags as key-value-mapping func (m *ccMetric) Tags() map[string]string { - tags := make(map[string]string, len(m.tags)) - for _, tag := range m.tags { - tags[tag.Key] = tag.Value - } - return tags + return m.tags } -// TagList returns the list of tags +// TagList returns the the list of tags as sorted list of key value tags func (m *ccMetric) TagList() []*lp.Tag { - return m.tags + tl := make([]*lp.Tag, 0, len(m.tags)) + for key, value := range m.tags { + tl = append(tl, &lp.Tag{Key: key, Value: value}) + } + sort.Slice(tl, func(i, j int) bool { return tl[i].Key < tl[j].Key }) + return tl } // Fields returns the list of fields as key-value-mapping @@ -99,112 +110,50 @@ func (m *ccMetric) SetTime(t time.Time) { // HasTag checks if a tag with key equal to is present in the list of tags func (m *ccMetric) HasTag(key string) bool { - for _, tag := range m.tags { - if tag.Key == key { - return true - } - } - return false + _, ok := m.tags[key] + return ok } // GetTag returns the tag with tag's key equal to func (m *ccMetric) GetTag(key string) (string, bool) { - for _, tag := range m.tags { - if tag.Key == key { - return tag.Value, true - } - } - return "", false + value, ok := m.tags[key] + return value, ok } // RemoveTag removes the tag with tag's key equal to // and keeps the tag list ordered by the keys func (m *ccMetric) RemoveTag(key string) { - for i, tag := range m.tags { - if tag.Key == key { - copy(m.tags[i:], m.tags[i+1:]) - m.tags[len(m.tags)-1] = nil - m.tags = m.tags[:len(m.tags)-1] - return - } - } + delete(m.tags, key) } // AddTag adds a tag (consisting of key and value) // and keeps the tag list ordered by the keys func (m *ccMetric) AddTag(key, value string) { - for i, tag := range m.tags { - if key > tag.Key { - continue - } - - if key == tag.Key { - tag.Value = value - return - } - - m.tags = append(m.tags, nil) - copy(m.tags[i+1:], m.tags[i:]) - m.tags[i] = &lp.Tag{Key: key, Value: value} - return - } - - m.tags = append(m.tags, &lp.Tag{Key: key, Value: value}) + m.tags[key] = value } // HasTag checks if a meta data tag with meta data's key equal to is present in the list of meta data tags func (m *ccMetric) HasMeta(key string) bool { - for _, tag := range m.meta { - if tag.Key == key { - return true - } - } - return false + _, ok := m.meta[key] + return ok } // GetMeta returns the meta data tag with meta data's key equal to func (m *ccMetric) GetMeta(key string) (string, bool) { - for _, tag := range m.meta { - if tag.Key == key { - return tag.Value, true - } - } - return "", false + value, ok := m.meta[key] + return value, ok } // RemoveMeta removes the meta data tag with tag's key equal to // and keeps the meta data tag list ordered by the keys func (m *ccMetric) RemoveMeta(key string) { - for i, tag := range m.meta { - if tag.Key == key { - copy(m.meta[i:], m.meta[i+1:]) - m.meta[len(m.meta)-1] = nil - m.meta = m.meta[:len(m.meta)-1] - return - } - } + delete(m.meta, key) } // AddMeta adds a meta data tag (consisting of key and value) // and keeps the meta data list ordered by the keys func (m *ccMetric) AddMeta(key, value string) { - for i, tag := range m.meta { - if key > tag.Key { - continue - } - - if key == tag.Key { - tag.Value = value - return - } - - m.meta = append(m.meta, nil) - copy(m.meta[i+1:], m.meta[i:]) - m.meta[i] = &lp.Tag{Key: key, Value: value} - return - } - - m.meta = append(m.meta, &lp.Tag{Key: key, Value: value}) + m.meta[key] = value } // AddField adds a field (consisting of key and value) to the unordered list of fields @@ -261,62 +210,49 @@ func New( ) (CCMetric, error) { m := &ccMetric{ name: name, - tags: nil, - fields: nil, + tags: make(map[string]string, len(tags)), + meta: make(map[string]string, len(meta)), + fields: make([]*lp.Field, 0, len(fields)), tm: tm, - meta: nil, } - // Sorted list of tags - if len(tags) > 0 { - m.tags = make([]*lp.Tag, 0, len(tags)) - for k, v := range tags { - m.tags = append(m.tags, - &lp.Tag{Key: k, Value: v}) - } - sort.Slice(m.tags, func(i, j int) bool { return m.tags[i].Key < m.tags[j].Key }) + // deep copy tags + for k, v := range tags { + m.tags[k] = v } - // Sorted list of meta data tags - if len(meta) > 0 { - m.meta = make([]*lp.Tag, 0, len(meta)) - for k, v := range meta { - m.meta = append(m.meta, - &lp.Tag{Key: k, Value: v}) - } - sort.Slice(m.meta, func(i, j int) bool { return m.meta[i].Key < m.meta[j].Key }) + // deep copy meta data tags + for k, v := range meta { + m.meta[k] = v } // Unsorted list of fields - if len(fields) > 0 { - m.fields = make([]*lp.Field, 0, len(fields)) - for k, v := range fields { - v := convertField(v) - if v == nil { - continue - } - m.AddField(k, v) + for k, v := range fields { + v := convertField(v) + if v == nil { + continue } + m.AddField(k, v) } return m, nil } // FromMetric copies the metric -func FromMetric(other CCMetric) CCMetric { +func FromMetric(other ccMetric) CCMetric { m := &ccMetric{ name: other.Name(), - tags: make([]*lp.Tag, len(other.TagList())), + tags: make(map[string]string), fields: make([]*lp.Field, len(other.FieldList())), - meta: make([]*lp.Tag, len(other.MetaList())), + meta: make(map[string]string), tm: other.Time(), } - for i, tag := range other.TagList() { - m.tags[i] = &lp.Tag{Key: tag.Key, Value: tag.Value} + for key, value := range other.Tags() { + m.tags[key] = value } - for i, s := range other.MetaList() { - m.meta[i] = &lp.Tag{Key: s.Key, Value: s.Value} + for key, value := range other.Meta() { + m.meta[key] = value } for i, field := range other.FieldList() { @@ -329,17 +265,14 @@ func FromMetric(other CCMetric) CCMetric { func FromInfluxMetric(other lp.Metric) CCMetric { m := &ccMetric{ name: other.Name(), - tags: make([]*lp.Tag, len(other.TagList())), + tags: make(map[string]string), fields: make([]*lp.Field, len(other.FieldList())), - meta: make([]*lp.Tag, 0), + meta: make(map[string]string), tm: other.Time(), } - for i, otherTag := range other.TagList() { - m.tags[i] = &lp.Tag{ - Key: otherTag.Key, - Value: otherTag.Value, - } + for _, otherTag := range other.TagList() { + m.tags[otherTag.Key] = otherTag.Value } for i, otherField := range other.FieldList() { diff --git a/internal/metricRouter/metricRouter.go b/internal/metricRouter/metricRouter.go index 870af02..83c14e7 100644 --- a/internal/metricRouter/metricRouter.go +++ b/internal/metricRouter/metricRouter.go @@ -141,11 +141,11 @@ func (r *metricRouter) EvalCondition(cond string, point lp.CCMetric) (bool, erro // Add metric name, tags, meta data, fields and timestamp to the parameter list params := make(map[string]interface{}) params["name"] = point.Name() - for _, t := range point.TagList() { - params[t.Key] = t.Value + for key, value := range point.Tags() { + params[key] = value } - for _, m := range point.MetaList() { - params[m.Key] = m.Value + for key, value := range point.Meta() { + params[key] = value } for _, f := range point.FieldList() { params[f.Key] = f.Value diff --git a/sinks/gangliaSink.go b/sinks/gangliaSink.go index 87506a0..3fd48e7 100644 --- a/sinks/gangliaSink.go +++ b/sinks/gangliaSink.go @@ -30,16 +30,16 @@ func (s *GangliaSink) Write(point lp.CCMetric) error { var err error = nil var tagsstr []string var argstr []string - for _, t := range point.TagList() { - switch t.Key { + for key, value := range point.Tags() { + switch key { case "cluster": - argstr = append(argstr, fmt.Sprintf("--cluster=%s", t.Value)) + argstr = append(argstr, fmt.Sprintf("--cluster=%s", value)) case "unit": - argstr = append(argstr, fmt.Sprintf("--units=%s", t.Value)) + argstr = append(argstr, fmt.Sprintf("--units=%s", value)) case "group": - argstr = append(argstr, fmt.Sprintf("--group=%s", t.Value)) + argstr = append(argstr, fmt.Sprintf("--group=%s", value)) default: - tagsstr = append(tagsstr, fmt.Sprintf("%s=%s", t.Key, t.Value)) + tagsstr = append(tagsstr, fmt.Sprintf("%s=%s", key, value)) } } if len(tagsstr) > 0 { diff --git a/sinks/influxSink.go b/sinks/influxSink.go index dca1572..7313490 100644 --- a/sinks/influxSink.go +++ b/sinks/influxSink.go @@ -5,10 +5,11 @@ import ( "crypto/tls" "errors" "fmt" + "log" + lp "github.com/ClusterCockpit/cc-metric-collector/internal/ccMetric" influxdb2 "github.com/influxdata/influxdb-client-go/v2" influxdb2Api "github.com/influxdata/influxdb-client-go/v2/api" - "log" ) type InfluxSink struct { @@ -61,12 +62,12 @@ func (s *InfluxSink) Init(config sinkConfig) error { func (s *InfluxSink) Write(point lp.CCMetric) error { tags := map[string]string{} fields := map[string]interface{}{} - for _, t := range point.TagList() { - tags[t.Key] = t.Value + for key, value := range point.Tags() { + tags[key] = value } if s.meta_as_tags { - for _, m := range point.MetaList() { - tags[m.Key] = m.Value + for key, value := range point.Meta() { + tags[key] = value } } for _, f := range point.FieldList() {