132 lines
4.2 KiB
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
|
|
}
|