Skip to content

Commit

Permalink
opentelemetry collector trace exporter (#497)
Browse files Browse the repository at this point in the history
* opentelemetry collector exporter

- missing load test
- missing resources

* fix review comments.

* add test for each SpanKind and Attribute Type.

* rename otelcol to otlp

* move exporter/trace/otlp to exporters/otlp

* more review comments.

* add alignment test.

* pass context to uploadSpans
  • Loading branch information
rghetia authored Mar 7, 2020
1 parent 9459629 commit 5850278
Show file tree
Hide file tree
Showing 13 changed files with 1,688 additions and 0 deletions.
18 changes: 18 additions & 0 deletions exporters/otlp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# OpenTelemetry Collector Go Exporter

[![GoDoc][godoc-image]][godoc-url]


This exporter converts OpenTelemetry [SpanData](https://github.com/open-telemetry/opentelemetry-go/blob/6769330394f78192df01cb59299e9e0f2e5e977b/sdk/export/trace/trace.go#L49)
to OpenTelemetry Protocol [Span](https://github.com/open-telemetry/opentelemetry-proto/blob/c20698d5bb483cf05de1a7c0e134b7c57e359674/opentelemetry/proto/trace/v1/trace.proto#L46)
and exports them to OpenTelemetry Collector.


## Installation

```bash
$ go get -u go.opentelemetry.io/otel/exporters/otlp
```

[godoc-url]: https://godoc.org/go.opentelemetry.io/otel/exporters/otlp

38 changes: 38 additions & 0 deletions exporters/otlp/alignment_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2020, 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 otlp

import (
"os"
"testing"
"unsafe"

ottest "go.opentelemetry.io/otel/internal/testing"
)

// Ensure struct alignment prior to running tests.
func TestMain(m *testing.M) {
fields := []ottest.FieldOffset{
{
Name: "Exporter.lastConnectErrPtr",
Offset: unsafe.Offsetof(Exporter{}.lastConnectErrPtr),
},
}
if !ottest.Aligned8Byte(fields, os.Stderr) {
os.Exit(1)
}

os.Exit(m.Run())
}
113 changes: 113 additions & 0 deletions exporters/otlp/connection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright 2020, 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 otlp

import (
"math/rand"
"sync/atomic"
"time"
"unsafe"
)

func (e *Exporter) lastConnectError() error {
errPtr := (*error)(atomic.LoadPointer(&e.lastConnectErrPtr))
if errPtr == nil {
return nil
}
return *errPtr
}

func (e *Exporter) saveLastConnectError(err error) {
var errPtr *error
if err != nil {
errPtr = &err
}
atomic.StorePointer(&e.lastConnectErrPtr, unsafe.Pointer(errPtr))
}

func (e *Exporter) setStateDisconnected(err error) {
e.saveLastConnectError(err)
select {
case e.disconnectedCh <- true:
default:
}
}

func (e *Exporter) setStateConnected() {
e.saveLastConnectError(nil)
}

func (e *Exporter) connected() bool {
return e.lastConnectError() == nil
}

const defaultConnReattemptPeriod = 10 * time.Second

func (e *Exporter) indefiniteBackgroundConnection() {
defer func() {
e.backgroundConnectionDoneCh <- true
}()

connReattemptPeriod := e.c.reconnectionPeriod
if connReattemptPeriod <= 0 {
connReattemptPeriod = defaultConnReattemptPeriod
}

// No strong seeding required, nano time can
// already help with pseudo uniqueness.
rng := rand.New(rand.NewSource(time.Now().UnixNano() + rand.Int63n(1024)))

// maxJitterNanos: 70% of the connectionReattemptPeriod
maxJitterNanos := int64(0.7 * float64(connReattemptPeriod))

for {
// Otherwise these will be the normal scenarios to enable
// reconnection if we trip out.
// 1. If we've stopped, return entirely
// 2. Otherwise block until we are disconnected, and
// then retry connecting
select {
case <-e.stopCh:
return

case <-e.disconnectedCh:
// Normal scenario that we'll wait for
}

if err := e.connect(); err == nil {
e.setStateConnected()
} else {
e.setStateDisconnected(err)
}

// Apply some jitter to avoid lockstep retrials of other
// collector-exporters. Lockstep retrials could result in an
// innocent DDOS, by clogging the machine's resources and network.
jitter := time.Duration(rng.Int63n(maxJitterNanos))
select {
case <-e.stopCh:
return
case <-time.After(connReattemptPeriod + jitter):
}
}
}

func (e *Exporter) connect() error {
cc, err := e.dialToCollector()
if err != nil {
return err
}
return e.enableConnections(cc)
}
16 changes: 16 additions & 0 deletions exporters/otlp/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2020, 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 otlp contains an OpenTelemetry tracing exporter for OpenTelemetry Collector.
package otlp // import "go.opentelemetry.io/otel/exporters/otlp"
103 changes: 103 additions & 0 deletions exporters/otlp/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright 2020, 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 otlp_test

import (
"context"
"fmt"
"log"
"time"

"google.golang.org/grpc/credentials"

"go.opentelemetry.io/otel/api/global"
"go.opentelemetry.io/otel/exporters/otlp"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)

func Example_insecure() {
exp, err := otlp.NewExporter(otlp.WithInsecure())
if err != nil {
log.Fatalf("Failed to create the collector exporter: %v", err)
}
defer func() {
_ = exp.Stop()
}()

tp, _ := sdktrace.NewProvider(
sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}),
sdktrace.WithBatcher(exp, // add following two options to ensure flush
sdktrace.WithScheduleDelayMillis(5),
sdktrace.WithMaxExportBatchSize(10),
))
if err != nil {
log.Fatalf("error creating trace provider: %v\n", err)
}

global.SetTraceProvider(tp)

tracer := global.TraceProvider().Tracer("test-tracer")

// Then use the OpenTelemetry tracing library, like we normally would.
ctx, span := tracer.Start(context.Background(), "CollectorExporter-Example")
defer span.End()

for i := 0; i < 10; i++ {
_, iSpan := tracer.Start(ctx, fmt.Sprintf("Sample-%d", i))
<-time.After(6 * time.Millisecond)
iSpan.End()
}
}

func Example_withTLS() {
// Please take at look at https://godoc.org/google.golang.org/grpc/credentials#TransportCredentials
// for ways on how to initialize gRPC TransportCredentials.
creds, err := credentials.NewClientTLSFromFile("my-cert.pem", "")
if err != nil {
log.Fatalf("failed to create gRPC client TLS credentials: %v", err)
}

exp, err := otlp.NewExporter(otlp.WithTLSCredentials(creds))
if err != nil {
log.Fatalf("failed to create the collector exporter: %v", err)
}
defer func() {
_ = exp.Stop()
}()

tp, err := sdktrace.NewProvider(
sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}),
sdktrace.WithBatcher(exp, // add following two options to ensure flush
sdktrace.WithScheduleDelayMillis(5),
sdktrace.WithMaxExportBatchSize(10),
))
if err != nil {
log.Fatalf("error creating trace provider: %v\n", err)
}

global.SetTraceProvider(tp)

tracer := global.TraceProvider().Tracer("test-tracer")

// Then use the OpenTelemetry tracing library, like we normally would.
ctx, span := tracer.Start(context.Background(), "Securely-Talking-To-Collector-Span")
defer span.End()

for i := 0; i < 10; i++ {
_, iSpan := tracer.Start(ctx, fmt.Sprintf("Sample-%d", i))
<-time.After(6 * time.Millisecond)
iSpan.End()
}
}
17 changes: 17 additions & 0 deletions exporters/otlp/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module go.opentelemetry.io/otel/exporters/otlp

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

require (
github.com/golang/protobuf v1.3.2
github.com/google/go-cmp v0.4.0
github.com/open-telemetry/opentelemetry-proto v0.0.0-20200219184922-5e1d5bc66d5a
github.com/stretchr/testify v1.4.0
go.opentelemetry.io/otel v0.2.3
golang.org/x/net v0.0.0-20190628185345-da137c7871d7 // indirect
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 // indirect
golang.org/x/text v0.3.2 // indirect
google.golang.org/grpc v1.27.1
)

go 1.13
Loading

0 comments on commit 5850278

Please sign in to comment.