opentelemetry_collector/receiver/vmagereceiver/vm_age_collector.go (140 lines of code) (raw):

package vmagereceiver import ( "context" "errors" "time" "go.opentelemetry.io/collector/consumer" "go.uber.org/zap" "github.com/googlecloudplatform/appengine-sidecars-docker/opentelemetry_collector/receiver/metricgenerator" metricspb "github.com/census-instrumentation/opencensus-proto/gen-go/metrics/v1" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/opencensus" ) // VMAgeCollector is a struct that generates metrics based on the // VM image age in the config. type VMAgeCollector struct { consumer consumer.Metrics collectorStartTime time.Time exportInterval time.Duration buildDate string done chan struct{} vmImageName string vmStartTime string vmReadyTime string logger *zap.Logger parsedBuildDate time.Time buildDateError bool parsedVMStartTime time.Time vmStartTimeError bool parsedVMReadyTime time.Time vmReadyTimeError bool labelValues []*metricspb.LabelValue } const ( defaultExportInterval = 10 * time.Minute ) // NewVMAgeCollector creates a new VMAgeCollector that generates metrics // based on the buildDate and vmImageName. func NewVMAgeCollector(exportInterval time.Duration, buildDate, vmImageName, vmStartTime, vmReadyTime string, consumer consumer.Metrics, logger *zap.Logger) *VMAgeCollector { if exportInterval <= 0 { exportInterval = defaultExportInterval } collector := &VMAgeCollector{ consumer: consumer, collectorStartTime: time.Now(), buildDate: buildDate, vmImageName: vmImageName, vmStartTime: vmStartTime, vmReadyTime: vmReadyTime, exportInterval: exportInterval, done: make(chan struct{}), logger: logger, } return collector } func (collector *VMAgeCollector) parseDate(date string) (time.Time, error) { return time.Parse(time.RFC3339Nano, date) } func calculateImageAge(buildDate time.Time, now time.Time) (float64, error) { imageAge := now.Sub(buildDate) imageAgeDays := imageAge.Hours() / 24 if imageAgeDays < 0 { return 0, errors.New("The vm build date is more recent than the current time") } return imageAgeDays, nil } // StartCollection starts a go routine with a ticker that periodically generates and exports the metrics. func (collector *VMAgeCollector) StartCollection() { collector.setupCollection() readyTime := float64(collector.parsedVMReadyTime.Sub(collector.parsedVMStartTime) / time.Second) go func() { ticker := time.NewTicker(collector.exportInterval) for { select { case <-ticker.C: collector.scrapeAndExportVMImageAge() collector.scrapeAndExportVMReadyTime(readyTime) case <-collector.done: return } } }() } func (collector *VMAgeCollector) setupCollection() { var err error collector.parsedBuildDate, err = collector.parseDate(collector.buildDate) collector.buildDateError = (err != nil) collector.parsedVMStartTime, err = collector.parseDate(collector.vmStartTime) if err != nil { collector.vmStartTimeError = true collector.logger.Error("Error parsing vmStartTime", zap.Error(err)) } collector.parsedVMReadyTime, err = collector.parseDate(collector.vmReadyTime) collector.vmReadyTimeError = (err != nil) if err != nil { collector.vmReadyTimeError = true collector.logger.Error("Error parsing vmReadyTime", zap.Error(err)) } collector.labelValues = []*metricspb.LabelValue{metricgenerator.MakeLabelValue(collector.vmImageName)} } // StopCollection stops the generation and export of the metrics. func (collector *VMAgeCollector) StopCollection() { close(collector.done) } func (collector *VMAgeCollector) makeErrorMetrics() *metricspb.Metric { timeseries := metricgenerator.MakeInt64TimeSeries(1, collector.collectorStartTime, time.Now(), collector.labelValues) return &metricspb.Metric{ MetricDescriptor: vmImageAgesErrorMetric, Timeseries: []*metricspb.TimeSeries{timeseries}, } } func makeMetrics(metricDescriptor *metricspb.MetricDescriptor, timeseries *metricspb.TimeSeries) []*metricspb.Metric { return []*metricspb.Metric{ { MetricDescriptor: metricDescriptor, Timeseries: []*metricspb.TimeSeries{timeseries}, }, } } func (collector *VMAgeCollector) export(metrics []*metricspb.Metric, errorKey string) { ctx := context.Background() err := collector.consumer.ConsumeMetrics(ctx, opencensus.OCToMetrics(nil, nil, metrics)) if err != nil { collector.logger.Error(errorKey, zap.Error(err)) } } func (collector *VMAgeCollector) scrapeAndExportVMImageAge() { var metrics []*metricspb.Metric if collector.buildDateError { metrics = []*metricspb.Metric{collector.makeErrorMetrics()} } else { imageAge, err := calculateImageAge(collector.parsedBuildDate, time.Now()) if err != nil { metrics = []*metricspb.Metric{collector.makeErrorMetrics()} } else { timeseries := metricgenerator.MakeDoubleTimeSeries(imageAge, collector.collectorStartTime, time.Now(), collector.labelValues) metrics = makeMetrics(vmImageAgeMetric, timeseries) } } collector.export(metrics, "Error sending VM image age metrics") } func (collector *VMAgeCollector) scrapeAndExportVMReadyTime(readyTime float64) { if collector.vmReadyTimeError { return } timeseries := metricgenerator.MakeDoubleTimeSeries(readyTime, collector.collectorStartTime, time.Now(), collector.labelValues) collector.export(makeMetrics(vmReadyTimeMetric, timeseries), "Error sending VM ready time metrics") }