From 7cb6e169b0997649e14e88f1a4dfc7fd0101bcd3 Mon Sep 17 00:00:00 2001 From: Yuri Shkuro Date: Fri, 22 Jul 2016 10:51:08 -0400 Subject: [PATCH] Replace build-in tracing with OpenTracing --- Makefile | 3 +- channel.go | 79 +- channel_test.go | 18 +- context.go | 20 +- context_builder.go | 40 +- context_header.go | 3 - ...porter_test.go => context_internal_test.go | 43 +- context_test.go | 35 +- glide.lock | 16 +- glide.yaml | 2 + hyperbahn/call.go | 8 +- inbound.go | 54 +- json/call.go | 3 + json/handler.go | 6 +- json/tracing_test.go | 68 + outbound.go | 55 +- subchannel.go | 6 + testutils/channel_opts.go | 18 - testutils/testtracing/propagation.go | 313 +++ testutils/testtracing/propagation_test.go | 130 ++ thrift/client.go | 1 + thrift/server.go | 1 + thrift/tracing_test.go | 112 + trace/reporter.go | 244 --- trace/reporter_test.go | 450 ---- trace/tcollector.thrift | 73 - trace/thrift/gen-go/tcollector/constants.go | 18 - .../gen-go/tcollector/tchan-tcollector.go | 173 -- trace/thrift/gen-go/tcollector/tcollector.go | 1096 ---------- trace/thrift/gen-go/tcollector/ttypes.go | 1835 ----------------- trace/thrift/mocks/TChanTCollector.go | 74 - tracereporter.go | 139 -- tracing.go | 228 +- tracing_internal_test.go | 115 ++ tracing_test.go | 366 +--- utils_for_test.go | 5 - 36 files changed, 1161 insertions(+), 4689 deletions(-) rename tracereporter_test.go => context_internal_test.go (54%) create mode 100644 json/tracing_test.go create mode 100644 testutils/testtracing/propagation.go create mode 100644 testutils/testtracing/propagation_test.go create mode 100644 thrift/tracing_test.go delete mode 100644 trace/reporter.go delete mode 100644 trace/reporter_test.go delete mode 100644 trace/tcollector.thrift delete mode 100644 trace/thrift/gen-go/tcollector/constants.go delete mode 100644 trace/thrift/gen-go/tcollector/tchan-tcollector.go delete mode 100644 trace/thrift/gen-go/tcollector/tcollector.go delete mode 100644 trace/thrift/gen-go/tcollector/ttypes.go delete mode 100644 trace/thrift/mocks/TChanTCollector.go delete mode 100644 tracereporter.go diff --git a/Makefile b/Makefile index 296d8f29..b63dfc55 100644 --- a/Makefile +++ b/Makefile @@ -133,13 +133,12 @@ else @echo "Not checking gofmt on" $(GO_VERSION) endif @echo "Checking for unresolved FIXMEs" - -git grep -i fixme | $(FILTER) | grep -v -e Makefile | tee -a lint.log + -git grep -i -n fixme | $(FILTER) | grep -v -e Makefile | tee -a lint.log @[ ! -s lint.log ] else @echo "Skipping linters on" $(GO_VERSION) endif - thrift_example: thrift_gen go build -o $(BUILD)/examples/thrift ./examples/thrift/main.go diff --git a/channel.go b/channel.go index 505aceea..5d9b7d9a 100644 --- a/channel.go +++ b/channel.go @@ -32,6 +32,7 @@ import ( "github.com/uber/tchannel-go/relay" "github.com/uber/tchannel-go/tnet" + "github.com/opentracing/opentracing-go" "golang.org/x/net/context" ) @@ -46,14 +47,8 @@ var ( const ( ephemeralHostPort = "0.0.0.0:0" - - // DefaultTraceSampleRate is the default sampling rate for traces. - DefaultTraceSampleRate = 1.0 ) -// TraceReporterFactory is the interface of the method to generate TraceReporter instance. -type TraceReporterFactory func(*Channel) TraceReporter - // ChannelOptions are used to control parameters on a create a TChannel type ChannelOptions struct { // Default Connection options @@ -84,15 +79,9 @@ type ChannelOptions struct { // Note: This is not a stable part of the API and may change. TimeNow func() time.Time - // Trace reporter to use for this channel. - TraceReporter TraceReporter - - // Trace reporter factory to generate trace reporter instance. - TraceReporterFactory TraceReporterFactory - - // TraceSampleRate is the rate of requests to sample, and should be in the range [0, 1]. - // If this value is not set, then DefaultTraceSampleRate is used. - TraceSampleRate *float64 + // Tracer is an OpenTracing Tracer used to manage distributed tracing spans. + // If not set, opentracing.GlobalTracer() is used. + Tracer opentracing.Tracer } // ChannelState is the state of a channel. @@ -146,14 +135,23 @@ type Channel struct { // channelConnectionCommon is the list of common objects that both use // and can be copied directly from the channel to the connection. type channelConnectionCommon struct { - log Logger - relayStats relay.Stats - relayLocal map[string]struct{} - statsReporter StatsReporter - traceReporter TraceReporter - subChannels *subChannelMap - timeNow func() time.Time - traceSampleRate float64 + log Logger + relayStats relay.Stats + relayLocal map[string]struct{} + statsReporter StatsReporter + tracer opentracing.Tracer + subChannels *subChannelMap + timeNow func() time.Time +} + +// Tracer returns the OpenTracing Tracer for this channel. If no tracer was provided +// in the configuration, returns opentracing.GlobalTracer(). Note that this approach +// allows opentracing.GlobalTracer() to be initialized _after_ the channel is created. +func (ccc channelConnectionCommon) Tracer() opentracing.Tracer { + if ccc.tracer != nil { + return ccc.tracer + } + return opentracing.GlobalTracer() } // NewChannel creates a new Channel. The new channel can be used to send outbound requests @@ -188,11 +186,6 @@ func NewChannel(serviceName string, opts *ChannelOptions) (*Channel, error) { timeNow = time.Now } - traceSampleRate := DefaultTraceSampleRate - if opts.TraceSampleRate != nil { - traceSampleRate = *opts.TraceSampleRate - } - relayStats := relay.NewNoopStats() if opts.RelayStats != nil { relayStats = opts.RelayStats @@ -203,12 +196,12 @@ func NewChannel(serviceName string, opts *ChannelOptions) (*Channel, error) { log: logger.WithFields( LogField{"service", serviceName}, LogField{"process", processName}), - relayStats: relayStats, - relayLocal: toStringSet(opts.RelayLocalHandlers), - statsReporter: statsReporter, - subChannels: &subChannelMap{}, - timeNow: timeNow, - traceSampleRate: traceSampleRate, + relayStats: relayStats, + relayLocal: toStringSet(opts.RelayLocalHandlers), + statsReporter: statsReporter, + subChannels: &subChannelMap{}, + timeNow: timeNow, + tracer: opts.Tracer, }, connectionOptions: opts.DefaultConnectionOptions, @@ -227,16 +220,6 @@ func NewChannel(serviceName string, opts *ChannelOptions) (*Channel, error) { ch.mutable.conns = make(map[uint32]*Connection) ch.createCommonStats() - // TraceReporter may use the channel, so we must initialize it once the channel is ready. - traceReporter := opts.TraceReporter - if opts.TraceReporterFactory != nil { - traceReporter = opts.TraceReporterFactory(ch) - } - if traceReporter == nil { - traceReporter = NullReporter - } - ch.traceReporter = traceReporter - ch.registerInternal() registerNewChannel(ch) @@ -317,6 +300,9 @@ type Registrar interface { // Peers returns the peer list for this Registrar. Peers() *PeerList + + // Tracer returns OpenTracing Tracer this channel was configured with + Tracer() opentracing.Tracer } // Register registers a handler for a method. @@ -456,11 +442,6 @@ func (ch *Channel) StatsReporter() StatsReporter { return ch.statsReporter } -// TraceReporter returns the trace reporter for this channel. -func (ch *Channel) TraceReporter() TraceReporter { - return ch.traceReporter -} - // StatsTags returns the common tags that should be used when reporting stats. // It returns a new map for each call. func (ch *Channel) StatsTags() map[string]string { diff --git a/channel_test.go b/channel_test.go index b1a7e2e6..8c129c06 100644 --- a/channel_test.go +++ b/channel_test.go @@ -25,6 +25,8 @@ import ( "os" "testing" + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/mocktracer" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -67,7 +69,6 @@ func TestStats(t *testing.T) { peerInfo := ch.PeerInfo() tags := ch.StatsTags() assert.NotNil(t, ch.StatsReporter(), "StatsReporter missing") - assert.NotNil(t, ch.TraceReporter(), "TraceReporter missing") assert.Equal(t, peerInfo.ProcessName, tags["app"], "app tag") assert.Equal(t, peerInfo.ServiceName, tags["service"], "service tag") assert.Equal(t, hostname, tags["host"], "hostname tag") @@ -112,3 +113,18 @@ func TestIsolatedSubChannelsDontSharePeers(t *testing.T) { assert.NotNil(t, sub.peers.peersByHostPort["127.0.0.1:3000"]) assert.Nil(t, isolatedSub.peers.peersByHostPort["127.0.0.1:3000"]) } + +func TestChannelTracerMethod(t *testing.T) { + tracer := mocktracer.New() + ch, err := NewChannel("svc", &ChannelOptions{ + Tracer: tracer, + }) + require.NoError(t, err) + defer ch.Close() + assert.Equal(t, tracer, ch.Tracer(), "expecting tracer passed at initialization") + + ch, err = NewChannel("svc", &ChannelOptions{}) + require.NoError(t, err) + defer ch.Close() + assert.EqualValues(t, opentracing.GlobalTracer(), ch.Tracer(), "expecting default tracer") +} diff --git a/context.go b/context.go index b49d39c1..f6eb7a76 100644 --- a/context.go +++ b/context.go @@ -36,7 +36,7 @@ const ( ) type tchannelCtxParams struct { - span *Span + tracingDisabled bool call IncomingCall options *CallOptions retryOptions *RetryOptions @@ -76,9 +76,7 @@ func getTChannelParams(ctx context.Context) *tchannelCtxParams { // NewContext returns a new root context used to make TChannel requests. func NewContext(timeout time.Duration) (context.Context, context.CancelFunc) { - return NewContextBuilder(timeout). - setSpan(NewRootSpan()). - Build() + return NewContextBuilder(timeout).Build() } // WrapContextForTest returns a copy of the given Context that is associated with the call. @@ -90,10 +88,9 @@ func WrapContextForTest(ctx context.Context, call IncomingCall) context.Context } // newIncomingContext creates a new context for an incoming call with the given span. -func newIncomingContext(call IncomingCall, timeout time.Duration, span *Span) (context.Context, context.CancelFunc) { +func newIncomingContext(call IncomingCall, timeout time.Duration) (context.Context, context.CancelFunc) { return NewContextBuilder(timeout). setIncomingCall(call). - setSpan(span). Build() } @@ -105,17 +102,16 @@ func CurrentCall(ctx context.Context) IncomingCall { return nil } -// CurrentSpan returns the Span value for the provided Context -func CurrentSpan(ctx context.Context) *Span { +func currentCallOptions(ctx context.Context) *CallOptions { if params := getTChannelParams(ctx); params != nil { - return params.span + return params.options } return nil } -func currentCallOptions(ctx context.Context) *CallOptions { +func isTracingDisabled(ctx context.Context) bool { if params := getTChannelParams(ctx); params != nil { - return params.options + return params.tracingDisabled } - return nil + return false } diff --git a/context_builder.go b/context_builder.go index a840daed..99abbc64 100644 --- a/context_builder.go +++ b/context_builder.go @@ -53,14 +53,12 @@ type ContextBuilder struct { // ParentContext to build the new context from. If empty, context.Background() is used. // The new (child) context inherits a number of properties from the parent context: - // - the tracing Span, unless replaced via SetExternalSpan() // - context fields, accessible via `ctx.Value(key)` // - headers if parent is a ContextWithHeaders, unless replaced via SetHeaders() ParentContext context.Context // Hidden fields: we do not want users outside of tchannel to set these. incomingCall IncomingCall - span *Span // replaceParentHeaders is set to true when SetHeaders() method is called. // It forces headers from ParentContext to be ignored. When false, parent @@ -153,12 +151,6 @@ func (cb *ContextBuilder) SetIncomingCallForTest(call IncomingCall) *ContextBuil return cb.setIncomingCall(call) } -// SetSpanForTest sets a tracing span in the context. -// This should only be used in unit tests. -func (cb *ContextBuilder) SetSpanForTest(span *Span) *ContextBuilder { - return cb.setSpan(span) -} - // SetRetryOptions sets RetryOptions in the context. func (cb *ContextBuilder) SetRetryOptions(retryOptions *RetryOptions) *ContextBuilder { cb.RetryOptions = retryOptions @@ -180,36 +172,11 @@ func (cb *ContextBuilder) SetParentContext(ctx context.Context) *ContextBuilder return cb } -// SetExternalSpan creates a new TChannel tracing Span from externally provided IDs -// and sets it as the current span for the context. -// Intended for integration with other Zipkin-like tracers. -func (cb *ContextBuilder) SetExternalSpan(traceID, spanID, parentID uint64, traced bool) *ContextBuilder { - span := newSpan(traceID, spanID, parentID, traced) - return cb.setSpan(span) -} - -func (cb *ContextBuilder) setSpan(span *Span) *ContextBuilder { - cb.span = span - return cb -} - func (cb *ContextBuilder) setIncomingCall(call IncomingCall) *ContextBuilder { cb.incomingCall = call return cb } -func (cb *ContextBuilder) getSpan() *Span { - if cb.span != nil { - return cb.span - } - if cb.ParentContext != nil { - if span := CurrentSpan(cb.ParentContext); span != nil { - return span - } - } - return NewRootSpan() -} - func (cb *ContextBuilder) getHeaders() map[string]string { if cb.ParentContext == nil || cb.replaceParentHeaders { return cb.Headers @@ -232,18 +199,13 @@ func (cb *ContextBuilder) getHeaders() map[string]string { // Build returns a ContextWithHeaders that can be used to make calls. func (cb *ContextBuilder) Build() (ContextWithHeaders, context.CancelFunc) { - span := cb.getSpan() - if cb.TracingDisabled { - span.EnableTracing(false) - } - params := &tchannelCtxParams{ options: cb.CallOptions, - span: span, call: cb.incomingCall, retryOptions: cb.RetryOptions, connectTimeout: cb.ConnectTimeout, hideListeningOnOutbound: cb.hideListeningOnOutbound, + tracingDisabled: cb.TracingDisabled, } parent := cb.ParentContext diff --git a/context_header.go b/context_header.go index 5aba01ff..ddc6a53a 100644 --- a/context_header.go +++ b/context_header.go @@ -82,9 +82,6 @@ func (c headerCtx) SetResponseHeaders(headers map[string]string) { // If the parent `ctx` is already an instance of ContextWithHeaders, its existing headers // will be ignored. In order to merge new headers with parent headers, use ContextBuilder. func WrapWithHeaders(ctx context.Context, headers map[string]string) ContextWithHeaders { - if hctx, ok := ctx.(headerCtx); ok { - ctx = hctx - } h := &headersContainer{ reqHeaders: headers, } diff --git a/tracereporter_test.go b/context_internal_test.go similarity index 54% rename from tracereporter_test.go rename to context_internal_test.go index 7280f3cb..d0c29183 100644 --- a/tracereporter_test.go +++ b/context_internal_test.go @@ -17,28 +17,41 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. - package tchannel import ( "testing" + "time" + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/mocktracer" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "golang.org/x/net/context" ) -func TestTraceReporterFactory(t *testing.T) { - var gotChannel *Channel - testTraceReporterFactory := func(ch *Channel) TraceReporter { - gotChannel = ch - return SimpleTraceReporter - } - tc, err := NewChannel("client", &ChannelOptions{ - TraceReporter: NullReporter, - TraceReporterFactory: testTraceReporterFactory, - }) - require.NoError(t, err) - defer tc.Close() - assert.Equal(t, tc, gotChannel, "TraceReporterFactory got wrong channel") - assert.Equal(t, tc.traceReporter, SimpleTraceReporter, "Wrong TraceReporter") +func TestNewContextBuilderDisableTracing(t *testing.T) { + ctx, cancel := NewContextBuilder(time.Second). + DisableTracing().Build() + defer cancel() + + assert.True(t, isTracingDisabled(ctx), "Tracing should be disabled") +} + +func TestCurrentSpan(t *testing.T) { + ctx := context.Background() + span := CurrentSpan(ctx) + require.NotNil(t, span, "CurrentSpan() should always return something") + + tracer := mocktracer.New() + sp := tracer.StartSpan("test") + ctx = opentracing.ContextWithSpan(ctx, sp) + span = CurrentSpan(ctx) + require.NotNil(t, span, "CurrentSpan() should always return something") + assert.EqualValues(t, 0, span.TraceID(), "mock tracer is not Zipkin-compatible") + + tracer.RegisterInjector(zipkinSpanFormat, new(zipkinInjector)) + span = CurrentSpan(ctx) + require.NotNil(t, span, "CurrentSpan() should always return something") + assert.NotEqual(t, uint64(0), span.TraceID(), "mock tracer is now Zipkin-compatible") } diff --git a/context_test.go b/context_test.go index 5f18ab3a..c25208b4 100644 --- a/context_test.go +++ b/context_test.go @@ -43,22 +43,6 @@ func TestWrapContextForTest(t *testing.T) { assert.Equal(t, call, CurrentCall(actual), "Incorrect call object returned.") } -func TestNewContextBuilderHasSpan(t *testing.T) { - ctx, cancel := NewContextBuilder(time.Second).Build() - defer cancel() - - assert.NotNil(t, CurrentSpan(ctx), "NewContext should contain span") - assert.True(t, CurrentSpan(ctx).TracingEnabled(), "Tracing should be enabled") -} - -func TestNewContextBuilderDisableTracing(t *testing.T) { - ctx, cancel := NewContextBuilder(time.Second). - DisableTracing().Build() - defer cancel() - - assert.False(t, CurrentSpan(ctx).TracingEnabled(), "Tracing should be disabled") -} - func TestNewContextTimeoutZero(t *testing.T) { ctx, cancel := NewContextBuilder(0).Build() defer cancel() @@ -124,11 +108,13 @@ func TestCurrentCallWithNilResult(t *testing.T) { func getParentContext(t *testing.T) ContextWithHeaders { ctx := context.WithValue(context.Background(), "some key", "some value") + assert.Equal(t, "some value", ctx.Value("some key")) ctx1, _ := NewContextBuilder(time.Second). SetParentContext(ctx). AddHeader("header key", "header value"). Build() + assert.Equal(t, "some value", ctx1.Value("some key")) return ctx1 } @@ -208,25 +194,12 @@ func TestContextWithHeadersAsContext(t *testing.T) { func TestContextBuilderParentContextSpan(t *testing.T) { ctx := getParentContext(t) - span := NewSpan(5, 4, 3) + assert.Equal(t, "some value", ctx.Value("some key")) ctx2, _ := NewContextBuilder(time.Second). SetParentContext(ctx). - SetSpanForTest(&span). - Build() - assert.Equal(t, &span, CurrentSpan(ctx2), "explicitly provided span used") - - ctx3, _ := NewContextBuilder(time.Second). - SetParentContext(ctx2). - Build() - assert.Equal(t, &span, CurrentSpan(ctx3), "span inherited from parent") - - ctx4, _ := NewContextBuilder(time.Second). - SetParentContext(ctx2). - SetExternalSpan(3, 2, 1, true). Build() - span4 := NewSpan(3, 2, 1) - assert.Equal(t, &span4, CurrentSpan(ctx4), "external span used") + assert.Equal(t, "some value", ctx2.Value("some key"), "key/value propagated from parent ctx") goroutines.VerifyNoLeaks(t, nil) } diff --git a/glide.lock b/glide.lock index d0633b92..8454c125 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 9950186274c3f039a7a65ea45573abacfca42050b0f40215c956ddfe7cd7f823 -updated: 2016-06-22T14:30:54.982533777-07:00 +hash: 111bdf9797d23d4b9f8833a9780e382dd53153af70ad286f99815cea4c2fdf4b +updated: 2016-07-11T23:04:29.707899334-04:00 imports: - name: github.com/apache/thrift version: 0e9fed1e12ed066865e46c6903782b2ef95f4650 @@ -18,13 +18,17 @@ imports: subpackages: - spew - name: github.com/jessevdk/go-flags - version: b9b882a3990882b05e02765f5df2cd3ad02874ee + version: f2785f5820ec967043de79c8be97edfc464ca745 +- name: github.com/opentracing/opentracing-go + version: b8a4c1a139ba3d38708fb69721c51c7c4de51014 + subpackages: + - ext - name: github.com/pmezard/go-difflib version: 792786c7400a136282c1664665ae0a8db921c6c2 subpackages: - difflib - name: github.com/prashantv/protectmem - version: dda468c7a8a992f1642b31604d732590921a30e5 + version: d0cabd7ce64237b84414449086fa42810ae01a92 - name: github.com/samuel/go-thrift version: e9042807f4f5bf47563df6992d3ea0857313e2be subpackages: @@ -41,8 +45,10 @@ imports: - require - name: github.com/uber-go/atomic version: e682c1008ac17bf26d2e4b5ad6cdd08520ed0b22 +- name: github.com/uber/jaeger-client-go + version: c60d0b9ab83360546da39a90ab06a538028bfbbe - name: golang.org/x/net - version: bc3663df0ac92f928d419e31e0d2af22e683a5a2 + version: b400c2eff1badec7022a8c8f5bea058b6315eed7 subpackages: - context - name: gopkg.in/yaml.v2 diff --git a/glide.yaml b/glide.yaml index 645868bf..da6f7841 100644 --- a/glide.yaml +++ b/glide.yaml @@ -44,3 +44,5 @@ import: - quantile - package: github.com/uber-go/atomic - package: github.com/prashantv/protectmem +- package: github.com/opentracing/opentracing-go +- package: github.com/uber/jaeger-client-go diff --git a/hyperbahn/call.go b/hyperbahn/call.go index 02ebbf49..16a83c2a 100644 --- a/hyperbahn/call.go +++ b/hyperbahn/call.go @@ -69,12 +69,12 @@ func (c *Client) sendAdvertise() error { } ctx, cancel := tchannel.NewContextBuilder(c.opts.Timeout). - SetRetryOptions(retryOpts).Build() + SetRetryOptions(retryOpts). + // Disable tracing on Hyperbahn advertise messages to avoid cascading failures (see #790). + DisableTracing(). + Build() defer cancel() - // Disable tracing on Hyperbahn advertise messages to avoid cascading failures (see #790). - tchannel.CurrentSpan(ctx).EnableTracing(false) - var resp AdResponse c.opts.Handler.On(SendAdvertise) if err := c.jsonClient.Call(ctx, "ad", c.createRequest(), &resp); err != nil { diff --git a/inbound.go b/inbound.go index 6f87473a..774d7176 100644 --- a/inbound.go +++ b/inbound.go @@ -25,6 +25,8 @@ import ( "fmt" "time" + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" "golang.org/x/net/context" ) @@ -63,7 +65,7 @@ func (c *Connection) handleCallReq(frame *Frame) bool { call := new(InboundCall) call.conn = c - ctx, cancel := newIncomingContext(call, callReq.TimeToLive, &callReq.Tracing) + ctx, cancel := newIncomingContext(call, callReq.TimeToLive) if !c.pendingExchangeMethodAdd() { // Connection is closed, no need to do anything. @@ -90,29 +92,14 @@ func (c *Connection) handleCallReq(frame *Frame) bool { response := new(InboundCallResponse) response.call = call response.calledAt = now - response.Annotations = Annotations{ - reporter: c.traceReporter, - timeNow: c.timeNow, - data: TraceData{ - Span: callReq.Tracing, - Source: TraceEndpoint{ - HostPort: c.localPeerInfo.HostPort, - ServiceName: callReq.Service, - }, - }, - binaryAnnotationsBacking: [2]BinaryAnnotation{ - {Key: "cn", Value: callReq.Headers[CallerName]}, - {Key: "as", Value: callReq.Headers[ArgScheme]}, - }, + response.timeNow = c.timeNow + response.span = c.extractInboundSpan(callReq) + if response.span != nil { + mex.ctx = opentracing.ContextWithSpan(mex.ctx, response.span) } - response.data.Target = response.data.Source - response.data.Annotations = response.annotationsBacking[:0] - response.data.BinaryAnnotations = response.binaryAnnotationsBacking[:] - response.AddAnnotationAt(AnnotationKeyServerReceive, now) response.mex = mex response.conn = c response.cancel = cancel - response.span = callReq.Tracing response.log = c.log.WithFields(LogField{"In-Response", callReq.ID()}) response.contents = newFragmentingWriter(response.log, response, initialFragment.checksumType.New()) response.headers = transportHeaders{} @@ -134,7 +121,6 @@ func (c *Connection) handleCallReq(frame *Frame) bool { call.initialFragment = initialFragment call.serviceName = string(callReq.Service) call.headers = callReq.Headers - call.span = callReq.Tracing call.response = response call.log = c.log.WithFields(LogField{"In-Call", callReq.ID()}) call.messageForFragment = func(initial bool) message { return new(callReqContinue) } @@ -186,9 +172,11 @@ func (c *Connection) dispatchInbound(_ uint32, _ uint32, call *InboundCall, fram return } - call.commonStatsTags["endpoint"] = string(call.method) + call.commonStatsTags["endpoint"] = call.methodString call.statsReporter.IncCounter("inbound.calls.recvd", call.commonStatsTags, 1) - call.response.SetMethod(string(call.method)) + if span := call.response.span; span != nil { + span.SetOperationName(call.methodString) + } // TODO(prashant): This is an expensive way to check for cancellation. Use a heap for timeouts. go func() { @@ -217,7 +205,6 @@ type InboundCall struct { method []byte methodString string headers transportHeaders - span Span statsReporter StatsReporter commonStatsTags map[string]string } @@ -314,16 +301,16 @@ func (call *InboundCall) doneReading(unexpected error) {} // An InboundCallResponse is used to send the response back to the calling peer type InboundCallResponse struct { reqResWriter - Annotations call *InboundCall cancel context.CancelFunc // calledAt is the time the inbound call was routed to the application. calledAt time.Time + timeNow func() time.Time applicationError bool systemError bool headers transportHeaders - span Span + span opentracing.Span statsReporter StatsReporter commonStatsTags map[string]string } @@ -341,10 +328,6 @@ func (response *InboundCallResponse) SendSystemError(err error) error { response.call.releasePreviousFragment() span := CurrentSpan(response.mex.ctx) - if span == nil { - response.log.Error("Missing span when sending system error") - span = &Span{} - } return response.conn.SendSystemError(response.mex.msgID, *span, err) } @@ -381,9 +364,14 @@ func (response *InboundCallResponse) Arg3Writer() (ArgWriter, error) { // For incoming calls, the last message is sending the call response. func (response *InboundCallResponse) doneSending() { // TODO(prashant): Move this to when the message is actually being sent. - now := response.GetTime() - response.AddAnnotationAt(AnnotationKeyServerSend, now) - response.Report() + now := response.timeNow() + + if span := response.span; span != nil { + if response.applicationError || response.systemError { + ext.Error.Set(span, true) + } + span.FinishWithOptions(opentracing.FinishOptions{FinishTime: now}) + } latency := now.Sub(response.calledAt) response.statsReporter.RecordTimer("inbound.calls.latency", response.commonStatsTags, latency) diff --git a/json/call.go b/json/call.go index 731bc37f..091af4f5 100644 --- a/json/call.go +++ b/json/call.go @@ -60,6 +60,9 @@ func NewClient(ch *tchannel.Channel, targetService string, opts *ClientOptions) } func makeCall(call *tchannel.OutboundCall, headers, arg3In, respHeaders, arg3Out, errorOut interface{}) (bool, string, error) { + if mapHeaders, ok := headers.(map[string]string); ok { + headers = tchannel.InjectOutboundSpan(call.Response(), mapHeaders) + } if err := tchannel.NewArgWriter(call.Arg2Writer()).WriteJSON(headers); err != nil { return false, "arg2 write failed", err } diff --git a/json/handler.go b/json/handler.go index ca0007f0..4354ce1e 100644 --- a/json/handler.go +++ b/json/handler.go @@ -26,6 +26,7 @@ import ( "github.com/uber/tchannel-go" + "github.com/opentracing/opentracing-go" "golang.org/x/net/context" ) @@ -77,6 +78,7 @@ type handler struct { handler reflect.Value argType reflect.Type isArgMap bool + tracer func() opentracing.Tracer } func toHandler(f interface{}) (*handler, error) { @@ -85,7 +87,7 @@ func toHandler(f interface{}) (*handler, error) { return nil, err } argType := hV.Type().In(1) - return &handler{hV, argType, argType.Kind() == reflect.Map}, nil + return &handler{handler: hV, argType: argType, isArgMap: argType.Kind() == reflect.Map}, nil } // Register registers the specified methods specified as a map from method name to the @@ -111,6 +113,7 @@ func Register(registrar tchannel.Registrar, funcs Handlers, onError func(context if err != nil { return fmt.Errorf("%v cannot be used as a handler: %v", m, err) } + h.tracer = registrar.Tracer handlers[m] = h registrar.Register(handler, m) } @@ -124,6 +127,7 @@ func (h *handler) Handle(tctx context.Context, call *tchannel.InboundCall) error if err := tchannel.NewArgReader(call.Arg2Reader()).ReadJSON(&headers); err != nil { return fmt.Errorf("arg2 read failed: %v", err) } + tctx = tchannel.ExtractInboundSpan(tctx, call, headers, h.tracer()) ctx := WithHeaders(tctx, headers) var arg3 reflect.Value diff --git a/json/tracing_test.go b/json/tracing_test.go new file mode 100644 index 00000000..9b8b55d2 --- /dev/null +++ b/json/tracing_test.go @@ -0,0 +1,68 @@ +package json_test + +import ( + "testing" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/json" + + . "github.com/uber/tchannel-go/testutils/testtracing" + "golang.org/x/net/context" +) + +// JSONHandler tests tracing over JSON encoding +type JSONHandler struct { + TraceHandler + t *testing.T +} + +func (h *JSONHandler) firstCall(ctx context.Context, req *TracingRequest) (*TracingResponse, error) { + jctx := json.Wrap(ctx) + response := new(TracingResponse) + peer := h.Ch.Peers().GetOrAdd(h.Ch.PeerInfo().HostPort) + if err := json.CallPeer(jctx, peer, h.Ch.PeerInfo().ServiceName, "call", req, response); err != nil { + return nil, err + } + return response, nil +} + +func (h *JSONHandler) callJSON(ctx json.Context, req *TracingRequest) (*TracingResponse, error) { + return h.HandleCall(ctx, req, + func(ctx context.Context, req *TracingRequest) (*TracingResponse, error) { + jctx := ctx.(json.Context) + peer := h.Ch.Peers().GetOrAdd(h.Ch.PeerInfo().HostPort) + childResp := new(TracingResponse) + if err := json.CallPeer(jctx, peer, h.Ch.PeerInfo().ServiceName, "call", req, childResp); err != nil { + return nil, err + } + return childResp, nil + }) +} + +func (h *JSONHandler) onError(ctx context.Context, err error) { h.t.Errorf("onError %v", err) } + +func TestJSONTracingPropagation(t *testing.T) { + suite := &PropagationTestSuite{ + Encoding: EncodingInfo{Format: tchannel.JSON, HeadersSupported: true}, + Register: func(t *testing.T, ch *tchannel.Channel) TracingCall { + handler := &JSONHandler{TraceHandler: TraceHandler{Ch: ch}, t: t} + json.Register(ch, json.Handlers{"call": handler.callJSON}, handler.onError) + return handler.firstCall + }, + TestCases: map[TracerType][]PropagationTestCase{ + Noop: { + {2, true, "", 0}, + {2, false, "", 0}, + }, + Mock: { + {2, true, BaggageValue, 0}, + {2, false, BaggageValue, 6}, + }, + Jaeger: { + {2, true, BaggageValue, 0}, + {2, false, BaggageValue, 6}, + }, + }, + } + suite.Run(t) +} diff --git a/outbound.go b/outbound.go index 93876337..346c8cb1 100644 --- a/outbound.go +++ b/outbound.go @@ -25,6 +25,9 @@ import ( "time" "github.com/uber/tchannel-go/typed" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" "golang.org/x/net/context" ) @@ -106,44 +109,14 @@ func (c *Connection) beginCall(ctx context.Context, serviceName, methodName stri } call.contents = newFragmentingWriter(call.log, call, c.checksumType.New()) - span := CurrentSpan(ctx) - if span != nil { - call.callReq.Tracing = *span.NewChildSpan() - } else { - // TODO(mmihic): Potentially reject calls that are made outside a root context? - call.callReq.Tracing.EnableTracing(false) - } - call.callReq.Tracing.sampleRootSpan(c.traceSampleRate) response := new(OutboundCallResponse) response.startedAt = now - response.Annotations = Annotations{ - reporter: c.traceReporter, - timeNow: c.timeNow, - data: TraceData{ - Span: call.callReq.Tracing, - Source: TraceEndpoint{ - HostPort: c.localPeerInfo.HostPort, - ServiceName: c.localPeerInfo.ServiceName, - }, - Target: TraceEndpoint{ - HostPort: c.remotePeerInfo.HostPort, - ServiceName: serviceName, - }, - Method: methodName, - }, - binaryAnnotationsBacking: [2]BinaryAnnotation{ - {Key: "cn", Value: call.callReq.Headers[CallerName]}, - {Key: "as", Value: call.callReq.Headers[ArgScheme]}, - }, - } - response.data.Annotations = response.annotationsBacking[:0] - response.data.BinaryAnnotations = response.binaryAnnotationsBacking[:] - response.AddAnnotationAt(AnnotationKeyClientSend, now) - + response.timeNow = c.timeNow response.requestState = callOptions.RequestState response.mex = mex response.log = c.log.WithFields(LogField{"Out-Response", requestID}) + response.span = c.startOutboundSpan(ctx, serviceName, methodName, call, now) response.messageForFragment = func(initial bool) message { if initial { return &response.callRes @@ -241,13 +214,14 @@ func (call *OutboundCall) doneSending() {} // An OutboundCallResponse is the response to an outbound call type OutboundCallResponse struct { reqResReader - Annotations callRes callRes requestState *RequestState // startedAt is the time at which the outbound call was started. startedAt time.Time + timeNow func() time.Time + span opentracing.Span statsReporter StatsReporter commonStatsTags map[string]string } @@ -332,13 +306,22 @@ func cloneTags(tags map[string]string) map[string]string { // doneReading shuts down the message exchange for this call. // For outgoing calls, the last message is reading the call response. func (response *OutboundCallResponse) doneReading(unexpected error) { - now := response.GetTime() - response.AddAnnotationAt(AnnotationKeyClientReceive, now) - response.Report() + now := response.timeNow() isSuccess := unexpected == nil && !response.ApplicationError() lastAttempt := isSuccess || !response.requestState.HasRetries(unexpected) + // TODO how should this work with retries? + if span := response.span; span != nil { + if unexpected != nil { + span.LogEventWithPayload("error", unexpected) + } + if !isSuccess && lastAttempt { + ext.Error.Set(span, true) + } + span.FinishWithOptions(opentracing.FinishOptions{FinishTime: now}) + } + latency := now.Sub(response.startedAt) response.statsReporter.RecordTimer("outbound.calls.per-attempt.latency", response.commonStatsTags, latency) if lastAttempt { diff --git a/subchannel.go b/subchannel.go index 08b2b906..1ab2d276 100644 --- a/subchannel.go +++ b/subchannel.go @@ -24,6 +24,7 @@ import ( "fmt" "sync" + "github.com/opentracing/opentracing-go" "golang.org/x/net/context" ) @@ -166,6 +167,11 @@ func (c *SubChannel) StatsTags() map[string]string { return tags } +// Tracer returns OpenTracing Tracer from the top channel. +func (c *SubChannel) Tracer() opentracing.Tracer { + return c.topChannel.Tracer() +} + // Register a new subchannel for the given serviceName func (subChMap *subChannelMap) registerNewSubChannel(serviceName string, ch *Channel) *SubChannel { subChMap.Lock() diff --git a/testutils/channel_opts.go b/testutils/channel_opts.go index 104b38d9..11015e4e 100644 --- a/testutils/channel_opts.go +++ b/testutils/channel_opts.go @@ -113,18 +113,6 @@ func (o *ChannelOpts) SetStatsReporter(statsReporter tchannel.StatsReporter) *Ch return o } -// SetTraceReporter sets TraceReporter in ChannelOptions. -func (o *ChannelOpts) SetTraceReporter(traceReporter tchannel.TraceReporter) *ChannelOpts { - o.TraceReporter = traceReporter - return o -} - -// SetTraceReporterFactory sets TraceReporterFactory in ChannelOptions. -func (o *ChannelOpts) SetTraceReporterFactory(factory tchannel.TraceReporterFactory) *ChannelOpts { - o.TraceReporterFactory = factory - return o -} - // SetFramePool sets FramePool in DefaultConnectionOptions. func (o *ChannelOpts) SetFramePool(framePool tchannel.FramePool) *ChannelOpts { o.DefaultConnectionOptions.FramePool = framePool @@ -137,12 +125,6 @@ func (o *ChannelOpts) SetTimeNow(timeNow func() time.Time) *ChannelOpts { return o } -// SetTraceSampleRate sets the TraceSampleRate in ChannelOptions. -func (o *ChannelOpts) SetTraceSampleRate(sampleRate float64) *ChannelOpts { - o.ChannelOptions.TraceSampleRate = &sampleRate - return o -} - // DisableLogVerification disables log verification for this channel. func (o *ChannelOpts) DisableLogVerification() *ChannelOpts { o.LogVerification.Disabled = true diff --git a/testutils/testtracing/propagation.go b/testutils/testtracing/propagation.go new file mode 100644 index 00000000..17837542 --- /dev/null +++ b/testutils/testtracing/propagation.go @@ -0,0 +1,313 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testtracing + +import ( + "fmt" + "testing" + "time" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/testutils" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/mocktracer" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber/jaeger-client-go" + "golang.org/x/net/context" +) + +const ( + // BaggageKey is used for testing baggage propagation + BaggageKey = "luggage" + + // BaggageValue is used for testing baggage propagation + BaggageValue = "suitcase" +) + +// TracingRequest tests tracing capabilities in a given server. +type TracingRequest struct { + // ForwardCount tells the server how many times to forward this request to itself recursively + ForwardCount int +} + +// TracingResponse captures the trace info observed in the server and its downstream calls +type TracingResponse struct { + TraceID uint64 + SpanID uint64 + ParentID uint64 + TracingEnabled bool + Child *TracingResponse + Luggage string +} + +// ObserveSpan extracts an OpenTracing span from the context and populates the response. +func (r *TracingResponse) ObserveSpan(ctx context.Context) *TracingResponse { + if span := opentracing.SpanFromContext(ctx); span != nil { + if mockSpan, ok := span.(*mocktracer.MockSpan); ok { + sc := mockSpan.Context().(*mocktracer.MockSpanContext) + r.TraceID = uint64(sc.TraceID) + r.SpanID = uint64(sc.SpanID) + r.ParentID = uint64(mockSpan.ParentID) + r.TracingEnabled = sc.Sampled + } else if span := tchannel.CurrentSpan(ctx); span != nil { + r.TraceID = span.TraceID() + r.SpanID = span.SpanID() + r.ParentID = span.ParentID() + r.TracingEnabled = span.Flags()&1 == 1 + } + r.Luggage = span.Context().BaggageItem(BaggageKey) + } + return r +} + +// TraceHandler is a base class for testing tracing propagation +type TraceHandler struct { + Ch *tchannel.Channel +} + +// HandleCall is used by handlers from different encodings as the main business logic. +// It respects the ForwardCount input parameter to make downstream calls, and returns +// a result containing the observed tracing span and the downstream results. +func (h *TraceHandler) HandleCall( + ctx context.Context, + req *TracingRequest, + downstream TracingCall, +) (*TracingResponse, error) { + var childResp *TracingResponse + if req.ForwardCount > 0 { + downstreamReq := &TracingRequest{ForwardCount: req.ForwardCount - 1} + if resp, err := downstream(ctx, downstreamReq); err == nil { + childResp = resp + } else { + return nil, err + } + } + + resp := &TracingResponse{Child: childResp} + resp.ObserveSpan(ctx) + + return resp, nil +} + +// TracerType is a convenient enum to indicate which type of tracer is being used in the test. +// It is a string because it's printed as part of the test description in the logs. +type TracerType string + +const ( + // Noop is for the default no-op tracer from OpenTracing + Noop TracerType = "NOOP" + // Mock tracer, baggage-capable, non-Zipkin trace IDs + Mock TracerType = "MOCK" + // Jaeger is Uber's tracer, baggage-capable, Zipkin-style trace IDs + Jaeger TracerType = "JAEGER" +) + +// TracingCall is used in a few other structs here +type TracingCall func(ctx context.Context, req *TracingRequest) (*TracingResponse, error) + +// EncodingInfo describes the encoding used with tracing propagation test +type EncodingInfo struct { + Format tchannel.Format + HeadersSupported bool +} + +// PropagationTestSuite is a collection of test cases for a certain encoding +type PropagationTestSuite struct { + Encoding EncodingInfo + Register func(t *testing.T, ch *tchannel.Channel) TracingCall + TestCases map[TracerType][]PropagationTestCase +} + +// PropagationTestCase describes a single propagation test case and expected results +type PropagationTestCase struct { + ForwardCount int + TracingDisabled bool + ExpectedBaggage string + ExpectedSpanCount int +} + +type tracerChoice struct { + tracerType TracerType + tracer opentracing.Tracer + spansRecorded func() int + resetSpans func() + isFake bool + zipkinCompatible bool +} + +// Run executes the test cases in the test suite against 3 different tracer implementations +func (s *PropagationTestSuite) Run(t *testing.T) { + tests := []struct { + name string + run func(t *testing.T) + }{ + {"Noop_Tracer", s.runWithNoopTracer}, + {"Mock_Tracer", s.runWithMockTracer}, + {"Jaeger_Tracer", s.runWithJaegerTracer}, + } + for _, test := range tests { + t.Logf("Running with %s", test.name) + test.run(t) + } +} + +func (s *PropagationTestSuite) runWithNoopTracer(t *testing.T) { + s.runWithTracer(t, tracerChoice{ + tracer: nil, // will cause opentracing.GlobalTracer() to be used + tracerType: Noop, + spansRecorded: func() int { return 0 }, + resetSpans: func() {}, + isFake: true, + }) +} + +func (s *PropagationTestSuite) runWithMockTracer(t *testing.T) { + mockTracer := mocktracer.New() + s.runWithTracer(t, tracerChoice{ + tracerType: Mock, + tracer: mockTracer, + spansRecorded: func() int { + return len(MockTracerSampledSpans(mockTracer)) + }, + resetSpans: func() { + mockTracer.Reset() + }, + }) +} + +func (s *PropagationTestSuite) runWithJaegerTracer(t *testing.T) { + jaegerReporter := jaeger.NewInMemoryReporter() + jaegerTracer, jaegerCloser := jaeger.NewTracer(testutils.DefaultServerName, + jaeger.NewConstSampler(true), + jaegerReporter) + // To enable logging, use composite reporter: + // jaeger.NewCompositeReporter(jaegerReporter, jaeger.NewLoggingReporter(jaeger.StdLogger))) + defer jaegerCloser.Close() + s.runWithTracer(t, tracerChoice{ + tracerType: Jaeger, + tracer: jaegerTracer, + spansRecorded: func() int { + return len(jaegerReporter.GetSpans()) + }, + resetSpans: func() { + jaegerReporter.Reset() + }, + zipkinCompatible: true, + }) +} + +func (s *PropagationTestSuite) runWithTracer(t *testing.T, tracer tracerChoice) { + testCases, ok := s.TestCases[tracer.tracerType] + if !ok { + t.Logf("No test cases for encoding=%s and tracer=%s", s.Encoding.Format, tracer.tracerType) + return + } + opts := &testutils.ChannelOpts{ + ChannelOptions: tchannel.ChannelOptions{Tracer: tracer.tracer}, + DisableRelay: true, + } + ch := testutils.NewServer(t, opts) + defer ch.Close() + ch.Peers().Add(ch.PeerInfo().HostPort) + call := s.Register(t, ch) + for _, tt := range testCases { + s.runTestCase(t, tracer, ch, tt, call) + } +} + +func (s *PropagationTestSuite) runTestCase( + t *testing.T, + tracer tracerChoice, + ch *tchannel.Channel, + test PropagationTestCase, + call TracingCall, +) { + descr := fmt.Sprintf("test %+v with tracer %+v", test, tracer) + ch.Logger().Debugf("Starting tracing test %s", descr) + + tracer.resetSpans() + + span := ch.Tracer().StartSpan("client") + span.Context().SetBaggageItem(BaggageKey, BaggageValue) + ctx := opentracing.ContextWithSpan(context.Background(), span) + + ctxBuilder := tchannel.NewContextBuilder(5 * time.Second).SetParentContext(ctx) + if test.TracingDisabled { + ctxBuilder.DisableTracing() + } + ctx, cancel := ctxBuilder.Build() + defer cancel() + + req := &TracingRequest{ForwardCount: test.ForwardCount} + ch.Logger().Infof("Sending tracing request %+v", req) + response, err := call(ctx, req) + require.NoError(t, err) + ch.Logger().Infof("Received tracing response %+v", response) + + // Spans are finished in inbound.doneSending() or outbound.doneReading(), + // which are called on different go-routines and may execute *after* the + // response has been received by the client. Give them a chance to run. + for i := 0; i < 1000; i++ { + if spanCount := tracer.spansRecorded(); spanCount == test.ExpectedSpanCount { + break + } + time.Sleep(testutils.Timeout(time.Millisecond)) + } + spanCount := tracer.spansRecorded() + ch.Logger().Debugf("end span count: %d", spanCount) + + // finish span after taking count of recorded spans, as we're only interested + // in the count of spans created by RPC calls. + span.Finish() + + root := new(TracingResponse).ObserveSpan(ctx) + + if !tracer.isFake { + assert.Equal(t, uint64(0), root.ParentID) + assert.NotEqual(t, uint64(0), root.TraceID) + } + + assert.Equal(t, test.ExpectedSpanCount, spanCount, "Wrong span count; %s", descr) + + for r, cnt := response, 0; r != nil || cnt <= test.ForwardCount; r, cnt = r.Child, cnt+1 { + require.NotNil(t, r, "Expecting response for forward=%d; %s", cnt, descr) + if !tracer.isFake { + if tracer.zipkinCompatible || s.Encoding.HeadersSupported { + assert.Equal(t, root.TraceID, r.TraceID, "traceID should be the same; %s", descr) + } + assert.Equal(t, test.ExpectedBaggage, r.Luggage, "baggage should propagate; %s", descr) + } + } + ch.Logger().Debugf("Finished tracing test %s", descr) +} + +// MockTracerSampledSpans is a helper function that returns only sampled spans from MockTracer +func MockTracerSampledSpans(tracer *mocktracer.MockTracer) []*mocktracer.MockSpan { + var spans []*mocktracer.MockSpan + for _, span := range tracer.FinishedSpans() { + if span.Context().(*mocktracer.MockSpanContext).Sampled { + spans = append(spans, span) + } + } + return spans +} diff --git a/testutils/testtracing/propagation_test.go b/testutils/testtracing/propagation_test.go new file mode 100644 index 00000000..deb7dd42 --- /dev/null +++ b/testutils/testtracing/propagation_test.go @@ -0,0 +1,130 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testtracing + +import ( + json_encoding "encoding/json" + "testing" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/raw" + + "golang.org/x/net/context" +) + +func requestFromRaw(args *raw.Args) *TracingRequest { + r := new(TracingRequest) + r.ForwardCount = int(args.Arg3[0]) + return r +} + +func requestToRaw(r *TracingRequest) []byte { + return []byte{byte(r.ForwardCount)} +} + +func responseFromRaw(t *testing.T, arg3 []byte) (*TracingResponse, error) { + var r TracingResponse + err := json_encoding.Unmarshal(arg3, &r) + if err != nil { + return nil, err + } + return &r, nil +} + +func responseToRaw(t *testing.T, r *TracingResponse) (*raw.Res, error) { + jsonBytes, err := json_encoding.Marshal(r) + if err != nil { + return nil, err + } + return &raw.Res{Arg3: jsonBytes}, nil +} + +// RawHandler tests tracing over Raw encoding +type RawHandler struct { + TraceHandler + t *testing.T +} + +func (h *RawHandler) Handle(ctx context.Context, args *raw.Args) (*raw.Res, error) { + req := requestFromRaw(args) + res, err := h.HandleCall(ctx, req, + func(ctx context.Context, req *TracingRequest) (*TracingResponse, error) { + _, arg3, _, err := raw.Call(ctx, h.Ch, h.Ch.PeerInfo().HostPort, + h.Ch.PeerInfo().ServiceName, "rawcall", nil, requestToRaw(req)) + if err != nil { + return nil, err + } + return responseFromRaw(h.t, arg3) + }) + if err != nil { + return nil, err + } + return responseToRaw(h.t, res) +} + +func (h *RawHandler) OnError(ctx context.Context, err error) { h.t.Errorf("onError %v", err) } + +func (h *RawHandler) firstCall(ctx context.Context, req *TracingRequest) (*TracingResponse, error) { + _, arg3, _, err := raw.Call(ctx, h.Ch, h.Ch.PeerInfo().HostPort, h.Ch.PeerInfo().ServiceName, + "rawcall", nil, requestToRaw(req)) + if err != nil { + return nil, err + } + return responseFromRaw(h.t, arg3) +} + +func TestRawTracingPropagation(t *testing.T) { + suite := &PropagationTestSuite{ + Encoding: EncodingInfo{Format: tchannel.Raw, HeadersSupported: false}, + Register: func(t *testing.T, ch *tchannel.Channel) TracingCall { + handler := &RawHandler{ + TraceHandler: TraceHandler{Ch: ch}, + t: t, + } + ch.Register(raw.Wrap(handler), "rawcall") + return handler.firstCall + }, + // Since Raw encoding does not support headers, there is no baggage propagation + TestCases: map[TracerType][]PropagationTestCase{ + Noop: { + {2, true, "", 0}, + {2, false, "", 0}, + }, + Mock: { + // Since Raw encoding does not propagate generic traces, the tracingDisable + // only affects the first outbound span (it's not sampled), but the other + // two outbound spans are still sampled and recorded. + {2, true, "", 2}, + // Since Raw encoding does not propagate generic traces, we record 3 spans + // for outbound calls, but none for inbound calls. + {2, false, "", 3}, + }, + Jaeger: { + // Since Jaeger is Zipkin-compatible, it is able to keep track of tracingDisabled + {2, true, "", 0}, + // Since Jaeger is Zipkin-compatible, it is able to decode the trace + // even from the Raw encoding. + {2, false, "", 6}, + }, + }, + } + suite.Run(t) +} diff --git a/thrift/client.go b/thrift/client.go index 7633a7f9..e07d2ea6 100644 --- a/thrift/client.go +++ b/thrift/client.go @@ -66,6 +66,7 @@ func writeArgs(call *tchannel.OutboundCall, headers map[string]string, req thrif if err != nil { return err } + headers = tchannel.InjectOutboundSpan(call.Response(), headers) if err := WriteHeaders(writer, headers); err != nil { return err } diff --git a/thrift/server.go b/thrift/server.go index 789dce63..7db063b1 100644 --- a/thrift/server.go +++ b/thrift/server.go @@ -125,6 +125,7 @@ func (s *Server) handle(origCtx context.Context, handler handler, method string, return err } + origCtx = tchannel.ExtractInboundSpan(origCtx, call, headers, s.ch.Tracer()) ctx := s.ctxFn(origCtx, method, headers) wp := getProtocolReader(reader) diff --git a/thrift/tracing_test.go b/thrift/tracing_test.go new file mode 100644 index 00000000..03a789b6 --- /dev/null +++ b/thrift/tracing_test.go @@ -0,0 +1,112 @@ +package thrift_test + +import ( + json_encoding "encoding/json" + "testing" + + "github.com/uber/tchannel-go" + . "github.com/uber/tchannel-go/testutils/testtracing" + "github.com/uber/tchannel-go/thrift" + gen "github.com/uber/tchannel-go/thrift/gen-go/test" + + "golang.org/x/net/context" +) + +// ThriftHandler tests tracing over Thrift encoding +type ThriftHandler struct { + TraceHandler + thriftClient gen.TChanSimpleService + t *testing.T +} + +func requestFromThrift(req *gen.Data) *TracingRequest { + r := new(TracingRequest) + r.ForwardCount = int(req.I3) + return r +} + +func requestToThrift(r *TracingRequest) *gen.Data { + return &gen.Data{I3: int32(r.ForwardCount)} +} + +func responseFromThrift(t *testing.T, res *gen.Data) (*TracingResponse, error) { + var r TracingResponse + if err := json_encoding.Unmarshal([]byte(res.S2), &r); err != nil { + return nil, err + } + return &r, nil +} + +func responseToThrift(t *testing.T, r *TracingResponse) (*gen.Data, error) { + jsonBytes, err := json_encoding.Marshal(r) + if err != nil { + return nil, err + } + return &gen.Data{S2: string(jsonBytes)}, nil +} + +func (h *ThriftHandler) Call(ctx thrift.Context, arg *gen.Data) (*gen.Data, error) { + req := requestFromThrift(arg) + res, err := h.HandleCall(ctx, req, + func(ctx context.Context, req *TracingRequest) (*TracingResponse, error) { + tctx := ctx.(thrift.Context) + res, err := h.thriftClient.Call(tctx, requestToThrift(req)) + if err != nil { + return nil, err + } + return responseFromThrift(h.t, res) + }) + if err != nil { + return nil, err + } + return responseToThrift(h.t, res) +} + +func (h *ThriftHandler) Simple(ctx thrift.Context) error { + return nil +} + +func (h *ThriftHandler) firstCall(ctx context.Context, req *TracingRequest) (*TracingResponse, error) { + tctx := thrift.Wrap(ctx) + res, err := h.thriftClient.Call(tctx, requestToThrift(req)) + if err != nil { + return nil, err + } + return responseFromThrift(h.t, res) +} + +func TestThriftTracingPropagation(t *testing.T) { + suite := &PropagationTestSuite{ + Encoding: EncodingInfo{Format: tchannel.Thrift, HeadersSupported: true}, + Register: func(t *testing.T, ch *tchannel.Channel) TracingCall { + opts := &thrift.ClientOptions{HostPort: ch.PeerInfo().HostPort} + thriftClient := thrift.NewClient(ch, ch.PeerInfo().ServiceName, opts) + handler := &ThriftHandler{ + TraceHandler: TraceHandler{Ch: ch}, + t: t, + thriftClient: gen.NewTChanSimpleServiceClient(thriftClient), + } + + // Register Thrift handler + server := thrift.NewServer(ch) + server.Register(gen.NewTChanSimpleServiceServer(handler)) + + return handler.firstCall + }, + TestCases: map[TracerType][]PropagationTestCase{ + Noop: { + {2, true, "", 0}, + {2, false, "", 0}, + }, + Mock: { + {2, true, BaggageValue, 0}, + {2, false, BaggageValue, 6}, + }, + Jaeger: { + {2, true, BaggageValue, 0}, + {2, false, BaggageValue, 6}, + }, + }, + } + suite.Run(t) +} diff --git a/trace/reporter.go b/trace/reporter.go deleted file mode 100644 index 2e27a63e..00000000 --- a/trace/reporter.go +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright (c) 2015 Uber Technologies, Inc. - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -// Package trace provides methods to submit Zipkin style spans to TCollector. -package trace - -import ( - "encoding/base64" - "encoding/binary" - "fmt" - "net" - "strconv" - "time" - - tc "github.com/uber/tchannel-go" - "github.com/uber/tchannel-go/thrift" - "github.com/uber/tchannel-go/trace/thrift/gen-go/tcollector" -) - -const ( - tcollectorServiceName = "tcollector" - chanBufferSize = 100 -) - -// TCollectorReporter is a trace reporter that submits trace spans to TCollector. -type TCollectorReporter struct { - tchannel *tc.Channel - client tcollector.TChanTCollector - curHostIP uint32 - c chan tc.TraceData - logger tc.Logger -} - -// NewTCollectorReporter return trace reporter that submits span to TCollector. -func NewTCollectorReporter(ch *tc.Channel) *TCollectorReporter { - thriftClient := thrift.NewClient(ch, tcollectorServiceName, nil) - client := tcollector.NewTChanTCollectorClient(thriftClient) - - curHostIP, err := tc.ListenIP() - if err != nil { - ch.Logger().WithFields(tc.ErrField(err)).Warn("TCollector TraceReporter failed to get IP.") - curHostIP = net.IPv4(0, 0, 0, 0) - } - - // create the goroutine method to actually to the submit Span. - reporter := &TCollectorReporter{ - tchannel: ch, - client: client, - c: make(chan tc.TraceData, chanBufferSize), - logger: ch.Logger(), - curHostIP: inetAton(curHostIP.String()), - } - go reporter.worker() - return reporter -} - -// Report method will submit trace span to tcollector server. -func (r *TCollectorReporter) Report(data tc.TraceData) { - select { - case r.c <- data: - default: - r.logger.Infof("TCollectorReporter: buffer channel for trace is full") - } -} - -func (r *TCollectorReporter) report(data *tc.TraceData) error { - ctx, cancel := tc.NewContextBuilder(time.Second). - DisableTracing(). - SetRetryOptions(&tc.RetryOptions{RetryOn: tc.RetryNever}). - SetShardKey(base64Encode(data.Span.TraceID())).Build() - defer cancel() - - thriftSpan, err := buildSpan(data, r.curHostIP) - if err != nil { - return err - } - // client submit - _, err = r.client.Submit(ctx, thriftSpan) - return err -} - -func (r *TCollectorReporter) worker() { - for data := range r.c { - if err := r.report(&data); err != nil { - r.logger.Infof("Span report failed: %v", err) - } - } -} - -func buildEndpoint(ep tc.TraceEndpoint) (*tcollector.Endpoint, error) { - host, portStr, err := net.SplitHostPort(ep.HostPort) - if err != nil { - return nil, err - } - - port, err := strconv.Atoi(portStr) - if err != nil { - return nil, err - } - - return &tcollector.Endpoint{ - Ipv4: int32(inetAton(host)), - Port: int32(port), - ServiceName: ep.ServiceName, - }, nil -} - -// buildSpan builds a tcollector span based on tchannel span. -func buildSpan(data *tc.TraceData, curHostIP uint32) (*tcollector.Span, error) { - source, err := buildEndpoint(data.Source) - if err != nil { - return nil, err - } - if source.Ipv4 == 0 { - source.Ipv4 = int32(curHostIP) - } - - target, err := buildEndpoint(data.Target) - if err != nil { - return nil, err - } - - binaryAnns, err := buildBinaryAnnotations(data.BinaryAnnotations) - if err != nil { - return nil, err - } - thriftSpan := tcollector.Span{ - TraceId: uint64ToBytes(data.Span.TraceID()), - SpanHost: source, - Host: target, - Name: data.Method, - ID: uint64ToBytes(data.Span.SpanID()), - ParentId: uint64ToBytes(data.Span.ParentID()), - Annotations: buildAnnotations(data.Annotations), - BinaryAnnotations: binaryAnns, - } - - return &thriftSpan, nil -} - -func buildBinaryAnnotation(ann tc.BinaryAnnotation) (*tcollector.BinaryAnnotation, error) { - bann := &tcollector.BinaryAnnotation{Key: ann.Key} - switch v := ann.Value.(type) { - case bool: - bann.AnnotationType = tcollector.AnnotationType_BOOL - bann.BoolValue = &v - case int64: - bann.AnnotationType = tcollector.AnnotationType_I64 - temp := v - bann.IntValue = &temp - case int32: - bann.AnnotationType = tcollector.AnnotationType_I32 - temp := int64(v) - bann.IntValue = &temp - case int16: - bann.AnnotationType = tcollector.AnnotationType_I16 - temp := int64(v) - bann.IntValue = &temp - case int: - bann.AnnotationType = tcollector.AnnotationType_I32 - temp := int64(v) - bann.IntValue = &temp - case string: - bann.AnnotationType = tcollector.AnnotationType_STRING - bann.StringValue = &v - case []byte: - bann.AnnotationType = tcollector.AnnotationType_BYTES - bann.BytesValue = v - case float32: - bann.AnnotationType = tcollector.AnnotationType_DOUBLE - temp := float64(v) - bann.DoubleValue = &temp - case float64: - bann.AnnotationType = tcollector.AnnotationType_DOUBLE - temp := float64(v) - bann.DoubleValue = &temp - default: - return nil, fmt.Errorf("unrecognized data type: %T", v) - } - return bann, nil -} - -func buildBinaryAnnotations(anns []tc.BinaryAnnotation) ([]*tcollector.BinaryAnnotation, error) { - binaryAnns := make([]*tcollector.BinaryAnnotation, len(anns)) - for i, ann := range anns { - b, err := buildBinaryAnnotation(ann) - binaryAnns[i] = b - if err != nil { - return nil, err - } - } - return binaryAnns, nil -} - -func buildAnnotations(anns []tc.Annotation) []*tcollector.Annotation { - tAnns := make([]*tcollector.Annotation, len(anns)) - for i, ann := range anns { - tAnns[i] = &tcollector.Annotation{ - Timestamp: float64(ann.Timestamp.UnixNano() / 1e6), - Value: string(ann.Key), - } - } - return tAnns -} - -// inetAton converts string Ipv4 to uint32 -func inetAton(ip string) uint32 { - ipBytes := net.ParseIP(ip).To4() - return binary.BigEndian.Uint32(ipBytes) -} - -// base64Encode encodes uint64 with base64 StdEncoding. -func base64Encode(data uint64) string { - return base64.StdEncoding.EncodeToString(uint64ToBytes(data)) -} - -// uint64ToBytes converts uint64 to bytes. -func uint64ToBytes(i uint64) []byte { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, uint64(i)) - return buf -} - -// TCollectorReporterFactory builds TCollectorReporter using a given TChannel instance. -func TCollectorReporterFactory(tchannel *tc.Channel) tc.TraceReporter { - return NewTCollectorReporter(tchannel) -} diff --git a/trace/reporter_test.go b/trace/reporter_test.go deleted file mode 100644 index d7218a6f..00000000 --- a/trace/reporter_test.go +++ /dev/null @@ -1,450 +0,0 @@ -// Copyright (c) 2015 Uber Technologies, Inc. - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package trace - -import ( - "testing" - "time" - - "github.com/uber/tchannel-go" - "github.com/uber/tchannel-go/json" - "github.com/uber/tchannel-go/testutils" - "github.com/uber/tchannel-go/thrift" - gen "github.com/uber/tchannel-go/trace/thrift/gen-go/tcollector" - "github.com/uber/tchannel-go/trace/thrift/mocks" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" -) - -func TestTraceReporterFactory(t *testing.T) { - opts := testutils.NewOpts(). - SetServiceName("svc"). - SetTraceReporterFactory(TCollectorReporterFactory) - ch := testutils.NewClient(t, opts) - - // Create a TCollector channel, and add it as a peer to ch so Report works. - mockServer := new(mocks.TChanTCollector) - tcollectorCh := setupServer(t, mockServer) - ch.Peers().Add(tcollectorCh.PeerInfo().HostPort) - - called := make(chan int) - ret := &gen.Response{Ok: true} - mockServer.On("Submit", mock.Anything, mock.Anything).Return(ret, nil).Run(func(args mock.Arguments) { - called <- 1 - }) - - // Make some calls, and validate that the trace reporter is not called recursively. - require.NoError(t, json.Register(tcollectorCh, json.Handlers{"op": func(ctx json.Context, arg map[string]interface{}) (map[string]interface{}, error) { - return arg, nil - }}, nil), "Register failed") - ctx, cancel := json.NewContext(time.Second) - defer cancel() - for i := 0; i < 5; i++ { - var res map[string]string - assert.NoError(t, json.CallSC(ctx, ch.GetSubChannel(tcollectorServiceName), "op", nil, &res), "call failed") - } - - // Verify the spans being reported. - for i := 0; i < 5; i++ { - select { - case <-called: - case <-time.After(time.Second): - t.Errorf("Expected submit for call %v", i) - } - } - - // Verify that no other spans are reported. - select { - case <-called: - t.Errorf("Too many spans reported") - case <-time.After(time.Millisecond): - } -} - -func TestBuildSpan(t *testing.T) { - data := &tchannel.TraceData{ - Span: *tchannel.NewRootSpan(), - BinaryAnnotations: []tchannel.BinaryAnnotation{{Key: "cn", Value: "string"}}, - Source: tchannel.TraceEndpoint{ - HostPort: "0.0.0.0:0", - ServiceName: "testServer", - }, - Target: tchannel.TraceEndpoint{ - HostPort: "127.0.0.1:9999", - ServiceName: "targetServer", - }, - Method: "test", - } - _, data.Annotations = RandomAnnotations() - - thriftSpan, err := buildSpan(data, 12345) - assert.NoError(t, err) - binaryAnns, err := buildBinaryAnnotations(data.BinaryAnnotations) - assert.NoError(t, err) - expectedSpan := &gen.Span{ - TraceId: uint64ToBytes(data.Span.TraceID()), - SpanHost: &gen.Endpoint{ - Ipv4: 12345, - Port: 0, - ServiceName: "testServer", - }, - Host: &gen.Endpoint{ - Ipv4: (int32)(inetAton("127.0.0.1")), - Port: 9999, - ServiceName: "targetServer", - }, - Name: "test", - ID: uint64ToBytes(data.Span.SpanID()), - ParentId: uint64ToBytes(data.Span.ParentID()), - Annotations: buildAnnotations(data.Annotations), - BinaryAnnotations: binaryAnns, - } - - assert.Equal(t, thriftSpan, expectedSpan, "Span mismatch") -} - -func TestInetAton(t *testing.T) { - assert.Equal(t, inetAton("1.2.3.4"), uint32(16909060)) -} - -func TestUInt64ToBytes(t *testing.T) { - assert.Equal(t, uint64ToBytes(54613478251749257), []byte("\x00\xc2\x06\xabK$\xdf\x89")) -} - -func TestBase64Encode(t *testing.T) { - assert.Equal(t, base64Encode(12711515087145684), "AC0pDj1TitQ=") -} - -func TestBuildAnnotations(t *testing.T) { - baseTime, testAnnotations := RandomAnnotations() - baseTimeMillis := float64(1420167845000) - testExpected := []*gen.Annotation{ - { - Timestamp: baseTimeMillis + 1000, - Value: "cr", - }, - { - Timestamp: baseTimeMillis + 2000.0, - Value: "cs", - }, - { - Timestamp: baseTimeMillis + 3000, - Value: "sr", - }, - { - Timestamp: baseTimeMillis + 4000, - Value: "ss", - }, - } - - makeTCAnnotations := func(ts time.Time) []tchannel.Annotation { - return []tchannel.Annotation{{ - Key: tchannel.AnnotationKeyClientReceive, - Timestamp: ts, - }} - } - makeGenAnnotations := func(ts float64) []*gen.Annotation { - return []*gen.Annotation{{ - Value: "cr", - Timestamp: ts, - }} - } - - tests := []struct { - annotations []tchannel.Annotation - expected []*gen.Annotation - }{ - { - annotations: nil, - expected: []*gen.Annotation{}, - }, - { - annotations: makeTCAnnotations(baseTime.Add(time.Nanosecond)), - expected: makeGenAnnotations(baseTimeMillis), - }, - { - annotations: makeTCAnnotations(baseTime.Add(time.Microsecond)), - expected: makeGenAnnotations(baseTimeMillis), - }, - { - annotations: makeTCAnnotations(baseTime.Add(time.Millisecond)), - expected: makeGenAnnotations(baseTimeMillis + 1), - }, - { - annotations: testAnnotations, - expected: testExpected, - }, - } - - for _, tt := range tests { - got := buildAnnotations(tt.annotations) - assert.Equal(t, tt.expected, got, "result spans mismatch") - } -} - -func RandomAnnotations() (time.Time, []tchannel.Annotation) { - baseTime := time.Date(2015, 1, 2, 3, 4, 5, 6, time.UTC) - return baseTime, []tchannel.Annotation{ - { - Key: tchannel.AnnotationKeyClientReceive, - Timestamp: baseTime.Add(time.Second), - }, - { - Key: tchannel.AnnotationKeyClientSend, - Timestamp: baseTime.Add(2 * time.Second), - }, - { - Key: tchannel.AnnotationKeyServerReceive, - Timestamp: baseTime.Add(3 * time.Second), - }, - { - Key: tchannel.AnnotationKeyServerSend, - Timestamp: baseTime.Add(4 * time.Second), - }, - } -} - -type testArgs struct { - s *mocks.TChanTCollector - c tchannel.TraceReporter -} - -func ctxArg() mock.AnythingOfTypeArgument { - return mock.AnythingOfType("tchannel.headerCtx") -} - -func TestSubmit(t *testing.T) { - withSetup(t, func(ctx thrift.Context, args testArgs) { - data, expected := submitArgs(t) - called := make(chan struct{}) - ret := &gen.Response{Ok: true} - args.s.On("Submit", ctxArg(), expected).Return(ret, nil).Run(func(_ mock.Arguments) { - close(called) - }) - args.c.Report(*data) - - // wait for the server's Submit to get called - select { - case <-time.After(time.Second): - t.Fatal("Submit not called") - case <-called: - } - }) -} - -func submitArgs(t testing.TB) (*tchannel.TraceData, *gen.Span) { - data := &tchannel.TraceData{ - Span: *tchannel.NewRootSpan(), - BinaryAnnotations: RandomBinaryAnnotations(), - Method: "echo", - Source: tchannel.TraceEndpoint{ - HostPort: "127.0.0.1:8888", - ServiceName: "testServer", - }, - Target: tchannel.TraceEndpoint{ - HostPort: "127.0.0.1:9999", - ServiceName: "targetServer", - }, - } - _, data.Annotations = RandomAnnotations() - - genSpan, err := buildSpan(data, 0) - require.NoError(t, err, "Build test span failed") - - return data, genSpan -} - -func TestSubmitNotRetried(t *testing.T) { - withSetup(t, func(ctx thrift.Context, args testArgs) { - data, expected := submitArgs(t) - count := 0 - args.s.On("Submit", ctxArg(), expected).Return(nil, tchannel.ErrServerBusy).Run(func(_ mock.Arguments) { - count++ - }) - args.c.Report(*data) - - // Report another span that we use to detect that the previous span has been processed. - data, expected = submitArgs(t) - completed := make(chan struct{}) - args.s.On("Submit", ctxArg(), expected).Return(nil, nil).Run(func(_ mock.Arguments) { - close(completed) - }) - args.c.Report(*data) - - // wait for the server's Submit to get called - select { - case <-time.After(time.Second): - t.Fatal("Submit not called") - case <-completed: - } - - // Verify that the initial span was not retried multiple times. - assert.Equal(t, 1, count, "tcollector Submit should not be retried") - }) -} - -func withSetup(t *testing.T, f func(ctx thrift.Context, args testArgs)) { - args := testArgs{ - s: new(mocks.TChanTCollector), - } - - ctx, cancel := thrift.NewContext(time.Second * 10) - defer cancel() - - // Start server - tchan := setupServer(t, args.s) - defer tchan.Close() - - // Get client1 - args.c = getClient(t, tchan.PeerInfo().HostPort) - - f(ctx, args) - - args.s.AssertExpectations(t) -} - -func setupServer(t *testing.T, h *mocks.TChanTCollector) *tchannel.Channel { - tchan := testutils.NewServer(t, testutils.NewOpts().SetServiceName(tcollectorServiceName)) - server := thrift.NewServer(tchan) - server.Register(gen.NewTChanTCollectorServer(h)) - return tchan -} - -func getClient(t *testing.T, dst string) tchannel.TraceReporter { - tchan := testutils.NewClient(t, nil) - tchan.Peers().Add(dst) - return NewTCollectorReporter(tchan) -} - -func BenchmarkBuildThrift(b *testing.B) { - data, _ := submitArgs(b) - b.ResetTimer() - for i := 0; i < b.N; i++ { - buildSpan(data, 0) - } -} - -type BinaryAnnotationTestArgs struct { - annotation tchannel.BinaryAnnotation - expected *gen.BinaryAnnotation -} - -func generateBinaryAnnotationsTestCase() []BinaryAnnotationTestArgs { - s := "testString" - ii64 := int64(5) - _ii64 := int64(5) - ii32 := int32(6) - _ii32 := int64(6) - ii16 := int16(7) - _ii16 := int64(7) - i := 8 - _i := int64(8) - b := false - f32 := float32(5.0) - _f32 := float64(5.0) - f64 := float64(6.0) - _f64 := float64(6.0) - bs := []byte{4, 3, 2} - return []BinaryAnnotationTestArgs{ - { - annotation: tchannel.BinaryAnnotation{Key: "string", Value: s}, - expected: &gen.BinaryAnnotation{Key: "string", StringValue: &s, AnnotationType: gen.AnnotationType_STRING}, - }, - { - annotation: tchannel.BinaryAnnotation{Key: "int", Value: i}, - expected: &gen.BinaryAnnotation{Key: "int", IntValue: &_i, AnnotationType: gen.AnnotationType_I32}, - }, - { - annotation: tchannel.BinaryAnnotation{Key: "int16", Value: ii16}, - expected: &gen.BinaryAnnotation{Key: "int16", IntValue: &_ii16, AnnotationType: gen.AnnotationType_I16}, - }, - { - annotation: tchannel.BinaryAnnotation{Key: "int32", Value: ii32}, - expected: &gen.BinaryAnnotation{Key: "int32", IntValue: &_ii32, AnnotationType: gen.AnnotationType_I32}, - }, - { - annotation: tchannel.BinaryAnnotation{Key: "int64", Value: ii64}, - expected: &gen.BinaryAnnotation{Key: "int64", IntValue: &_ii64, AnnotationType: gen.AnnotationType_I64}, - }, - { - annotation: tchannel.BinaryAnnotation{Key: "bool", Value: b}, - expected: &gen.BinaryAnnotation{Key: "bool", BoolValue: &b, AnnotationType: gen.AnnotationType_BOOL}, - }, - { - annotation: tchannel.BinaryAnnotation{Key: "float32", Value: f32}, - expected: &gen.BinaryAnnotation{Key: "float32", DoubleValue: &_f32, AnnotationType: gen.AnnotationType_DOUBLE}, - }, - { - annotation: tchannel.BinaryAnnotation{Key: "float64", Value: f64}, - expected: &gen.BinaryAnnotation{Key: "float64", DoubleValue: &_f64, AnnotationType: gen.AnnotationType_DOUBLE}, - }, - { - annotation: tchannel.BinaryAnnotation{Key: "bytes", Value: bs}, - expected: &gen.BinaryAnnotation{Key: "bytes", BytesValue: bs, AnnotationType: gen.AnnotationType_BYTES}, - }, - } -} - -func RandomBinaryAnnotations() []tchannel.BinaryAnnotation { - args := generateBinaryAnnotationsTestCase() - bAnns := make([]tchannel.BinaryAnnotation, len(args)) - for i, arg := range args { - bAnns[i] = arg.annotation - } - return bAnns -} - -func TestBuildBinaryAnnotation(t *testing.T) { - tests := generateBinaryAnnotationsTestCase() - for _, tt := range tests { - result, err := buildBinaryAnnotation(tt.annotation) - assert.NoError(t, err, "Failed to build binary annotations.") - assert.Equal(t, tt.expected, result, "BinaryAnnotation is mismatched.") - } -} - -func TestBuildBinaryAnnotationsWithEmptyList(t *testing.T) { - result, err := buildBinaryAnnotations(nil) - assert.NoError(t, err, "Failed to build binary annotations.") - assert.Equal(t, len(result), 0, "BinaryAnnotations should be empty.") -} - -func TestBuildBinaryAnnotationsWithMultiItems(t *testing.T) { - tests := generateBinaryAnnotationsTestCase() - var binaryAnns []tchannel.BinaryAnnotation - var expectedAnns []*gen.BinaryAnnotation - for _, tt := range tests { - binaryAnns = append(binaryAnns, tt.annotation) - expectedAnns = append(expectedAnns, tt.expected) - } - result, err := buildBinaryAnnotations(binaryAnns) - assert.NoError(t, err, "Failed to build binary annotations.") - assert.Equal(t, expectedAnns, result, "BinaryAnnotation is mismatched.") -} - -func TestBuildBinaryAnnotationsWithError(t *testing.T) { - _, err := buildBinaryAnnotations( - []tchannel.BinaryAnnotation{{Key: "app", Value: []bool{false}}}, - ) - assert.Error(t, err, "An Error was expected.") -} diff --git a/trace/tcollector.thrift b/trace/tcollector.thrift deleted file mode 100644 index fb1b87b2..00000000 --- a/trace/tcollector.thrift +++ /dev/null @@ -1,73 +0,0 @@ -// One span refers to one RPC call. The `host` field, which is of type -// `Endpoint`, will be the server being hit by the call. - -struct Endpoint { - 1: required i32 ipv4 - 2: required i32 port - 3: required string serviceName -} - -// Regular annotations just associate a timestamp with a string value -struct Annotation { - // Timestamp is in milliseconds since epoch. This is converted to - // microseconds since epoch in the query service since that's what the - // web frontend expects. - 1: required double timestamp - 2: required string value // event name - what happened at the timestamp? - 3: optional i32 duration // how long did the method take in ms -} - -enum AnnotationType { BOOL, BYTES, I16, I32, I64, DOUBLE, STRING } - -// Binary annotations associate a string key with a value of a particular type. -// A better name would've been "tags". -struct BinaryAnnotation { - 1: required string key - 2: optional string stringValue - 3: optional double doubleValue - 4: optional bool boolValue - 5: optional binary bytesValue - 6: optional i64 intValue - 7: required AnnotationType annotationType -} - -struct Span { - 1: required binary traceId // unique trace id, use for all spans in trace - 2: required Endpoint host // host being remotely procedure called - 3: required string name // span name, rpc method for example - 4: required binary id // unique span id, only used for this span - 5: required binary parentId // parent span id, 0 if no parent - 6: required list annotations - 7: required list binaryAnnotations - 8: optional bool debug = 0 - 9: optional Endpoint spanHost // host that sent this span. The #2 'host' field above is deprecated, set this one instead. -} - -struct Response { - 1: required bool ok -} - -enum SamplingStrategyType { PROBABILISTIC, RATE_LIMITING } - -// ProbabilisticSamplingStrategy randomly samples a fixed percentage of all traces. -struct ProbabilisticSamplingStrategy { - 1: required double samplingRate // percentage expressed as rate (0..1] -} - -// RateLimitingStrategy samples traces with a rate that does not exceed specified number of traces per second. -// The recommended implementation approach is leaky bucket. -struct RateLimitingSamplingStrategy { - 1: required i16 maxTracesPerSecond -} - -struct SamplingStrategyResponse { - 1: required SamplingStrategyType strategyType - 2: optional ProbabilisticSamplingStrategy probabilisticSampling - 3: optional RateLimitingSamplingStrategy rateLimitingSampling -} - -service TCollector { - Response submit(1: Span span) - list submitBatch(1: list spans) - SamplingStrategyResponse getSamplingStrategy(1: string serviceName) -} diff --git a/trace/thrift/gen-go/tcollector/constants.go b/trace/thrift/gen-go/tcollector/constants.go deleted file mode 100644 index 842ad26a..00000000 --- a/trace/thrift/gen-go/tcollector/constants.go +++ /dev/null @@ -1,18 +0,0 @@ -// Autogenerated by Thrift Compiler (1.0.0-dev) -// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - -package tcollector - -import ( - "bytes" - "fmt" - "github.com/apache/thrift/lib/go/thrift" -) - -// (needed to ensure safety because of naive import list construction.) -var _ = thrift.ZERO -var _ = fmt.Printf -var _ = bytes.Equal - -func init() { -} diff --git a/trace/thrift/gen-go/tcollector/tchan-tcollector.go b/trace/thrift/gen-go/tcollector/tchan-tcollector.go deleted file mode 100644 index 2252cdec..00000000 --- a/trace/thrift/gen-go/tcollector/tchan-tcollector.go +++ /dev/null @@ -1,173 +0,0 @@ -// @generated Code generated by thrift-gen. Do not modify. - -// Package tcollector is generated code used to make or handle TChannel calls using Thrift. -package tcollector - -import ( - "fmt" - - athrift "github.com/apache/thrift/lib/go/thrift" - "github.com/uber/tchannel-go/thrift" -) - -// Interfaces for the service and client for the services defined in the IDL. - -// TChanTCollector is the interface that defines the server handler and client interface. -type TChanTCollector interface { - GetSamplingStrategy(ctx thrift.Context, serviceName string) (*SamplingStrategyResponse, error) - Submit(ctx thrift.Context, span *Span) (*Response, error) - SubmitBatch(ctx thrift.Context, spans []*Span) ([]*Response, error) -} - -// Implementation of a client and service handler. - -type tchanTCollectorClient struct { - thriftService string - client thrift.TChanClient -} - -func NewTChanTCollectorInheritedClient(thriftService string, client thrift.TChanClient) *tchanTCollectorClient { - return &tchanTCollectorClient{ - thriftService, - client, - } -} - -// NewTChanTCollectorClient creates a client that can be used to make remote calls. -func NewTChanTCollectorClient(client thrift.TChanClient) TChanTCollector { - return NewTChanTCollectorInheritedClient("TCollector", client) -} - -func (c *tchanTCollectorClient) GetSamplingStrategy(ctx thrift.Context, serviceName string) (*SamplingStrategyResponse, error) { - var resp TCollectorGetSamplingStrategyResult - args := TCollectorGetSamplingStrategyArgs{ - ServiceName: serviceName, - } - success, err := c.client.Call(ctx, c.thriftService, "getSamplingStrategy", &args, &resp) - if err == nil && !success { - } - - return resp.GetSuccess(), err -} - -func (c *tchanTCollectorClient) Submit(ctx thrift.Context, span *Span) (*Response, error) { - var resp TCollectorSubmitResult - args := TCollectorSubmitArgs{ - Span: span, - } - success, err := c.client.Call(ctx, c.thriftService, "submit", &args, &resp) - if err == nil && !success { - } - - return resp.GetSuccess(), err -} - -func (c *tchanTCollectorClient) SubmitBatch(ctx thrift.Context, spans []*Span) ([]*Response, error) { - var resp TCollectorSubmitBatchResult - args := TCollectorSubmitBatchArgs{ - Spans: spans, - } - success, err := c.client.Call(ctx, c.thriftService, "submitBatch", &args, &resp) - if err == nil && !success { - } - - return resp.GetSuccess(), err -} - -type tchanTCollectorServer struct { - handler TChanTCollector -} - -// NewTChanTCollectorServer wraps a handler for TChanTCollector so it can be -// registered with a thrift.Server. -func NewTChanTCollectorServer(handler TChanTCollector) thrift.TChanServer { - return &tchanTCollectorServer{ - handler, - } -} - -func (s *tchanTCollectorServer) Service() string { - return "TCollector" -} - -func (s *tchanTCollectorServer) Methods() []string { - return []string{ - "getSamplingStrategy", - "submit", - "submitBatch", - } -} - -func (s *tchanTCollectorServer) Handle(ctx thrift.Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { - switch methodName { - case "getSamplingStrategy": - return s.handleGetSamplingStrategy(ctx, protocol) - case "submit": - return s.handleSubmit(ctx, protocol) - case "submitBatch": - return s.handleSubmitBatch(ctx, protocol) - - default: - return false, nil, fmt.Errorf("method %v not found in service %v", methodName, s.Service()) - } -} - -func (s *tchanTCollectorServer) handleGetSamplingStrategy(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { - var req TCollectorGetSamplingStrategyArgs - var res TCollectorGetSamplingStrategyResult - - if err := req.Read(protocol); err != nil { - return false, nil, err - } - - r, err := - s.handler.GetSamplingStrategy(ctx, req.ServiceName) - - if err != nil { - return false, nil, err - } else { - res.Success = r - } - - return err == nil, &res, nil -} - -func (s *tchanTCollectorServer) handleSubmit(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { - var req TCollectorSubmitArgs - var res TCollectorSubmitResult - - if err := req.Read(protocol); err != nil { - return false, nil, err - } - - r, err := - s.handler.Submit(ctx, req.Span) - - if err != nil { - return false, nil, err - } else { - res.Success = r - } - - return err == nil, &res, nil -} - -func (s *tchanTCollectorServer) handleSubmitBatch(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { - var req TCollectorSubmitBatchArgs - var res TCollectorSubmitBatchResult - - if err := req.Read(protocol); err != nil { - return false, nil, err - } - - r, err := - s.handler.SubmitBatch(ctx, req.Spans) - - if err != nil { - return false, nil, err - } else { - res.Success = r - } - - return err == nil, &res, nil -} diff --git a/trace/thrift/gen-go/tcollector/tcollector.go b/trace/thrift/gen-go/tcollector/tcollector.go deleted file mode 100644 index e64fd911..00000000 --- a/trace/thrift/gen-go/tcollector/tcollector.go +++ /dev/null @@ -1,1096 +0,0 @@ -// Autogenerated by Thrift Compiler (1.0.0-dev) -// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - -package tcollector - -import ( - "bytes" - "fmt" - "github.com/apache/thrift/lib/go/thrift" -) - -// (needed to ensure safety because of naive import list construction.) -var _ = thrift.ZERO -var _ = fmt.Printf -var _ = bytes.Equal - -type TCollector interface { - // Parameters: - // - Span - Submit(span *Span) (r *Response, err error) - // Parameters: - // - Spans - SubmitBatch(spans []*Span) (r []*Response, err error) - // Parameters: - // - ServiceName - GetSamplingStrategy(serviceName string) (r *SamplingStrategyResponse, err error) -} - -type TCollectorClient struct { - Transport thrift.TTransport - ProtocolFactory thrift.TProtocolFactory - InputProtocol thrift.TProtocol - OutputProtocol thrift.TProtocol - SeqId int32 -} - -func NewTCollectorClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *TCollectorClient { - return &TCollectorClient{Transport: t, - ProtocolFactory: f, - InputProtocol: f.GetProtocol(t), - OutputProtocol: f.GetProtocol(t), - SeqId: 0, - } -} - -func NewTCollectorClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *TCollectorClient { - return &TCollectorClient{Transport: t, - ProtocolFactory: nil, - InputProtocol: iprot, - OutputProtocol: oprot, - SeqId: 0, - } -} - -// Parameters: -// - Span -func (p *TCollectorClient) Submit(span *Span) (r *Response, err error) { - if err = p.sendSubmit(span); err != nil { - return - } - return p.recvSubmit() -} - -func (p *TCollectorClient) sendSubmit(span *Span) (err error) { - oprot := p.OutputProtocol - if oprot == nil { - oprot = p.ProtocolFactory.GetProtocol(p.Transport) - p.OutputProtocol = oprot - } - p.SeqId++ - if err = oprot.WriteMessageBegin("submit", thrift.CALL, p.SeqId); err != nil { - return - } - args := TCollectorSubmitArgs{ - Span: span, - } - if err = args.Write(oprot); err != nil { - return - } - if err = oprot.WriteMessageEnd(); err != nil { - return - } - return oprot.Flush() -} - -func (p *TCollectorClient) recvSubmit() (value *Response, err error) { - iprot := p.InputProtocol - if iprot == nil { - iprot = p.ProtocolFactory.GetProtocol(p.Transport) - p.InputProtocol = iprot - } - method, mTypeId, seqId, err := iprot.ReadMessageBegin() - if err != nil { - return - } - if method != "submit" { - err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "submit failed: wrong method name") - return - } - if p.SeqId != seqId { - err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "submit failed: out of sequence response") - return - } - if mTypeId == thrift.EXCEPTION { - error2 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") - var error3 error - error3, err = error2.Read(iprot) - if err != nil { - return - } - if err = iprot.ReadMessageEnd(); err != nil { - return - } - err = error3 - return - } - if mTypeId != thrift.REPLY { - err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "submit failed: invalid message type") - return - } - result := TCollectorSubmitResult{} - if err = result.Read(iprot); err != nil { - return - } - if err = iprot.ReadMessageEnd(); err != nil { - return - } - value = result.GetSuccess() - return -} - -// Parameters: -// - Spans -func (p *TCollectorClient) SubmitBatch(spans []*Span) (r []*Response, err error) { - if err = p.sendSubmitBatch(spans); err != nil { - return - } - return p.recvSubmitBatch() -} - -func (p *TCollectorClient) sendSubmitBatch(spans []*Span) (err error) { - oprot := p.OutputProtocol - if oprot == nil { - oprot = p.ProtocolFactory.GetProtocol(p.Transport) - p.OutputProtocol = oprot - } - p.SeqId++ - if err = oprot.WriteMessageBegin("submitBatch", thrift.CALL, p.SeqId); err != nil { - return - } - args := TCollectorSubmitBatchArgs{ - Spans: spans, - } - if err = args.Write(oprot); err != nil { - return - } - if err = oprot.WriteMessageEnd(); err != nil { - return - } - return oprot.Flush() -} - -func (p *TCollectorClient) recvSubmitBatch() (value []*Response, err error) { - iprot := p.InputProtocol - if iprot == nil { - iprot = p.ProtocolFactory.GetProtocol(p.Transport) - p.InputProtocol = iprot - } - method, mTypeId, seqId, err := iprot.ReadMessageBegin() - if err != nil { - return - } - if method != "submitBatch" { - err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "submitBatch failed: wrong method name") - return - } - if p.SeqId != seqId { - err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "submitBatch failed: out of sequence response") - return - } - if mTypeId == thrift.EXCEPTION { - error4 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") - var error5 error - error5, err = error4.Read(iprot) - if err != nil { - return - } - if err = iprot.ReadMessageEnd(); err != nil { - return - } - err = error5 - return - } - if mTypeId != thrift.REPLY { - err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "submitBatch failed: invalid message type") - return - } - result := TCollectorSubmitBatchResult{} - if err = result.Read(iprot); err != nil { - return - } - if err = iprot.ReadMessageEnd(); err != nil { - return - } - value = result.GetSuccess() - return -} - -// Parameters: -// - ServiceName -func (p *TCollectorClient) GetSamplingStrategy(serviceName string) (r *SamplingStrategyResponse, err error) { - if err = p.sendGetSamplingStrategy(serviceName); err != nil { - return - } - return p.recvGetSamplingStrategy() -} - -func (p *TCollectorClient) sendGetSamplingStrategy(serviceName string) (err error) { - oprot := p.OutputProtocol - if oprot == nil { - oprot = p.ProtocolFactory.GetProtocol(p.Transport) - p.OutputProtocol = oprot - } - p.SeqId++ - if err = oprot.WriteMessageBegin("getSamplingStrategy", thrift.CALL, p.SeqId); err != nil { - return - } - args := TCollectorGetSamplingStrategyArgs{ - ServiceName: serviceName, - } - if err = args.Write(oprot); err != nil { - return - } - if err = oprot.WriteMessageEnd(); err != nil { - return - } - return oprot.Flush() -} - -func (p *TCollectorClient) recvGetSamplingStrategy() (value *SamplingStrategyResponse, err error) { - iprot := p.InputProtocol - if iprot == nil { - iprot = p.ProtocolFactory.GetProtocol(p.Transport) - p.InputProtocol = iprot - } - method, mTypeId, seqId, err := iprot.ReadMessageBegin() - if err != nil { - return - } - if method != "getSamplingStrategy" { - err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "getSamplingStrategy failed: wrong method name") - return - } - if p.SeqId != seqId { - err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "getSamplingStrategy failed: out of sequence response") - return - } - if mTypeId == thrift.EXCEPTION { - error6 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") - var error7 error - error7, err = error6.Read(iprot) - if err != nil { - return - } - if err = iprot.ReadMessageEnd(); err != nil { - return - } - err = error7 - return - } - if mTypeId != thrift.REPLY { - err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "getSamplingStrategy failed: invalid message type") - return - } - result := TCollectorGetSamplingStrategyResult{} - if err = result.Read(iprot); err != nil { - return - } - if err = iprot.ReadMessageEnd(); err != nil { - return - } - value = result.GetSuccess() - return -} - -type TCollectorProcessor struct { - processorMap map[string]thrift.TProcessorFunction - handler TCollector -} - -func (p *TCollectorProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { - p.processorMap[key] = processor -} - -func (p *TCollectorProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { - processor, ok = p.processorMap[key] - return processor, ok -} - -func (p *TCollectorProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { - return p.processorMap -} - -func NewTCollectorProcessor(handler TCollector) *TCollectorProcessor { - - self8 := &TCollectorProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} - self8.processorMap["submit"] = &tCollectorProcessorSubmit{handler: handler} - self8.processorMap["submitBatch"] = &tCollectorProcessorSubmitBatch{handler: handler} - self8.processorMap["getSamplingStrategy"] = &tCollectorProcessorGetSamplingStrategy{handler: handler} - return self8 -} - -func (p *TCollectorProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { - name, _, seqId, err := iprot.ReadMessageBegin() - if err != nil { - return false, err - } - if processor, ok := p.GetProcessorFunction(name); ok { - return processor.Process(seqId, iprot, oprot) - } - iprot.Skip(thrift.STRUCT) - iprot.ReadMessageEnd() - x9 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) - oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) - x9.Write(oprot) - oprot.WriteMessageEnd() - oprot.Flush() - return false, x9 - -} - -type tCollectorProcessorSubmit struct { - handler TCollector -} - -func (p *tCollectorProcessorSubmit) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { - args := TCollectorSubmitArgs{} - if err = args.Read(iprot); err != nil { - iprot.ReadMessageEnd() - x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) - oprot.WriteMessageBegin("submit", thrift.EXCEPTION, seqId) - x.Write(oprot) - oprot.WriteMessageEnd() - oprot.Flush() - return false, err - } - - iprot.ReadMessageEnd() - result := TCollectorSubmitResult{} - var retval *Response - var err2 error - if retval, err2 = p.handler.Submit(args.Span); err2 != nil { - x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing submit: "+err2.Error()) - oprot.WriteMessageBegin("submit", thrift.EXCEPTION, seqId) - x.Write(oprot) - oprot.WriteMessageEnd() - oprot.Flush() - return true, err2 - } else { - result.Success = retval - } - if err2 = oprot.WriteMessageBegin("submit", thrift.REPLY, seqId); err2 != nil { - err = err2 - } - if err2 = result.Write(oprot); err == nil && err2 != nil { - err = err2 - } - if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { - err = err2 - } - if err2 = oprot.Flush(); err == nil && err2 != nil { - err = err2 - } - if err != nil { - return - } - return true, err -} - -type tCollectorProcessorSubmitBatch struct { - handler TCollector -} - -func (p *tCollectorProcessorSubmitBatch) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { - args := TCollectorSubmitBatchArgs{} - if err = args.Read(iprot); err != nil { - iprot.ReadMessageEnd() - x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) - oprot.WriteMessageBegin("submitBatch", thrift.EXCEPTION, seqId) - x.Write(oprot) - oprot.WriteMessageEnd() - oprot.Flush() - return false, err - } - - iprot.ReadMessageEnd() - result := TCollectorSubmitBatchResult{} - var retval []*Response - var err2 error - if retval, err2 = p.handler.SubmitBatch(args.Spans); err2 != nil { - x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing submitBatch: "+err2.Error()) - oprot.WriteMessageBegin("submitBatch", thrift.EXCEPTION, seqId) - x.Write(oprot) - oprot.WriteMessageEnd() - oprot.Flush() - return true, err2 - } else { - result.Success = retval - } - if err2 = oprot.WriteMessageBegin("submitBatch", thrift.REPLY, seqId); err2 != nil { - err = err2 - } - if err2 = result.Write(oprot); err == nil && err2 != nil { - err = err2 - } - if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { - err = err2 - } - if err2 = oprot.Flush(); err == nil && err2 != nil { - err = err2 - } - if err != nil { - return - } - return true, err -} - -type tCollectorProcessorGetSamplingStrategy struct { - handler TCollector -} - -func (p *tCollectorProcessorGetSamplingStrategy) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { - args := TCollectorGetSamplingStrategyArgs{} - if err = args.Read(iprot); err != nil { - iprot.ReadMessageEnd() - x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) - oprot.WriteMessageBegin("getSamplingStrategy", thrift.EXCEPTION, seqId) - x.Write(oprot) - oprot.WriteMessageEnd() - oprot.Flush() - return false, err - } - - iprot.ReadMessageEnd() - result := TCollectorGetSamplingStrategyResult{} - var retval *SamplingStrategyResponse - var err2 error - if retval, err2 = p.handler.GetSamplingStrategy(args.ServiceName); err2 != nil { - x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing getSamplingStrategy: "+err2.Error()) - oprot.WriteMessageBegin("getSamplingStrategy", thrift.EXCEPTION, seqId) - x.Write(oprot) - oprot.WriteMessageEnd() - oprot.Flush() - return true, err2 - } else { - result.Success = retval - } - if err2 = oprot.WriteMessageBegin("getSamplingStrategy", thrift.REPLY, seqId); err2 != nil { - err = err2 - } - if err2 = result.Write(oprot); err == nil && err2 != nil { - err = err2 - } - if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { - err = err2 - } - if err2 = oprot.Flush(); err == nil && err2 != nil { - err = err2 - } - if err != nil { - return - } - return true, err -} - -// HELPER FUNCTIONS AND STRUCTURES - -// Attributes: -// - Span -type TCollectorSubmitArgs struct { - Span *Span `thrift:"span,1" db:"span" json:"span"` -} - -func NewTCollectorSubmitArgs() *TCollectorSubmitArgs { - return &TCollectorSubmitArgs{} -} - -var TCollectorSubmitArgs_Span_DEFAULT *Span - -func (p *TCollectorSubmitArgs) GetSpan() *Span { - if !p.IsSetSpan() { - return TCollectorSubmitArgs_Span_DEFAULT - } - return p.Span -} -func (p *TCollectorSubmitArgs) IsSetSpan() bool { - return p.Span != nil -} - -func (p *TCollectorSubmitArgs) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.ReadField1(iprot); err != nil { - return err - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - return nil -} - -func (p *TCollectorSubmitArgs) ReadField1(iprot thrift.TProtocol) error { - p.Span = &Span{} - if err := p.Span.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Span), err) - } - return nil -} - -func (p *TCollectorSubmitArgs) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("submit_args"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *TCollectorSubmitArgs) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("span", thrift.STRUCT, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:span: ", p), err) - } - if err := p.Span.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Span), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:span: ", p), err) - } - return err -} - -func (p *TCollectorSubmitArgs) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("TCollectorSubmitArgs(%+v)", *p) -} - -// Attributes: -// - Success -type TCollectorSubmitResult struct { - Success *Response `thrift:"success,0" db:"success" json:"success,omitempty"` -} - -func NewTCollectorSubmitResult() *TCollectorSubmitResult { - return &TCollectorSubmitResult{} -} - -var TCollectorSubmitResult_Success_DEFAULT *Response - -func (p *TCollectorSubmitResult) GetSuccess() *Response { - if !p.IsSetSuccess() { - return TCollectorSubmitResult_Success_DEFAULT - } - return p.Success -} -func (p *TCollectorSubmitResult) IsSetSuccess() bool { - return p.Success != nil -} - -func (p *TCollectorSubmitResult) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 0: - if err := p.ReadField0(iprot); err != nil { - return err - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - return nil -} - -func (p *TCollectorSubmitResult) ReadField0(iprot thrift.TProtocol) error { - p.Success = &Response{} - if err := p.Success.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Success), err) - } - return nil -} - -func (p *TCollectorSubmitResult) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("submit_result"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField0(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *TCollectorSubmitResult) writeField0(oprot thrift.TProtocol) (err error) { - if p.IsSetSuccess() { - if err := oprot.WriteFieldBegin("success", thrift.STRUCT, 0); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) - } - if err := p.Success.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Success), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) - } - } - return err -} - -func (p *TCollectorSubmitResult) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("TCollectorSubmitResult(%+v)", *p) -} - -// Attributes: -// - Spans -type TCollectorSubmitBatchArgs struct { - Spans []*Span `thrift:"spans,1" db:"spans" json:"spans"` -} - -func NewTCollectorSubmitBatchArgs() *TCollectorSubmitBatchArgs { - return &TCollectorSubmitBatchArgs{} -} - -func (p *TCollectorSubmitBatchArgs) GetSpans() []*Span { - return p.Spans -} -func (p *TCollectorSubmitBatchArgs) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.ReadField1(iprot); err != nil { - return err - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - return nil -} - -func (p *TCollectorSubmitBatchArgs) ReadField1(iprot thrift.TProtocol) error { - _, size, err := iprot.ReadListBegin() - if err != nil { - return thrift.PrependError("error reading list begin: ", err) - } - tSlice := make([]*Span, 0, size) - p.Spans = tSlice - for i := 0; i < size; i++ { - _elem10 := &Span{} - if err := _elem10.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem10), err) - } - p.Spans = append(p.Spans, _elem10) - } - if err := iprot.ReadListEnd(); err != nil { - return thrift.PrependError("error reading list end: ", err) - } - return nil -} - -func (p *TCollectorSubmitBatchArgs) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("submitBatch_args"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *TCollectorSubmitBatchArgs) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("spans", thrift.LIST, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:spans: ", p), err) - } - if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Spans)); err != nil { - return thrift.PrependError("error writing list begin: ", err) - } - for _, v := range p.Spans { - if err := v.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) - } - } - if err := oprot.WriteListEnd(); err != nil { - return thrift.PrependError("error writing list end: ", err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:spans: ", p), err) - } - return err -} - -func (p *TCollectorSubmitBatchArgs) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("TCollectorSubmitBatchArgs(%+v)", *p) -} - -// Attributes: -// - Success -type TCollectorSubmitBatchResult struct { - Success []*Response `thrift:"success,0" db:"success" json:"success,omitempty"` -} - -func NewTCollectorSubmitBatchResult() *TCollectorSubmitBatchResult { - return &TCollectorSubmitBatchResult{} -} - -var TCollectorSubmitBatchResult_Success_DEFAULT []*Response - -func (p *TCollectorSubmitBatchResult) GetSuccess() []*Response { - return p.Success -} -func (p *TCollectorSubmitBatchResult) IsSetSuccess() bool { - return p.Success != nil -} - -func (p *TCollectorSubmitBatchResult) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 0: - if err := p.ReadField0(iprot); err != nil { - return err - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - return nil -} - -func (p *TCollectorSubmitBatchResult) ReadField0(iprot thrift.TProtocol) error { - _, size, err := iprot.ReadListBegin() - if err != nil { - return thrift.PrependError("error reading list begin: ", err) - } - tSlice := make([]*Response, 0, size) - p.Success = tSlice - for i := 0; i < size; i++ { - _elem11 := &Response{} - if err := _elem11.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem11), err) - } - p.Success = append(p.Success, _elem11) - } - if err := iprot.ReadListEnd(); err != nil { - return thrift.PrependError("error reading list end: ", err) - } - return nil -} - -func (p *TCollectorSubmitBatchResult) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("submitBatch_result"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField0(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *TCollectorSubmitBatchResult) writeField0(oprot thrift.TProtocol) (err error) { - if p.IsSetSuccess() { - if err := oprot.WriteFieldBegin("success", thrift.LIST, 0); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) - } - if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Success)); err != nil { - return thrift.PrependError("error writing list begin: ", err) - } - for _, v := range p.Success { - if err := v.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) - } - } - if err := oprot.WriteListEnd(); err != nil { - return thrift.PrependError("error writing list end: ", err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) - } - } - return err -} - -func (p *TCollectorSubmitBatchResult) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("TCollectorSubmitBatchResult(%+v)", *p) -} - -// Attributes: -// - ServiceName -type TCollectorGetSamplingStrategyArgs struct { - ServiceName string `thrift:"serviceName,1" db:"serviceName" json:"serviceName"` -} - -func NewTCollectorGetSamplingStrategyArgs() *TCollectorGetSamplingStrategyArgs { - return &TCollectorGetSamplingStrategyArgs{} -} - -func (p *TCollectorGetSamplingStrategyArgs) GetServiceName() string { - return p.ServiceName -} -func (p *TCollectorGetSamplingStrategyArgs) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.ReadField1(iprot); err != nil { - return err - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - return nil -} - -func (p *TCollectorGetSamplingStrategyArgs) ReadField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadString(); err != nil { - return thrift.PrependError("error reading field 1: ", err) - } else { - p.ServiceName = v - } - return nil -} - -func (p *TCollectorGetSamplingStrategyArgs) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("getSamplingStrategy_args"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *TCollectorGetSamplingStrategyArgs) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("serviceName", thrift.STRING, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:serviceName: ", p), err) - } - if err := oprot.WriteString(string(p.ServiceName)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.serviceName (1) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:serviceName: ", p), err) - } - return err -} - -func (p *TCollectorGetSamplingStrategyArgs) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("TCollectorGetSamplingStrategyArgs(%+v)", *p) -} - -// Attributes: -// - Success -type TCollectorGetSamplingStrategyResult struct { - Success *SamplingStrategyResponse `thrift:"success,0" db:"success" json:"success,omitempty"` -} - -func NewTCollectorGetSamplingStrategyResult() *TCollectorGetSamplingStrategyResult { - return &TCollectorGetSamplingStrategyResult{} -} - -var TCollectorGetSamplingStrategyResult_Success_DEFAULT *SamplingStrategyResponse - -func (p *TCollectorGetSamplingStrategyResult) GetSuccess() *SamplingStrategyResponse { - if !p.IsSetSuccess() { - return TCollectorGetSamplingStrategyResult_Success_DEFAULT - } - return p.Success -} -func (p *TCollectorGetSamplingStrategyResult) IsSetSuccess() bool { - return p.Success != nil -} - -func (p *TCollectorGetSamplingStrategyResult) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 0: - if err := p.ReadField0(iprot); err != nil { - return err - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - return nil -} - -func (p *TCollectorGetSamplingStrategyResult) ReadField0(iprot thrift.TProtocol) error { - p.Success = &SamplingStrategyResponse{} - if err := p.Success.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Success), err) - } - return nil -} - -func (p *TCollectorGetSamplingStrategyResult) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("getSamplingStrategy_result"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField0(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *TCollectorGetSamplingStrategyResult) writeField0(oprot thrift.TProtocol) (err error) { - if p.IsSetSuccess() { - if err := oprot.WriteFieldBegin("success", thrift.STRUCT, 0); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) - } - if err := p.Success.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Success), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) - } - } - return err -} - -func (p *TCollectorGetSamplingStrategyResult) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("TCollectorGetSamplingStrategyResult(%+v)", *p) -} diff --git a/trace/thrift/gen-go/tcollector/ttypes.go b/trace/thrift/gen-go/tcollector/ttypes.go deleted file mode 100644 index c596e9a0..00000000 --- a/trace/thrift/gen-go/tcollector/ttypes.go +++ /dev/null @@ -1,1835 +0,0 @@ -// Autogenerated by Thrift Compiler (1.0.0-dev) -// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - -package tcollector - -import ( - "bytes" - "database/sql/driver" - "errors" - "fmt" - "github.com/apache/thrift/lib/go/thrift" -) - -// (needed to ensure safety because of naive import list construction.) -var _ = thrift.ZERO -var _ = fmt.Printf -var _ = bytes.Equal - -var GoUnusedProtection__ int - -type AnnotationType int64 - -const ( - AnnotationType_BOOL AnnotationType = 0 - AnnotationType_BYTES AnnotationType = 1 - AnnotationType_I16 AnnotationType = 2 - AnnotationType_I32 AnnotationType = 3 - AnnotationType_I64 AnnotationType = 4 - AnnotationType_DOUBLE AnnotationType = 5 - AnnotationType_STRING AnnotationType = 6 -) - -func (p AnnotationType) String() string { - switch p { - case AnnotationType_BOOL: - return "BOOL" - case AnnotationType_BYTES: - return "BYTES" - case AnnotationType_I16: - return "I16" - case AnnotationType_I32: - return "I32" - case AnnotationType_I64: - return "I64" - case AnnotationType_DOUBLE: - return "DOUBLE" - case AnnotationType_STRING: - return "STRING" - } - return "" -} - -func AnnotationTypeFromString(s string) (AnnotationType, error) { - switch s { - case "BOOL": - return AnnotationType_BOOL, nil - case "BYTES": - return AnnotationType_BYTES, nil - case "I16": - return AnnotationType_I16, nil - case "I32": - return AnnotationType_I32, nil - case "I64": - return AnnotationType_I64, nil - case "DOUBLE": - return AnnotationType_DOUBLE, nil - case "STRING": - return AnnotationType_STRING, nil - } - return AnnotationType(0), fmt.Errorf("not a valid AnnotationType string") -} - -func AnnotationTypePtr(v AnnotationType) *AnnotationType { return &v } - -func (p AnnotationType) MarshalText() ([]byte, error) { - return []byte(p.String()), nil -} - -func (p *AnnotationType) UnmarshalText(text []byte) error { - q, err := AnnotationTypeFromString(string(text)) - if err != nil { - return err - } - *p = q - return nil -} - -func (p *AnnotationType) Scan(value interface{}) error { - v, ok := value.(int64) - if !ok { - return errors.New("Scan value is not int64") - } - *p = AnnotationType(v) - return nil -} - -func (p *AnnotationType) Value() (driver.Value, error) { - if p == nil { - return nil, nil - } - return int64(*p), nil -} - -type SamplingStrategyType int64 - -const ( - SamplingStrategyType_PROBABILISTIC SamplingStrategyType = 0 - SamplingStrategyType_RATE_LIMITING SamplingStrategyType = 1 -) - -func (p SamplingStrategyType) String() string { - switch p { - case SamplingStrategyType_PROBABILISTIC: - return "PROBABILISTIC" - case SamplingStrategyType_RATE_LIMITING: - return "RATE_LIMITING" - } - return "" -} - -func SamplingStrategyTypeFromString(s string) (SamplingStrategyType, error) { - switch s { - case "PROBABILISTIC": - return SamplingStrategyType_PROBABILISTIC, nil - case "RATE_LIMITING": - return SamplingStrategyType_RATE_LIMITING, nil - } - return SamplingStrategyType(0), fmt.Errorf("not a valid SamplingStrategyType string") -} - -func SamplingStrategyTypePtr(v SamplingStrategyType) *SamplingStrategyType { return &v } - -func (p SamplingStrategyType) MarshalText() ([]byte, error) { - return []byte(p.String()), nil -} - -func (p *SamplingStrategyType) UnmarshalText(text []byte) error { - q, err := SamplingStrategyTypeFromString(string(text)) - if err != nil { - return err - } - *p = q - return nil -} - -func (p *SamplingStrategyType) Scan(value interface{}) error { - v, ok := value.(int64) - if !ok { - return errors.New("Scan value is not int64") - } - *p = SamplingStrategyType(v) - return nil -} - -func (p *SamplingStrategyType) Value() (driver.Value, error) { - if p == nil { - return nil, nil - } - return int64(*p), nil -} - -// Attributes: -// - Ipv4 -// - Port -// - ServiceName -type Endpoint struct { - Ipv4 int32 `thrift:"ipv4,1,required" db:"ipv4" json:"ipv4"` - Port int32 `thrift:"port,2,required" db:"port" json:"port"` - ServiceName string `thrift:"serviceName,3,required" db:"serviceName" json:"serviceName"` -} - -func NewEndpoint() *Endpoint { - return &Endpoint{} -} - -func (p *Endpoint) GetIpv4() int32 { - return p.Ipv4 -} - -func (p *Endpoint) GetPort() int32 { - return p.Port -} - -func (p *Endpoint) GetServiceName() string { - return p.ServiceName -} -func (p *Endpoint) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - var issetIpv4 bool = false - var issetPort bool = false - var issetServiceName bool = false - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.ReadField1(iprot); err != nil { - return err - } - issetIpv4 = true - case 2: - if err := p.ReadField2(iprot); err != nil { - return err - } - issetPort = true - case 3: - if err := p.ReadField3(iprot); err != nil { - return err - } - issetServiceName = true - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - if !issetIpv4 { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Ipv4 is not set")) - } - if !issetPort { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Port is not set")) - } - if !issetServiceName { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ServiceName is not set")) - } - return nil -} - -func (p *Endpoint) ReadField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI32(); err != nil { - return thrift.PrependError("error reading field 1: ", err) - } else { - p.Ipv4 = v - } - return nil -} - -func (p *Endpoint) ReadField2(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI32(); err != nil { - return thrift.PrependError("error reading field 2: ", err) - } else { - p.Port = v - } - return nil -} - -func (p *Endpoint) ReadField3(iprot thrift.TProtocol) error { - if v, err := iprot.ReadString(); err != nil { - return thrift.PrependError("error reading field 3: ", err) - } else { - p.ServiceName = v - } - return nil -} - -func (p *Endpoint) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("Endpoint"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := p.writeField2(oprot); err != nil { - return err - } - if err := p.writeField3(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *Endpoint) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("ipv4", thrift.I32, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:ipv4: ", p), err) - } - if err := oprot.WriteI32(int32(p.Ipv4)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.ipv4 (1) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:ipv4: ", p), err) - } - return err -} - -func (p *Endpoint) writeField2(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("port", thrift.I32, 2); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:port: ", p), err) - } - if err := oprot.WriteI32(int32(p.Port)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.port (2) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 2:port: ", p), err) - } - return err -} - -func (p *Endpoint) writeField3(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("serviceName", thrift.STRING, 3); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:serviceName: ", p), err) - } - if err := oprot.WriteString(string(p.ServiceName)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.serviceName (3) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 3:serviceName: ", p), err) - } - return err -} - -func (p *Endpoint) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("Endpoint(%+v)", *p) -} - -// Attributes: -// - Timestamp -// - Value -// - Duration -type Annotation struct { - Timestamp float64 `thrift:"timestamp,1,required" db:"timestamp" json:"timestamp"` - Value string `thrift:"value,2,required" db:"value" json:"value"` - Duration *int32 `thrift:"duration,3" db:"duration" json:"duration,omitempty"` -} - -func NewAnnotation() *Annotation { - return &Annotation{} -} - -func (p *Annotation) GetTimestamp() float64 { - return p.Timestamp -} - -func (p *Annotation) GetValue() string { - return p.Value -} - -var Annotation_Duration_DEFAULT int32 - -func (p *Annotation) GetDuration() int32 { - if !p.IsSetDuration() { - return Annotation_Duration_DEFAULT - } - return *p.Duration -} -func (p *Annotation) IsSetDuration() bool { - return p.Duration != nil -} - -func (p *Annotation) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - var issetTimestamp bool = false - var issetValue bool = false - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.ReadField1(iprot); err != nil { - return err - } - issetTimestamp = true - case 2: - if err := p.ReadField2(iprot); err != nil { - return err - } - issetValue = true - case 3: - if err := p.ReadField3(iprot); err != nil { - return err - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - if !issetTimestamp { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Timestamp is not set")) - } - if !issetValue { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Value is not set")) - } - return nil -} - -func (p *Annotation) ReadField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadDouble(); err != nil { - return thrift.PrependError("error reading field 1: ", err) - } else { - p.Timestamp = v - } - return nil -} - -func (p *Annotation) ReadField2(iprot thrift.TProtocol) error { - if v, err := iprot.ReadString(); err != nil { - return thrift.PrependError("error reading field 2: ", err) - } else { - p.Value = v - } - return nil -} - -func (p *Annotation) ReadField3(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI32(); err != nil { - return thrift.PrependError("error reading field 3: ", err) - } else { - p.Duration = &v - } - return nil -} - -func (p *Annotation) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("Annotation"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := p.writeField2(oprot); err != nil { - return err - } - if err := p.writeField3(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *Annotation) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("timestamp", thrift.DOUBLE, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:timestamp: ", p), err) - } - if err := oprot.WriteDouble(float64(p.Timestamp)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.timestamp (1) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:timestamp: ", p), err) - } - return err -} - -func (p *Annotation) writeField2(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("value", thrift.STRING, 2); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:value: ", p), err) - } - if err := oprot.WriteString(string(p.Value)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.value (2) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 2:value: ", p), err) - } - return err -} - -func (p *Annotation) writeField3(oprot thrift.TProtocol) (err error) { - if p.IsSetDuration() { - if err := oprot.WriteFieldBegin("duration", thrift.I32, 3); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:duration: ", p), err) - } - if err := oprot.WriteI32(int32(*p.Duration)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.duration (3) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 3:duration: ", p), err) - } - } - return err -} - -func (p *Annotation) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("Annotation(%+v)", *p) -} - -// Attributes: -// - Key -// - StringValue -// - DoubleValue -// - BoolValue -// - BytesValue -// - IntValue -// - AnnotationType -type BinaryAnnotation struct { - Key string `thrift:"key,1,required" db:"key" json:"key"` - StringValue *string `thrift:"stringValue,2" db:"stringValue" json:"stringValue,omitempty"` - DoubleValue *float64 `thrift:"doubleValue,3" db:"doubleValue" json:"doubleValue,omitempty"` - BoolValue *bool `thrift:"boolValue,4" db:"boolValue" json:"boolValue,omitempty"` - BytesValue []byte `thrift:"bytesValue,5" db:"bytesValue" json:"bytesValue,omitempty"` - IntValue *int64 `thrift:"intValue,6" db:"intValue" json:"intValue,omitempty"` - AnnotationType AnnotationType `thrift:"annotationType,7,required" db:"annotationType" json:"annotationType"` -} - -func NewBinaryAnnotation() *BinaryAnnotation { - return &BinaryAnnotation{} -} - -func (p *BinaryAnnotation) GetKey() string { - return p.Key -} - -var BinaryAnnotation_StringValue_DEFAULT string - -func (p *BinaryAnnotation) GetStringValue() string { - if !p.IsSetStringValue() { - return BinaryAnnotation_StringValue_DEFAULT - } - return *p.StringValue -} - -var BinaryAnnotation_DoubleValue_DEFAULT float64 - -func (p *BinaryAnnotation) GetDoubleValue() float64 { - if !p.IsSetDoubleValue() { - return BinaryAnnotation_DoubleValue_DEFAULT - } - return *p.DoubleValue -} - -var BinaryAnnotation_BoolValue_DEFAULT bool - -func (p *BinaryAnnotation) GetBoolValue() bool { - if !p.IsSetBoolValue() { - return BinaryAnnotation_BoolValue_DEFAULT - } - return *p.BoolValue -} - -var BinaryAnnotation_BytesValue_DEFAULT []byte - -func (p *BinaryAnnotation) GetBytesValue() []byte { - return p.BytesValue -} - -var BinaryAnnotation_IntValue_DEFAULT int64 - -func (p *BinaryAnnotation) GetIntValue() int64 { - if !p.IsSetIntValue() { - return BinaryAnnotation_IntValue_DEFAULT - } - return *p.IntValue -} - -func (p *BinaryAnnotation) GetAnnotationType() AnnotationType { - return p.AnnotationType -} -func (p *BinaryAnnotation) IsSetStringValue() bool { - return p.StringValue != nil -} - -func (p *BinaryAnnotation) IsSetDoubleValue() bool { - return p.DoubleValue != nil -} - -func (p *BinaryAnnotation) IsSetBoolValue() bool { - return p.BoolValue != nil -} - -func (p *BinaryAnnotation) IsSetBytesValue() bool { - return p.BytesValue != nil -} - -func (p *BinaryAnnotation) IsSetIntValue() bool { - return p.IntValue != nil -} - -func (p *BinaryAnnotation) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - var issetKey bool = false - var issetAnnotationType bool = false - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.ReadField1(iprot); err != nil { - return err - } - issetKey = true - case 2: - if err := p.ReadField2(iprot); err != nil { - return err - } - case 3: - if err := p.ReadField3(iprot); err != nil { - return err - } - case 4: - if err := p.ReadField4(iprot); err != nil { - return err - } - case 5: - if err := p.ReadField5(iprot); err != nil { - return err - } - case 6: - if err := p.ReadField6(iprot); err != nil { - return err - } - case 7: - if err := p.ReadField7(iprot); err != nil { - return err - } - issetAnnotationType = true - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - if !issetKey { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Key is not set")) - } - if !issetAnnotationType { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field AnnotationType is not set")) - } - return nil -} - -func (p *BinaryAnnotation) ReadField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadString(); err != nil { - return thrift.PrependError("error reading field 1: ", err) - } else { - p.Key = v - } - return nil -} - -func (p *BinaryAnnotation) ReadField2(iprot thrift.TProtocol) error { - if v, err := iprot.ReadString(); err != nil { - return thrift.PrependError("error reading field 2: ", err) - } else { - p.StringValue = &v - } - return nil -} - -func (p *BinaryAnnotation) ReadField3(iprot thrift.TProtocol) error { - if v, err := iprot.ReadDouble(); err != nil { - return thrift.PrependError("error reading field 3: ", err) - } else { - p.DoubleValue = &v - } - return nil -} - -func (p *BinaryAnnotation) ReadField4(iprot thrift.TProtocol) error { - if v, err := iprot.ReadBool(); err != nil { - return thrift.PrependError("error reading field 4: ", err) - } else { - p.BoolValue = &v - } - return nil -} - -func (p *BinaryAnnotation) ReadField5(iprot thrift.TProtocol) error { - if v, err := iprot.ReadBinary(); err != nil { - return thrift.PrependError("error reading field 5: ", err) - } else { - p.BytesValue = v - } - return nil -} - -func (p *BinaryAnnotation) ReadField6(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 6: ", err) - } else { - p.IntValue = &v - } - return nil -} - -func (p *BinaryAnnotation) ReadField7(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI32(); err != nil { - return thrift.PrependError("error reading field 7: ", err) - } else { - temp := AnnotationType(v) - p.AnnotationType = temp - } - return nil -} - -func (p *BinaryAnnotation) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("BinaryAnnotation"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := p.writeField2(oprot); err != nil { - return err - } - if err := p.writeField3(oprot); err != nil { - return err - } - if err := p.writeField4(oprot); err != nil { - return err - } - if err := p.writeField5(oprot); err != nil { - return err - } - if err := p.writeField6(oprot); err != nil { - return err - } - if err := p.writeField7(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *BinaryAnnotation) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("key", thrift.STRING, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:key: ", p), err) - } - if err := oprot.WriteString(string(p.Key)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.key (1) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:key: ", p), err) - } - return err -} - -func (p *BinaryAnnotation) writeField2(oprot thrift.TProtocol) (err error) { - if p.IsSetStringValue() { - if err := oprot.WriteFieldBegin("stringValue", thrift.STRING, 2); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:stringValue: ", p), err) - } - if err := oprot.WriteString(string(*p.StringValue)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.stringValue (2) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 2:stringValue: ", p), err) - } - } - return err -} - -func (p *BinaryAnnotation) writeField3(oprot thrift.TProtocol) (err error) { - if p.IsSetDoubleValue() { - if err := oprot.WriteFieldBegin("doubleValue", thrift.DOUBLE, 3); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:doubleValue: ", p), err) - } - if err := oprot.WriteDouble(float64(*p.DoubleValue)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.doubleValue (3) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 3:doubleValue: ", p), err) - } - } - return err -} - -func (p *BinaryAnnotation) writeField4(oprot thrift.TProtocol) (err error) { - if p.IsSetBoolValue() { - if err := oprot.WriteFieldBegin("boolValue", thrift.BOOL, 4); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:boolValue: ", p), err) - } - if err := oprot.WriteBool(bool(*p.BoolValue)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.boolValue (4) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 4:boolValue: ", p), err) - } - } - return err -} - -func (p *BinaryAnnotation) writeField5(oprot thrift.TProtocol) (err error) { - if p.IsSetBytesValue() { - if err := oprot.WriteFieldBegin("bytesValue", thrift.STRING, 5); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 5:bytesValue: ", p), err) - } - if err := oprot.WriteBinary(p.BytesValue); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.bytesValue (5) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 5:bytesValue: ", p), err) - } - } - return err -} - -func (p *BinaryAnnotation) writeField6(oprot thrift.TProtocol) (err error) { - if p.IsSetIntValue() { - if err := oprot.WriteFieldBegin("intValue", thrift.I64, 6); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 6:intValue: ", p), err) - } - if err := oprot.WriteI64(int64(*p.IntValue)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.intValue (6) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 6:intValue: ", p), err) - } - } - return err -} - -func (p *BinaryAnnotation) writeField7(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("annotationType", thrift.I32, 7); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 7:annotationType: ", p), err) - } - if err := oprot.WriteI32(int32(p.AnnotationType)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.annotationType (7) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 7:annotationType: ", p), err) - } - return err -} - -func (p *BinaryAnnotation) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("BinaryAnnotation(%+v)", *p) -} - -// Attributes: -// - TraceId -// - Host -// - Name -// - ID -// - ParentId -// - Annotations -// - BinaryAnnotations -// - Debug -// - SpanHost -type Span struct { - TraceId []byte `thrift:"traceId,1,required" db:"traceId" json:"traceId"` - Host *Endpoint `thrift:"host,2,required" db:"host" json:"host"` - Name string `thrift:"name,3,required" db:"name" json:"name"` - ID []byte `thrift:"id,4,required" db:"id" json:"id"` - ParentId []byte `thrift:"parentId,5,required" db:"parentId" json:"parentId"` - Annotations []*Annotation `thrift:"annotations,6,required" db:"annotations" json:"annotations"` - BinaryAnnotations []*BinaryAnnotation `thrift:"binaryAnnotations,7,required" db:"binaryAnnotations" json:"binaryAnnotations"` - Debug bool `thrift:"debug,8" db:"debug" json:"debug,omitempty"` - SpanHost *Endpoint `thrift:"spanHost,9" db:"spanHost" json:"spanHost,omitempty"` -} - -func NewSpan() *Span { - return &Span{} -} - -func (p *Span) GetTraceId() []byte { - return p.TraceId -} - -var Span_Host_DEFAULT *Endpoint - -func (p *Span) GetHost() *Endpoint { - if !p.IsSetHost() { - return Span_Host_DEFAULT - } - return p.Host -} - -func (p *Span) GetName() string { - return p.Name -} - -func (p *Span) GetID() []byte { - return p.ID -} - -func (p *Span) GetParentId() []byte { - return p.ParentId -} - -func (p *Span) GetAnnotations() []*Annotation { - return p.Annotations -} - -func (p *Span) GetBinaryAnnotations() []*BinaryAnnotation { - return p.BinaryAnnotations -} - -var Span_Debug_DEFAULT bool = false - -func (p *Span) GetDebug() bool { - return p.Debug -} - -var Span_SpanHost_DEFAULT *Endpoint - -func (p *Span) GetSpanHost() *Endpoint { - if !p.IsSetSpanHost() { - return Span_SpanHost_DEFAULT - } - return p.SpanHost -} -func (p *Span) IsSetHost() bool { - return p.Host != nil -} - -func (p *Span) IsSetDebug() bool { - return p.Debug != Span_Debug_DEFAULT -} - -func (p *Span) IsSetSpanHost() bool { - return p.SpanHost != nil -} - -func (p *Span) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - var issetTraceId bool = false - var issetHost bool = false - var issetName bool = false - var issetID bool = false - var issetParentId bool = false - var issetAnnotations bool = false - var issetBinaryAnnotations bool = false - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.ReadField1(iprot); err != nil { - return err - } - issetTraceId = true - case 2: - if err := p.ReadField2(iprot); err != nil { - return err - } - issetHost = true - case 3: - if err := p.ReadField3(iprot); err != nil { - return err - } - issetName = true - case 4: - if err := p.ReadField4(iprot); err != nil { - return err - } - issetID = true - case 5: - if err := p.ReadField5(iprot); err != nil { - return err - } - issetParentId = true - case 6: - if err := p.ReadField6(iprot); err != nil { - return err - } - issetAnnotations = true - case 7: - if err := p.ReadField7(iprot); err != nil { - return err - } - issetBinaryAnnotations = true - case 8: - if err := p.ReadField8(iprot); err != nil { - return err - } - case 9: - if err := p.ReadField9(iprot); err != nil { - return err - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - if !issetTraceId { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceId is not set")) - } - if !issetHost { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Host is not set")) - } - if !issetName { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Name is not set")) - } - if !issetID { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ID is not set")) - } - if !issetParentId { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ParentId is not set")) - } - if !issetAnnotations { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Annotations is not set")) - } - if !issetBinaryAnnotations { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field BinaryAnnotations is not set")) - } - return nil -} - -func (p *Span) ReadField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadBinary(); err != nil { - return thrift.PrependError("error reading field 1: ", err) - } else { - p.TraceId = v - } - return nil -} - -func (p *Span) ReadField2(iprot thrift.TProtocol) error { - p.Host = &Endpoint{} - if err := p.Host.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Host), err) - } - return nil -} - -func (p *Span) ReadField3(iprot thrift.TProtocol) error { - if v, err := iprot.ReadString(); err != nil { - return thrift.PrependError("error reading field 3: ", err) - } else { - p.Name = v - } - return nil -} - -func (p *Span) ReadField4(iprot thrift.TProtocol) error { - if v, err := iprot.ReadBinary(); err != nil { - return thrift.PrependError("error reading field 4: ", err) - } else { - p.ID = v - } - return nil -} - -func (p *Span) ReadField5(iprot thrift.TProtocol) error { - if v, err := iprot.ReadBinary(); err != nil { - return thrift.PrependError("error reading field 5: ", err) - } else { - p.ParentId = v - } - return nil -} - -func (p *Span) ReadField6(iprot thrift.TProtocol) error { - _, size, err := iprot.ReadListBegin() - if err != nil { - return thrift.PrependError("error reading list begin: ", err) - } - tSlice := make([]*Annotation, 0, size) - p.Annotations = tSlice - for i := 0; i < size; i++ { - _elem0 := &Annotation{} - if err := _elem0.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem0), err) - } - p.Annotations = append(p.Annotations, _elem0) - } - if err := iprot.ReadListEnd(); err != nil { - return thrift.PrependError("error reading list end: ", err) - } - return nil -} - -func (p *Span) ReadField7(iprot thrift.TProtocol) error { - _, size, err := iprot.ReadListBegin() - if err != nil { - return thrift.PrependError("error reading list begin: ", err) - } - tSlice := make([]*BinaryAnnotation, 0, size) - p.BinaryAnnotations = tSlice - for i := 0; i < size; i++ { - _elem1 := &BinaryAnnotation{} - if err := _elem1.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem1), err) - } - p.BinaryAnnotations = append(p.BinaryAnnotations, _elem1) - } - if err := iprot.ReadListEnd(); err != nil { - return thrift.PrependError("error reading list end: ", err) - } - return nil -} - -func (p *Span) ReadField8(iprot thrift.TProtocol) error { - if v, err := iprot.ReadBool(); err != nil { - return thrift.PrependError("error reading field 8: ", err) - } else { - p.Debug = v - } - return nil -} - -func (p *Span) ReadField9(iprot thrift.TProtocol) error { - p.SpanHost = &Endpoint{} - if err := p.SpanHost.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.SpanHost), err) - } - return nil -} - -func (p *Span) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("Span"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := p.writeField2(oprot); err != nil { - return err - } - if err := p.writeField3(oprot); err != nil { - return err - } - if err := p.writeField4(oprot); err != nil { - return err - } - if err := p.writeField5(oprot); err != nil { - return err - } - if err := p.writeField6(oprot); err != nil { - return err - } - if err := p.writeField7(oprot); err != nil { - return err - } - if err := p.writeField8(oprot); err != nil { - return err - } - if err := p.writeField9(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *Span) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("traceId", thrift.STRING, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:traceId: ", p), err) - } - if err := oprot.WriteBinary(p.TraceId); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.traceId (1) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:traceId: ", p), err) - } - return err -} - -func (p *Span) writeField2(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("host", thrift.STRUCT, 2); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:host: ", p), err) - } - if err := p.Host.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Host), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 2:host: ", p), err) - } - return err -} - -func (p *Span) writeField3(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("name", thrift.STRING, 3); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:name: ", p), err) - } - if err := oprot.WriteString(string(p.Name)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.name (3) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 3:name: ", p), err) - } - return err -} - -func (p *Span) writeField4(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("id", thrift.STRING, 4); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:id: ", p), err) - } - if err := oprot.WriteBinary(p.ID); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.id (4) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 4:id: ", p), err) - } - return err -} - -func (p *Span) writeField5(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("parentId", thrift.STRING, 5); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 5:parentId: ", p), err) - } - if err := oprot.WriteBinary(p.ParentId); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.parentId (5) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 5:parentId: ", p), err) - } - return err -} - -func (p *Span) writeField6(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("annotations", thrift.LIST, 6); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 6:annotations: ", p), err) - } - if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Annotations)); err != nil { - return thrift.PrependError("error writing list begin: ", err) - } - for _, v := range p.Annotations { - if err := v.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) - } - } - if err := oprot.WriteListEnd(); err != nil { - return thrift.PrependError("error writing list end: ", err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 6:annotations: ", p), err) - } - return err -} - -func (p *Span) writeField7(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("binaryAnnotations", thrift.LIST, 7); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 7:binaryAnnotations: ", p), err) - } - if err := oprot.WriteListBegin(thrift.STRUCT, len(p.BinaryAnnotations)); err != nil { - return thrift.PrependError("error writing list begin: ", err) - } - for _, v := range p.BinaryAnnotations { - if err := v.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) - } - } - if err := oprot.WriteListEnd(); err != nil { - return thrift.PrependError("error writing list end: ", err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 7:binaryAnnotations: ", p), err) - } - return err -} - -func (p *Span) writeField8(oprot thrift.TProtocol) (err error) { - if p.IsSetDebug() { - if err := oprot.WriteFieldBegin("debug", thrift.BOOL, 8); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 8:debug: ", p), err) - } - if err := oprot.WriteBool(bool(p.Debug)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.debug (8) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 8:debug: ", p), err) - } - } - return err -} - -func (p *Span) writeField9(oprot thrift.TProtocol) (err error) { - if p.IsSetSpanHost() { - if err := oprot.WriteFieldBegin("spanHost", thrift.STRUCT, 9); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 9:spanHost: ", p), err) - } - if err := p.SpanHost.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.SpanHost), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 9:spanHost: ", p), err) - } - } - return err -} - -func (p *Span) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("Span(%+v)", *p) -} - -// Attributes: -// - Ok -type Response struct { - Ok bool `thrift:"ok,1,required" db:"ok" json:"ok"` -} - -func NewResponse() *Response { - return &Response{} -} - -func (p *Response) GetOk() bool { - return p.Ok -} -func (p *Response) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - var issetOk bool = false - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.ReadField1(iprot); err != nil { - return err - } - issetOk = true - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - if !issetOk { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Ok is not set")) - } - return nil -} - -func (p *Response) ReadField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadBool(); err != nil { - return thrift.PrependError("error reading field 1: ", err) - } else { - p.Ok = v - } - return nil -} - -func (p *Response) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("Response"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *Response) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("ok", thrift.BOOL, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:ok: ", p), err) - } - if err := oprot.WriteBool(bool(p.Ok)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.ok (1) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:ok: ", p), err) - } - return err -} - -func (p *Response) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("Response(%+v)", *p) -} - -// Attributes: -// - SamplingRate -type ProbabilisticSamplingStrategy struct { - SamplingRate float64 `thrift:"samplingRate,1,required" db:"samplingRate" json:"samplingRate"` -} - -func NewProbabilisticSamplingStrategy() *ProbabilisticSamplingStrategy { - return &ProbabilisticSamplingStrategy{} -} - -func (p *ProbabilisticSamplingStrategy) GetSamplingRate() float64 { - return p.SamplingRate -} -func (p *ProbabilisticSamplingStrategy) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - var issetSamplingRate bool = false - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.ReadField1(iprot); err != nil { - return err - } - issetSamplingRate = true - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - if !issetSamplingRate { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field SamplingRate is not set")) - } - return nil -} - -func (p *ProbabilisticSamplingStrategy) ReadField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadDouble(); err != nil { - return thrift.PrependError("error reading field 1: ", err) - } else { - p.SamplingRate = v - } - return nil -} - -func (p *ProbabilisticSamplingStrategy) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("ProbabilisticSamplingStrategy"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *ProbabilisticSamplingStrategy) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("samplingRate", thrift.DOUBLE, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:samplingRate: ", p), err) - } - if err := oprot.WriteDouble(float64(p.SamplingRate)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.samplingRate (1) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:samplingRate: ", p), err) - } - return err -} - -func (p *ProbabilisticSamplingStrategy) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("ProbabilisticSamplingStrategy(%+v)", *p) -} - -// Attributes: -// - MaxTracesPerSecond -type RateLimitingSamplingStrategy struct { - MaxTracesPerSecond int16 `thrift:"maxTracesPerSecond,1,required" db:"maxTracesPerSecond" json:"maxTracesPerSecond"` -} - -func NewRateLimitingSamplingStrategy() *RateLimitingSamplingStrategy { - return &RateLimitingSamplingStrategy{} -} - -func (p *RateLimitingSamplingStrategy) GetMaxTracesPerSecond() int16 { - return p.MaxTracesPerSecond -} -func (p *RateLimitingSamplingStrategy) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - var issetMaxTracesPerSecond bool = false - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.ReadField1(iprot); err != nil { - return err - } - issetMaxTracesPerSecond = true - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - if !issetMaxTracesPerSecond { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field MaxTracesPerSecond is not set")) - } - return nil -} - -func (p *RateLimitingSamplingStrategy) ReadField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI16(); err != nil { - return thrift.PrependError("error reading field 1: ", err) - } else { - p.MaxTracesPerSecond = v - } - return nil -} - -func (p *RateLimitingSamplingStrategy) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("RateLimitingSamplingStrategy"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *RateLimitingSamplingStrategy) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("maxTracesPerSecond", thrift.I16, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:maxTracesPerSecond: ", p), err) - } - if err := oprot.WriteI16(int16(p.MaxTracesPerSecond)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.maxTracesPerSecond (1) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:maxTracesPerSecond: ", p), err) - } - return err -} - -func (p *RateLimitingSamplingStrategy) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("RateLimitingSamplingStrategy(%+v)", *p) -} - -// Attributes: -// - StrategyType -// - ProbabilisticSampling -// - RateLimitingSampling -type SamplingStrategyResponse struct { - StrategyType SamplingStrategyType `thrift:"strategyType,1,required" db:"strategyType" json:"strategyType"` - ProbabilisticSampling *ProbabilisticSamplingStrategy `thrift:"probabilisticSampling,2" db:"probabilisticSampling" json:"probabilisticSampling,omitempty"` - RateLimitingSampling *RateLimitingSamplingStrategy `thrift:"rateLimitingSampling,3" db:"rateLimitingSampling" json:"rateLimitingSampling,omitempty"` -} - -func NewSamplingStrategyResponse() *SamplingStrategyResponse { - return &SamplingStrategyResponse{} -} - -func (p *SamplingStrategyResponse) GetStrategyType() SamplingStrategyType { - return p.StrategyType -} - -var SamplingStrategyResponse_ProbabilisticSampling_DEFAULT *ProbabilisticSamplingStrategy - -func (p *SamplingStrategyResponse) GetProbabilisticSampling() *ProbabilisticSamplingStrategy { - if !p.IsSetProbabilisticSampling() { - return SamplingStrategyResponse_ProbabilisticSampling_DEFAULT - } - return p.ProbabilisticSampling -} - -var SamplingStrategyResponse_RateLimitingSampling_DEFAULT *RateLimitingSamplingStrategy - -func (p *SamplingStrategyResponse) GetRateLimitingSampling() *RateLimitingSamplingStrategy { - if !p.IsSetRateLimitingSampling() { - return SamplingStrategyResponse_RateLimitingSampling_DEFAULT - } - return p.RateLimitingSampling -} -func (p *SamplingStrategyResponse) IsSetProbabilisticSampling() bool { - return p.ProbabilisticSampling != nil -} - -func (p *SamplingStrategyResponse) IsSetRateLimitingSampling() bool { - return p.RateLimitingSampling != nil -} - -func (p *SamplingStrategyResponse) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - var issetStrategyType bool = false - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.ReadField1(iprot); err != nil { - return err - } - issetStrategyType = true - case 2: - if err := p.ReadField2(iprot); err != nil { - return err - } - case 3: - if err := p.ReadField3(iprot); err != nil { - return err - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - if !issetStrategyType { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field StrategyType is not set")) - } - return nil -} - -func (p *SamplingStrategyResponse) ReadField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI32(); err != nil { - return thrift.PrependError("error reading field 1: ", err) - } else { - temp := SamplingStrategyType(v) - p.StrategyType = temp - } - return nil -} - -func (p *SamplingStrategyResponse) ReadField2(iprot thrift.TProtocol) error { - p.ProbabilisticSampling = &ProbabilisticSamplingStrategy{} - if err := p.ProbabilisticSampling.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.ProbabilisticSampling), err) - } - return nil -} - -func (p *SamplingStrategyResponse) ReadField3(iprot thrift.TProtocol) error { - p.RateLimitingSampling = &RateLimitingSamplingStrategy{} - if err := p.RateLimitingSampling.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.RateLimitingSampling), err) - } - return nil -} - -func (p *SamplingStrategyResponse) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("SamplingStrategyResponse"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := p.writeField2(oprot); err != nil { - return err - } - if err := p.writeField3(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *SamplingStrategyResponse) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("strategyType", thrift.I32, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:strategyType: ", p), err) - } - if err := oprot.WriteI32(int32(p.StrategyType)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.strategyType (1) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:strategyType: ", p), err) - } - return err -} - -func (p *SamplingStrategyResponse) writeField2(oprot thrift.TProtocol) (err error) { - if p.IsSetProbabilisticSampling() { - if err := oprot.WriteFieldBegin("probabilisticSampling", thrift.STRUCT, 2); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:probabilisticSampling: ", p), err) - } - if err := p.ProbabilisticSampling.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.ProbabilisticSampling), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 2:probabilisticSampling: ", p), err) - } - } - return err -} - -func (p *SamplingStrategyResponse) writeField3(oprot thrift.TProtocol) (err error) { - if p.IsSetRateLimitingSampling() { - if err := oprot.WriteFieldBegin("rateLimitingSampling", thrift.STRUCT, 3); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:rateLimitingSampling: ", p), err) - } - if err := p.RateLimitingSampling.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.RateLimitingSampling), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 3:rateLimitingSampling: ", p), err) - } - } - return err -} - -func (p *SamplingStrategyResponse) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("SamplingStrategyResponse(%+v)", *p) -} diff --git a/trace/thrift/mocks/TChanTCollector.go b/trace/thrift/mocks/TChanTCollector.go deleted file mode 100644 index c1cb8201..00000000 --- a/trace/thrift/mocks/TChanTCollector.go +++ /dev/null @@ -1,74 +0,0 @@ -package mocks - -import "github.com/uber/tchannel-go/trace/thrift/gen-go/tcollector" -import "github.com/stretchr/testify/mock" - -import "github.com/uber/tchannel-go/thrift" - -type TChanTCollector struct { - mock.Mock -} - -func (_m *TChanTCollector) GetSamplingStrategy(_ctx thrift.Context, _serviceName string) (*tcollector.SamplingStrategyResponse, error) { - ret := _m.Called(_ctx, _serviceName) - - var r0 *tcollector.SamplingStrategyResponse - if rf, ok := ret.Get(0).(func(thrift.Context, string) *tcollector.SamplingStrategyResponse); ok { - r0 = rf(_ctx, _serviceName) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*tcollector.SamplingStrategyResponse) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(thrift.Context, string) error); ok { - r1 = rf(_ctx, _serviceName) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} -func (_m *TChanTCollector) Submit(_ctx thrift.Context, _span *tcollector.Span) (*tcollector.Response, error) { - ret := _m.Called(_ctx, _span) - - var r0 *tcollector.Response - if rf, ok := ret.Get(0).(func(thrift.Context, *tcollector.Span) *tcollector.Response); ok { - r0 = rf(_ctx, _span) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*tcollector.Response) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(thrift.Context, *tcollector.Span) error); ok { - r1 = rf(_ctx, _span) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} -func (_m *TChanTCollector) SubmitBatch(_ctx thrift.Context, _spans []*tcollector.Span) ([]*tcollector.Response, error) { - ret := _m.Called(_ctx, _spans) - - var r0 []*tcollector.Response - if rf, ok := ret.Get(0).(func(thrift.Context, []*tcollector.Span) []*tcollector.Response); ok { - r0 = rf(_ctx, _spans) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]*tcollector.Response) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(thrift.Context, []*tcollector.Span) error); ok { - r1 = rf(_ctx, _spans) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/tracereporter.go b/tracereporter.go deleted file mode 100644 index ed6f4f3b..00000000 --- a/tracereporter.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright (c) 2015 Uber Technologies, Inc. - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package tchannel - -import ( - "log" - "time" -) - -// When reporting spans, we report: -// bunch of "binary" annotations (eg - -// AnnotationKey is the key for annotations. -type AnnotationKey string - -// Known annotation keys -const ( - AnnotationKeyClientSend = "cs" - AnnotationKeyClientReceive = "cr" - AnnotationKeyServerSend = "ss" - AnnotationKeyServerReceive = "sr" -) - -// TraceEndpoint represents a service endpoint. -type TraceEndpoint struct { - HostPort string - ServiceName string -} - -// TraceData is the data reported to the TracerReporter. -type TraceData struct { - Span Span - Annotations []Annotation - BinaryAnnotations []BinaryAnnotation - Source TraceEndpoint - Target TraceEndpoint - Method string -} - -// BinaryAnnotation is additional context information about the span. -type BinaryAnnotation struct { - Key string - // Value contains one of: string, float64, bool, []byte, int64 - Value interface{} -} - -// Annotation represents a specific event and the timestamp at which it occurred. -type Annotation struct { - Key AnnotationKey - Timestamp time.Time -} - -// TraceReporter is the interface used to report Trace spans. -type TraceReporter interface { - // Report method is intended to report Span information. - Report(data TraceData) -} - -// TraceReporterFunc allows using a function as a TraceReporter. -type TraceReporterFunc func(TraceData) - -// Report calls the underlying function. -func (f TraceReporterFunc) Report(data TraceData) { - f(data) -} - -// NullReporter is the default TraceReporter which does not do anything. -var NullReporter TraceReporter = nullReporter{} - -type nullReporter struct{} - -func (nullReporter) Report(_ TraceData) { -} - -// SimpleTraceReporter is a trace reporter which prints using the default logger. -var SimpleTraceReporter TraceReporter = simpleTraceReporter{} - -type simpleTraceReporter struct{} - -func (simpleTraceReporter) Report(data TraceData) { - log.Printf("SimpleTraceReporter.Report span: %+v", data) -} - -// Annotations is used to track annotations and report them to a TraceReporter. -type Annotations struct { - timeNow func() time.Time - reporter TraceReporter - data TraceData - - annotationsBacking [2]Annotation - binaryAnnotationsBacking [2]BinaryAnnotation -} - -// SetMethod sets the method being called. -func (as *Annotations) SetMethod(method string) { - as.data.Method = method -} - -// GetTime returns the time using the timeNow function stored in the annotations. -func (as *Annotations) GetTime() time.Time { - return as.timeNow() -} - -// AddBinaryAnnotation adds a binary annotation. -func (as *Annotations) AddBinaryAnnotation(key string, value interface{}) { - binaryAnnotation := BinaryAnnotation{Key: key, Value: value} - as.data.BinaryAnnotations = append(as.data.BinaryAnnotations, binaryAnnotation) -} - -// AddAnnotationAt adds a standard annotation with the specified time. -func (as *Annotations) AddAnnotationAt(key AnnotationKey, ts time.Time) { - annotation := Annotation{Key: key, Timestamp: ts} - as.data.Annotations = append(as.data.Annotations, annotation) -} - -// Report reports the annotations to the given trace reporter, if tracing is enabled in the span. -func (as *Annotations) Report() { - if as.data.Span.TracingEnabled() { - as.reporter.Report(as.data) - } -} diff --git a/tracing.go b/tracing.go index 0f0b5669..7fa70e6d 100644 --- a/tracing.go +++ b/tracing.go @@ -22,23 +22,26 @@ package tchannel import ( "fmt" - "math/rand" + "net" + "strconv" + "time" "github.com/uber/tchannel-go/trand" "github.com/uber/tchannel-go/typed" -) - -const ( - tracingFlagEnabled byte = 0x01 - // The default tracing flags used when a new root span is created. - defaultTracingFlags = tracingFlagEnabled + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "golang.org/x/net/context" ) -// traceRng is a thread-safe random number generator for generating trace IDs. -var traceRng = trand.NewSeeded() +// zipkinSpanFormat defines a name for OpenTracing carrier format that tracer may support. +// It is used to extract zipkin-style trace/span IDs from the OpenTracing Span, which are +// otherwise not exposed explicitly. +// NB: the string value is what's actually shared between implementations +const zipkinSpanFormat = "zipkin-span-format" -// Span represents Zipkin-style span. +// Span is an internal representation of Zipkin-compatible OpenTracing Span. +// It is used as OpenTracing inject/extract Carrier with ZipkinSpanFormat. type Span struct { traceID uint64 parentID uint64 @@ -46,8 +49,17 @@ type Span struct { flags byte } +var ( + // traceRng is a thread-safe random number generator for generating trace IDs. + traceRng = trand.NewSeeded() + + // emptySpan is returned from CurrentSpan(ctx) when there is no OpenTracing + // Span in ctx, to avoid returning nil. + emptySpan Span +) + func (s Span) String() string { - return fmt.Sprintf("TraceID=%d,ParentID=%d,SpanID=%d", s.traceID, s.parentID, s.spanID) + return fmt.Sprintf("TraceID=%x,ParentID=%x,SpanID=%x", s.traceID, s.parentID, s.spanID) } func (s *Span) read(r *typed.ReadBuffer) error { @@ -66,16 +78,14 @@ func (s *Span) write(w *typed.WriteBuffer) error { return w.Err() } -// NewRootSpan creates a new top-level Span for a call-graph within the provided context -func NewRootSpan() *Span { - return &Span{ - traceID: uint64(traceRng.Int63()), - flags: defaultTracingFlags, - } +func (s *Span) initRandom() { + s.traceID = uint64(traceRng.Int63()) + s.spanID = s.traceID + s.parentID = 0 } -// TraceID returns the trace id for the entire call graph of requests. Established at the outermost -// edge service and propagated through all calls +// TraceID returns the trace id for the entire call graph of requests. Established +// at the outermost edge service and propagated through all calls func (s Span) TraceID() uint64 { return s.traceID } // ParentID returns the id of the parent span in this call graph @@ -84,53 +94,165 @@ func (s Span) ParentID() uint64 { return s.parentID } // SpanID returns the id of this specific RPC func (s Span) SpanID() uint64 { return s.spanID } -// EnableTracing controls whether tracing is enabled for this context -func (s *Span) EnableTracing(enabled bool) { - if enabled { - s.flags |= tracingFlagEnabled - } else { - s.flags &= ^tracingFlagEnabled - } +// Flags returns flags bitmap. Interpretation of the bits is up to the tracing system. +func (s Span) Flags() byte { return s.flags } + +type injectableSpan Span + +// SetTraceID sets traceID +func (s *injectableSpan) SetTraceID(traceID uint64) { s.traceID = traceID } + +// SetSpanID sets spanID +func (s *injectableSpan) SetSpanID(spanID uint64) { s.spanID = spanID } + +// SetParentID sets parentID +func (s *injectableSpan) SetParentID(parentID uint64) { s.parentID = parentID } + +// SetFlags sets flags +func (s *injectableSpan) SetFlags(flags byte) { s.flags = flags } + +// initFromOpenTracing initializes injectableSpan fields from an OpenTracing Span, +// assuming the tracing implementation supports Zipkin-style span IDs. +func (s *injectableSpan) initFromOpenTracing(span opentracing.Span) error { + return span.Tracer().Inject(span.Context(), zipkinSpanFormat, s) } -// TracingEnabled checks whether tracing is enabled for this context -func (s Span) TracingEnabled() bool { return (s.flags & tracingFlagEnabled) == tracingFlagEnabled } +// CurrentSpan extracts OpenTracing Span from the Context, and if found tries to +// extract zipkin-style trace/span IDs from it using ZipkinSpanFormat carrier. +// If there is no OpenTracing Span in the Context, an empty span is returned. +func CurrentSpan(ctx context.Context) *Span { + if sp := opentracing.SpanFromContext(ctx); sp != nil { + var injectable injectableSpan + if err := injectable.initFromOpenTracing(sp); err == nil { + span := Span(injectable) + return &span + } + // return empty span on error, instead of possibly a partially filled one + } + return &emptySpan +} -// NewChildSpan begins a new child span in the provided Context -func (s Span) NewChildSpan() *Span { - childSpan := &Span{ - traceID: s.traceID, - parentID: s.spanID, - flags: s.flags, +// startOutboundSpan creates a new tracing span to represent the outbound RPC call. +// If the context already contains a span, it will be used as a parent, otherwise +// a new root span is created. +// +// If the tracer supports Zipkin-style trace IDs, then call.callReq.Tracing is +// initialized with those IDs. Otherwise it is assigned random values. +func (c *Connection) startOutboundSpan(ctx context.Context, serviceName, methodName string, call *OutboundCall, startTime time.Time) opentracing.Span { + var parent opentracing.SpanContext // ok to be nil + if s := opentracing.SpanFromContext(ctx); s != nil { + parent = s.Context() + } + span := c.Tracer().StartSpan( + methodName, + opentracing.ChildOf(parent), + opentracing.StartTime(startTime), + ) + if isTracingDisabled(ctx) { + ext.SamplingPriority.Set(span, 0) } - if s.spanID == 0 { - childSpan.spanID = childSpan.traceID + ext.SpanKindRPCClient.Set(span) + ext.PeerService.Set(span, serviceName) + setPeerHostPort(span, c.remotePeerInfo.HostPort) + span.SetTag("as", call.callReq.Headers[ArgScheme]) + injectable := injectableSpan{} + if err := injectable.initFromOpenTracing(span); err == nil { + call.callReq.Tracing = Span(injectable) } else { - childSpan.spanID = uint64(traceRng.Int63()) + call.callReq.Tracing.initRandom() } - return childSpan + return span } -func (s *Span) sampleRootSpan(sampleRate float64) { - // Sampling only affects root spans - if s.ParentID() != 0 { - return +// InjectOutboundSpan retrieves OpenTracing Span from `response`, where it is stored +// when the outbound call is initiated. The tracing API is used to serialize the span +// into the application `headers`, which will propagate tracing context to the server. +// Returns modified headers containing serialized tracing context. +func InjectOutboundSpan(response *OutboundCallResponse, headers map[string]string) map[string]string { + span := response.span + if span == nil { + return headers + } + if headers == nil { + headers = make(map[string]string) } + carrier := opentracing.TextMapCarrier(headers) + if err := span.Tracer().Inject(span.Context(), opentracing.TextMap, carrier); err != nil { + // Something had to go seriously wrong for Inject to fail, usually a setup problem. + // A good Tracer implementation may also emit a metric. + response.log.Error("Failed to inject tracing span: " + err.Error()) + } + return headers +} - if rand.Float64() > sampleRate { - s.EnableTracing(false) +// extractInboundSpan attempts to create a new OpenTracing Span for inbound request +// using only trace IDs stored in the frame's tracing field. It only works if the +// tracer understand Zipkin-style trace IDs. If such attempt fails, another attempt +// will be made from the higher level function ExtractInboundSpan() once the +// application headers are read from the wire. +func (c *Connection) extractInboundSpan(callReq *callReq) opentracing.Span { + spanCtx, err := c.Tracer().Extract(zipkinSpanFormat, &callReq.Tracing) + if err != nil { + if err != opentracing.ErrUnsupportedFormat && err != opentracing.ErrSpanContextNotFound { + c.log.Error("Failed to extract Zipkin-style span: " + err.Error()) + } + return nil + } + if spanCtx == nil { + return nil } + operationName := "" // not known at this point, will be set later + span := c.Tracer().StartSpan(operationName, ext.RPCServerOption(spanCtx)) + span.SetTag("as", callReq.Headers[ArgScheme]) + ext.PeerService.Set(span, callReq.Headers[CallerName]) + setPeerHostPort(span, c.remotePeerInfo.HostPort) + return span } -func newSpan(traceID, spanID, parentID uint64, tracingEnabled bool) *Span { - flags := byte(0) - if tracingEnabled { - flags = tracingFlagEnabled +// ExtractInboundSpan is a higher level version of extractInboundSpan(). +// If the lower-level attempt to create a span from incoming request was +// successful (e.g. when then Tracer supports Zipkin-style trace IDs), +// then the application headers are only used to read the Baggage and add +// it to the existing span. Otherwise, the standard OpenTracing API supported +// by all tracers is used to deserialize the tracing context from the +// application headers and start a new server-side span. +// Once the span is started, it is wrapped in a new Context, which is returned. +func ExtractInboundSpan(ctx context.Context, call *InboundCall, headers map[string]string, tracer opentracing.Tracer) context.Context { + var span = call.Response().span + if span != nil { + if headers != nil { + // extract SpanContext from headers, but do not start another span with it, + // just get the baggage and copy to the already created span + carrier := opentracing.TextMapCarrier(headers) + if sc, err := tracer.Extract(opentracing.TextMap, carrier); err == nil { + sc.ForeachBaggageItem(func(k, v string) bool { + span.Context().SetBaggageItem(k, v) + return true + }) + } + } + } else { + var parent opentracing.SpanContext + if headers != nil { + carrier := opentracing.TextMapCarrier(headers) + if p, err := tracer.Extract(opentracing.TextMap, carrier); err == nil { + parent = p + } + } + span = tracer.StartSpan(call.MethodString(), ext.RPCServerOption(parent)) + ext.PeerService.Set(span, call.CallerName()) + span.SetTag("as", string(call.Format())) + setPeerHostPort(span, call.RemotePeer().HostPort) + call.Response().span = span } - return &Span{ - traceID: traceID, - spanID: spanID, - parentID: parentID, - flags: flags, + return opentracing.ContextWithSpan(ctx, span) +} + +func setPeerHostPort(span opentracing.Span, hostPort string) { + if host, port, err := net.SplitHostPort(hostPort); err == nil { + ext.PeerHostname.Set(span, host) + if p, err := strconv.Atoi(port); err == nil { + ext.PeerPort.Set(span, uint16(p)) + } } } diff --git a/tracing_internal_test.go b/tracing_internal_test.go index 20bf8a1f..c0d526ee 100644 --- a/tracing_internal_test.go +++ b/tracing_internal_test.go @@ -25,8 +25,12 @@ import ( "github.com/uber/tchannel-go/typed" + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/opentracing/opentracing-go/mocktracer" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "golang.org/x/net/context" ) func TestTracingSpanEncoding(t *testing.T) { @@ -57,3 +61,114 @@ func TestTracingSpanEncoding(t *testing.T) { assert.Equal(t, s1, s2, "Roundtrip of span failed") } + +func TestTracingInjectorExtractor(t *testing.T) { + tracer := mocktracer.New() + tracer.RegisterInjector(zipkinSpanFormat, new(zipkinInjector)) + tracer.RegisterExtractor(zipkinSpanFormat, new(zipkinExtractor)) + + sp := tracer.StartSpan("x") + injectable := injectableSpan{} + err := tracer.Inject(sp.Context(), zipkinSpanFormat, &injectable) + require.NoError(t, err) + + tsp := Span(injectable) + assert.NotEqual(t, uint64(0), tsp.TraceID()) + assert.NotEqual(t, uint64(0), tsp.SpanID()) + + sp2, err := tracer.Extract(zipkinSpanFormat, &tsp) + require.NoError(t, err) + require.NotNil(t, sp2) +} + +func TestSpanString(t *testing.T) { + span := Span{traceID: 15} + assert.Equal(t, "TraceID=f,ParentID=0,SpanID=0", span.String()) +} + +func TestExtractInboundSpanWithZipkinTracer(t *testing.T) { + tracer := mocktracer.New() + callReq := new(callReq) + callReq.Tracing = Span{traceID: 1, spanID: 2, flags: 1} + callReq.Headers = transportHeaders{ + ArgScheme: string(JSON), + CallerName: "caller", + } + c := Connection{ + channelConnectionCommon: channelConnectionCommon{tracer: tracer}, + remotePeerInfo: PeerInfo{HostPort: "host:123"}, + } + + // fail to extract with zipkin format, as MockTracer does not support it + assert.Nil(t, c.extractInboundSpan(callReq), "zipkin format not available") + + // add zipkin format extractor and try again + tracer.RegisterExtractor(zipkinSpanFormat, new(zipkinExtractor)) + span := c.extractInboundSpan(callReq) + require.NotNil(t, span, "zipkin format available") + + // validate the extracted span was correctly populated + s1, ok := span.(*mocktracer.MockSpan) + require.True(t, ok) + assert.Equal(t, 1, s1.SpanContext.TraceID) + assert.Equal(t, 2, s1.ParentID) + assert.True(t, s1.SpanContext.Sampled) + assert.Equal(t, "", s1.OperationName, "operation name unknown initially") + assert.Equal(t, string(JSON), s1.Tag("as")) + assert.Equal(t, "caller", s1.Tag(string(ext.PeerService))) + assert.Equal(t, "host", s1.Tag(string(ext.PeerHostname))) + assert.Equal(t, uint16(123), s1.Tag(string(ext.PeerPort))) + + // start a temporary span so that we can populate headers with baggage + tempSpan := tracer.StartSpan("test") + tempSpan.Context().SetBaggageItem("x", "y") + headers := make(map[string]string) + carrier := opentracing.TextMapCarrier(headers) + err := tracer.Inject(tempSpan.Context(), opentracing.TextMap, carrier) + assert.NoError(t, err) + + // run the public ExtractInboundSpan method with application headers + inCall := &InboundCall{ + response: &InboundCallResponse{ + span: span, + }, + } + ctx := context.Background() + ctx2 := ExtractInboundSpan(ctx, inCall, headers, tracer) + span = opentracing.SpanFromContext(ctx2) + s2, ok := span.(*mocktracer.MockSpan) + require.True(t, ok) + assert.Equal(t, s1, s2, "should be the same span started previously") + assert.Equal(t, "y", s2.Context().BaggageItem("x"), "baggage should've been added") +} + +type zipkinInjector struct{} + +func (z *zipkinInjector) Inject(sc *mocktracer.MockSpanContext, carrier interface{}) error { + span, ok := carrier.(*injectableSpan) + if !ok { + return opentracing.ErrInvalidCarrier + } + span.SetTraceID(uint64(sc.TraceID)) + span.SetSpanID(uint64(sc.SpanID)) + if sc.Sampled { + span.SetFlags(1) + } else { + span.SetFlags(0) + } + return nil +} + +type zipkinExtractor struct{} + +func (z *zipkinExtractor) Extract(carrier interface{}) (*mocktracer.MockSpanContext, error) { + span, ok := carrier.(*Span) + if !ok { + return nil, opentracing.ErrInvalidCarrier + } + return &mocktracer.MockSpanContext{ + TraceID: int(span.traceID), + SpanID: int(span.spanID), + Sampled: span.flags&1 == 1, + }, nil +} diff --git a/tracing_test.go b/tracing_test.go index 69c5a67a..1d013ee3 100644 --- a/tracing_test.go +++ b/tracing_test.go @@ -21,314 +21,120 @@ package tchannel_test import ( - "fmt" - "math/rand" "testing" "time" . "github.com/uber/tchannel-go" - "github.com/uber/tchannel-go/json" - "github.com/uber/tchannel-go/raw" "github.com/uber/tchannel-go/testutils" + "github.com/uber/tchannel-go/testutils/testtracing" + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/opentracing/opentracing-go/mocktracer" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/net/context" ) -type TracingRequest struct { - ForwardCount int +// JSONHandler tests tracing over JSON encoding +type JSONHandler struct { + testtracing.TraceHandler + t *testing.T } -type TracingResponse struct { - TraceID uint64 - SpanID uint64 - ParentID uint64 - TracingEnabled bool - Child *TracingResponse +func (h *JSONHandler) callJSON(ctx json.Context, req *testtracing.TracingRequest) (*testtracing.TracingResponse, error) { + resp := new(testtracing.TracingResponse) + resp.ObserveSpan(ctx) + return resp, nil } -type traceHandler struct { - ch *Channel - t *testing.T -} +func (h *JSONHandler) onError(ctx context.Context, err error) { h.t.Errorf("onError %v", err) } -func (h *traceHandler) call(ctx json.Context, req *TracingRequest) (*TracingResponse, error) { - span := CurrentSpan(ctx) - if span == nil { - return nil, fmt.Errorf("tracing not found") - } +func TestTracingSpanAttributes(t *testing.T) { + tracer := mocktracer.New() - var childResp *TracingResponse - if req.ForwardCount > 0 { - sc := h.ch.Peers().GetOrAdd(h.ch.PeerInfo().HostPort) - childResp = new(TracingResponse) - require.NoError(h.t, json.CallPeer(ctx, sc, h.ch.PeerInfo().ServiceName, "call", nil, childResp)) + opts := &testutils.ChannelOpts{ + ChannelOptions: ChannelOptions{Tracer: tracer}, + DisableRelay: true, } + WithVerifiedServer(t, opts, func(ch *Channel, hostPort string) { + // Register JSON handler + jsonHandler := &JSONHandler{TraceHandler: testtracing.TraceHandler{Ch: ch}, t: t} + json.Register(ch, json.Handlers{"call": jsonHandler.callJSON}, jsonHandler.onError) - return &TracingResponse{ - TraceID: span.TraceID(), - SpanID: span.SpanID(), - ParentID: span.ParentID(), - TracingEnabled: span.TracingEnabled(), - Child: childResp, - }, nil -} + span := ch.Tracer().StartSpan("client") + span.Context().SetBaggageItem(testtracing.BaggageKey, testtracing.BaggageValue) + ctx := opentracing.ContextWithSpan(context.Background(), span) + root := new(testtracing.TracingResponse).ObserveSpan(ctx) -func (h *traceHandler) onError(ctx context.Context, err error) { - h.t.Errorf("onError %v", err) -} - -func TestTracingPropagates(t *testing.T) { - WithVerifiedServer(t, nil, func(ch *Channel, hostPort string) { - handler := &traceHandler{t: t, ch: ch} - json.Register(ch, json.Handlers{ - "call": handler.call, - }, handler.onError) - - ctx, cancel := json.NewContext(time.Second) + ctx, cancel := NewContextBuilder(2 * time.Second).SetParentContext(ctx).Build() defer cancel() peer := ch.Peers().GetOrAdd(ch.PeerInfo().HostPort) - var response TracingResponse - require.NoError(t, json.CallPeer(ctx, peer, ch.PeerInfo().ServiceName, "call", &TracingRequest{ - ForwardCount: 1, - }, &response)) - - clientSpan := CurrentSpan(ctx) - require.NotNil(t, clientSpan) - assert.Equal(t, uint64(0), clientSpan.ParentID()) - assert.NotEqual(t, uint64(0), clientSpan.TraceID()) - assert.True(t, clientSpan.TracingEnabled(), "Tracing should be enabled") - assert.Equal(t, clientSpan.TraceID(), response.TraceID) - assert.Equal(t, clientSpan.SpanID(), response.ParentID) - assert.True(t, response.TracingEnabled, "Tracing should be enabled") - assert.Equal(t, response.TraceID, response.SpanID, "traceID = spanID for root span") - - nestedResponse := response.Child - require.NotNil(t, nestedResponse) - assert.Equal(t, clientSpan.TraceID(), nestedResponse.TraceID) - assert.Equal(t, response.SpanID, nestedResponse.ParentID) - assert.True(t, response.TracingEnabled, "Tracing should be enabled") - assert.NotEqual(t, response.SpanID, nestedResponse.SpanID) - }) -} - -func TestTraceReportingEnabled(t *testing.T) { - initialTime := time.Date(2015, 2, 1, 10, 10, 0, 0, time.UTC) - - var state struct { - signal chan struct{} - - call TraceData - span Span - } - testTraceReporter := TraceReporterFunc(func(data TraceData) { - defer close(state.signal) - - span := data.Span - data.Span = Span{} - state.call = data - state.span = span - }) - - traceReporterOpts := testutils.NewOpts().SetTraceReporter(testTraceReporter) - tests := []struct { - name string - serverOpts *testutils.ChannelOpts - clientOpts *testutils.ChannelOpts - expected []Annotation - fromServer bool - }{ - { - name: "inbound", - serverOpts: traceReporterOpts, - expected: []Annotation{ - {Key: "sr", Timestamp: initialTime.Add(2 * time.Second)}, - {Key: "ss", Timestamp: initialTime.Add(3 * time.Second)}, - }, - fromServer: true, - }, - { - name: "outbound", - clientOpts: traceReporterOpts, - expected: []Annotation{ - {Key: "cs", Timestamp: initialTime.Add(time.Second)}, - {Key: "cr", Timestamp: initialTime.Add(6 * time.Second)}, - }, - }, - } - - for _, tt := range tests { - serverNow, serverNowFn := testutils.NowStub(initialTime.Add(time.Second)) - clientNow, clientNowFn := testutils.NowStub(initialTime) - serverNowFn(time.Second) - clientNowFn(time.Second) - - // Note: we disable the relay as the relay shares the same options - // and since the relay would call timeNow, it causes a mismatch in - // the expected timestamps. - tt.serverOpts = testutils.DefaultOpts(tt.serverOpts).SetTimeNow(serverNow).NoRelay() - tt.clientOpts = testutils.DefaultOpts(tt.clientOpts).SetTimeNow(clientNow) - - WithVerifiedServer(t, tt.serverOpts, func(ch *Channel, hostPort string) { - state.signal = make(chan struct{}) - - testutils.RegisterEcho(ch, func() { - clientNowFn(5 * time.Second) - }) - - clientCh := testutils.NewClient(t, tt.clientOpts) - defer clientCh.Close() - ctx, cancel := NewContext(time.Second) - defer cancel() - - _, _, _, err := raw.Call(ctx, clientCh, hostPort, ch.PeerInfo().ServiceName, "echo", nil, []byte("arg3")) - require.NoError(t, err, "raw.Call failed") - - binaryAnnotations := []BinaryAnnotation{ - {"cn", clientCh.PeerInfo().ServiceName}, - {"as", Raw.String()}, - } - target := TraceEndpoint{ - HostPort: hostPort, - ServiceName: ch.ServiceName(), - } - source := target - if !tt.fromServer { - source = TraceEndpoint{ - HostPort: "0.0.0.0:0", - ServiceName: clientCh.ServiceName(), - } - } - - select { - case <-state.signal: - case <-time.After(time.Second): - t.Fatalf("Did not receive trace report within timeout") - } - - expected := TraceData{Annotations: tt.expected, BinaryAnnotations: binaryAnnotations, Source: source, Target: target, Method: "echo"} - assert.Equal(t, expected, state.call, "%v: Report args mismatch", tt.name) - curSpan := CurrentSpan(ctx) - assert.Equal(t, NewSpan(curSpan.TraceID(), curSpan.TraceID(), 0), state.span, "Span mismatch") - }) - } -} - -func TestTraceReportingDisabled(t *testing.T) { - var gotCalls int - testTraceReporter := TraceReporterFunc(func(_ TraceData) { - gotCalls++ - }) - - traceReporterOpts := testutils.NewOpts().SetTraceReporter(testTraceReporter) - WithVerifiedServer(t, traceReporterOpts, func(ch *Channel, hostPort string) { - ch.Register(raw.Wrap(newTestHandler(t)), "echo") - - ctx, cancel := NewContext(time.Second) - defer cancel() - - CurrentSpan(ctx).EnableTracing(false) - _, _, _, err := raw.Call(ctx, ch, hostPort, ch.PeerInfo().ServiceName, "echo", nil, []byte("arg3")) - require.NoError(t, err, "raw.Call failed") - - assert.Equal(t, 0, gotCalls, "TraceReporter should not report if disabled") - }) -} - -func TestTraceSamplingRate(t *testing.T) { - rand.Seed(10) - - tests := []struct { - sampleRate float64 // if this is < 0, then the value is not set. - count int - expectedMin int - expectedMax int - }{ - {1.0, 100, 100, 100}, - {0.5, 100, 40, 60}, - {0.1, 100, 5, 15}, - {0, 100, 0, 0}, - {-1, 100, 100, 100}, // default of 1.0 should be used. - } - - for _, tt := range tests { - WithVerifiedServer(t, nil, func(ch *Channel, hostPort string) { - var reportedTraces int - testTraceReporter := TraceReporterFunc(func(_ TraceData) { - reportedTraces++ - }) - - var tracedCalls int - testutils.RegisterFunc(ch, "t", func(ctx context.Context, args *raw.Args) (*raw.Res, error) { - if CurrentSpan(ctx).TracingEnabled() { - tracedCalls++ - } - - return &raw.Res{}, nil - }) - - opts := testutils.NewOpts().SetTraceReporter(testTraceReporter) - if tt.sampleRate >= 0 { - opts.SetTraceSampleRate(tt.sampleRate) + var response testtracing.TracingResponse + require.NoError(t, json.CallPeer(json.Wrap(ctx), peer, ch.PeerInfo().ServiceName, + "call", &testtracing.TracingRequest{}, &response)) + + // Spans are finished in inbound.doneSending() or outbound.doneReading(), + // which are called on different go-routines and may execute *after* the + // response has been received by the client. Give them a chance to finish. + for i := 0; i < 1000; i++ { + if spanCount := len(testtracing.MockTracerSampledSpans(tracer)); spanCount == 2 { + break } - - client := testutils.NewClient(t, opts) - defer client.Close() - - for i := 0; i < tt.count; i++ { - ctx, cancel := NewContext(time.Second) - defer cancel() - - _, _, _, err := raw.Call(ctx, client, hostPort, ch.PeerInfo().ServiceName, "t", nil, nil) - require.NoError(t, err, "raw.Call failed") + time.Sleep(testutils.Timeout(time.Millisecond)) + } + spans := testtracing.MockTracerSampledSpans(tracer) + spanCount := len(spans) + ch.Logger().Debugf("end span count: %d", spanCount) + + // finish span after taking count of recorded spans + span.Finish() + + require.Equal(t, 2, spanCount, "Wrong span count") + assert.Equal(t, root.TraceID, response.TraceID, "Trace ID must match root span") + assert.Equal(t, testtracing.BaggageValue, response.Luggage, "Baggage must match") + + var parent, child *mocktracer.MockSpan + for _, s := range spans { + if s.Tag("span.kind") == ext.SpanKindRPCClientEnum { + parent = s + ch.Logger().Debugf("Found parent span: %+v", s) + } else if s.Tag("span.kind") == ext.SpanKindRPCServerEnum { + child = s + ch.Logger().Debugf("Found child span: %+v", s) } - - assert.Equal(t, reportedTraces, tracedCalls, - "Number of traces report doesn't match calls with tracing enabled") - assert.True(t, tracedCalls >= tt.expectedMin, - "Number of trace enabled calls (%v) expected to be greater than %v", tracedCalls, tt.expectedMin) - assert.True(t, tracedCalls <= tt.expectedMax, - "Number of trace enabled calls (%v) expected to be less than %v", tracedCalls, tt.expectedMax) - }) - } -} - -func TestChildCallsNotSampled(t *testing.T) { - var traceEnabledCalls int - - s1 := testutils.NewServer(t, testutils.NewOpts().SetTraceSampleRate(0.0001)) - defer s1.Close() - s2 := testutils.NewServer(t, nil) - defer s2.Close() - - testutils.RegisterFunc(s1, "s1", func(ctx context.Context, args *raw.Args) (*raw.Res, error) { - _, _, _, err := raw.Call(ctx, s1, s2.PeerInfo().HostPort, s2.ServiceName(), "s2", nil, nil) - require.NoError(t, err, "raw.Call from s1 to s2 failed") - return &raw.Res{}, nil - }) - - testutils.RegisterFunc(s2, "s2", func(ctx context.Context, args *raw.Args) (*raw.Res, error) { - if CurrentSpan(ctx).TracingEnabled() { - traceEnabledCalls++ } - return &raw.Res{}, nil - }) - - client := testutils.NewClient(t, nil) - defer client.Close() - const numCalls = 100 - for i := 0; i < numCalls; i++ { - ctx, cancel := NewContext(time.Second) - defer cancel() + require.NotNil(t, parent) + require.NotNil(t, child) - _, _, _, err := raw.Call(ctx, client, s1.PeerInfo().HostPort, s1.ServiceName(), "s1", nil, nil) - require.NoError(t, err, "raw.Call to s1 failed") - } + traceID := func(s opentracing.Span) int { + return s.Context().(*mocktracer.MockSpanContext).TraceID + } + spanID := func(s *mocktracer.MockSpan) int { + return s.Context().(*mocktracer.MockSpanContext).SpanID + } + sampled := func(s *mocktracer.MockSpan) bool { + return s.Context().(*mocktracer.MockSpanContext).Sampled + } - // Even though s1 has sampling enabled, it should not affect incoming calls. - assert.Equal(t, numCalls, traceEnabledCalls, "Trace sampling should not inbound calls") + require.Equal(t, traceID(span), traceID(parent), "parent must be found") + require.Equal(t, traceID(span), traceID(child), "child must be found") + assert.Equal(t, traceID(parent), traceID(child)) + assert.Equal(t, spanID(parent), child.ParentID) + assert.True(t, sampled(parent), "should be sampled") + assert.True(t, sampled(child), "should be sampled") + assert.Equal(t, "call", parent.OperationName) + assert.Equal(t, "call", child.OperationName) + assert.Equal(t, "testService", parent.Tag("peer.service")) + assert.Equal(t, "testService", child.Tag("peer.service")) + assert.Equal(t, "json", parent.Tag("as")) + assert.Equal(t, "json", child.Tag("as")) + assert.NotNil(t, parent.Tag("peer.hostname")) + assert.NotNil(t, child.Tag("peer.hostname")) + assert.NotNil(t, parent.Tag("peer.port")) + assert.NotNil(t, child.Tag("peer.port")) + }) } diff --git a/utils_for_test.go b/utils_for_test.go index c548e648..3f1f8bd2 100644 --- a/utils_for_test.go +++ b/utils_for_test.go @@ -82,8 +82,3 @@ func InboundConnection(call IncomingCall) (*Connection, net.Conn) { conn := inboundCall.conn return conn, conn.conn } - -// NewSpan returns a Span for testing. -func NewSpan(traceID uint64, spanID uint64, parentID uint64) Span { - return Span{traceID: traceID, spanID: spanID, parentID: parentID, flags: defaultTracingFlags} -}