Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

exporter/stackdriver: stackdriver support #139

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions exporter/stackdriverexporter/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2019, OpenTelemetry 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 (
"github.com/open-telemetry/opentelemetry-service/config/configmodels"
)

// ConfigV2 defines configuration for Prometheus exporter.
type ConfigV2 struct {
configmodels.ExporterSettings `mapstructure:",squash"` // squash ensures fields are correctly decoded in embedded struct.
ProjectID string `mapstructure:"project,omitempty"`
EnableTracing bool `mapstructure:"enable_tracing,omitempty"`
EnableMetrics bool `mapstructure:"enable_metrics,omitempty"`
MetricPrefix string `mapstructure:"metric_prefix,omitempty"`
}
107 changes: 107 additions & 0 deletions exporter/stackdriverexporter/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright 2019, OpenTelemetry 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 (
"fmt"
"time"

"go.uber.org/zap"

"contrib.go.opencensus.io/exporter/stackdriver"
"github.com/open-telemetry/opentelemetry-service/config/configerror"
"github.com/open-telemetry/opentelemetry-service/config/configmodels"
"github.com/open-telemetry/opentelemetry-service/consumer"
"github.com/open-telemetry/opentelemetry-service/exporter"
"github.com/open-telemetry/opentelemetry-service/exporter/exporterwrapper"
)

var _ = exporter.RegisterFactory(&factory{})

const (
// The value of "type" key in configuration.
typeStr = "stackdriver"
)

// factory is the factory for Prometheus exporter.
type factory struct {
}

var sde *stackdriver.Exporter

// Type gets the type of the Exporter config created by this factory.
func (f *factory) Type() string {
return typeStr
}

// CreateDefaultConfig creates the default configuration for exporter.
func (f *factory) CreateDefaultConfig() configmodels.Exporter {
return &ConfigV2{
ExporterSettings: configmodels.ExporterSettings{
TypeVal: typeStr,
NameVal: typeStr,
},
}
}

