exporter/trace/cloudtrace.go (111 lines of code) (raw):
// Copyright 2019 OpenTelemetry Authors
// Copyright 2021 Google LLC
//
// 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 trace
import (
"context"
"errors"
"fmt"
"time"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
traceapi "cloud.google.com/go/trace/apiv2"
"golang.org/x/oauth2/google"
"google.golang.org/api/option"
)
// Option is function type that is passed to the exporter initialization function.
type Option func(*options)
// options contains options for configuring the exporter.
type options struct {
// 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 handled with the
// OpenTelemetry global handler, which defaults to logging.
// Optional.
errorHandler otel.ErrorHandler
// 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
// mapAttribute maps otel attribute keys to cloud trace attribute keys
mapAttribute AttributeMapping
// 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
// traceClientOptions are additional options to be passed
// to the underlying Stackdriver Trace API client.
// Optional.
traceClientOptions []option.ClientOption
// timeout for all API calls. If not set, defaults to 12 seconds.
timeout time.Duration
// destinationProjectQuota sets whether the request should use quota from
// the destination project for the request.
destinationProjectQuota bool
}
// WithProjectID sets Google Cloud Platform project as projectID.
// Without using this option, it automatically detects the project ID
// from the default credential detection process.
// Please find the detailed order of the default credential detection process on the doc:
// https://godoc.org/golang.org/x/oauth2/google#FindDefaultCredentials
func WithProjectID(projectID string) func(o *options) {
return func(o *options) {
o.projectID = projectID
}
}
// WithDestinationProjectQuota enables per-request usage of the destination
// project's quota. For example, when setting the gcp.project.id resource attribute.
func WithDestinationProjectQuota() func(o *options) {
return func(o *options) {
o.destinationProjectQuota = true
}
}
// WithErrorHandler sets the hook to be called when there is an error
// occurred on uploading the span data to Stackdriver.
// If no custom hook is set, errors are logged.
func WithErrorHandler(handler otel.ErrorHandler) func(o *options) {
return func(o *options) {
o.errorHandler = handler
}
}
// WithTraceClientOptions sets additionial client options for tracing.
func WithTraceClientOptions(opts []option.ClientOption) func(o *options) {
return func(o *options) {
o.traceClientOptions = opts
}
}
// WithContext sets the context that trace exporter and metric exporter
// relies on.
func WithContext(ctx context.Context) func(o *options) {
return func(o *options) {
o.context = ctx
}
}
// WithTimeout sets the timeout for trace exporter and metric exporter
// If unset, it defaults to a 12 second timeout.
func WithTimeout(t time.Duration) func(o *options) {
return func(o *options) {
o.timeout = t
}
}
// AttributeMapping determines how to map from OpenTelemetry span attribute keys to
// cloud trace attribute keys.
type AttributeMapping func(attribute.Key) attribute.Key
// WithAttributeMapping configures how to map OpenTelemetry span attributes
// to google cloud trace span attributes. By default, it maps to attributes
// that are used prominently in the trace UI.
func WithAttributeMapping(mapping AttributeMapping) func(o *options) {
return func(o *options) {
o.mapAttribute = mapping
}
}
func (o *options) handleError(err error) {
if o.errorHandler != nil {
o.errorHandler.Handle(err)
return
}
otel.Handle(err)
}
// defaultTimeout is used as default when timeout is not set in newContextWithTimout.
const defaultTimeout = 12 * time.Second
// Exporter is a trace exporter that uploads data to Stackdriver.
//
// TODO(yoshifumi): add a metrics exporter once the spec definition
// process and the sampler implementation are done.
type Exporter struct {
traceExporter *traceExporter
}
// New creates a new Exporter thats implements trace.Exporter.
func New(opts ...Option) (*Exporter, error) {
o := options{
context: context.Background(),
mapAttribute: defaultAttributeMapping,
}
for _, opt := range opts {
opt(&o)
}
return newExporterWithOptions(&o)
}
func newExporterWithOptions(o *options) (*Exporter, error) {
if o.projectID == "" {
creds, err := google.FindDefaultCredentials(o.context, 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
}
te, err := newTraceExporter(o)
if err != nil {
return nil, err
}
return &Exporter{
traceExporter: te,
}, nil
}
func newContextWithTimeout(ctx context.Context, timeout time.Duration) (context.Context, func()) {
if timeout <= 0 {
timeout = defaultTimeout
}
return context.WithTimeout(ctx, timeout)
}
// ExportSpans exports a ReadOnlySpan to Stackdriver Trace.
func (e *Exporter) ExportSpans(ctx context.Context, spanData []sdktrace.ReadOnlySpan) error {
return e.traceExporter.ExportSpans(ctx, spanData)
}
// Shutdown waits for exported data to be uploaded.
//
// For our purposes it closed down the client.
func (e *Exporter) Shutdown(ctx context.Context) error {
return e.traceExporter.Shutdown(ctx)
}