下面列出了org.springframework.boot.test.system.CapturedOutput#brave.Span.Kind 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
void beforeQuery(CON connectionKey, STMT statementKey, String dataSourceName) {
SpanWithScope spanWithScope = null;
if (traceTypes.contains(TraceType.QUERY)) {
Span statementSpan = tracer.nextSpan().name("jdbc:/" + dataSourceName + SleuthListenerAutoConfiguration.SPAN_QUERY_POSTFIX);
statementSpan.kind(Kind.CLIENT);
statementSpan.start();
spanWithScope = new SpanWithScope(statementSpan, tracer.withSpanInScope(statementSpan));
}
StatementInfo statementInfo = new StatementInfo(spanWithScope);
ConnectionInfo connectionInfo = openConnections.get(connectionKey);
if (connectionInfo == null) {
// Connection may be closed after statement preparation, but before statement execution.
return;
}
connectionInfo.getNestedStatements().put(statementKey, statementInfo);
}
/**
* <em>Note:</em>If the key is {@linkplain Tags#SAMPLING_PRIORITY} and the value is zero, the
* current span will be abandoned and future references to the {@link #context()} will be
* unsampled. This does not affect the active span, nor does it affect any equivalent instances of
* this object. This is a best efforts means to handle late sampling decisions.
*/
@Override public BraveSpan setTag(String key, Number value) {
if (finishCalled) return this;
if (trySetPeer(key, value)) return this;
// handle late sampling decision
if (Tags.SAMPLING_PRIORITY.getKey().equals(key) && value.intValue() == 0) {
delegate.abandon();
// convert the span to no-op
Kind kind = context.kind;
delegate = tracer.toSpan(delegate.context().toBuilder().sampled(false).build());
context = BraveSpanContext.create(delegate.context());
context.kind = kind;
}
return setTag(key, value.toString());
}
@Nullable static Kind trySetKind(String key, String value) {
if (!Tags.SPAN_KIND.getKey().equals(key)) return null;
Kind kind;
if (Tags.SPAN_KIND_CLIENT.equals(value)) {
kind = Kind.CLIENT;
} else if (Tags.SPAN_KIND_SERVER.equals(value)) {
kind = Kind.SERVER;
} else if (Tags.SPAN_KIND_PRODUCER.equals(value)) {
kind = Kind.PRODUCER;
} else if (Tags.SPAN_KIND_CONSUMER.equals(value)) {
kind = Kind.CONSUMER;
} else {
return null;
}
return kind;
}
/**
* Injects the underlying context using B3 encoding by default.
*/
@Override public <C> void inject(SpanContext spanContext, Format<C> format, C carrier) {
BraveSpanContext braveContext = ((BraveSpanContext) spanContext);
if (carrier instanceof BinaryInject) {
BinaryCodec.INSTANCE.inject(braveContext.unwrap(), (BinaryInject) carrier);
return;
}
if (!(carrier instanceof TextMapInject)) {
throw new UnsupportedOperationException(carrier + " not instanceof TextMapInject");
}
Kind kind = braveContext.kind;
Injector<TextMapInject> injector = null;
if (Kind.CLIENT.equals(kind)) {
injector = formatToClientInjector.get(format);
} else if (Kind.PRODUCER.equals(kind)) {
injector = formatToProducerInjector.get(format);
} else if (Kind.CONSUMER.equals(kind)) {
injector = formatToConsumerInjector.get(format);
}
if (injector == null) injector = formatToInjector.get(format);
if (injector == null) {
throw new UnsupportedOperationException(format + " not in " + formatToInjector.keySet());
}
injector.inject(braveContext.unwrap(), (TextMapInject) carrier);
}
@Test
public void integrationTest() throws Exception {
ManagedChannel inProcessManagedChannel = this.clientManagedChannelBuilder
.inProcessChannelBuilder("testServer").directExecutor().build();
HelloServiceGrpcClient client = new HelloServiceGrpcClient(
inProcessManagedChannel);
assertThat(client.sayHello("Testy McTest Face"))
.isEqualTo("Hello Testy McTest Face");
assertThat(this.spans).hasSize(2);
assertThat(this.spans.get(0).kind()).isEqualTo(Kind.SERVER);
assertThat(this.spans.get(1).kind()).isEqualTo(Kind.CLIENT);
// ManagedChannel does not implement Closeable...
inProcessManagedChannel.shutdownNow();
}
@Test
public void exception_logging_span_handler_logs_synchronous_exceptions(
CapturedOutput capture) {
try {
new RestTemplate().getForObject("http://localhost:" + port() + "/",
String.class);
BDDAssertions.fail("should fail due to runtime exception");
}
catch (Exception e) {
}
then(this.currentTraceContext.get()).isNull();
MutableSpan fromFirstTraceFilterFlow = spanHandler.takeRemoteSpanWithErrorMessage(
Kind.SERVER,
"Request processing failed; nested exception is java.lang.RuntimeException: Throwing exception");
then(fromFirstTraceFilterFlow.tags()).containsEntry("http.method", "GET")
.containsEntry("mvc.controller.class", "BasicErrorController");
// Trace IDs in logs: issue#714
String hex = fromFirstTraceFilterFlow.traceId();
thenLogsForExceptionLoggingFilterContainTracingInformation(capture, hex);
}
@Bean
SpanHandler uncaughtExceptionThrown(CurrentTraceContext currentTraceContext) {
return new SpanHandler() {
@Override
public boolean end(TraceContext context, MutableSpan span, Cause cause) {
if (span.kind() != Kind.SERVER || span.error() == null
|| !log.isErrorEnabled()) {
return true; // don't add overhead as we only log server errors
}
// In TracingFilter, the exception is raised in scope. This is is more
// explicit to ensure it works in other tech such as WebFlux.
try (Scope scope = currentTraceContext.maybeScope(context)) {
log.error("Uncaught exception thrown", span.error());
}
return true;
}
@Override
public String toString() {
return "UncaughtExceptionThrown";
}
};
}
/**
* Overrides the injection format used for the indicated {@link Request#spanKind() span kind}.
*
* <p><em>Note</em>: {@link Kind#SERVER} is not a valid inject format, and will be ignored.
*/
public FactoryBuilder injectFormat(Kind kind, Format format) {
if (kind == null) throw new NullPointerException("kind == null");
if (format == null) throw new NullPointerException("format == null");
switch (kind) { // handle injectable formats
case CLIENT:
injectorFactoryBuilder.clientInjectorFunctions(format);
break;
case PRODUCER:
injectorFactoryBuilder.producerInjectorFunctions(format);
break;
case CONSUMER:
injectorFactoryBuilder.consumerInjectorFunctions(format);
break;
default: // SERVER is nonsense as it cannot be injected
}
return this;
}
/**
* Like {@link #injectFormat}, but writes two formats.
*
* For example, you can set {@link Kind#CLIENT} spans to inject both {@link Format#MULTI} and
* {@link Format#SINGLE}, for transition use cases.
*/
public FactoryBuilder injectFormats(Kind kind, Format format1, Format format2) {
if (kind == null) throw new NullPointerException("kind == null");
if (format1 == null) throw new NullPointerException("format1 == null");
if (format2 == null) throw new NullPointerException("format2 == null");
if (format1.equals(format2)) throw new IllegalArgumentException("format1 == format2");
if (!format1.equals(Format.MULTI) && !format2.equals(Format.MULTI)) {
throw new IllegalArgumentException("One argument must be Format.MULTI");
}
switch (kind) { // handle injectable formats
case CLIENT:
injectorFactoryBuilder.clientInjectorFunctions(format1, format2);
break;
case PRODUCER:
injectorFactoryBuilder.producerInjectorFunctions(format1, format2);
break;
case CONSUMER:
injectorFactoryBuilder.consumerInjectorFunctions(format1, format2);
break;
default: // SERVER is nonsense as it cannot be injected
}
return this;
}
@Override public <C> void inject(SpanContext spanContext, Format<C> format, C carrier) {
if (!(carrier instanceof TextMapInject)) {
throw new UnsupportedOperationException(carrier + " not instanceof TextMapInject");
}
BraveSpanContext braveContext = ((BraveSpanContext) spanContext);
Kind kind = braveContext.kind;
Injector<TextMapInject> injector;
if (Kind.CLIENT.equals(kind)) {
injector = clientInjector;
} else if (Kind.PRODUCER.equals(kind)) {
injector = producerInjector;
} else if (Kind.CONSUMER.equals(kind)) {
injector = consumerInjector;
} else {
injector = this.injector;
}
injector.inject(braveContext.context, (TextMapInject) carrier);
}
@Test public void oneFunction_injects_deferred() {
DeferredInjector<Object> deferredInjector =
(DeferredInjector<Object>) oneFunction.newInjector(setter);
for (Kind kind : injectableKinds) {
when(request.spanKind()).thenReturn(kind);
deferredInjector.inject(context, request);
assertThat(oneCount.getAndSet(0)).isOne();
}
deferredInjector.inject(context, notRequest);
assertThat(oneCount.getAndSet(0)).isOne();
// works with nonsense
when(request.spanKind()).thenReturn(Kind.SERVER);
deferredInjector.inject(context, request);
assertThat(oneCount.getAndSet(0)).isOne();
}
@Test public void oneFunction_injects_remote() {
when(remoteSetter.spanKind()).thenReturn(Kind.CLIENT);
RemoteInjector<Request> remoteInjector =
(RemoteInjector<Request>) oneFunction.newInjector(remoteSetter);
for (Kind kind : injectableKinds) {
when(remoteSetter.spanKind()).thenReturn(kind);
Injector<Request> nextRemoteInjector = oneFunction.newInjector(remoteSetter);
assertThat(nextRemoteInjector).isEqualTo(remoteInjector);
assertThat(nextRemoteInjector).hasSameHashCodeAs(remoteInjector);
assertThat(nextRemoteInjector).hasToString(remoteInjector.toString());
assertThat(nextRemoteInjector).extracting("injectorFunction")
.isEqualTo(remoteInjector.injectorFunction);
nextRemoteInjector.inject(context, request);
assertThat(oneCount.getAndSet(0)).isOne();
}
}
@Test public void twoFunctions_injects_remote() {
when(remoteSetter.spanKind()).thenReturn(Kind.CLIENT);
RemoteInjector<Request> remoteInjector =
(RemoteInjector<Request>) twoFunctions.newInjector(remoteSetter);
for (Kind kind : injectableKinds) {
when(remoteSetter.spanKind()).thenReturn(kind);
Injector<Request> nextRemoteInjector = twoFunctions.newInjector(remoteSetter);
assertThat(nextRemoteInjector).isEqualTo(remoteInjector);
assertThat(nextRemoteInjector).hasSameHashCodeAs(remoteInjector);
assertThat(nextRemoteInjector).hasToString(remoteInjector.toString());
assertThat(nextRemoteInjector).extracting("injectorFunction")
.isEqualTo(remoteInjector.injectorFunction);
nextRemoteInjector.inject(context, request);
assertThat(oneCount.getAndSet(0)).isOne();
assertThat(twoCount.getAndSet(0)).isOne();
}
}
void beforeGetConnection(CON connectionKey, String dataSourceName) {
SpanWithScope spanWithScope = null;
if (traceTypes.contains(TraceType.CONNECTION)) {
Span connectionSpan = tracer.nextSpan().name("jdbc:/" + dataSourceName + SleuthListenerAutoConfiguration.SPAN_CONNECTION_POSTFIX);
connectionSpan.kind(Kind.CLIENT);
connectionSpan.start();
spanWithScope = new SpanWithScope(connectionSpan, tracer.withSpanInScope(connectionSpan));
}
ConnectionInfo connectionInfo = new ConnectionInfo(spanWithScope);
openConnections.put(connectionKey, connectionInfo);
}
@Nullable
Span newSpan(String method) {
Tracer tracer = Tracing.currentTracer();
if (tracer == null) {
return null;
}
return tracer.nextSpan().kind(Kind.CLIENT).name(method).tag("component", name).start();
}
@Override
public void createChildSpan(TracingWrapper<ZipkinWrapper> parent) {
operationName = UUID.randomUUID().toString().replace("-","");
if (parent == null) {
// root node we start a new trace
span = tracing.tracer().newTrace().name(operationName + "-root")
.start();
} else {
brave.Span parentClient = parent.get().tracing.tracer().newChild(parent.get().span.context())
.kind(Kind.CLIENT)
.name(operationName + "-client")
.start();
// TODO if I finish this later the span is cached
// and joined with server span and reported as a single span.
// to properly solve this we have to look into the tags.
// However there is another problem jaeger adds only one span.kind
// (even if span contains cs,cr,sr,ss)
// And it filters out core annotations, so there is no way how to find out
// that there is a dependency link in this span.
// https://github.com/jaegertracing/jaeger/issues/451
parentClient.finish();
span = tracing.tracer().joinSpan(parentClient.context())
.name(operationName + "-server")
.kind(Kind.SERVER)
.start();
}
}
@DataProvider
public static Object[][] dataProviderKind() {
return new Object[][] {
{Tags.SPAN_KIND_CLIENT, Kind.CLIENT},
{Tags.SPAN_KIND_SERVER, Kind.SERVER},
{Tags.SPAN_KIND_PRODUCER, Kind.PRODUCER},
{Tags.SPAN_KIND_CONSUMER, Kind.CONSUMER}
};
}
@Test @UseDataProvider("dataProviderKind")
public void spanKind_beforeStart(String tagValue, Kind kind) {
tracer.buildSpan("foo")
.withTag(Tags.SPAN_KIND.getKey(), tagValue)
.start().finish();
MutableSpan span = spans.get(0);
assertThat(span.kind())
.isEqualTo(kind);
assertThat(span.tags())
.isEmpty();
}
/**
* This keeps the code maintenance free in the rare case there is disparity between Brave and
* Zipkin kind values.
*/
static Map<Kind, Span.Kind> generateKindMap() {
Map<Kind, Span.Kind> result = new LinkedHashMap<>();
// Note: Both Brave and Zipkin treat null kind as a local/in-process span
for (Kind kind : Kind.values()) {
try {
result.put(kind, Span.Kind.valueOf(kind.name()));
} catch (RuntimeException e) {
logger.warning("Could not map Brave kind " + kind + " to Zipkin");
}
}
return result;
}
@Test
public void should_tag_with_value_from_null_expression() {
new RestTemplate().getForObject("http://localhost:" + port() + "/null-parameter",
String.class);
then(this.currentTraceContext.get()).isNull();
then(spanHandler.takeRemoteSpan(Kind.SERVER).tags()).containsEntry("foo", "1001");
}
@Test
public void should_tag_with_value_from_non_null_expression() {
new RestTemplate().getForObject(
"http://localhost:" + port() + "/null-parameter?bar=10", String.class);
then(this.currentTraceContext.get()).isNull();
then(spanHandler.takeRemoteSpan(Kind.SERVER).tags()).containsEntry("foo", "11");
}
@Test
public void should_create_spans_for_endpoint_returning_unsuccessful_result() {
try {
new RestTemplate().getForObject(
"http://localhost:" + port() + "/test_bad_request", String.class);
fail("should throw exception");
}
catch (HttpClientErrorException e) {
}
then(this.currentTraceContext.get()).isNull();
MutableSpan span = spanHandler.takeRemoteSpanWithErrorTag(Kind.SERVER, "400");
then(span.tags()).containsEntry("http.status_code", "400");
then(span.tags()).containsEntry("http.path", "/test_bad_request");
}
@Test
void shouldSubmitSpanWhenSampled() throws Exception {
final SpanCollector collector = new SpanCollector();
final Tracing tracing = Tracing.newBuilder()
.localServiceName(TEST_SERVICE)
.addSpanHandler(collector)
.sampler(Sampler.create(1.0f))
.build();
final RequestLog requestLog = testRemoteInvocation(tracing, null);
// check span name
final MutableSpan span = collector.spans().poll(10, TimeUnit.SECONDS);
assertThat(span).isNotNull();
assertThat(span.name()).isEqualTo(TEST_SPAN);
// check kind
assertThat(span.kind()).isSameAs(Kind.CLIENT);
// only one span should be submitted
assertThat(collector.spans().poll(1, TimeUnit.SECONDS)).isNull();
// check # of annotations (we add wire annotations)
assertThat(span.annotations()).hasSize(2);
assertTags(span);
assertThat(span.traceId().length()).isEqualTo(16);
// check duration is correct from request log
assertThat(span.finishTimestamp() - span.startTimestamp())
.isEqualTo(requestLog.totalDurationNanos() / 1000);
// check service name
assertThat(span.localServiceName()).isEqualTo(TEST_SERVICE);
// check remote service name
assertThat(span.remoteServiceName()).isEqualTo(null);
}
@Test
void shouldSubmitSpanWhenRequestIsSampled() throws Exception {
final SpanCollector collector = new SpanCollector();
final RequestLog requestLog = testServiceInvocation(collector,
RequestContextCurrentTraceContext.ofDefault(),
1.0f);
// check span name
final MutableSpan span = collector.spans().take();
assertThat(span.name()).isEqualTo(TEST_METHOD);
// check kind
assertThat(span.kind()).isSameAs(Kind.SERVER);
// only one span should be submitted
assertThat(collector.spans().poll(1, TimeUnit.SECONDS)).isNull();
// check # of annotations (we add wire annotations)
assertThat(span.annotations()).hasSize(2);
// check tags
assertTags(span);
// check service name
assertThat(span.localServiceName()).isEqualTo(TEST_SERVICE);
// check duration is correct from request log
assertThat(span.finishTimestamp() - span.startTimestamp())
.isEqualTo(requestLog.totalDurationNanos() / 1000);
}
@Test public void span_shared_JSON_V2() {
MutableSpan span = clientSpan;
span.kind(Kind.SERVER);
span.setShared();
assertThat(new String(encoder.encode(clientSpan), UTF_8))
.isEqualTo(
"{\"traceId\":\"7180c278b62e8f6a216a2aea45d08fc9\",\"parentId\":\"6b221d5bc9e6496c\",\"id\":\"5b4185666d50f68b\",\"kind\":\"SERVER\",\"name\":\"get\",\"timestamp\":1472470996199000,\"duration\":207000,\"localEndpoint\":{\"serviceName\":\"frontend\",\"ipv4\":\"127.0.0.1\"},\"remoteEndpoint\":{\"serviceName\":\"backend\",\"ipv4\":\"192.168.99.101\",\"port\":9000},\"annotations\":[{\"timestamp\":1472470996238000,\"value\":\"foo\"},{\"timestamp\":1472470996403000,\"value\":\"bar\"}],\"tags\":{\"clnt/finagle.version\":\"6.45.0\",\"http.path\":\"/api\"},\"shared\":true}");
}
void kindBasedFunctions_injects_deferred(Kind kind, AtomicInteger kindCallCount) {
when(request.spanKind()).thenReturn(kind);
DeferredInjector<Object> nextDeferredInjector =
(DeferredInjector) kindBasedFunctions.newInjector(setter);
nextDeferredInjector.inject(context, request);
for (AtomicInteger callCount : allCounts) {
if (callCount.equals(kindCallCount)) {
assertThat(callCount.getAndSet(0)).isOne();
} else {
assertThat(callCount.getAndSet(0)).isZero();
}
}
}
void kindBasedFunctions_injects_remote(Kind kind, AtomicInteger kindCallCount) {
when(remoteSetter.spanKind()).thenReturn(kind);
Injector<Request> nextRemoteInjector = kindBasedFunctions.newInjector(remoteSetter);
nextRemoteInjector.inject(context, request);
for (AtomicInteger callCount : allCounts) {
if (callCount.equals(kindCallCount)) {
assertThat(callCount.getAndSet(0)).isOne();
} else {
assertThat(callCount.getAndSet(0)).isZero();
}
}
}
@Test
public void reportsClientKindToZipkin() throws Exception {
prepareExecuteSelect(QUERY);
assertThat(spans)
.extracting(MutableSpan::kind)
.containsExactly(Kind.CLIENT);
}
@Test public void finish_error_but_null_result_DubboServerRequest() {
Span span = tracing.tracer().nextSpan().kind(Span.Kind.SERVER).start();
Throwable error = new RuntimeException("melted");
FinishSpan.finish(filter, serverRequest, null, error, span);
testSpanHandler.takeRemoteSpanWithError(Kind.SERVER, error);
}
@Test public void create_null_result_value_and_error_DubboClientRequest() {
Span span = tracing.tracer().nextSpan().kind(Span.Kind.CLIENT).start();
FinishSpan.create(filter, clientRequest, mock(Result.class), span)
.accept(null, null);
testSpanHandler.takeRemoteSpan(Kind.CLIENT);
}