Skip to content

Commit

Permalink
OTLP trace gRPC exporter (#1922)
Browse files Browse the repository at this point in the history
* draft: otlp exporter trace grpc

* address comments and improve usability

* change passthrough example to go 1.15

* code cleanups

* copy dependencies to `otlptrace`

* fix typo on makefile

* address comments

* update code with latest changes

* fix comments

Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
  • Loading branch information
paivagustavo and MrAlias committed May 27, 2021
1 parent 5a8f7ff commit dfe2b6f
Show file tree
Hide file tree
Showing 58 changed files with 5,305 additions and 1 deletion.
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 @@ -31,6 +31,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 {
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)
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 {
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 {
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) {
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())
}

0 comments on commit dfe2b6f

Please sign in to comment.