forked from open-telemetry/opentelemetry-collector
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathotlp_metricfamily.go
More file actions
155 lines (139 loc) · 5.25 KB
/
otlp_metricfamily.go
File metadata and controls
155 lines (139 loc) · 5.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package internal
import (
"sort"
"strings"
"github.com/prometheus/prometheus/pkg/labels"
"github.com/prometheus/prometheus/pkg/textparse"
"go.opentelemetry.io/collector/consumer/pdata"
)
type metricFamilyPdata struct {
// We are composing the already present metricFamily to
// make for a scalable migration, so that we only edit target
// fields progressively, when we are ready to make changes.
metricFamily
mtype pdata.MetricDataType
groups map[string]*metricGroupPdata
}
// metricGroupPdata, represents a single metric of a metric family. for example a histogram metric is usually represent by
// a couple data complexValue (buckets and count/sum), a group of a metric family always share a same set of tags. for
// simple types like counter and gauge, each data point is a group of itself
type metricGroupPdata struct {
// We are composing the already present metricGroup to
// make for a scalable migration, so that we only edit target
// fields progressively, when we are ready to make changes.
metricGroup
family *metricFamilyPdata
}
func newMetricFamilyPdata(metricName string, mc MetadataCache) MetricFamily {
familyName := normalizeMetricName(metricName)
// lookup metadata based on familyName
metadata, ok := mc.Metadata(familyName)
if !ok && metricName != familyName {
// use the original metricName as metricFamily
familyName = metricName
// perform a 2nd lookup with the original metric name. it can happen if there's a metric which is not histogram
// or summary, but ends with one of those _count/_sum suffixes
metadata, ok = mc.Metadata(metricName)
// still not found, this can happen when metric has no TYPE HINT
if !ok {
metadata.Metric = familyName
metadata.Type = textparse.MetricTypeUnknown
}
}
return &metricFamilyPdata{
mtype: convToPdataMetricType(metadata.Type),
groups: make(map[string]*metricGroupPdata),
metricFamily: metricFamily{
name: familyName,
mc: mc,
droppedTimeseries: 0,
labelKeys: make(map[string]bool),
labelKeysOrdered: make([]string, 0),
metadata: &metadata,
groupOrders: make(map[string]int),
},
}
}
// updateLabelKeys is used to store all the label keys of a same metric family in observed order. since prometheus
// receiver removes any label with empty value before feeding it to an appender, in order to figure out all the labels
// from the same metric family we will need to keep track of what labels have ever been observed.
func (mf *metricFamilyPdata) updateLabelKeys(ls labels.Labels) {
for _, l := range ls {
if isUsefulLabelPdata(mf.mtype, l.Name) {
if _, ok := mf.labelKeys[l.Name]; !ok {
mf.labelKeys[l.Name] = true
// use insertion sort to maintain order
i := sort.SearchStrings(mf.labelKeysOrdered, l.Name)
mf.labelKeysOrdered = append(mf.labelKeysOrdered, "")
copy(mf.labelKeysOrdered[i+1:], mf.labelKeysOrdered[i:])
mf.labelKeysOrdered[i] = l.Name
}
}
}
}
// Purposefully being referenced to avoid lint warnings about being "unused".
var _ = (*metricFamilyPdata)(nil).updateLabelKeys
func (mf *metricFamilyPdata) isCumulativeTypePdata() bool {
return mf.mtype == pdata.MetricDataTypeDoubleSum ||
mf.mtype == pdata.MetricDataTypeIntSum ||
mf.mtype == pdata.MetricDataTypeHistogram ||
mf.mtype == pdata.MetricDataTypeSummary
}
func (mf *metricFamilyPdata) loadMetricGroupOrCreate(groupKey string, ls labels.Labels, ts int64) *metricGroupPdata {
mg, ok := mf.groups[groupKey]
if !ok {
mg = &metricGroupPdata{
family: mf,
metricGroup: metricGroup{
ls: ls,
ts: ts,
complexValue: make([]*dataPoint, 0),
},
}
mf.groups[groupKey] = mg
// maintaining data insertion order is helpful to generate stable/reproducible metric output
mf.groupOrders[groupKey] = len(mf.groupOrders)
}
return mg
}
func (mf *metricFamilyPdata) Add(metricName string, ls labels.Labels, t int64, v float64) error {
groupKey := mf.getGroupKey(ls)
mg := mf.loadMetricGroupOrCreate(groupKey, ls, t)
switch mf.mtype {
case pdata.MetricDataTypeHistogram, pdata.MetricDataTypeSummary:
switch {
case strings.HasSuffix(metricName, metricsSuffixSum):
// always use the timestamp from sum (count is ok too), because the startTs from quantiles won't be reliable
// in cases like remote server restart
mg.ts = t
mg.sum = v
mg.hasSum = true
case strings.HasSuffix(metricName, metricsSuffixCount):
mg.count = v
mg.hasCount = true
default:
boundary, err := getBoundaryPdata(mf.mtype, ls)
if err != nil {
mf.droppedTimeseries++
return err
}
mg.complexValue = append(mg.complexValue, &dataPoint{value: v, boundary: boundary})
}
default:
mg.value = v
}
return nil
}