Skip to content

Commit

Permalink
Merge 3f847c4 into 18b7739
Browse files Browse the repository at this point in the history
  • Loading branch information
malafeev committed Jul 4, 2018
2 parents 18b7739 + 3f847c4 commit 6e3c4f3
Show file tree
Hide file tree
Showing 7 changed files with 269 additions and 20 deletions.
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ A `ClientTracingInterceptor` also has default settings, which you can override b

- `withOperationName(String operationName)`: Define how the operation name is constructed for all spans created for this intercepted client. Default is the name of the RPC method. More details in the `Operation Name` section.
- `withActiveSpanSource(ActiveSpanSource activeSpanSource)`: Define how to extract the current active span, if any. This is needed if you want your client to continue a trace instead of starting a new one. More details in the `Active Span Source` section.
- `withActiveSpanContextSource(ActiveSpanContextSource activeSpanContextSource)`: Define how to extract the current active span context, if any. This is needed if you want your client to continue a trace instead of starting a new one. More details in the `Active Span Source` section.
- `withStreaming()`: Logs to the client span whenever a message is sent or a response is received. *Note:* This package supports streaming but has not been rigorously tested. If you come across any issues, please let us know.
- `withVerbosity()`: Logs to the client span additional events, such as call started, message sent, half close (client finished sending messages), response received, and call complete. Default only logs if a call is cancelled.
- `withTracedAttributes(ClientRequestAttribute... attrs)`: Sets tags on the client span in case you want to track information about the RPC call. See ClientRequestAttribute.java for a list of traceable request attributes.
Expand Down Expand Up @@ -231,6 +232,32 @@ We also provide two built-in implementations:
- `ActiveSpanSource.GRPC_CONTEXT` uses the current `io.grpc.Context` and returns the active span for `OpenTracingContextKey`. This is the default active span source.
- `ActiveSpanSource.NONE` always returns null as the active span, which means the client will always start a new trace

## Active Span Context Sources

Instead of `ActiveSpanSource` it's possible to use `ActiveSpanContextSource` if span is not available

```java
import io.opentracing.Span;

ClientTracingInterceptor interceptor = new ClientTracingInterceptor
.Builder(tracer)
...
.withActiveSpanContextSource(new ActiveSpanContextSource() {
@Override
public Span getActiveSpanContext() {
return Context.get(OPENTRACING_SPAN_CONTEXT_KEY);
}
})
...
.build();
```

We also provide two built-in implementations:

- `ActiveSpanContextSource.GRPC_CONTEXT` uses the current `io.grpc.Context` and returns the active span context for `OpenTracingContextKey`.
- `ActiveSpanContextSource.NONE` always returns null as the active span context, which means the client will always start a new trace


## Integrating with Other Interceptors
Although we provide `ServerTracingInterceptor.intercept(service)` and `ClientTracingInterceptor.intercept(channel)` methods, you don't want to use these if you're chaining multiple interceptors. Instead, use the following code (preferably putting the tracing interceptor at the top of the interceptor stack so that it traces the entire request lifecycle, including other interceptors):

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2017-2018 The OpenTracing Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package io.opentracing.contrib.grpc;

import io.opentracing.SpanContext;

