package metric import ( "fmt" "regexp" "sort" ) type Pattern interface { // Name returns the name of the metric this pattern is designated to. Name() string // Match returns whether a map of labels with its label values // match this pattern. Match(labels map[string]string) bool // IsValid returns whether the pattern is valid. IsValid() bool } type pattern struct { name string labels map[string]*regexp.Regexp valid bool } // NewPattern creates a new pattern with the given prefix and group name. There // has to be an even number of parameter, which is ("label", "labelvalue", "label", // "labelvalue" ...). The label value will be interpreted as regular expression. func NewPattern(name string, labels ...string) Pattern { p := &pattern{ name: name, labels: make(map[string]*regexp.Regexp), } if len(labels)%2 == 0 { for i := 0; i < len(labels); i += 2 { exp, err := regexp.Compile(labels[i+1]) if err != nil { fmt.Printf("error: %s\n", err) continue } p.labels[labels[i]] = exp } } if len(p.labels) == len(labels)/2 { p.valid = true } return p } func (p *pattern) Name() string { return p.name } func (p *pattern) Match(labels map[string]string) bool { if !p.valid { return false } if len(p.labels) == 0 { return true } for pname, pexp := range p.labels { value, ok := labels[pname] if !ok { return false } if !pexp.MatchString(value) { return false } } return true } func (p *pattern) IsValid() bool { return p.valid } type Metrics interface { Value(name string, labels ...string) Value Values(name string, labels ...string) []Value Labels(name string, label string) []string All() []Value Add(v Value) String() string } type metrics struct { values []Value } func NewMetrics() *metrics { return &metrics{} } func (m *metrics) String() string { s := "" for _, v := range m.values { s += v.String() } return s } func (m *metrics) Values(name string, labels ...string) []Value { if len(labels)%2 != 0 { return []Value{} } patterns := []Pattern{ NewPattern(name, labels...), } values := []Value{} for _, v := range m.values { if !v.Match(patterns) { continue } values = append(values, v) } return values } func (m *metrics) Value(name string, labels ...string) Value { vs := m.Values(name, labels...) if len(vs) == 0 { return nullValue } return vs[0] } func (m *metrics) All() []Value { return m.values } func (m *metrics) Labels(name string, label string) []string { vs := m.Values(name) values := make(map[string]struct{}) for _, v := range vs { l := v.L(label) if len(l) == 0 { break } values[l] = struct{}{} } labelvalues := []string{} for v := range values { labelvalues = append(labelvalues, v) } return labelvalues } func (m *metrics) Add(v Value) { if v == nil { return } m.values = append(m.values, v) } type Value interface { Name() string Val() float64 L(name string) string Labels() map[string]string Match(patterns []Pattern) bool Hash() string String() string } type value struct { name string labels map[string]string value float64 hash string } var nullValue Value = NewValue(NewDesc("", "", nil), 0) func NewValue(description *Description, v float64, elms ...string) Value { if len(description.labels) != len(elms) { return nil } val := &value{ name: description.name, value: v, labels: make(map[string]string), } labels := []string{} for i, label := range description.labels { val.labels[label] = elms[i] labels = append(labels, label) } val.hash = fmt.Sprintf("%s:", val.name) sort.Strings(labels) for _, k := range labels { val.hash += k + "=" + val.labels[k] + " " } return val } func (v *value) Hash() string { return v.hash } func (v *value) String() string { s := fmt.Sprintf("%s: %f {", v.name, v.value) for k, v := range v.labels { s += k + "=" + v + " " } s += "}" return s } func (v *value) Name() string { return v.name } func (v *value) Val() float64 { return v.value } func (v *value) L(name string) string { l, ok := v.labels[name] if !ok { return "" } return l } func (v *value) Labels() map[string]string { if len(v.labels) == 0 { return nil } l := make(map[string]string, len(v.labels)) for k, v := range v.labels { l[k] = v } return l } func (v *value) Match(patterns []Pattern) bool { if len(patterns) == 0 { return true } for _, p := range patterns { if v.name != p.Name() { continue } if !p.Match(v.labels) { continue } return true } return false } type Description struct { name string description string labels []string } func NewDesc(name, description string, labels []string) *Description { return &Description{ name: name, description: description, labels: labels, } } func (d *Description) Name() string { return d.name } func (d *Description) Description() string { return d.description } type Collector interface { Prefix() string Describe() []*Description Collect() Metrics Stop() } type Reader interface { Collect([]Pattern) Metrics }