opentelemetry-collector/exporter/stackdriverexporter/stackdriver.go

132 lines
4.2 KiB
Go

// Copyright 2018, OpenCensus 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 stackdriverexporter
import (
"context"
"fmt"
"sync"
"time"
"contrib.go.opencensus.io/exporter/stackdriver"
"github.com/spf13/viper"
"go.opencensus.io/trace"
"github.com/census-instrumentation/opencensus-service/consumer"
"github.com/census-instrumentation/opencensus-service/data"
"github.com/census-instrumentation/opencensus-service/exporter/exporterwrapper"
)
type stackdriverConfig struct {
ProjectID string `mapstructure:"project,omitempty"`
EnableTracing bool `mapstructure:"enable_tracing,omitempty"`
EnableMetrics bool `mapstructure:"enable_metrics,omitempty"`
MetricPrefix string `mapstructure:"metric_prefix,omitempty"`
}
// TODO: Add metrics support to the exporterwrapper.
type stackdriverExporter struct {
exporter *stackdriver.Exporter
}
var _ consumer.MetricsConsumer = (*stackdriverExporter)(nil)
// StackdriverTraceExportersFromViper unmarshals the viper and returns an consumer.TraceConsumer targeting
// Stackdriver according to the configuration settings.
func StackdriverTraceExportersFromViper(v *viper.Viper) (tps []consumer.TraceConsumer, mps []consumer.MetricsConsumer, doneFns []func() error, err error) {
var cfg struct {
Stackdriver *stackdriverConfig `mapstructure:"stackdriver"`
}
if err := v.Unmarshal(&cfg); err != nil {
return nil, nil, nil, err
}
sc := cfg.Stackdriver
if sc == nil {
return nil, nil, nil, nil
}
if !sc.EnableTracing && !sc.EnableMetrics {
return nil, nil, nil, nil
}
// TODO: For each ProjectID, create a different exporter
// or at least a unique Stackdriver client per ProjectID.
if sc.ProjectID == "" {
return nil, nil, nil, fmt.Errorf("Stackdriver config requires a project ID")
}
sde, serr := stackdriver.NewExporter(stackdriver.Options{
ProjectID: sc.ProjectID,
MetricPrefix: sc.MetricPrefix,
// Stackdriver Metrics mandates a minimum of 60 seconds for
// reporting metrics. We have to enforce this as per the advisory
// at https://cloud.google.com/monitoring/custom-metrics/creating-metrics#writing-ts
// which says:
//
// "If you want to write more than one point to the same time series, then use a separate call
// to the timeSeries.create method for each point. Don't make the calls faster than one time per
// minute. If you are adding data points to different time series, then there is no rate limitation."
BundleDelayThreshold: 61 * time.Second,
})
if serr != nil {
return nil, nil, nil, fmt.Errorf("Cannot configure Stackdriver Trace exporter: %v", serr)
}
exp := &stackdriverExporter{
exporter: sde,
}
// TODO: Examine "contrib.go.opencensus.io/exporter/stackdriver" to see
// if trace.ExportSpan was constraining and if perhaps the Stackdriver
// upload can use the context and information from the Node.
if sc.EnableTracing {
tps = append(tps, exporterwrapper.NewExporterWrapper("stackdriver", sde))
}
if sc.EnableMetrics {
mps = append(mps, exp)
}
doneFns = append(doneFns, func() error {
sde.Flush()
return nil
})
return
}
func (sde *stackdriverExporter) ConsumeMetricsData(ctx context.Context, md data.MetricsData) error {
ctx, span := trace.StartSpan(ctx,
"opencensus.service.exporter.stackdriver.ExportMetricsData",
trace.WithSampler(trace.NeverSample()))
defer span.End()
var setErrorOnce sync.Once
for _, metric := range md.Metrics {
err := sde.exporter.ExportMetric(ctx, md.Node, md.Resource, metric)
if err != nil {
setErrorOnce.Do(func() {
span.SetStatus(trace.Status{Code: trace.StatusCodeInternal, Message: err.Error()})
})
span.Annotate([]trace.Attribute{
trace.StringAttribute("error", err.Error()),
}, "Error encountered")
}
}
return nil
}