// CreateTraceExporter creates a trace exporter based on this config.
func (f *factory) CreateTraceExporter(logger *zap.Logger, cfg configmodels.Exporter) (consumer.TraceConsumer, exporter.StopFunc, error) {
scfg := cfg.(*ConfigV2)

if !scfg.EnableTracing && !scfg.EnableMetrics {
return nil, nil, nil
}

// TODO: For each ProjectID, create a different exporter
// or at least a unique Stackdriver client per ProjectID.

sde, err := stackdriver.NewExporter(stackdriver.Options{
// If the project ID is an empty string, it will be set by default based on
// the project this is running on in GCP.
ProjectID: scfg.ProjectID,

MetricPrefix: scfg.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 err != nil {
return nil, nil, fmt.Errorf("Cannot configure Stackdriver Trace exporter: %v", err)
}

sexp, err := exporterwrapper.NewExporterWrapper("stackdriver_trace", "ocservice.exporter.Stackdriver.ConsumeTraceData", sde)
if err != nil {
return nil, nil, err
}

return sexp, stopFunc, nil
}

// CreateMetricsExporter creates a metrics exporter based on this config.
func (f *factory) CreateMetricsExporter(logger *zap.Logger, cfg configmodels.Exporter) (consumer.MetricsConsumer, exporter.StopFunc, error) {
return nil, nil, configerror.ErrDataTypeIsNotSupported
}

func stopFunc() error {
sde.Flush()
return nil
}
134 changes: 134 additions & 0 deletions exporter/stackdriverexporter/stackdriver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright 2019, OpenTelemetry 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/open-telemetry/opentelemetry-service/consumer"
"github.com/open-telemetry/opentelemetry-service/consumer/consumerdata"
"github.com/open-telemetry/opentelemetry-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.

sde, serr := stackdriver.NewExporter(stackdriver.Options{
// If the project ID is an empty string, it will be set by default based on
// the project this is running on in GCP.
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,
}

sdte, err := exporterwrapper.NewExporterWrapper("stackdriver_trace", "ocservice.exporter.Stackdriver.ConsumeTraceData", sde)
if err != nil {
return nil, nil, nil, err
}

// 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, sdte)
}

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 consumerdata.MetricsData) error {
ctx, span := trace.StartSpan(ctx,
"opencensus.service.exporter.stackdriver.ExportMetricsData",
trace.WithSampler(trace.NeverSample()))
defer span.End()

var setErrorOnce sync.Once

err := sde.exporter.ExportMetricsProto(ctx, md.Node, md.Resource, md.Metrics)
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
}
17 changes: 17 additions & 0 deletions exporter/stackdriverexporter/stackdriverexporter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright 2019, OpenTelemetry 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

// TODO: Add tests.
4 changes: 1 addition & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
module github.com/open-telemetry/opentelemetry-service

require (
cloud.google.com/go v0.38.0 // indirect
contrib.go.opencensus.io/exporter/jaeger v0.1.1-0.20190430175949-e8b55949d948
contrib.go.opencensus.io/exporter/ocagent v0.5.0
contrib.go.opencensus.io/exporter/prometheus v0.1.0
contrib.go.opencensus.io/exporter/stackdriver v0.12.2
contrib.go.opencensus.io/exporter/zipkin v0.1.1
contrib.go.opencensus.io/resource v0.1.1
github.com/VividCortex/gohistogram v1.0.0 // indirect
github.com/apache/thrift v0.0.0-20161221203622-b2a4d4ae21c7
github.com/aws/aws-sdk-go v1.19.18 // indirect
github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b // indirect
github.com/census-instrumentation/opencensus-proto v0.2.0
github.com/go-kit/kit v0.8.0
Expand Down Expand Up @@ -45,7 +44,6 @@ require (
go.opencensus.io v0.22.0
go.uber.org/zap v1.10.0
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a // indirect
google.golang.org/api v0.5.0
google.golang.org/grpc v1.21.0
gopkg.in/yaml.v2 v2.2.2
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ contrib.go.opencensus.io/exporter/ocagent v0.5.0 h1:TKXjQSRS0/cCDrP7KvkgU6SmILtF
contrib.go.opencensus.io/exporter/ocagent v0.5.0/go.mod h1:ImxhfLRpxoYiSq891pBrLVhN+qmP8BTVvdH2YLs7Gl0=
contrib.go.opencensus.io/exporter/prometheus v0.1.0 h1:SByaIoWwNgMdPSgl5sMqM2KDE5H/ukPWBRo314xiDvg=
contrib.go.opencensus.io/exporter/prometheus v0.1.0/go.mod h1:cGFniUXGZlKRjzOyuZJ6mgB+PgBcCIa79kEKR8YCW+A=
contrib.go.opencensus.io/exporter/stackdriver v0.12.2 h1:jU1p9F07ASK11wYgSTPKtFlTvTtCDj6R1d3nRt0ZHDE=
contrib.go.opencensus.io/exporter/stackdriver v0.12.2/go.mod h1:iwB6wGarfphGGe/e5CWqyUk/cLzKnWsOKPVW3no6OTw=
contrib.go.opencensus.io/exporter/zipkin v0.1.1 h1:PR+1zWqY8ceXs1qDQQIlgXe+sdiwCf0n32bH4+Epk8g=
contrib.go.opencensus.io/exporter/zipkin v0.1.1/go.mod h1:GMvdSl3eJ2gapOaLKzTKE3qDgUkJ86k9k3yY2eqwkzc=
contrib.go.opencensus.io/resource v0.1.1 h1:4r2CANuYhKGmYWP02+5E94rLRcS/YeD+KlxSrOsMxk0=
Expand Down Expand Up @@ -142,6 +144,7 @@ github.com/google/gofuzz v0.0.0-20150304233714-bbcb9da2d746/go.mod h1:HP5RmnzzSN
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20180605153948-8b03ce837f34/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/googleapis/gax-go/v2 v2.0.4 h1:hU4mGcQI4DaAYW+IbTun+2qEZVFxK0ySjQLTbS0VQKc=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gnostic v0.0.0-20180520015035-48a0ecefe2e4 h1:yxHFSapGMUoyn+3v6LiJJxoJhvbDqIq8me0gAWehnSU=
github.com/googleapis/gnostic v0.0.0-20180520015035-48a0ecefe2e4/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
Expand Down Expand Up @@ -200,6 +203,7 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb v0.0.0-20170331210902-15e594fc09f1/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
github.com/inigohu/opentelemetry-service v0.0.0-20190707162328-ea50ba4202a0 h1:rwaXlqhT+1XUdo4FLVOldcm3ifWP3RQVfydx0THOGug=
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
github.com/jaegertracing/jaeger v1.9.0 h1:xtwGp/+H8kvT6q8LRrEOxMhZZGKO4Hsziy4pRmLIczM=
Expand Down
3 changes: 3 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/open-telemetry/opentelemetry-service/exporter/jaegerexporter"
"github.com/open-telemetry/opentelemetry-service/exporter/opencensusexporter"
"github.com/open-telemetry/opentelemetry-service/exporter/prometheusexporter"
"github.com/open-telemetry/opentelemetry-service/exporter/stackdriverexporter"
"github.com/open-telemetry/opentelemetry-service/exporter/zipkinexporter"
"github.com/open-telemetry/opentelemetry-service/receiver/opencensusreceiver"
"github.com/open-telemetry/opentelemetry-service/receiver/prometheusreceiver"
Expand Down Expand Up @@ -439,6 +440,7 @@ func eqLocalHost(host string) bool {
// + jaeger
// + opencensus
// + prometheus
// + stackdriver
func ExportersFromViperConfig(logger *zap.Logger, v *viper.Viper) ([]consumer.TraceConsumer, []consumer.MetricsConsumer, []func() error, error) {
parseFns := []struct {
name string
Expand All @@ -448,6 +450,7 @@ func ExportersFromViperConfig(logger *zap.Logger, v *viper.Viper) ([]consumer.Tr
{name: "jaeger", fn: jaegerexporter.JaegerExportersFromViper},
{name: "opencensus", fn: opencensusexporter.OpenCensusTraceExportersFromViper},
{name: "prometheus", fn: prometheusexporter.PrometheusExportersFromViper},
{name: "stackdriver", fn: stackdriverexporter.StackdriverTraceExportersFromViper},
}

var traceExporters []consumer.TraceConsumer
Expand Down