1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
|
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !disable_events
package otel_test
import (
"bytes"
"context"
"fmt"
"io"
"testing"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/trace"
"golang.org/x/exp/event"
"golang.org/x/exp/event/otel"
)
func TestTrace(t *testing.T) {
// Verify that otel and event traces work well together.
// This test uses a single, fixed span tree (see makeTraceSpec).
// Each test case varies which of the individual spans are
// created directly from an otel tracer, and which are created
// using the event package.
want := "root (f (g h) p (q r))"
for i, tfunc := range []func(int) bool{
func(int) bool { return true },
func(int) bool { return false },
func(i int) bool { return i%2 == 0 },
func(i int) bool { return i%2 == 1 },
func(i int) bool { return i%3 == 0 },
func(i int) bool { return i%3 == 1 },
} {
ctx, tr, shutdown := setupOtel()
// There are 7 spans, so we create a 7-element slice.
// tfunc determines, for each index, whether it holds
// an otel tracer or nil.
tracers := make([]trace.Tracer, 7)
for i := 0; i < len(tracers); i++ {
if tfunc(i) {
tracers[i] = tr
}
}
s := makeTraceSpec(tracers)
s.apply(ctx)
got := shutdown()
if got != want {
t.Errorf("#%d: got %v, want %v", i, got, want)
}
}
}
func makeTraceSpec(tracers []trace.Tracer) *traceSpec {
return &traceSpec{
name: "root",
tracer: tracers[0],
children: []*traceSpec{
{
name: "f",
tracer: tracers[1],
children: []*traceSpec{
{name: "g", tracer: tracers[2]},
{name: "h", tracer: tracers[3]},
},
},
{
name: "p",
tracer: tracers[4],
children: []*traceSpec{
{name: "q", tracer: tracers[5]},
{name: "r", tracer: tracers[6]},
},
},
},
}
}
type traceSpec struct {
name string
tracer trace.Tracer // nil for event
children []*traceSpec
}
// apply builds spans for the traceSpec and all its children,
// If the traceSpec has a non-nil tracer, it is used to create the span.
// Otherwise, event.Trace.Start is used.
func (s *traceSpec) apply(ctx context.Context) {
if s.tracer != nil {
var span trace.Span
ctx, span = s.tracer.Start(ctx, s.name)
defer span.End()
} else {
ctx = event.Start(ctx, s.name)
defer event.End(ctx)
}
for _, c := range s.children {
c.apply(ctx)
}
}
func setupOtel() (context.Context, trace.Tracer, func() string) {
ctx := context.Background()
e := newTestExporter()
bsp := sdktrace.NewSimpleSpanProcessor(e)
stp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(bsp))
tracer := stp.Tracer("")
ee := event.NewExporter(otel.NewTraceHandler(tracer), nil)
ctx = event.WithExporter(ctx, ee)
return ctx, tracer, func() string { stp.Shutdown(ctx); return e.got }
}
// testExporter is an otel exporter for traces
type testExporter struct {
m map[trace.SpanID][]sdktrace.ReadOnlySpan // key is parent SpanID
got string
}
var _ sdktrace.SpanExporter = (*testExporter)(nil)
func newTestExporter() *testExporter {
return &testExporter{m: map[trace.SpanID][]sdktrace.ReadOnlySpan{}}
}
func (e *testExporter) ExportSpans(ctx context.Context, ss []sdktrace.ReadOnlySpan) error {
for _, s := range ss {
sid := s.Parent().SpanID()
e.m[sid] = append(e.m[sid], s)
}
return nil
}
func (e *testExporter) Shutdown(ctx context.Context) error {
root := e.m[trace.SpanID{}][0]
var buf bytes.Buffer
e.print(&buf, root)
e.got = buf.String()
return nil
}
func (e *testExporter) print(w io.Writer, ss sdktrace.ReadOnlySpan) {
fmt.Fprintf(w, "%s", ss.Name())
children := e.m[ss.SpanContext().SpanID()]
if len(children) > 0 {
fmt.Fprint(w, " (")
for i, ss := range children {
if i != 0 {
fmt.Fprint(w, " ")
}
e.print(w, ss)
}
fmt.Fprint(w, ")")
}
}
|