/**
* An interface that defines how to get the current active span context
*/
public interface ActiveSpanContextSource {

/**
* ActiveSpanContextSource implementation that always returns
* null as the active span context
*/
ActiveSpanContextSource NONE = new ActiveSpanContextSource() {
@Override
public SpanContext getActiveSpanContext() {
return null;
}
};

/**
* ActiveSpanContextSource implementation that returns the
* current span context stored in the GRPC context under
* {@link OpenTracingContextKey}
*/
ActiveSpanContextSource GRPC_CONTEXT = new ActiveSpanContextSource() {
@Override
public SpanContext getActiveSpanContext() {
return OpenTracingContextKey.activeSpanContext();
}
};

/**
* @return the active span context
*/
SpanContext getActiveSpanContext();

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import io.grpc.MethodDescriptor;
import io.grpc.Status;
import io.opentracing.Span;
import io.opentracing.SpanContext;
import io.opentracing.Tracer;
import io.opentracing.propagation.Format;
import io.opentracing.propagation.TextMap;
Expand All @@ -47,6 +48,7 @@ public class ClientTracingInterceptor implements ClientInterceptor {
private final boolean verbose;
private final Set<ClientRequestAttribute> tracedAttributes;
private final ActiveSpanSource activeSpanSource;
private final ActiveSpanContextSource activeSpanContextSource;

/**
* @param tracer to use to trace requests
Expand All @@ -58,18 +60,20 @@ public ClientTracingInterceptor(Tracer tracer) {
this.verbose = false;
this.tracedAttributes = new HashSet<ClientRequestAttribute>();
this.activeSpanSource = ActiveSpanSource.GRPC_CONTEXT;
this.activeSpanContextSource = null;
}

private ClientTracingInterceptor(Tracer tracer, OperationNameConstructor operationNameConstructor,
boolean streaming,
boolean verbose, Set<ClientRequestAttribute> tracedAttributes,
ActiveSpanSource activeSpanSource) {
ActiveSpanSource activeSpanSource, ActiveSpanContextSource activeSpanContextSource) {
this.tracer = tracer;
this.operationNameConstructor = operationNameConstructor;
this.streaming = streaming;
this.verbose = verbose;
this.tracedAttributes = tracedAttributes;
this.activeSpanSource = activeSpanSource;
this.activeSpanContextSource = activeSpanContextSource;
}

/**
Expand All @@ -82,6 +86,19 @@ public Channel intercept(Channel channel) {
return ClientInterceptors.intercept(channel, this);
}

private SpanContext getActiveSpanContext() {
if (activeSpanSource != null) {
Span activeSpan = activeSpanSource.getActiveSpan();
if (activeSpan != null) {
return activeSpan.context();
}
}
if (activeSpanContextSource != null) {
return activeSpanContextSource.getActiveSpanContext();
}
return null;
}

@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
MethodDescriptor<ReqT, RespT> method,
Expand All @@ -90,8 +107,8 @@ public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
) {
final String operationName = operationNameConstructor.constructOperationName(method);

Span activeSpan = this.activeSpanSource.getActiveSpan();
final Span span = createSpanFromParent(activeSpan, operationName);
SpanContext activeSpanContext = getActiveSpanContext();
final Span span = createSpanFromParent(activeSpanContext, operationName);

for (ClientRequestAttribute attr : this.tracedAttributes) {
switch (attr) {
Expand Down Expand Up @@ -230,11 +247,11 @@ public void sendMessage(ReqT message) {
};
}

private Span createSpanFromParent(Span parentSpan, String operationName) {
if (parentSpan == null) {
private Span createSpanFromParent(SpanContext parentSpanContext, String operationName) {
if (parentSpanContext == null) {
return tracer.buildSpan(operationName).start();
} else {
return tracer.buildSpan(operationName).asChildOf(parentSpan).start();
return tracer.buildSpan(operationName).asChildOf(parentSpanContext).start();
}
}

Expand All @@ -249,6 +266,7 @@ public static class Builder {
private boolean verbose;
private Set<ClientRequestAttribute> tracedAttributes;
private ActiveSpanSource activeSpanSource;
private ActiveSpanContextSource activeSpanContextSource;

/**
* @param tracer to use for this intercepter
Expand Down Expand Up @@ -314,12 +332,24 @@ public Builder withActiveSpanSource(ActiveSpanSource activeSpanSource) {
return this;
}

/**
* @param activeSpanContextSource that provides a method of getting the
* active span context before the client call
* @return this Builder configured to start client span as children
* of the span context returned by activeSpanContextSource.getActiveSpanContext()
*/
public Builder withActiveSpanContextSource(ActiveSpanContextSource activeSpanContextSource) {
this.activeSpanContextSource = activeSpanContextSource;
return this;
}

/**
* @return a ClientTracingInterceptor with this Builder's configuration
*/
public ClientTracingInterceptor build() {
return new ClientTracingInterceptor(this.tracer, this.operationNameConstructor,
this.streaming, this.verbose, this.tracedAttributes, this.activeSpanSource);
this.streaming, this.verbose, this.tracedAttributes, this.activeSpanSource,
this.activeSpanContextSource);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import io.grpc.Context;
import io.opentracing.Span;
import io.opentracing.SpanContext;

/**
* A {@link io.grpc.Context} key for the current OpenTracing trace state.
Expand All @@ -25,7 +26,9 @@
public class OpenTracingContextKey {

public static final String KEY_NAME = "io.opentracing.active-span";
public static final String KEY_CONTEXT_NAME = "io.opentracing.active-span-context";
private static final Context.Key<Span> key = Context.key(KEY_NAME);
private static final Context.Key<SpanContext> keyContext = Context.key(KEY_CONTEXT_NAME);

/**
* @return the active span for the current request
Expand All @@ -40,4 +43,15 @@ public static Span activeSpan() {
public static Context.Key<Span> getKey() {
return key;
}

/**
* @return the OpenTracing context key for span context
*/
public static Context.Key<SpanContext> getSpanContextKey() {
return keyContext;
}

public static SpanContext activeSpanContext() {
return keyContext.get();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
}
}

Context ctxWithSpan = Context.current().withValue(OpenTracingContextKey.getKey(), span);
Context ctxWithSpan = Context.current().withValue(OpenTracingContextKey.getKey(), span)
.withValue(OpenTracingContextKey.getSpanContextKey(), span.context());
ServerCall.Listener<ReqT> listenerWithContext = Contexts
.interceptCall(ctxWithSpan, call, headers, next);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright 2017-2018 The OpenTracing Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package io.opentracing.contrib.grpc;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

import io.grpc.Context;
import io.opentracing.Span;
import io.opentracing.mock.MockTracer;
import org.junit.Before;
import org.junit.Test;

public class ActiveSpanContextSourceTest {

private final MockTracer tracer = new MockTracer();

@Before
public void before() {
tracer.reset();
}

@Test
public void TestDefaultNone() {
ActiveSpanContextSource ss = ActiveSpanContextSource.NONE;
assertNull("active span context should always be null", ss.getActiveSpanContext());

Span span = tracer.buildSpan("s0").start();
Context ctx = Context.current().withValue(OpenTracingContextKey.getKey(), span);
Context previousCtx = ctx.attach();

assertNull("active span context should always be null", ss.getActiveSpanContext());

ctx.detach(previousCtx);
span.finish();
}

@Test
public void TestDefaultGrpc() {
ActiveSpanContextSource ss = ActiveSpanContextSource.GRPC_CONTEXT;
assertNull("active span context should be null, no span context in OpenTracingContextKey",
ss.getActiveSpanContext());

Span span = tracer.buildSpan("s0").start();
Context ctx = Context.current()
.withValue(OpenTracingContextKey.getSpanContextKey(), span.context());
Context previousCtx = ctx.attach();

assertEquals("active span context should be OpenTracingContextKey.activeSpanContext()",
ss.getActiveSpanContext(), span.context());

ctx.detach(previousCtx);
span.finish();

assertNull("active span context should be null, no span context in OpenTracingContextKey",
ss.getActiveSpanContext());
}

}
Loading

0 comments on commit 6e3c4f3

Please sign in to comment.