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

OTLP trace gRPC exporter #1922

Merged
merged 11 commits into from May 27, 2021
20 changes: 20 additions & 0 deletions .github/dependabot.yml
Expand Up @@ -236,3 +236,23 @@ updates:
schedule:
day: sunday
interval: weekly
-
package-ecosystem: gomod
directory: /exporters/otlp/otlptrace
labels:
- dependencies
- go
- "Skip Changelog"
schedule:
day: sunday
interval: weekly
-
package-ecosystem: gomod
directory: /exporters/otlp/otlptrace/otlptracegrpc
labels:
- dependencies
- go
- "Skip Changelog"
schedule:
day: sunday
interval: weekly
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -30,6 +30,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
It can be used to decode a `TraceState` from a `tracestate` header string value. (#1937)
- The `Len` method is added to the `TraceState` type in the `go.opentelemetry.io/otel/trace` package.
This method returns the number of list-members the `TraceState` holds. (#1937)
- Creates package `go.opentelemetry.io/otel/exporters/otlp/otlptrace` that defines a trace exporter that uses a `otlptrace.Client` to send data.
Creates package `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc` implementing a gRPC `otlptrace.Client` and offers convenience functions, `NewExportPipeline` and `InstallNewPipeline`, to setup and install a `otlptrace.Exporter` in tracing .(#1922)

### Changed

Expand Down
1 change: 1 addition & 0 deletions Makefile
Expand Up @@ -168,6 +168,7 @@ dependabot-check:
); \
if [ -n "$$result" ]; then \
echo "missing go.mod dependabot check:"; echo "$$result"; \
echo "new modules need to be added to the .github/dependabot.yml file"; \
exit 1; \
fi

Expand Down
4 changes: 4 additions & 0 deletions bridge/opencensus/go.mod
Expand Up @@ -57,3 +57,7 @@ replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric
replace go.opentelemetry.io/otel/trace => ../../trace

replace go.opentelemetry.io/otel/example/passthrough => ../../example/passthrough

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc
4 changes: 4 additions & 0 deletions bridge/opentracing/go.mod
Expand Up @@ -53,3 +53,7 @@ replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric
replace go.opentelemetry.io/otel/trace => ../../trace

replace go.opentelemetry.io/otel/example/passthrough => ../../example/passthrough

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc
4 changes: 4 additions & 0 deletions example/jaeger/go.mod
Expand Up @@ -53,3 +53,7 @@ replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric
replace go.opentelemetry.io/otel/trace => ../../trace

replace go.opentelemetry.io/otel/example/passthrough => ../passthrough

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc
4 changes: 4 additions & 0 deletions example/namedtracer/go.mod
Expand Up @@ -54,3 +54,7 @@ replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric
replace go.opentelemetry.io/otel/trace => ../../trace

replace go.opentelemetry.io/otel/example/passthrough => ../passthrough

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc
4 changes: 4 additions & 0 deletions example/opencensus/go.mod
Expand Up @@ -55,3 +55,7 @@ replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric
replace go.opentelemetry.io/otel/trace => ../../trace

replace go.opentelemetry.io/otel/example/passthrough => ../passthrough

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc
4 changes: 4 additions & 0 deletions example/otel-collector/go.mod
Expand Up @@ -57,3 +57,7 @@ replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric
replace go.opentelemetry.io/otel/trace => ../../trace

replace go.opentelemetry.io/otel/example/passthrough => ../passthrough

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc
6 changes: 5 additions & 1 deletion example/passthrough/go.mod
@@ -1,6 +1,6 @@
module go.opentelemetry.io/otel/example/passthrough

go 1.16
go 1.15

require (
go.opentelemetry.io/otel v0.20.0
Expand Down Expand Up @@ -55,3 +55,7 @@ replace go.opentelemetry.io/otel/sdk/export/metric => ../../sdk/export/metric
replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric

replace go.opentelemetry.io/otel/sdk/trace => ../../sdk/trace

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc
4 changes: 4 additions & 0 deletions example/prom-collector/go.mod
Expand Up @@ -56,3 +56,7 @@ replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric
replace go.opentelemetry.io/otel/trace => ../../trace

replace go.opentelemetry.io/otel/example/passthrough => ../passthrough

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc
4 changes: 4 additions & 0 deletions example/prometheus/go.mod
Expand Up @@ -53,3 +53,7 @@ replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric
replace go.opentelemetry.io/otel/trace => ../../trace

replace go.opentelemetry.io/otel/example/passthrough => ../passthrough

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc
4 changes: 4 additions & 0 deletions example/zipkin/go.mod
Expand Up @@ -54,3 +54,7 @@ replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric
replace go.opentelemetry.io/otel/trace => ../../trace

replace go.opentelemetry.io/otel/example/passthrough => ../passthrough

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc
4 changes: 4 additions & 0 deletions exporters/metric/prometheus/go.mod
Expand Up @@ -58,3 +58,7 @@ replace go.opentelemetry.io/otel/sdk/metric => ../../../sdk/metric
replace go.opentelemetry.io/otel/trace => ../../../trace

replace go.opentelemetry.io/otel/example/passthrough => ../../../example/passthrough

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../otlp/otlptrace

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../otlp/otlptrace/otlptracegrpc
4 changes: 4 additions & 0 deletions exporters/otlp/go.mod
Expand Up @@ -64,4 +64,8 @@ replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric

replace go.opentelemetry.io/otel/trace => ../../trace

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ./otlptrace

replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ./otlptrace/otlptracegrpc

replace go.opentelemetry.io/otel/example/passthrough => ../../example/passthrough
43 changes: 43 additions & 0 deletions exporters/otlp/otlptrace/clients.go
@@ -0,0 +1,43 @@
// Copyright The 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 otlptrace // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace"

import (
"context"

tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
)

// Client manages connections to the collector, handles the
// transformation of data into wire format, and the transmission of that
// data to the collector.
type Client interface {
// Start should establish connection(s) to endpoint(s). It is
// called just once by the exporter, so the implementation
// does not need to worry about idempotence and locking.
Start(ctx context.Context) error
// Stop should close the connections. The function is called
// only once by the exporter, so the implementation does not
// need to worry about idempotence, but it may be called
// concurrently with UploadTraces, so proper
// locking is required. The function serves as a
// synchronization point - after the function returns, the
// process of closing connections is assumed to be finished.
Stop(ctx context.Context) error
// UploadTraces should transform the passed traces to the wire
// format and send it to the collector. May be called
// concurrently.
UploadTraces(ctx context.Context, protoSpans []*tracepb.ResourceSpans) error
}
131 changes: 131 additions & 0 deletions exporters/otlp/otlptrace/exporter.go
@@ -0,0 +1,131 @@
// Copyright The 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 otlptrace // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace"

import (
"context"
"errors"
"sync"

"go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform"

"go.opentelemetry.io/otel"
tracesdk "go.opentelemetry.io/otel/sdk/trace"
)

var (
errAlreadyStarted = errors.New("already started")
)

// Exporter exports trace data in the OTLP wire format.
type Exporter struct {
paivagustavo marked this conversation as resolved.
Show resolved Hide resolved
client Client

mu sync.RWMutex
started bool

startOnce sync.Once
stopOnce sync.Once
}

// ExportSpans exports a batch of spans.
func (e *Exporter) ExportSpans(ctx context.Context, ss []tracesdk.ReadOnlySpan) error {
protoSpans := tracetransform.Spans(ss)
paivagustavo marked this conversation as resolved.
Show resolved Hide resolved
if len(protoSpans) == 0 {
return nil
}

return e.client.UploadTraces(ctx, protoSpans)
}

// Start establishes a connection to the receiving endpoint.
func (e *Exporter) Start(ctx context.Context) error {
paivagustavo marked this conversation as resolved.
Show resolved Hide resolved
var err = errAlreadyStarted
e.startOnce.Do(func() {
e.mu.Lock()
e.started = true
e.mu.Unlock()
err = e.client.Start(ctx)
})

return err
}

// Shutdown flushes all exports and closes all connections to the receiving endpoint.
func (e *Exporter) Shutdown(ctx context.Context) error {
paivagustavo marked this conversation as resolved.
Show resolved Hide resolved
e.mu.RLock()
started := e.started
e.mu.RUnlock()

if !started {
return nil
}

var err error

e.stopOnce.Do(func() {
err = e.client.Stop(ctx)
e.mu.Lock()
e.started = false
e.mu.Unlock()
})

return err
}

var _ tracesdk.SpanExporter = (*Exporter)(nil)

// NewExporter constructs a new Exporter and starts it.
func NewExporter(ctx context.Context, client Client) (*Exporter, error) {
exp := NewUnstartedExporter(client)
if err := exp.Start(ctx); err != nil {
return nil, err
}
return exp, nil
}

// NewUnstartedExporter constructs a new Exporter and does not start it.
func NewUnstartedExporter(client Client) *Exporter {
return &Exporter{
client: client,
}
}

// NewExportPipeline sets up a complete export pipeline
// with the recommended TracerProvider setup.
func NewExportPipeline(ctx context.Context, client Client) (*Exporter, *tracesdk.TracerProvider, error) {
exp, err := NewExporter(ctx, client)
if err != nil {
return nil, nil, err
}

tracerProvider := tracesdk.NewTracerProvider(
tracesdk.WithBatcher(exp),
)

return exp, tracerProvider, nil
}

// InstallNewPipeline instantiates a NewExportPipeline with the
// recommended configuration and registers it globally.
func InstallNewPipeline(ctx context.Context, client Client) (*Exporter, *tracesdk.TracerProvider, error) {
paivagustavo marked this conversation as resolved.
Show resolved Hide resolved
exp, tp, err := NewExportPipeline(ctx, client)
if err != nil {
return nil, nil, err
}

otel.SetTracerProvider(tp)
return exp, tp, err
}
66 changes: 66 additions & 0 deletions exporters/otlp/otlptrace/exporter_test.go
@@ -0,0 +1,66 @@
// Copyright The 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 otlptrace_test

import (
"context"
"testing"

"go.opentelemetry.io/otel/exporters/otlp/otlptrace"

"github.com/stretchr/testify/assert"

"go.opentelemetry.io/otel"
tracesdk "go.opentelemetry.io/otel/sdk/trace"
tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
)

type noopClient struct {
}

var _ otlptrace.Client = (*noopClient)(nil)

func (m *noopClient) Start(_ context.Context) error {
return nil
}

func (m *noopClient) Stop(ctx context.Context) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
return nil
}

func (m *noopClient) UploadTraces(_ context.Context, _ []*tracepb.ResourceSpans) error {
return nil
}

func (m *noopClient) Reset() {
}

func TestInstallNewPipeline(t *testing.T) {
ctx := context.Background()
_, _, err := otlptrace.InstallNewPipeline(ctx, &noopClient{})
assert.NoError(t, err)
assert.IsType(t, &tracesdk.TracerProvider{}, otel.GetTracerProvider())
}

func TestNewExportPipeline(t *testing.T) {
_, tp, err := otlptrace.NewExportPipeline(context.Background(), &noopClient{})
assert.NoError(t, err)
assert.NotEqual(t, tp, otel.GetTracerProvider())
}