Skip to content
This repository has been archived by the owner on May 23, 2023. It is now read-only.

Commit

Permalink
Merge pull request #19 from opentracing/ext-tags
Browse files Browse the repository at this point in the history
Adds ext/tags mechanism for adding common tags to the span
  • Loading branch information
yurishkuro committed Dec 29, 2015
2 parents 61e3c73 + 00ab685 commit 82bcc14
Show file tree
Hide file tree
Showing 16 changed files with 337 additions and 13 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

.PHONY: test
test:
go test ./...
go test -v -cover ./...

.PHONY: lint
lint:
Expand All @@ -16,5 +16,5 @@ vet:

.PHONY: example
example:
go build -o build/dapperish-example ./examples/dapperish/.
go build -o build/dapperish-example ./examples/dapperish.go
./build/dapperish-example
3 changes: 2 additions & 1 deletion examples/dapperish/main.go → examples/dapperish.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"runtime"
"strings"

"github.com/opentracing/api-golang/examples/dapperish"
"github.com/opentracing/api-golang/opentracing"
)

Expand Down Expand Up @@ -78,7 +79,7 @@ func server() {
}

func main() {
opentracing.InitDefaultTracer(NewTracer("dapperish_tester"))
opentracing.InitDefaultTracer(dapperish.NewTracer("dapperish_tester"))

go server()
go client()
Expand Down
2 changes: 1 addition & 1 deletion examples/dapperish/dapper.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package main
package dapperish

import (
"bytes"
Expand Down
2 changes: 1 addition & 1 deletion examples/dapperish/random.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package main
package dapperish

import (
"math/rand"
Expand Down
2 changes: 1 addition & 1 deletion examples/dapperish/trivialrecorder.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package main
package dapperish

import (
"fmt"
Expand Down
52 changes: 52 additions & 0 deletions opentracing/ext/tags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package ext

import (
"github.com/opentracing/api-golang/opentracing"
)

var (
// PeerXXX tags can be emitted by either client-side of server-side to describe
// the other side/service in a peer-to-peer communications, like an RPC call.

// PeerService records the service name of the peer
PeerService = &stringTag{"peer.service"}

// PeerHostname records the host name of the peer
PeerHostname = &stringTag{"peer.hostname"}

// PeerHostIPv4 records IP v4 host address of the peer
PeerHostIPv4 = &uint32Tag{"peer.ipv4"}

// PeerHostIPv6 records IP v6 host address of the peer
PeerHostIPv6 = &stringTag{"peer.ipv6"}

// PeerPort records port number of the peer
PeerPort = &uint16Tag{"peer.port"}
)

type stringTag struct {
Key string
}

// Add adds a string tag to the `span`
func (tag *stringTag) Add(span opentracing.Span, value string) {
span.SetTag(tag.Key, value)
}

type uint32Tag struct {
Key string
}

// Add adds a uint32 tag to the `span`
func (tag *uint32Tag) Add(span opentracing.Span, value uint32) {
span.SetTag(tag.Key, value)
}

type uint16Tag struct {
Key string
}

// Add adds a uint16 tag to the `span`
func (tag *uint16Tag) Add(span opentracing.Span, value uint16) {
span.SetTag(tag.Key, value)
}
34 changes: 34 additions & 0 deletions opentracing/ext/tags_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package ext_test

import (
"testing"

"github.com/opentracing/api-golang/opentracing/ext"
"github.com/opentracing/api-golang/opentracing/standardtracer"
"github.com/opentracing/api-golang/testutils"
"github.com/opentracing/api-golang/testutils/assert"
)

func TestPeerTags(t *testing.T) {
if ext.PeerService.Key != "peer.service" {
t.Fatalf("Invalid PeerService.Key %v", ext.PeerService.Key)
}
recorder := testutils.NewInMemoryRecorder("test-process")
tracer := standardtracer.New(recorder, &testutils.SimpleTraceContextSource{})
span := tracer.StartTrace("my-trace")
ext.PeerService.Add(span, "my-service")
ext.PeerHostname.Add(span, "my-hostname")
ext.PeerHostIPv4.Add(span, uint32(127<<24|1))
ext.PeerHostIPv6.Add(span, "::")
ext.PeerPort.Add(span, uint16(8080))
span.Finish()
if len(recorder.GetSpans()) != 1 {
t.Fatal("Span not recorded")
}
rawSpan := recorder.GetSpans()[0]
assert.EqualValues(t, "my-service", rawSpan.Tags[ext.PeerService.Key])
assert.EqualValues(t, "my-hostname", rawSpan.Tags[ext.PeerHostname.Key])
assert.EqualValues(t, uint32(127<<24|1), rawSpan.Tags[ext.PeerHostIPv4.Key])
assert.EqualValues(t, "::", rawSpan.Tags[ext.PeerHostIPv6.Key])
assert.EqualValues(t, 8080, rawSpan.Tags[ext.PeerPort.Key])
}
4 changes: 1 addition & 3 deletions opentracing/raw.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ package opentracing
import "time"

// Tags is a generic map from an arbitrary string key to an opaque value type.
//
// TODO: decide on restrictions (if any) for the values: just strings and POD?
// just simple maps and slices of same? arbitrary objects?
// The underlying tracing system is responsible for interpreting and serializing the values.
type Tags map[string]interface{}

// RawSpan encapsulates all state associated with a (finished) Span.
Expand Down
10 changes: 8 additions & 2 deletions opentracing/span.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@ type Span interface {
// Creates and starts a child span.
StartChild(operationName string) Span

// Adds a tag to the span. The `value` is immediately coerced into a string
// using fmt.Sprint().
// Adds a tag to the span.
//
// Tag values can be of arbitrary types, however the treatment of complex
// types is dependent on the underlying tracing system implementation.
// It is expected that most tracing systems will handle primitive types
// like strings and numbers. If a tracing system cannot understand how
// to handle a particular value type, it may ignore the tag, but shall
// not panic.
//
// If there is a pre-existing tag set for `key`, it is overwritten.
SetTag(key string, value interface{}) Span
Expand Down
4 changes: 2 additions & 2 deletions opentracing/standardtracer/standardimpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func (s *standardSpan) StartChild(operationName string) opentracing.Span {
func (s *standardSpan) SetTag(key string, value interface{}) opentracing.Span {
s.lock.Lock()
defer s.lock.Unlock()
s.raw.Tags[key] = fmt.Sprint(value)
s.raw.Tags[key] = value
return s
}

Expand All @@ -46,7 +46,7 @@ func (s *standardSpan) SetTags(tags opentracing.Tags) opentracing.Span {
defer s.lock.Unlock()

for k, v := range tags {
s.raw.Tags[k] = fmt.Sprint(v)
s.raw.Tags[k] = v
}
return s
}
Expand Down
57 changes: 57 additions & 0 deletions testutils/assert/assert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package assert

import (
"reflect"
"testing"
)

// NOTE: the functions in this module are borrowed from https://github.com/stretchr/testify
// to avoid creating a dependency (until Go comes up with proper dependency manager)

// EqualValues asserts that two objects are equal or convertable to the same types
// and equal.
//
// assert.EqualValues(t, uint32(123), int32(123), "123 and 123 should be equal")
//
// If assertion is not successful, execution halts via t.Fatalf() call
func EqualValues(t *testing.T, expected, actual interface{}) bool {
if !ObjectsAreEqualValues(expected, actual) {
t.Fatalf("Not equal: %#v (expected)\n"+
" != %#v (actual)", expected, actual)
}
return true
}

// ObjectsAreEqual determines if two objects are considered equal.
func ObjectsAreEqual(expected, actual interface{}) bool {

if expected == nil || actual == nil {
return expected == actual
}

if reflect.DeepEqual(expected, actual) {
return true
}

return false

}

// ObjectsAreEqualValues determines whether two objects are equal,
// or if their values are equal.
func ObjectsAreEqualValues(expected, actual interface{}) bool {
if ObjectsAreEqual(expected, actual) {
return true
}

actualType := reflect.TypeOf(actual)
expectedValue := reflect.ValueOf(expected)
if expectedValue.Type().ConvertibleTo(actualType) {
// Attempt comparison after type conversion
if reflect.DeepEqual(actual, expectedValue.Convert(actualType).Interface()) {
return true
}
}

return false
}
11 changes: 11 additions & 0 deletions testutils/assert/assert_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package assert_test

import (
"github.com/opentracing/api-golang/testutils/assert"
"testing"
)

func TestAssert(t *testing.T) {
assert.EqualValues(t, int16(123), int16(123))
assert.EqualValues(t, int16(123), int32(123))
}
66 changes: 66 additions & 0 deletions testutils/recorder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package testutils

import (
"sync"

"github.com/opentracing/api-golang/opentracing"
)

// InMemoryRecorder is a simple thread-safe implementation of opentracing.Recorder
// that stores all reported spans in memory, accessible via reporter.GetSpans()
type InMemoryRecorder struct {
processName string
spans []*opentracing.RawSpan
tags opentracing.Tags
lock sync.Mutex
}

// NewInMemoryRecorder instantiates a new InMemoryRecorder with the given `processName`
func NewInMemoryRecorder(processName string) *InMemoryRecorder {
return &InMemoryRecorder{
processName: processName,
spans: make([]*opentracing.RawSpan, 0),
tags: make(opentracing.Tags),
}
}

// ProcessName implements ProcessName() of opentracing.Recorder
func (recorder *InMemoryRecorder) ProcessName() string {
return recorder.processName
}

// SetTag implements SetTag() of opentracing.Recorder. Tags can be retrieved via recorder.GetTags()
func (recorder *InMemoryRecorder) SetTag(key string, val interface{}) opentracing.ProcessIdentifier {
recorder.lock.Lock()
defer recorder.lock.Unlock()
recorder.tags[key] = val
return recorder
}

// RecordSpan implements RecordSpan() of opentracing.Recorder.
// The recorder spans can be retrieved via recorder.Spans slice.
func (recorder *InMemoryRecorder) RecordSpan(span *opentracing.RawSpan) {
recorder.lock.Lock()
defer recorder.lock.Unlock()
recorder.spans = append(recorder.spans, span)
}

// GetSpans returns a snapshot of spans recorded so far.
func (recorder *InMemoryRecorder) GetSpans() []*opentracing.RawSpan {
recorder.lock.Lock()
defer recorder.lock.Unlock()
spans := make([]*opentracing.RawSpan, len(recorder.spans))
copy(spans, recorder.spans)
return spans
}

// GetTags returns a snapshot of tags.
func (recorder *InMemoryRecorder) GetTags() opentracing.Tags {
recorder.lock.Lock()
defer recorder.lock.Unlock()
tags := make(opentracing.Tags)
for k, v := range recorder.tags {
tags[k] = v
}
return tags
}
42 changes: 42 additions & 0 deletions testutils/recorder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package testutils_test

import (
"testing"
"time"

"github.com/opentracing/api-golang/examples/dapperish"
"github.com/opentracing/api-golang/opentracing"
"github.com/opentracing/api-golang/testutils"
)

func TestInMemoryRecorderSpans(t *testing.T) {
recorder := testutils.NewInMemoryRecorder("unit-test")
var apiRecorder opentracing.Recorder = recorder
if apiRecorder.ProcessName() != "unit-test" {
t.Fatalf("Invalid process name")
}
span := &opentracing.RawSpan{
TraceContext: &dapperish.DapperishTraceContext{},
Operation: "test-span",
Start: time.Now(),
Duration: -1,
}
apiRecorder.RecordSpan(span)
if len(recorder.GetSpans()) != 1 {
t.Fatal("No spans recorded")
}
if recorder.GetSpans()[0] != span {
t.Fatal("Span not recorded")
}
}

func TestInMemoryRecorderTags(t *testing.T) {
recorder := testutils.NewInMemoryRecorder("unit-test")
recorder.SetTag("tag1", "hello")
if len(recorder.GetTags()) != 1 {
t.Fatal("Tag not stored")
}
if recorder.GetTags()["tag1"] != "hello" {
t.Fatal("tag1 != hello")
}
}
Loading

0 comments on commit 82bcc14

Please sign in to comment.