mirror of https://github.com/knative/caching.git
502 lines
20 KiB
Go
502 lines
20 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 stackdriver contains the OpenCensus exporters for
|
|
// Stackdriver Monitoring and Stackdriver Tracing.
|
|
//
|
|
// This exporter can be used to send metrics to Stackdriver Monitoring and traces
|
|
// to Stackdriver trace.
|
|
//
|
|
// The package uses Application Default Credentials to authenticate by default.
|
|
// See: https://developers.google.com/identity/protocols/application-default-credentials
|
|
//
|
|
// Alternatively, pass the authentication options in both the MonitoringClientOptions
|
|
// and the TraceClientOptions fields of Options.
|
|
//
|
|
// Stackdriver Monitoring
|
|
//
|
|
// This exporter support exporting OpenCensus views to Stackdriver Monitoring.
|
|
// Each registered view becomes a metric in Stackdriver Monitoring, with the
|
|
// tags becoming labels.
|
|
//
|
|
// The aggregation function determines the metric kind: LastValue aggregations
|
|
// generate Gauge metrics and all other aggregations generate Cumulative metrics.
|
|
//
|
|
// In order to be able to push your stats to Stackdriver Monitoring, you must:
|
|
//
|
|
// 1. Create a Cloud project: https://support.google.com/cloud/answer/6251787?hl=en
|
|
// 2. Enable billing: https://support.google.com/cloud/answer/6288653#new-billing
|
|
// 3. Enable the Stackdriver Monitoring API: https://console.cloud.google.com/apis/dashboard
|
|
//
|
|
// These steps enable the API but don't require that your app is hosted on Google Cloud Platform.
|
|
//
|
|
// Stackdriver Trace
|
|
//
|
|
// This exporter supports exporting Trace Spans to Stackdriver Trace. It also
|
|
// supports the Google "Cloud Trace" propagation format header.
|
|
package stackdriver // import "contrib.go.opencensus.io/exporter/stackdriver"
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
"time"
|
|
|
|
metadataapi "cloud.google.com/go/compute/metadata"
|
|
traceapi "cloud.google.com/go/trace/apiv2"
|
|
"contrib.go.opencensus.io/exporter/stackdriver/monitoredresource"
|
|
opencensus "go.opencensus.io"
|
|
"go.opencensus.io/resource"
|
|
"go.opencensus.io/resource/resourcekeys"
|
|
"go.opencensus.io/stats/view"
|
|
"go.opencensus.io/trace"
|
|
"golang.org/x/oauth2/google"
|
|
"google.golang.org/api/option"
|
|
monitoredrespb "google.golang.org/genproto/googleapis/api/monitoredres"
|
|
|
|
commonpb "github.com/census-instrumentation/opencensus-proto/gen-go/agent/common/v1"
|
|
metricspb "github.com/census-instrumentation/opencensus-proto/gen-go/metrics/v1"
|
|
resourcepb "github.com/census-instrumentation/opencensus-proto/gen-go/resource/v1"
|
|
"go.opencensus.io/metric/metricdata"
|
|
)
|
|
|
|
// Options contains options for configuring the exporter.
|
|
type Options struct {
|
|
// ProjectID is the identifier of the Stackdriver
|
|
// project the user is uploading the stats data to.
|
|
// If not set, this will default to your "Application Default Credentials".
|
|
// For details see: https://developers.google.com/accounts/docs/application-default-credentials.
|
|
//
|
|
// It will be used in the project_id label of a Stackdriver monitored
|
|
// resource if the resource does not inherently belong to a specific
|
|
// project, e.g. on-premise resource like k8s_container or generic_task.
|
|
ProjectID string
|
|
|
|
// Location is the identifier of the GCP or AWS cloud region/zone in which
|
|
// the data for a resource is stored.
|
|
// If not set, it will default to the location provided by the metadata server.
|
|
//
|
|
// It will be used in the location label of a Stackdriver monitored resource
|
|
// if the resource does not inherently belong to a specific project, e.g.
|
|
// on-premise resource like k8s_container or generic_task.
|
|
Location string
|
|
|
|
// OnError is the hook to be called when there is
|
|
// an error uploading the stats or tracing data.
|
|
// If no custom hook is set, errors are logged.
|
|
// Optional.
|
|
OnError func(err error)
|
|
|
|
// MonitoringClientOptions are additional options to be passed
|
|
// to the underlying Stackdriver Monitoring API client.
|
|
// Optional.
|
|
MonitoringClientOptions []option.ClientOption
|
|
|
|
// TraceClientOptions are additional options to be passed
|
|
// to the underlying Stackdriver Trace API client.
|
|
// Optional.
|
|
TraceClientOptions []option.ClientOption
|
|
|
|
// BundleDelayThreshold determines the max amount of time
|
|
// the exporter can wait before uploading view data or trace spans to
|
|
// the backend.
|
|
// Optional.
|
|
BundleDelayThreshold time.Duration
|
|
|
|
// BundleCountThreshold determines how many view data events or trace spans
|
|
// can be buffered before batch uploading them to the backend.
|
|
// Optional.
|
|
BundleCountThreshold int
|
|
|
|
// TraceSpansBufferMaxBytes is the maximum size (in bytes) of spans that
|
|
// will be buffered in memory before being dropped.
|
|
//
|
|
// If unset, a default of 8MB will be used.
|
|
TraceSpansBufferMaxBytes int
|
|
|
|
// Resource sets the MonitoredResource against which all views will be
|
|
// recorded by this exporter.
|
|
//
|
|
// All Stackdriver metrics created by this exporter are custom metrics,
|
|
// so only a limited number of MonitoredResource types are supported, see:
|
|
// https://cloud.google.com/monitoring/custom-metrics/creating-metrics#which-resource
|
|
//
|
|
// An important consideration when setting the Resource here is that
|
|
// Stackdriver Monitoring only allows a single writer per
|
|
// TimeSeries, see: https://cloud.google.com/monitoring/api/v3/metrics-details#intro-time-series
|
|
// A TimeSeries is uniquely defined by the metric type name
|
|
// (constructed from the view name and the MetricPrefix), the Resource field,
|
|
// and the set of label key/value pairs (in OpenCensus terminology: tag).
|
|
//
|
|
// If no custom Resource is set, a default MonitoredResource
|
|
// with type global and no resource labels will be used. If you explicitly
|
|
// set this field, you may also want to set custom DefaultMonitoringLabels.
|
|
//
|
|
// Deprecated: Use MonitoredResource instead.
|
|
Resource *monitoredrespb.MonitoredResource
|
|
|
|
// MonitoredResource sets the MonitoredResource against which all views will be
|
|
// recorded by this exporter.
|
|
//
|
|
// All Stackdriver metrics created by this exporter are custom metrics,
|
|
// so only a limited number of MonitoredResource types are supported, see:
|
|
// https://cloud.google.com/monitoring/custom-metrics/creating-metrics#which-resource
|
|
//
|
|
// An important consideration when setting the MonitoredResource here is that
|
|
// Stackdriver Monitoring only allows a single writer per
|
|
// TimeSeries, see: https://cloud.google.com/monitoring/api/v3/metrics-details#intro-time-series
|
|
// A TimeSeries is uniquely defined by the metric type name
|
|
// (constructed from the view name and the MetricPrefix), the MonitoredResource field,
|
|
// and the set of label key/value pairs (in OpenCensus terminology: tag).
|
|
//
|
|
// If no custom MonitoredResource is set AND if Resource is also not set then
|
|
// a default MonitoredResource with type global and no resource labels will be used.
|
|
// If you explicitly set this field, you may also want to set custom DefaultMonitoringLabels.
|
|
//
|
|
// This field replaces Resource field. If this is set then it will override the
|
|
// Resource field.
|
|
// Optional, but encouraged.
|
|
MonitoredResource monitoredresource.Interface
|
|
|
|
// ResourceDetector provides a hook to discover arbitrary resource information.
|
|
//
|
|
// The translation function provided in MapResource must be able to conver the
|
|
// the resource information to a Stackdriver monitored resource.
|
|
//
|
|
// If this field is unset, resource type and tags will automatically be discovered through
|
|
// the OC_RESOURCE_TYPE and OC_RESOURCE_LABELS environment variables.
|
|
ResourceDetector resource.Detector
|
|
|
|
// MapResource converts a OpenCensus resource to a Stackdriver monitored resource.
|
|
//
|
|
// If this field is unset, DefaultMapResource will be used which encodes a set of default
|
|
// conversions from auto-detected resources to well-known Stackdriver monitored resources.
|
|
MapResource func(*resource.Resource) *monitoredrespb.MonitoredResource
|
|
|
|
// MetricPrefix overrides the prefix of a Stackdriver metric names.
|
|
// Optional. If unset defaults to "custom.googleapis.com/opencensus/".
|
|
// If GetMetricPrefix is non-nil, this option is ignored.
|
|
MetricPrefix string
|
|
|
|
// GetMetricDisplayName allows customizing the display name for the metric
|
|
// associated with the given view. By default it will be:
|
|
// MetricPrefix + view.Name
|
|
GetMetricDisplayName func(view *view.View) string
|
|
|
|
// GetMetricType allows customizing the metric type for the given view.
|
|
// By default, it will be:
|
|
// "custom.googleapis.com/opencensus/" + view.Name
|
|
//
|
|
// See: https://cloud.google.com/monitoring/api/ref_v3/rest/v3/projects.metricDescriptors#MetricDescriptor
|
|
// Depreacted. Use GetMetricPrefix instead.
|
|
GetMetricType func(view *view.View) string
|
|
|
|
// GetMetricPrefix allows customizing the metric prefix for the given metric name.
|
|
// If it is not set, MetricPrefix is used. If MetricPrefix is not set, it defaults to:
|
|
// "custom.googleapis.com/opencensus/"
|
|
//
|
|
// See: https://cloud.google.com/monitoring/api/ref_v3/rest/v3/projects.metricDescriptors#MetricDescriptor
|
|
GetMetricPrefix func(name string) string
|
|
|
|
// DefaultTraceAttributes will be appended to every span that is exported to
|
|
// Stackdriver Trace.
|
|
DefaultTraceAttributes map[string]interface{}
|
|
|
|
// DefaultMonitoringLabels are labels added to every metric created by this
|
|
// exporter in Stackdriver Monitoring.
|
|
//
|
|
// If unset, this defaults to a single label with key "opencensus_task" and
|
|
// value "go-<pid>@<hostname>". This default ensures that the set of labels
|
|
// together with the default Resource (global) are unique to this
|
|
// process, as required by Stackdriver Monitoring.
|
|
//
|
|
// If you set DefaultMonitoringLabels, make sure that the Resource field
|
|
// together with these labels is unique to the
|
|
// current process. This is to ensure that there is only a single writer to
|
|
// each TimeSeries in Stackdriver.
|
|
//
|
|
// Set this to &Labels{} (a pointer to an empty Labels) to avoid getting the
|
|
// default "opencensus_task" label. You should only do this if you know that
|
|
// the Resource you set uniquely identifies this Go process.
|
|
DefaultMonitoringLabels *Labels
|
|
|
|
// Context allows you to provide a custom context for API calls.
|
|
//
|
|
// This context will be used several times: first, to create Stackdriver
|
|
// trace and metric clients, and then every time a new batch of traces or
|
|
// stats needs to be uploaded.
|
|
//
|
|
// Do not set a timeout on this context. Instead, set the Timeout option.
|
|
//
|
|
// If unset, context.Background() will be used.
|
|
Context context.Context
|
|
|
|
// SkipCMD enforces to skip all the CreateMetricDescriptor calls.
|
|
// These calls are important in order to configure the unit of the metrics,
|
|
// but in some cases all the exported metrics are builtin (unit is configured)
|
|
// or the unit is not important.
|
|
SkipCMD bool
|
|
|
|
// Timeout for all API calls. If not set, defaults to 5 seconds.
|
|
Timeout time.Duration
|
|
|
|
// ReportingInterval sets the interval between reporting metrics.
|
|
// If it is set to zero then default value is used.
|
|
ReportingInterval time.Duration
|
|
|
|
// NumberOfWorkers sets the number of go rountines that send requests
|
|
// to Stackdriver Monitoring and Trace. The minimum number of workers is 1.
|
|
NumberOfWorkers int
|
|
|
|
// ResourceByDescriptor may be provided to supply monitored resource dynamically
|
|
// based on the metric Descriptor. Most users will not need to set this,
|
|
// but should instead set ResourceDetector.
|
|
//
|
|
// The MonitoredResource and ResourceDetector fields are ignored if this
|
|
// field is set to a non-nil value.
|
|
//
|
|
// The ResourceByDescriptor is called to derive monitored resources from
|
|
// metric.Descriptor and the label map associated with the time-series.
|
|
// If any label is used for the derived resource then it will be removed
|
|
// from the label map. The remaining labels in the map are returned to
|
|
// be used with the time-series.
|
|
//
|
|
// If the func set to this field does not return valid resource even for one
|
|
// time-series then it will result into an error for the entire CreateTimeSeries request
|
|
// which may contain more than one time-series.
|
|
ResourceByDescriptor func(*metricdata.Descriptor, map[string]string) (map[string]string, monitoredresource.Interface)
|
|
|
|
// Override the user agent value supplied to Monitoring APIs and included as an
|
|
// attribute in trace data.
|
|
UserAgent string
|
|
}
|
|
|
|
const defaultTimeout = 5 * time.Second
|
|
|
|
var defaultDomain = path.Join("custom.googleapis.com", "opencensus")
|
|
|
|
var defaultUserAgent = fmt.Sprintf("opencensus-go %s; stackdriver-exporter %s", opencensus.Version(), version)
|
|
|
|
// Exporter is a stats and trace exporter that uploads data to Stackdriver.
|
|
//
|
|
// You can create a single Exporter and register it as both a trace exporter
|
|
// (to export to Stackdriver Trace) and a stats exporter (to integrate with
|
|
// Stackdriver Monitoring).
|
|
type Exporter struct {
|
|
traceExporter *traceExporter
|
|
statsExporter *statsExporter
|
|
}
|
|
|
|
// NewExporter creates a new Exporter that implements both stats.Exporter and
|
|
// trace.Exporter.
|
|
func NewExporter(o Options) (*Exporter, error) {
|
|
if o.ProjectID == "" {
|
|
ctx := o.Context
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
creds, err := google.FindDefaultCredentials(ctx, traceapi.DefaultAuthScopes()...)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("stackdriver: %v", err)
|
|
}
|
|
if creds.ProjectID == "" {
|
|
return nil, errors.New("stackdriver: no project found with application default credentials")
|
|
}
|
|
o.ProjectID = creds.ProjectID
|
|
}
|
|
if o.Location == "" {
|
|
if metadataapi.OnGCE() {
|
|
zone, err := metadataapi.Zone()
|
|
if err != nil {
|
|
// This error should be logged with a warning level.
|
|
err = fmt.Errorf("setting Stackdriver default location failed: %s", err)
|
|
if o.OnError != nil {
|
|
o.OnError(err)
|
|
} else {
|
|
log.Print(err)
|
|
}
|
|
} else {
|
|
o.Location = zone
|
|
}
|
|
}
|
|
}
|
|
|
|
if o.MonitoredResource != nil {
|
|
o.Resource = convertMonitoredResourceToPB(o.MonitoredResource)
|
|
}
|
|
if o.MapResource == nil {
|
|
o.MapResource = DefaultMapResource
|
|
}
|
|
if o.ResourceDetector != nil {
|
|
// For backwards-compatibility we still respect the deprecated resource field.
|
|
if o.Resource != nil {
|
|
return nil, errors.New("stackdriver: ResourceDetector must not be used in combination with deprecated resource fields")
|
|
}
|
|
res, err := o.ResourceDetector(o.Context)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("stackdriver: detect resource: %s", err)
|
|
}
|
|
// Populate internal resource labels for defaulting project_id, location, and
|
|
// generic resource labels of applicable monitored resources.
|
|
if res.Labels == nil {
|
|
res.Labels = make(map[string]string)
|
|
}
|
|
res.Labels[stackdriverProjectID] = o.ProjectID
|
|
res.Labels[resourcekeys.CloudKeyZone] = o.Location
|
|
res.Labels[stackdriverGenericTaskNamespace] = "default"
|
|
res.Labels[stackdriverGenericTaskJob] = path.Base(os.Args[0])
|
|
res.Labels[stackdriverGenericTaskID] = getTaskValue()
|
|
log.Printf("OpenCensus detected resource: %v", res)
|
|
|
|
o.Resource = o.MapResource(res)
|
|
log.Printf("OpenCensus using monitored resource: %v", o.Resource)
|
|
}
|
|
if o.MetricPrefix != "" && !strings.HasSuffix(o.MetricPrefix, "/") {
|
|
o.MetricPrefix = o.MetricPrefix + "/"
|
|
}
|
|
if o.UserAgent == "" {
|
|
o.UserAgent = defaultUserAgent
|
|
}
|
|
|
|
se, err := newStatsExporter(o)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
te, err := newTraceExporter(o)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &Exporter{
|
|
statsExporter: se,
|
|
traceExporter: te,
|
|
}, nil
|
|
}
|
|
|
|
// ExportView exports to the Stackdriver Monitoring if view data
|
|
// has one or more rows.
|
|
// Deprecated: use ExportMetrics and StartMetricsExporter instead.
|
|
func (e *Exporter) ExportView(vd *view.Data) {
|
|
e.statsExporter.ExportView(vd)
|
|
}
|
|
|
|
// ExportMetricsProto exports OpenCensus Metrics Proto to Stackdriver Monitoring synchronously,
|
|
// without de-duping or adding proto metrics to the bundler.
|
|
func (e *Exporter) ExportMetricsProto(ctx context.Context, node *commonpb.Node, rsc *resourcepb.Resource, metrics []*metricspb.Metric) error {
|
|
_, err := e.statsExporter.PushMetricsProto(ctx, node, rsc, metrics)
|
|
return err
|
|
}
|
|
|
|
// PushMetricsProto simliar with ExportMetricsProto but returns the number of dropped timeseries.
|
|
func (e *Exporter) PushMetricsProto(ctx context.Context, node *commonpb.Node, rsc *resourcepb.Resource, metrics []*metricspb.Metric) (int, error) {
|
|
return e.statsExporter.PushMetricsProto(ctx, node, rsc, metrics)
|
|
}
|
|
|
|
// ExportMetrics exports OpenCensus Metrics to Stackdriver Monitoring
|
|
func (e *Exporter) ExportMetrics(ctx context.Context, metrics []*metricdata.Metric) error {
|
|
return e.statsExporter.ExportMetrics(ctx, metrics)
|
|
}
|
|
|
|
// StartMetricsExporter starts exporter by creating an interval reader that reads metrics
|
|
// from all registered producers at set interval and exports them.
|
|
// Use StopMetricsExporter to stop exporting metrics.
|
|
// Previously, it required registering exporter to export stats collected by opencensus.
|
|
// exporter := stackdriver.NewExporter(stackdriver.Option{})
|
|
// view.RegisterExporter(exporter)
|
|
// Now, it requires to call StartMetricsExporter() to export stats and metrics collected by opencensus.
|
|
// exporter := stackdriver.NewExporter(stackdriver.Option{})
|
|
// exporter.StartMetricsExporter()
|
|
// defer exporter.StopMetricsExporter()
|
|
//
|
|
// Both approach should not be used simultaenously. Otherwise it may result into unknown behavior.
|
|
// Previous approach continues to work as before but will not report newly define metrics such
|
|
// as gauges.
|
|
func (e *Exporter) StartMetricsExporter() error {
|
|
return e.statsExporter.startMetricsReader()
|
|
}
|
|
|
|
// StopMetricsExporter stops exporter from exporting metrics.
|
|
func (e *Exporter) StopMetricsExporter() {
|
|
e.statsExporter.stopMetricsReader()
|
|
}
|
|
|
|
// ExportSpan exports a SpanData to Stackdriver Trace.
|
|
func (e *Exporter) ExportSpan(sd *trace.SpanData) {
|
|
if len(e.traceExporter.o.DefaultTraceAttributes) > 0 {
|
|
sd = e.sdWithDefaultTraceAttributes(sd)
|
|
}
|
|
e.traceExporter.ExportSpan(sd)
|
|
}
|
|
|
|
// PushTraceSpans exports a bundle of OpenCensus Spans.
|
|
// Returns number of dropped spans.
|
|
func (e *Exporter) PushTraceSpans(ctx context.Context, node *commonpb.Node, rsc *resourcepb.Resource, spans []*trace.SpanData) (int, error) {
|
|
return e.traceExporter.pushTraceSpans(ctx, node, rsc, spans)
|
|
}
|
|
|
|
func (e *Exporter) sdWithDefaultTraceAttributes(sd *trace.SpanData) *trace.SpanData {
|
|
newSD := *sd
|
|
newSD.Attributes = make(map[string]interface{})
|
|
for k, v := range e.traceExporter.o.DefaultTraceAttributes {
|
|
newSD.Attributes[k] = v
|
|
}
|
|
for k, v := range sd.Attributes {
|
|
newSD.Attributes[k] = v
|
|
}
|
|
return &newSD
|
|
}
|
|
|
|
// Flush waits for exported data to be uploaded.
|
|
//
|
|
// This is useful if your program is ending and you do not
|
|
// want to lose recent stats or spans.
|
|
func (e *Exporter) Flush() {
|
|
e.statsExporter.Flush()
|
|
e.traceExporter.Flush()
|
|
}
|
|
|
|
func (o Options) handleError(err error) {
|
|
if o.OnError != nil {
|
|
o.OnError(err)
|
|
return
|
|
}
|
|
log.Printf("Failed to export to Stackdriver: %v", err)
|
|
}
|
|
|
|
func newContextWithTimeout(ctx context.Context, timeout time.Duration) (context.Context, func()) {
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
if timeout <= 0 {
|
|
timeout = defaultTimeout
|
|
}
|
|
return context.WithTimeout(ctx, timeout)
|
|
}
|
|
|
|
// convertMonitoredResourceToPB converts MonitoredResource data in to
|
|
// protocol buffer.
|
|
func convertMonitoredResourceToPB(mr monitoredresource.Interface) *monitoredrespb.MonitoredResource {
|
|
mrpb := new(monitoredrespb.MonitoredResource)
|
|
var labels map[string]string
|
|
mrpb.Type, labels = mr.MonitoredResource()
|
|
mrpb.Labels = make(map[string]string)
|
|
for k, v := range labels {
|
|
mrpb.Labels[k] = v
|
|
}
|
|
return mrpb
|
|
}
|