Skip to content

Commit

Permalink
Change Honecomb to new interfaces (#802)
Browse files Browse the repository at this point in the history
Fix bug related to Resource present in the Span. OpenCensus protocol defines that when Resource is present in the Span the top level Resource is overwritten not merged.

Signed-off-by: Bogdan Drutu <[email protected]>
  • Loading branch information
bogdandrutu committed Aug 25, 2020
1 parent 01b752a commit 92cde40
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 173 deletions.
2 changes: 1 addition & 1 deletion cmd/otelcontribcol/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func components() (component.Factories, error) {
kinesisexporter.NewFactory(),
awsxrayexporter.NewFactory(),
carbonexporter.NewFactory(),
&honeycombexporter.Factory{},
honeycombexporter.NewFactory(),
jaegerthrifthttpexporter.NewFactory(),
lightstepexporter.NewFactory(),
newrelicexporter.NewFactory(),
Expand Down
2 changes: 1 addition & 1 deletion exporter/honeycombexporter/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func TestLoadConfig(t *testing.T) {
factories, err := componenttest.ExampleComponents()
assert.Nil(t, err)

factory := &Factory{}
factory := NewFactory()
factories.Exporters[configmodels.Type(typeStr)] = factory
cfg, err := configtest.LoadConfigFile(
t, path.Join(".", "testdata", "config.yaml"), factories,
Expand Down
35 changes: 16 additions & 19 deletions exporter/honeycombexporter/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,27 @@
package honeycombexporter

import (
"context"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/configerror"
"go.opentelemetry.io/collector/config/configmodels"
"go.uber.org/zap"
"go.opentelemetry.io/collector/exporter/exporterhelper"
)

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

// Factory is the factory for the Honeycomb exporter.
type Factory struct{}

// Type gets the type of the Exporter config created by this factory.
func (f *Factory) Type() configmodels.Type {
return configmodels.Type(typeStr)
// NewFactory creates a factory for Honeycomb exporter.
func NewFactory() component.ExporterFactory {
return exporterhelper.NewFactory(
typeStr,
createDefaultConfig,
exporterhelper.WithTraces(createTraceExporter))
}

// CreateDefaultConfig creates the default configuration for the exporter.
func (f *Factory) CreateDefaultConfig() configmodels.Exporter {
func createDefaultConfig() configmodels.Exporter {
return &Config{
ExporterSettings: configmodels.ExporterSettings{
TypeVal: configmodels.Type(typeStr),
Expand All @@ -49,14 +49,11 @@ func (f *Factory) CreateDefaultConfig() configmodels.Exporter {
}
}

// CreateTraceExporter creates a trace exporter based on this config.
func (f *Factory) CreateTraceExporter(logger *zap.Logger, cfg configmodels.Exporter) (component.TraceExporterOld, error) {
func createTraceExporter(
_ context.Context,
params component.ExporterCreateParams,
cfg configmodels.Exporter,
) (component.TraceExporter, error) {
eCfg := cfg.(*Config)
return newHoneycombTraceExporter(eCfg, logger)
}

// CreateMetricsExporter always returns nil.
func (f *Factory) CreateMetricsExporter(logger *zap.Logger,
cfg configmodels.Exporter) (component.MetricsExporterOld, error) {
return nil, configerror.ErrDataTypeIsNotSupported
return newHoneycombTraceExporter(eCfg, params.Logger)
}
23 changes: 11 additions & 12 deletions exporter/honeycombexporter/factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
package honeycombexporter

import (
"context"
"path"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/component/componenttest"
"go.opentelemetry.io/collector/config/configcheck"
"go.opentelemetry.io/collector/config/configmodels"
Expand All @@ -28,42 +30,39 @@ import (
)

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

func TestCreateTraceExporter(t *testing.T) {
logger := zap.NewNop()

factories, err := componenttest.ExampleComponents()
require.NoError(t, err)
factory := Factory{}
factories.Exporters[configmodels.Type(typeStr)] = &factory
factory := NewFactory()
factories.Exporters[configmodels.Type(typeStr)] = factory
cfg, err := configtest.LoadConfigFile(
t, path.Join(".", "testdata", "config.yaml"), factories,
)
require.NoError(t, err)

exporter, err := factory.CreateTraceExporter(logger, cfg.Exporters["honeycomb/customname"])
params := component.ExporterCreateParams{Logger: zap.NewNop()}
exporter, err := factory.CreateTraceExporter(context.Background(), params, cfg.Exporters["honeycomb/customname"])
assert.Nil(t, err)
assert.NotNil(t, exporter)
}

func TestCreateMetricsExporter(t *testing.T) {
logger := zap.NewNop()

factories, err := componenttest.ExampleComponents()
require.NoError(t, err)
factory := Factory{}
factories.Exporters[configmodels.Type(typeStr)] = &factory
factory := NewFactory()
factories.Exporters[configmodels.Type(typeStr)] = factory
cfg, err := configtest.LoadConfigFile(
t, path.Join(".", "testdata", "config.yaml"), factories,
)
require.NoError(t, err)

exporter, err := factory.CreateMetricsExporter(logger, cfg.Exporters["honeycomb/customname"])
params := component.ExporterCreateParams{Logger: zap.NewNop()}
exporter, err := factory.CreateMetricsExporter(context.Background(), params, cfg.Exporters["honeycomb/customname"])
assert.NotNil(t, err)
assert.Nil(t, exporter)
}
127 changes: 59 additions & 68 deletions exporter/honeycombexporter/honeycomb.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ import (
"time"

tracepb "github.com/census-instrumentation/opencensus-proto/gen-go/trace/v1"
libhoney "github.com/honeycombio/libhoney-go"
"github.com/honeycombio/libhoney-go"
"github.com/honeycombio/libhoney-go/transmission"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/component/componenterror"
"go.opentelemetry.io/collector/consumer/consumerdata"
"go.opentelemetry.io/collector/consumer/pdata"
"go.opentelemetry.io/collector/exporter/exporterhelper"
"go.opentelemetry.io/collector/translator/internaldata"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -78,7 +80,7 @@ type spanRefType int64

// newHoneycombTraceExporter creates and returns a new honeycombExporter. It
// wraps the exporter in the component.TraceExporterOld helper method.
func newHoneycombTraceExporter(cfg *Config, logger *zap.Logger) (component.TraceExporterOld, error) {
func newHoneycombTraceExporter(cfg *Config, logger *zap.Logger) (component.TraceExporter, error) {
libhoneyConfig := libhoney.Config{
WriteKey: cfg.APIKey,
Dataset: cfg.Dataset,
Expand All @@ -104,15 +106,15 @@ func newHoneycombTraceExporter(cfg *Config, logger *zap.Logger) (component.Trace
},
}

return exporterhelper.NewTraceExporterOld(
return exporterhelper.NewTraceExporter(
cfg,
exporter.pushTraceData,
exporterhelper.WithShutdown(exporter.Shutdown))
}

// pushTraceData is the method called when trace data is available. It will be
// responsible for sending a batch of events.
func (e *honeycombExporter) pushTraceData(ctx context.Context, td consumerdata.TraceData) (int, error) {
func (e *honeycombExporter) pushTraceData(ctx context.Context, td pdata.Traces) (int, error) {
var errs []error
goodSpans := 0

Expand All @@ -122,74 +124,72 @@ func (e *honeycombExporter) pushTraceData(ctx context.Context, td consumerdata.T
go e.RunErrorLogger(ctx, libhoney.TxResponses())
defer cancel()

// Extract Node and Resource attributes, labels and other information.
// Because these exist on the TraceData, they will be added to every span.
traceLevelFields := getTraceLevelFields(td)
addTraceLevelFields := func(ev *libhoney.Event) {
for k, v := range traceLevelFields {
ev.AddField(k, v)
}
}
octds := internaldata.TraceDataToOC(td)
for _, octd := range octds {

for _, span := range td.Spans {
ev := e.builder.NewEvent()
addTraceLevelFields(ev)

// Treat resource labels as underlays, with any same-keyed span attributes taking
// precedence. Apply them first.
if span.Resource != nil && span.Resource.Labels != nil {
resourceType := span.Resource.GetType()
if len(resourceType) != 0 {
ev.AddField("resource_type", resourceType)
}
for k, v := range span.Resource.Labels {
// Extract Node and Resource attributes, labels and other information.
// Because these exist on the TraceData, they will be added to every span.
traceLevelFields := getTraceLevelFields(octd.Node, octd.Resource, octd.SourceFormat)
addTraceLevelFields := func(ev *libhoney.Event, tlf map[string]interface{}) {
for k, v := range tlf {
ev.AddField(k, v)
}
}

if len(span.GetParentSpanId()) == 0 || hasRemoteParent(span) {
if td.Node != nil {
for k, v := range td.Node.Attributes {
ev.AddField(k, v)
}
}
}
for _, span := range octd.Spans {
ev := e.builder.NewEvent()

if attrs := spanAttributesToMap(span.GetAttributes()); attrs != nil {
for k, v := range attrs {
ev.AddField(k, v)
tlf := traceLevelFields
// If Resource present need to recalculate traceLevelFields
if span.Resource != nil {
tlf = getTraceLevelFields(octd.Node, span.Resource, octd.SourceFormat)
}
}
addTraceLevelFields(ev, tlf)

ev.Timestamp = timestampToTime(span.GetStartTime())
startTime := timestampToTime(span.GetStartTime())
endTime := timestampToTime(span.GetEndTime())

ev.Add(event{
ID: getHoneycombSpanID(span.GetSpanId()),
TraceID: getHoneycombTraceID(span.GetTraceId()),
ParentID: getHoneycombSpanID(span.GetParentSpanId()),
Name: truncatableStringAsString(span.GetName()),
DurationMilli: float64(endTime.Sub(startTime)) / float64(time.Millisecond),
HasRemoteParent: hasRemoteParent(span),
})

e.sendMessageEvents(td, span, traceLevelFields)
e.sendSpanLinks(span)
if len(span.GetParentSpanId()) == 0 || hasRemoteParent(span) {
if octd.Node != nil {
for k, v := range octd.Node.Attributes {
ev.AddField(k, v)
}
}
}

ev.AddField("status.code", getStatusCode(span.Status))
ev.AddField("status.message", getStatusMessage(span.Status))
ev.AddField("has_remote_parent", !span.GetSameProcessAsParentSpan().GetValue())
ev.AddField("child_span_count", span.GetChildSpanCount())
if attrs := spanAttributesToMap(span.GetAttributes()); attrs != nil {
for k, v := range attrs {
ev.AddField(k, v)
}
}

if err := ev.SendPresampled(); err != nil {
errs = append(errs, err)
} else {
goodSpans++
ev.Timestamp = timestampToTime(span.GetStartTime())
startTime := timestampToTime(span.GetStartTime())
endTime := timestampToTime(span.GetEndTime())

ev.Add(event{
ID: getHoneycombSpanID(span.GetSpanId()),
TraceID: getHoneycombTraceID(span.GetTraceId()),
ParentID: getHoneycombSpanID(span.GetParentSpanId()),
Name: truncatableStringAsString(span.GetName()),
DurationMilli: float64(endTime.Sub(startTime)) / float64(time.Millisecond),
HasRemoteParent: hasRemoteParent(span),
})

e.sendMessageEvents(octd, span, tlf)
e.sendSpanLinks(span)

ev.AddField("status.code", getStatusCode(span.Status))
ev.AddField("status.message", getStatusMessage(span.Status))
ev.AddField("has_remote_parent", !span.GetSameProcessAsParentSpan().GetValue())
ev.AddField("child_span_count", span.GetChildSpanCount())

if err := ev.SendPresampled(); err != nil {
errs = append(errs, err)
} else {
goodSpans++
}
}
}

return len(td.Spans) - goodSpans, componenterror.CombineErrors(errs)
return td.SpanCount() - goodSpans, componenterror.CombineErrors(errs)
}

// sendSpanLinks gets the list of links associated with this span and sends them as
Expand Down Expand Up @@ -244,15 +244,6 @@ func (e *honeycombExporter) sendMessageEvents(td consumerdata.TraceData, span *t
for k, v := range traceFields {
ev.AddField(k, v)
}
if span.Resource != nil && span.Resource.Labels != nil {
resourceType := span.Resource.GetType()
if len(resourceType) != 0 {
ev.AddField("resource_type", resourceType)
}
for k, v := range span.Resource.Labels {
ev.AddField(k, v)
}
}
if len(span.GetParentSpanId()) == 0 || hasRemoteParent(span) {
if td.Node != nil {
for k, v := range td.Node.Attributes {
Expand Down
Loading

0 comments on commit 92cde40

Please sign in to comment.