metrics/report.go (81 lines of code) (raw):
// Copyright 2017 Google Inc.
//
// 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
//
// https://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 metrics
import (
"fmt"
"reflect"
"time"
"github.com/GoogleCloudPlatform/ubbagent/util"
"github.com/google/uuid"
)
// MetricValue holds a single named metric value. Only one of the individual type fields should
// be non-nil.
type MetricValue struct {
Int64Value *int64 `json:"int64Value,omitempty"`
DoubleValue *float64 `json:"doubleValue,omitempty"`
}
// Validate returns an error if the metric value does not match its definition.
func (mv MetricValue) Validate(def Definition) error {
switch def.Type {
case IntType:
if mv.DoubleValue != nil {
return fmt.Errorf("double value specified for integer metric: %v", *mv.DoubleValue)
}
break
case DoubleType:
if mv.Int64Value != nil {
return fmt.Errorf("integer value specified for double metric: %v", *mv.Int64Value)
}
break
}
return nil
}
// MetricReport represents an aggregated interval for a unique metric + labels combination.
type MetricReport struct {
Name string `json:"name"`
StartTime time.Time `json:"startTime"`
EndTime time.Time `json:"endTime"`
Labels map[string]string `json:"labels"`
Value MetricValue `json:"value"`
}
// Equal returns if the two MetricReports are the same.
func (mr MetricReport) Equal(other MetricReport) bool {
// Time object equality must be checked using `Time.Equal`,
// not `==` or `reflect.DeepEqual`.
// See https://github.com/golang/go/issues/17875
return mr.Name == other.Name &&
mr.StartTime.Equal(other.StartTime) &&
mr.EndTime.Equal(other.EndTime) &&
reflect.DeepEqual(mr.Labels, other.Labels) &&
reflect.DeepEqual(mr.Value, other.Value)
}
// Copy returns a deep copy of the MetricReport
func (mr MetricReport) Copy() MetricReport {
dup := mr
if mr.Value.Int64Value != nil {
dup.Value.Int64Value = util.NewInt64(*mr.Value.Int64Value)
}
if mr.Value.DoubleValue != nil {
dup.Value.DoubleValue = util.NewFloat64(*mr.Value.DoubleValue)
}
return dup
}
// Validate returns an error if the report does not match its definition.
func (mr MetricReport) Validate(def Definition) error {
if mr.Name != def.Name {
return fmt.Errorf("incorrect metric name: %v", mr.Name)
}
if mr.StartTime.After(mr.EndTime) {
return fmt.Errorf("metric %v: StartTime > EndTime: %v > %v", mr.Name, mr.StartTime, mr.EndTime)
}
if err := mr.Value.Validate(def); err != nil {
return fmt.Errorf("metric %v: %v", mr.Name, err)
}
return nil
}
// StampedMetricReport is a MetricReport stamped with a unique identifier.
type StampedMetricReport struct {
MetricReport `json:",inline"`
Id string
}
// NewStampedMetricReport creates a new StampedMetricReport with a random, unique identifier.
func NewStampedMetricReport(report MetricReport) StampedMetricReport {
var stamped StampedMetricReport
id, err := uuid.NewRandom()
if err != nil {
panic(fmt.Sprintf("cannot create uuid for report: %+v", err))
}
stamped.Id = id.String()
stamped.MetricReport = report
return stamped
}
// Equal returns if the two StampedMetricReports are the same.
func (smr StampedMetricReport) Equal(other StampedMetricReport) bool {
return smr.Id == other.Id &&
smr.MetricReport.Equal(other.MetricReport)
}