Skip to content

Commit

Permalink
add support for AddLink to the OpenCensus bridge
Browse files Browse the repository at this point in the history
  • Loading branch information
dashpole committed Mar 28, 2024
1 parent ba5d126 commit 9abd728
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
At which point, users will be required to migrage their code, and this package will be deprecated then removed. (#5085)
- Add support for `Summary` metrics in the `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp` and `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` exporters. (#5100)
- Add `otel.scope.name` and `otel.scope.version` tags to spans exported by `go.opentelemetry.io/otel/exporters/zipkin`. (#5108)
- Add support for `AddLink` to `go.opentelemetry.io/otel/bridge/opencensus`. (#TODO)

### Changed

Expand Down
2 changes: 0 additions & 2 deletions bridge/opencensus/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@
//
// There are known limitations to the trace bridge:
//
// - The AddLink method for OpenCensus Spans is ignored, and an error is sent
// to the OpenTelemetry ErrorHandler.
// - The NewContext method of the OpenCensus Tracer cannot embed an OpenCensus
// Span in a context unless that Span was created by that Tracer.
// - Conversion of custom OpenCensus Samplers to OpenTelemetry is not
Expand Down
11 changes: 11 additions & 0 deletions bridge/opencensus/internal/oc2otel/attributes.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@ func Attributes(attr []octrace.Attribute) []attribute.KeyValue {
return otelAttr
}

func AttributesFromMap(attr map[string]interface{}) []attribute.KeyValue {
otelAttr := make([]attribute.KeyValue, 0, len(attr))
for k, v := range attr {
otelAttr = append(otelAttr, attribute.KeyValue{
Key: attribute.Key(k),
Value: AttributeValue(v),
})
}
return otelAttr
}

func AttributeValue(ocval interface{}) attribute.Value {
switch v := ocval.(type) {
case bool:
Expand Down
26 changes: 26 additions & 0 deletions bridge/opencensus/internal/oc2otel/attributes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,32 @@ func TestAttributes(t *testing.T) {
}
}

func TestAttributesFromMap(t *testing.T) {
in := map[string]interface{}{
"bool": true,
"int64": int64(49),
"float64": float64(1.618),
"key": "val",
}

want := []attribute.KeyValue{
attribute.Bool("bool", true),
attribute.Int64("int64", 49),
attribute.Float64("float64", 1.618),
attribute.String("key", "val"),
}
got := AttributesFromMap(in)

if len(got) != len(want) {
t.Errorf("Attributes conversion failed: want %#v, got %#v", want, got)
}
for i := range got {
if g, w := got[i], want[i]; g != w {
t.Errorf("Attributes conversion: want %#v, got %#v", w, g)
}
}
}

func TestAttributeValueUnknown(t *testing.T) {
got := AttributeValue([]byte{})
if got != attribute.StringValue("unknown") {
Expand Down
14 changes: 13 additions & 1 deletion bridge/opencensus/internal/span.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,20 @@ func (s *Span) AddMessageReceiveEvent(messageID, uncompressedByteSize, compresse
}

// AddLink adds a link to this span.
// This drops the OpenCensus LinkType because there is no such concept in OpenTelemetry.
func (s *Span) AddLink(l octrace.Link) {
Handle(fmt.Errorf("ignoring OpenCensus link %+v for span %q because OpenTelemetry doesn't support setting links after creation", l, s.String()))
s.otelSpan.AddLink(trace.Link{
SpanContext: trace.NewSpanContext(trace.SpanContextConfig{
TraceID: trace.TraceID(l.TraceID),
SpanID: trace.SpanID(l.SpanID),
// We don't know if this was sampled or not.
// Mark it as sampled, since sampled means
// "the caller may have recorded trace data":
// https://www.w3.org/TR/trace-context/#sampled-flag
TraceFlags: trace.FlagsSampled,
}),
Attributes: oc2otel.AttributesFromMap(l.Attributes),
})
}

// String prints a string representation of this span.
Expand Down
47 changes: 42 additions & 5 deletions bridge/opencensus/internal/span_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type span struct {
attrs []attribute.KeyValue
eName string
eOpts []trace.EventOption
links []trace.Link
}

func (s *span) IsRecording() bool { return s.recording }
Expand All @@ -37,6 +38,7 @@ func (s *span) SetName(n string) { s.name = n }
func (s *span) SetStatus(c codes.Code, d string) { s.sCode, s.sMsg = c, d }
func (s *span) SetAttributes(a ...attribute.KeyValue) { s.attrs = a }
func (s *span) AddEvent(n string, o ...trace.EventOption) { s.eName, s.eOpts = n, o }
func (s *span) AddLink(l trace.Link) { s.links = append(s.links, l) }

func TestSpanIsRecordingEvents(t *testing.T) {
s := &span{recording: true}
Expand Down Expand Up @@ -230,16 +232,51 @@ func TestSpanAddMessageReceiveEvent(t *testing.T) {
}

func TestSpanAddLinkFails(t *testing.T) {
h, restore := withHandler()
defer restore()

// OpenCensus does not try to set links if not recording.
s := &span{recording: true}
ocS := internal.NewSpan(s)
ocS.AddLink(octrace.Link{})
ocS.AddLink(octrace.Link{
TraceID: octrace.TraceID([16]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}),
SpanID: octrace.SpanID([8]byte{2, 0, 0, 0, 0, 0, 0, 0}),
Attributes: map[string]interface{}{
"foo": "bar",
"number": int64(3),
},
})

wantLinks := []trace.Link{
{
SpanContext: trace.NewSpanContext(trace.SpanContextConfig{
TraceFlags: trace.FlagsSampled,
}),
},
{
SpanContext: trace.NewSpanContext(trace.SpanContextConfig{
TraceID: trace.TraceID([]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}),
SpanID: trace.SpanID([]byte{2, 0, 0, 0, 0, 0, 0, 0}),
TraceFlags: trace.FlagsSampled,
}),
Attributes: []attribute.KeyValue{
attribute.String("foo", "bar"),
attribute.Int64("number", 3),
},
},
}

if len(s.links) != len(wantLinks) {
t.Fatalf("got wrong number of links; want %v, got %v", len(wantLinks), len(s.links))
}

if h.err == nil {
t.Error("span.AddLink failed to raise an error")
for i, l := range s.links {
if !l.SpanContext.Equal(wantLinks[i].SpanContext) {
t.Errorf("link[%v] has the wrong span context; want %+v, got %+v", i, wantLinks[i].SpanContext, l.SpanContext)
}
gotAttributeSet := attribute.NewSet(l.Attributes...)
wantAttributeSet := attribute.NewSet(wantLinks[i].Attributes...)
if !gotAttributeSet.Equals(&wantAttributeSet) {
t.Errorf("link[%v] has the wrong attributes; want %v, got %v", i, wantAttributeSet.Encoded(attribute.DefaultEncoder()), gotAttributeSet.Encoded(attribute.DefaultEncoder()))
}
}
}

Expand Down

0 comments on commit 9abd728

Please sign in to comment.