Skip to content

Commit

Permalink
Create initial structure for Tanzu Observability exporter (#3393)
Browse files Browse the repository at this point in the history
* Create initial structure for Tanzu Observability exporter

Signed-off-by: Peter Stone <[email protected]>

* remove accidental printf

Signed-off-by: Peter Stone <[email protected]>

* PR feedback: use confighttp helper

* PR feedback: remove config for attribute transformation

* PR feedback: update README to match config changes

Signed-off-by: Glenn Oppegard <[email protected]>

Co-authored-by: Glenn Oppegard <[email protected]>
  • Loading branch information
thepeterstone and oppegard authored May 18, 2021
1 parent b869ecb commit c3fb1c1
Show file tree
Hide file tree
Showing 9 changed files with 1,842 additions and 0 deletions.
1 change: 1 addition & 0 deletions exporter/tanzuobservabilityexporter/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ../../Makefile.Common
47 changes: 47 additions & 0 deletions exporter/tanzuobservabilityexporter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Tanzu Observability (Wavefront) Exporter

This exporter supports sending traces to [Tanzu Observability](https://tanzu.vmware.com/observability).

## Prerequisites

- [Obtain the Tanzu Observability by Wavefront API token.](https://docs.wavefront.com/wavefront_api.html#generating-an-api-token)
- [Set up and start a Tanzu Observability by Wavefront proxy](https://docs.wavefront.com/proxies_installing.html) and configure it with the API token you obtained.
- To have the proxy generate [span RED metrics](https://docs.wavefront.com/trace_data_details.html#red-metrics) from trace data, [configure](https://docs.wavefront.com/proxies_configuring.html) the proxy's `customTracingListenerPorts` and use it for the exporter's endpoint.

## Data Conversion

- Trace IDs and Span IDs are converted to UUIDs. For example, span IDs are left-padded with zeros to fit the correct size.
- Events are converted to [Span Logs](https://docs.wavefront.com/trace_data_details.html#span-logs).
- Kind is converted to the `span.kind` tag.
- Status is converted to `error`, `status.code` and `status.message` tags.
- TraceState is converted to the `w3c.tracestate` tag.

## Tanzu Observability Specific Attributes

- Application identity tags, which are [required by Tanzu Observability](https://docs.wavefront.com/trace_data_details.html#how-wavefront-uses-application-tags), are added if they are missing.
- `application` is set to "defaultApp".
- `service` is set to "defaultService".

## Exporter Configuration

```yaml
receivers:
examplereceiver:

processors:
batch:
timeout: 10s

exporters:
tanzuobservability:
traces:
# Hostname and `customTracingListenerPorts` of the Wavefront Proxy
endpoint: "http://localhost:30001"

service:
pipelines:
traces:
receivers: [examplereceiver]
processors: [batch]
exporters: [tanzuobservability]
```
41 changes: 41 additions & 0 deletions exporter/tanzuobservabilityexporter/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// 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 tanzuobservabilityexporter

import (
"fmt"

"go.opentelemetry.io/collector/config"
"go.opentelemetry.io/collector/config/confighttp"
)

type TracesConfig struct {
confighttp.HTTPClientSettings `mapstructure:",squash"` // squash ensures fields are correctly decoded in embedded struct.
}

// Config defines configuration options for the exporter.
type Config struct {
config.ExporterSettings `mapstructure:",squash"` // squash ensures fields are correctly decoded in embedded struct.

// Traces defines the Traces exporter specific configuration
Traces TracesConfig `mapstructure:"traces"`
}

func (c *Config) Validate() error {
if c.Traces.Endpoint == "" {
return fmt.Errorf("A non-empty traces.endpoint is required")
}
return nil
}
34 changes: 34 additions & 0 deletions exporter/tanzuobservabilityexporter/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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 tanzuobservabilityexporter

import (
"testing"

"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/config"
"go.opentelemetry.io/collector/config/confighttp"
)

func TestConfigRequiresNonEmptyEndpoint(t *testing.T) {
c := &Config{
ExporterSettings: config.ExporterSettings{},
Traces: TracesConfig{
HTTPClientSettings: confighttp.HTTPClientSettings{Endpoint: ""},
},
}

require.Error(t, c.Validate())
}
43 changes: 43 additions & 0 deletions exporter/tanzuobservabilityexporter/factory.go
Original file line number Diff line number Diff line change
@@ -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 tanzuobservabilityexporter

import (
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config"
"go.opentelemetry.io/collector/config/confighttp"
"go.opentelemetry.io/collector/exporter/exporterhelper"
)

const exporterType = "tanzuobservability"

// NewFactory creates a factory for the exporter.
func NewFactory() component.ExporterFactory {
return exporterhelper.NewFactory(
exporterType,
createDefaultConfig,
//exporterhelper.WithTraces(createTraceExporter),
)
}

func createDefaultConfig() config.Exporter {
tracesCfg := TracesConfig{
HTTPClientSettings: confighttp.HTTPClientSettings{Endpoint: "http://localhost:30001"},
}
return &Config{
ExporterSettings: config.NewExporterSettings(config.NewID(exporterType)),
Traces: tracesCfg,
}
}
61 changes: 61 additions & 0 deletions exporter/tanzuobservabilityexporter/factory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// 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 tanzuobservabilityexporter

import (
"path"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component/componenttest"
"go.opentelemetry.io/collector/config"
"go.opentelemetry.io/collector/config/configcheck"
"go.opentelemetry.io/collector/config/confighttp"
"go.opentelemetry.io/collector/config/configtest"
)

func TestCreateDefaultConfig(t *testing.T) {
cfg := createDefaultConfig()
assert.NotNil(t, cfg, "failed to create default config")
require.NoError(t, configcheck.ValidateConfig(cfg))

actual, ok := cfg.(*Config)
require.True(t, ok, "invalid Config: %#v", cfg)
assert.Equal(t, "http://localhost:30001", actual.Traces.Endpoint)
}

func TestLoadConfig(t *testing.T) {
factories, err := componenttest.NopFactories()
assert.NoError(t, err)

factory := NewFactory()
// TODO come back to config.Type
factories.Exporters[config.Type(exporterType)] = factory
cfg, err := configtest.LoadConfigFile(t, path.Join(".", "testdata", "config.yaml"), factories)

require.NoError(t, err)
require.NotNil(t, cfg)

actual, ok := cfg.Exporters[config.NewID("tanzuobservability")]
require.True(t, ok)
expected := &Config{
ExporterSettings: config.NewExporterSettings(config.NewID("tanzuobservability")),
Traces: TracesConfig{
HTTPClientSettings: confighttp.HTTPClientSettings{Endpoint: "http://localhost:40001"},
},
}
assert.Equal(t, expected, actual)
}
9 changes: 9 additions & 0 deletions exporter/tanzuobservabilityexporter/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module github.com/open-telemetry/opentelemetry-collector-contrib/exporter/tanzuobservabilityexporter

go 1.15

require (
github.com/stretchr/testify v1.7.0
go.opentelemetry.io/collector v0.26.1-0.20210514011731-65a43fe39980
go.uber.org/zap v1.16.0
)
Loading

0 comments on commit c3fb1c1

Please sign in to comment.