com.nike.wingtips.Tracer类的使用及代码示例

x33g5p2x  于2022-01-30 转载在 其他  
字(19.9k)|赞(0)|评价(0)|浏览(100)

本文整理了Java中com.nike.wingtips.Tracer类的一些代码示例,展示了Tracer类的具体用法。这些代码示例主要来源于Github/Stackoverflow/Maven等平台,是从一些精选项目中提取出来的代码,具有较强的参考意义,能在一定程度帮忙到你。Tracer类的具体详情如下:
包路径:com.nike.wingtips.Tracer
类名称:Tracer

Tracer介绍

[英]Tracer implementation based on the Google Dapper paper: http://static.googleusercontent.com/media/research.google.com/en/us/pubs/archive/36356.pdf

Instead using a data store to record the tracing information, SLF4J is used. The results will be logged and a separate process on the machine can be used to extract the tracing information from the logs and send it to a collector/data store/log aggregator/etc (if desired). This is consistent with the behavior described in the Google Dapper paper. Valid distributed trace spans will be logged to a SLF4J logger named #VALID_WINGTIPS_SPAN_LOGGER_NAME so you can pipe them to their own log file if desired. Similarly, invalid spans (where this class has detected incorrect usage and knows that the spans have invalid timing info, for example) will be logged to a SLF4J logger named #INVALID_WINGTIPS_SPAN_LOGGER_NAME. These specially-named loggers will not be used for any other purpose.

Sampling is determined using #rootSpanSamplingStrategy which defaults to sampling everything. You can override this by calling #setRootSpanSamplingStrategy(RootSpanSamplingStrategy).

You can be notified of span lifecycle events (i.e. for metrics counting) by adding a listener to #addSpanLifecycleListener(SpanLifecycleListener). NOTE: It's important that any SpanLifecycleListener you add is extremely lightweight or you risk distributed tracing becoming a major bottleneck for high throughput services. If any expensive work needs to be done in a SpanLifecycleListener then it should be done asynchronously on a thread or threadpool separate from the application worker threads.

The format of the logging output when a span is completed is determined by #spanLoggingRepresentation, which can be set by calling #setSpanLoggingRepresentation(SpanLoggingRepresentation). The default is SpanLoggingRepresentation#JSON, which causes the log messages to use Span#toJSON() to represent the span.

The span information is associated with a thread and is modeled as a stack, so it's possible to have nested spans inside an overall request span. These nested spans are referred to as "sub-spans" in this class. Sub-spans are started via #startSubSpan(String,SpanPurpose) and completed via #completeSubSpan(). See the recommended usage section below for more information.

RECOMMENDED USAGE: In a typical usage scenario you'll want to call one of the startRequest...() methods as soon as possible when a request enters your application, and you'll want to call #completeRequestSpan() as late as possible in the request/response cycle (ideally after the last of the response has been sent to the user, although some frameworks don't let you hook in that late). In between these two calls the span that was started (the "overall-request-span") is considered the "current span" for this thread and can be retrieved if necessary by calling #getCurrentSpan().

NOTE: Given the thread-local nature of this class you'll want to make sure the completion call is in a finally block or otherwise guaranteed to be called no matter what (even if the request fails with an error) to prevent problems when subsequent requests are processed on the same thread. This class does its best to recover from incorrect thread usage scenarios and log information about what happened but the best solution is to prevent the problems from occurring in the first place.

ALSO NOTE: Spans support Java try-with-resources statements to help guarantee proper usage in blocking/non-async scenarios (for asynchronous scenarios please refer to the asynchronous usage section of the Wingtips readme). Here are some examples.

Overall request span using try-with-resources:

try(Span requestSpan = Tracer.getInstance().startRequestWith*(...)) { 
// Traced blocking code for overall request (not asynchronous) goes here ... 
} 
// No finally block needed to properly complete the overall request span

Subspan using try-with-resources:

try (Span subspan = Tracer.getInstance().startSubSpan(...)) { 
// Traced blocking code for subspan (not asynchronous) goes here ... 
} 
// No finally block needed to properly complete the subspan

The "request span" described above is intended to track the work done for the overall request. If you have work inside the request that you want tracked as a separate nested span with the overall-request-span as its parent you can do so via the "sub-span" methods: #startSubSpan(String,SpanPurpose) and #completeSubSpan(). These nested sub-spans are pushed onto the span stack associated with the current thread and you can have them as deeply nested as you want, but just like with the overall-request-span you'll want to make sure the completion method is called in a finally block or otherwise guaranteed to be executed even if an error occurs. Each call to #startSubSpan(String,SpanPurpose) causes the "current span" to be the new sub-span's parent, and causes the new sub-span to become the "current span" by pushing it onto the span stack. Each call to #completeSubSpan() does the reverse by popping the current span off the span stack and completing and logging it, thus causing its parent to become the current span again.

One common use case for sub-spans is to track downstream calls separately from the overall request (e.g. HTTP calls to another service, database calls, or any other call that crosses network or application boundaries). Start a sub-span immediately before a downstream call and complete it immediately after the downstream call returns. You can inspect the sub-span to see how long the downstream call took from this application's point of view. If you do this around all your downstream calls you can subtract the total time spent for all downstream calls from the time spent for the overall-request-span to determine time spent in this application vs. time spent waiting for downstream calls to finish. And if the downstream service also performs distributed tracing and has an overall-request-span for its service call then you can subtract the downstream service's request-span time from this application's sub-span time around the downstream call to determine how much time was lost to network lag or any other bottlenecks between the services.

In addition to the logs this class outputs for spans it puts the trace ID and span JSON for the "current" span into the SLF4J MDC so that all your logs can be tagged with the current span's trace ID and/or full JSON. To utilize this you would need to add %X{traceId}} and/or %X{spanJson}} to your log pattern (NOTE: this only works with SLF4J frameworks that support MDC, e.g. logback and log4j). This causes all log messages, including ones that come from third party libraries and have no knowledge of distributed tracing, to be output with the current span's tracing information.

NOTE: Due to the thread-local nature of this class it is more effort to integrate with reactive (asynchronous non-blocking) frameworks like Netty or actor frameworks than with thread-per-request frameworks. But it is not terribly difficult and the benefit of having all your log messages tagged with tracing information is worth the effort. This class provides the following methods to help integrate with reactive frameworks:

  • #registerWithThread(Deque)
  • #unregisterFromThread()
  • #getCurrentSpanStackCopy()
    See the javadocs on those methods for more detailed information, but the general pattern would be to call the #registerWithThread(Deque) with the request's span stack whenever a thread starts to do some chunk of work for that request, and call #unregisterFromThread() when that chunk of work is done and the thread is about to be freed up to work on a different request. The span stack would need to follow the request no matter what thread was processing it, but assuming you can solve that problem in a reactive framework then the general pattern should work fine.

The asynchronous usage section of the Wingtips readme contains further details on asynchronous Wingtips usage, including helper classes and methods to automate or ease the handling of these scenarios. Please refer to that section of the readme if you have any asynchronous use cases.
[中]基于Google Dapper paper的Tracer实现:http://static.googleusercontent.com/media/research.google.com/en/us/pubs/archive/36356.pdf
使用SLF4J代替使用数据存储记录跟踪信息。结果将被记录,机器上的一个单独进程可用于从日志中提取跟踪信息,并将其发送给收集器/数据存储/日志聚合器/etc(如果需要)。这与Google Dapper论文中描述的行为一致。有效的分布式跟踪范围将被记录到名为#Valid_WINGTIPS_SPAN_logger_NAME的SLF4J记录器中,因此,如果需要,您可以通过管道将它们记录到自己的日志文件中。类似地,无效跨距(例如,此类检测到不正确的使用,并且知道跨距具有无效的计时信息)将被记录到名为#invalid_WINGTIPS_SPAN_logger_NAME的SLF4J记录器。这些特别命名的伐木工人不得用于任何其他用途。
采样是使用#rootSpanSamplingStrategy确定的,该策略默认为对所有内容进行采样。您可以通过调用#setRootSpanSamplingStrategy(RootSpanSamplingStrategy)来覆盖这一点。
通过向#addSpanLifecycleListener(SpanLifecycleListener)添加一个侦听器,可以通知您跨生命周期事件(即用于度量计数)。注意:添加的任何SpanLifecycleListener都必须非常轻量级,否则分布式跟踪可能会成为高吞吐量服务的主要瓶颈。如果需要在SpanLifecycleListener中完成任何昂贵的工作,那么应该在与应用程序工作线程分离的线程或线程池中异步完成。
跨度完成时,记录输出的格式由#spanLoggingRepresentation确定,可通过调用#setSpanLoggingRepresentation(spanLoggingRepresentation)来设置。默认值是SpanLoggingRepresentation#JSON,这会导致日志消息使用Span#toJSON()来表示Span。
跨度信息与一个线程关联,并被建模为一个堆栈,因此可以在整个请求跨度内嵌套跨度。这些嵌套跨距在此类中称为“子跨距”。子跨度通过#startSubSpan(String,SpanPurpose)开始,并通过#completeSubSpan()完成。有关更多信息,请参阅下面的推荐用法部分。
推荐用法:在一个典型的用法场景中,你需要调用startRequest中的一个。。。()在请求进入应用程序时尽快调用方法,并且您希望在请求/响应周期中尽可能晚地调用#completeRequestSpan()(理想情况下是在最后一个响应发送给用户之后,尽管有些框架不允许您这么晚才调用)。在这两次调用之间,已启动的范围(“总请求范围”)被视为该线程的“当前范围”,如有必要,可以通过调用#getCurrentSpan()来检索。
注意:考虑到此类的线程本地性质,您需要确保完成调用位于finally块中,或者无论发生什么情况(即使请求因错误而失败),都能保证被调用,以防止在同一线程上处理后续请求时出现问题。此类尽最大努力从错误的线程使用场景中恢复,并记录发生的情况,但最好的解决方案是从一开始就防止问题发生。
还要注意:Spans支持Java try with resources语句,以帮助确保在阻塞/非异步场景中正确使用(有关异步场景,请参阅the asynchronous usage section of the Wingtips readme)。下面是一些例子。
使用try with resources的总体请求范围:

try(Span requestSpan = Tracer.getInstance().startRequestWith*(...)) { 
// Traced blocking code for overall request (not asynchronous) goes here ... 
} 
// No finally block needed to properly complete the overall request span

使用资源试用的子扫描:

try (Span subspan = Tracer.getInstance().startSubSpan(...)) { 
// Traced blocking code for subspan (not asynchronous) goes here ... 
} 
// No finally block needed to properly complete the subspan

上述“请求范围”旨在跟踪为整个请求完成的工作。如果您希望将请求内部的工作作为单独的嵌套范围进行跟踪,并将整个请求范围作为其父级,则可以通过“子范围”方法进行跟踪:#startSubSpan(String,SpanPurpose)和#completeSubSpan()。这些嵌套的子跨度被推送到与当前线程相关联的跨度堆栈上,您可以让它们按照您想要的深度嵌套,但就像整个请求跨度一样,您需要确保在finally块中调用completion方法,或者确保即使发生错误也能执行。每次调用#startSubSpan(String,SpanPurpose)都会使“当前span”成为新子span的父级,并通过将新子span推到span堆栈上使其成为“当前span”。对#completeSubSpan()的每次调用都会将当前跨度从跨度堆栈中弹出并完成并记录,从而使其父级再次成为当前跨度,从而实现相反的效果。
子跨度的一个常见用例是,从整体请求中分别跟踪下游调用(例如,对另一个服务的HTTP调用、数据库调用或任何其他跨越网络或应用程序边界的调用)。在下行呼叫前立即启动子跨度,并在下行呼叫返回后立即完成。从这个应用程序的角度来看,您可以检查子跨度以查看下游调用花费了多长时间。如果对所有下游调用执行此操作,则可以从整个请求跨度所花费的时间中减去所有下游调用所花费的总时间,以确定在该应用程序中花费的时间与等待下游调用完成所花费的时间。如果下游服务也执行分布式跟踪,并且其服务调用有一个总的请求跨度,那么您可以从该应用程序围绕下游调用的子跨度时间中减去下游服务的请求跨度时间,以确定由于网络延迟或服务之间的任何其他瓶颈而损失的时间。
除了为span输出的日志之外,这个类还将“当前”span的跟踪ID和span JSON放入SLF4JMDC,这样所有日志都可以用当前span的跟踪ID和/或完整JSON进行标记。要利用这一点,您需要将%X{traceId}和/或%X{spanJson}添加到日志模式中(注意:这只适用于支持MDC的SLF4J框架,例如logback和log4j)。这会导致all日志消息(包括来自第三方库且不了解分布式跟踪的消息)与当前span的跟踪信息一起输出。
注意:由于这个类的线程本地特性,与Netty或actor框架等反应式(异步非阻塞)框架集成比与每请求线程框架集成更费劲。但这并不是非常困难,让所有日志消息都标记跟踪信息的好处是值得的。此类提供了以下方法来帮助与反应式框架集成:
*#带螺纹的寄存器(Deque)
*#从线程()中注销
*#getCurrentSpanStackCopy()
有关这些方法的更多详细信息,请参阅javadocs,但通常的模式是,每当线程开始为该请求执行某些工作时,就使用请求的跨度堆栈调用#registerWithThread(Deque),当那部分工作完成,线程即将被释放以处理不同的请求时,调用#unregisterFromThread()。不管是哪个线程在处理请求,span堆栈都需要遵循请求,但假设您可以在反应式框架中解决该问题,那么一般模式应该可以正常工作。
asynchronous usage section of the Wingtips readme包含关于异步翼尖使用的更多详细信息,包括帮助器类和方法,以自动化或简化这些场景的处理。如果您有任何异步用例,请参阅自述文件的该部分。

代码示例

代码示例来源:origin: Nike-Inc/wingtips

private void clearTracerSpanLifecycleListeners() {
  List<SpanLifecycleListener> listeners = new ArrayList<>(Tracer.getInstance().getSpanLifecycleListeners());
  for (SpanLifecycleListener listener : listeners) {
    Tracer.getInstance().removeSpanLifecycleListener(listener);
  }
}

代码示例来源:origin: Nike-Inc/wingtips

private HttpEntity getHttpEntityWithUserIdHeader() {
  HttpHeaders headers = new HttpHeaders();
  String userId = Tracer.getInstance().getCurrentSpan().getUserId();
  if (userIdHeaderKeys == null || userIdHeaderKeys.isEmpty() || userId == null) {
    return new HttpEntity(headers);
  }
  headers.set(userIdHeaderKeys.get(0), userId);
  return new HttpEntity(headers);
}

代码示例来源:origin: Nike-Inc/wingtips

/**
 * Constructor that extracts the current tracing and MDC information from the current thread using {@link
 * Tracer#getCurrentSpanStackCopy()} and {@link MDC#getCopyOfContextMap()}, and forwards the information to
 * the {@link SuccessCallbackWithTracing#SuccessCallbackWithTracing(SuccessCallback, Deque, Map)}
 * constructor. That tracing and MDC information will be associated with the thread when the given operation is
 * executed.
 *
 * <p>The operation you pass in cannot be null (an {@link IllegalArgumentException} will be thrown if you pass in
 * null for the operation).
 */
public SuccessCallbackWithTracing(SuccessCallback<T> origSuccessCallback) {
  this(origSuccessCallback, Tracer.getInstance().getCurrentSpanStackCopy(), MDC.getCopyOfContextMap());
}

代码示例来源:origin: Nike-Inc/wingtips

private void resetTracing() {
  MDC.clear();
  Tracer.getInstance().unregisterFromThread();
}

代码示例来源:origin: Nike-Inc/wingtips

/**
 * Initialize configuration.
 * Add Zipkin listener if our {@link WingtipsZipkinProperties} indicates it has the necessary properties specified.
 */
private void init() {
  if (wingtipsZipkinProperties.shouldApplyWingtipsToZipkinLifecycleListener()) {
    Tracer.getInstance().addSpanLifecycleListener(
      new WingtipsToZipkinLifecycleListener(
        wingtipsZipkinProperties.getServiceName(),
        wingtipsZipkinProperties.getLocalComponentNamespace(),
        wingtipsZipkinProperties.getBaseUrl()
      )
    );
  }
}

代码示例来源:origin: Nike-Inc/wingtips

private Pair<Deque<Span>, Map<String, String>> setupCurrentThreadWithTracingInfo() {
  resetTracing();
  Tracer.getInstance().startRequestWithRootSpan("request-" + UUID.randomUUID().toString());
  return Pair.of(Tracer.getInstance().getCurrentSpanStackCopy(), MDC.getCopyOfContextMap());
}

代码示例来源:origin: Nike-Inc/wingtips

@Test
public void handleSpanCloseMethod_completes_the_span_as_expected_subspan() {
  // given
  Span parentSpan = Tracer.getInstance().startRequestWithRootSpan("root");
  Span subspan = Tracer.getInstance().startSubSpan("subspan", SpanPurpose.LOCAL_ONLY);
  assertThat(Tracer.getInstance().getCurrentSpan()).isSameAs(subspan);
  assertThat(subspan.isCompleted()).isFalse();
  // when
  Tracer.getInstance().handleSpanCloseMethod(subspan);
  // then
  assertThat(subspan.isCompleted()).isTrue();
  assertThat(Tracer.getInstance().getCurrentSpan()).isSameAs(parentSpan);
}

代码示例来源:origin: Nike-Inc/wingtips

@Test
public void removeSpanLifecycleListener_should_work_as_advertised() {
  // given
  SpanLifecycleListener listener = mock(SpanLifecycleListener.class);
  Tracer.getInstance().addSpanLifecycleListener(listener);
  assertThat(Tracer.getInstance().getSpanLifecycleListeners()).hasSize(1);
  assertThat(Tracer.getInstance().getSpanLifecycleListeners().get(0)).isEqualTo(listener);
  // when
  boolean result = Tracer.getInstance().removeSpanLifecycleListener(listener);
  // then
  assertThat(result).isTrue();
  assertThat(Tracer.getInstance().getSpanLifecycleListeners()).isEmpty();
}

代码示例来源:origin: Nike-Inc/wingtips

@Test
public void addSpanLifecycleListener_should_work_as_advertised() {
  // given
  SpanLifecycleListener listener = mock(SpanLifecycleListener.class);
  assertThat(Tracer.getInstance().getSpanLifecycleListeners()).isEmpty();
  // when
  Tracer.getInstance().addSpanLifecycleListener(listener);
  // then
  assertThat(Tracer.getInstance().getSpanLifecycleListeners()).hasSize(1);
  assertThat(Tracer.getInstance().getSpanLifecycleListeners().get(0)).isEqualTo(listener);
}

代码示例来源:origin: Nike-Inc/wingtips

private void resetTracing() {
  MDC.clear();
  Tracer.getInstance().unregisterFromThread();
  List<SpanLifecycleListener> listeners = new ArrayList<>(Tracer.getInstance().getSpanLifecycleListeners());
  for (SpanLifecycleListener listener : listeners) {
    Tracer.getInstance().removeSpanLifecycleListener(listener);
  }
}

代码示例来源:origin: Nike-Inc/wingtips

private void resetTracer() {
  Tracer.getInstance().completeRequestSpan();
  Tracer.getInstance().setRootSpanSamplingStrategy(new SampleAllTheThingsStrategy());
  for (SpanLifecycleListener listener : new ArrayList<>(Tracer.getInstance().getSpanLifecycleListeners())) {
    Tracer.getInstance().removeSpanLifecycleListener(listener);
  }
  Tracer.getInstance().setSpanLoggingRepresentation(Tracer.SpanLoggingRepresentation.JSON);
}

代码示例来源:origin: Nike-Inc/wingtips

@Test
public void getCurrentSpan_should_return_current_span() throws Exception {
  // given
  Tracer tracer = Tracer.getInstance();
  tracer.startRequestWithRootSpan("test-span");
  // when
  Span span = tracer.getCurrentSpan();
  // then
  assertThat(span).isNotNull();
  assertThat(span.getSpanName()).isEqualTo("test-span");
}

代码示例来源:origin: Nike-Inc/riposte

public static ChannelHandlerContextMocks mockChannelHandlerContextWithTraceInfo(String userId) {
  if (Tracer.getInstance().getCurrentSpan() == null) {
    Tracer.getInstance().startRequestWithRootSpan("mockChannelHandlerContext", userId);
  }
  ChannelHandlerContextMocks channelHandlerMocks = mockChannelHandlerContext();
  when(channelHandlerMocks.mockHttpProcessingState.getLoggerMdcContextMap()).thenReturn(MDC.getCopyOfContextMap());
  when(channelHandlerMocks.mockHttpProcessingState.getDistributedTraceStack()).thenReturn(Tracer.getInstance().getCurrentSpanStackCopy());
  return channelHandlerMocks;
}

代码示例来源:origin: Nike-Inc/wingtips

private TracingState generateTracingStateOnCurrentThread() {
  Tracer.getInstance().startRequestWithRootSpan(UUID.randomUUID().toString());
  Tracer.getInstance().startSubSpan(UUID.randomUUID().toString(), Span.SpanPurpose.LOCAL_ONLY);
  return TracingState.getCurrentThreadTracingState();
}

代码示例来源:origin: Nike-Inc/wingtips

@Test
public void spanLifecycleListener_spanStarted_is_called_when_subspan_is_started() {
  // given
  SpanLifecycleListener listener1 = mock(SpanLifecycleListener.class);
  SpanLifecycleListener listener2 = mock(SpanLifecycleListener.class);
  Tracer tracer = Tracer.getInstance();
  tracer.addSpanLifecycleListener(listener1);
  tracer.addSpanLifecycleListener(listener2);
  tracer.startRequestWithRootSpan("newspan");
  // when
  Span subspan = tracer.startSubSpan("subspan", SpanPurpose.LOCAL_ONLY);
  // then
  verify(listener1).spanStarted(subspan);
  verify(listener1, times(0)).spanCompleted(subspan);
  verify(listener2).spanStarted(subspan);
  verify(listener2, times(0)).spanCompleted(subspan);
}

代码示例来源:origin: Nike-Inc/riposte

private void resetTracing() {
  MDC.clear();
  Tracer.getInstance().unregisterFromThread();
  removeSpanRecorderLifecycleListener();
  spanRecorder = new SpanRecorder();
  Tracer.getInstance().addSpanLifecycleListener(spanRecorder);
}

代码示例来源:origin: Nike-Inc/wingtips

@Test
public void spanLifecycleListener_spanStarted_is_called_when_new_request_span_is_started() {
  // given
  SpanLifecycleListener listener1 = mock(SpanLifecycleListener.class);
  SpanLifecycleListener listener2 = mock(SpanLifecycleListener.class);
  Tracer tracer = Tracer.getInstance();
  tracer.addSpanLifecycleListener(listener1);
  tracer.addSpanLifecycleListener(listener2);
  // when
  Span span = tracer.startRequestWithRootSpan("newspan");
  // then
  verify(listener1).spanStarted(span);
  verify(listener1, times(0)).spanCompleted(span);
  verify(listener2).spanStarted(span);
  verify(listener2, times(0)).spanCompleted(span);
}

代码示例来源:origin: Nike-Inc/wingtips

@Test
public void getCurrentManagedStatusForSpan_works_as_expected_for_managed_noncurrent() {
  // given
  Span nonCurrentRootSpan = Tracer.getInstance().startRequestWithRootSpan("root");
  Span nonCurrentSubspan = Tracer.getInstance().startSubSpan("subspan1", SpanPurpose.LOCAL_ONLY);
  Span currentSubspan = Tracer.getInstance().startSubSpan("subspan2", SpanPurpose.LOCAL_ONLY);
  // expect
  assertThat(Tracer.getInstance().getCurrentManagedStatusForSpan(nonCurrentRootSpan))
    .isEqualTo(TracerManagedSpanStatus.MANAGED_NON_CURRENT_ROOT_SPAN);
  assertThat(Tracer.getInstance().getCurrentManagedStatusForSpan(nonCurrentSubspan))
    .isEqualTo(TracerManagedSpanStatus.MANAGED_NON_CURRENT_SUB_SPAN);
}

代码示例来源:origin: Nike-Inc/riposte

private Pair<Deque<Span>, Map<String, String>> setupStateWithNewSpan(String spanName) {
  Deque<Span> origSpanInfo = Tracer.getInstance().unregisterFromThread();
  Map<String, String> origMdcInfo = MDC.getCopyOfContextMap();
  Tracer.getInstance().startRequestWithRootSpan(spanName);
  Pair<Deque<Span>, Map<String, String>> infoForStatePair = AsyncNettyHelper.linkTracingAndMdcToCurrentThread(origSpanInfo, origMdcInfo);
  state.setDistributedTraceStack(infoForStatePair.getLeft());
  state.setLoggerMdcContextMap(infoForStatePair.getRight());
  return infoForStatePair;
}

代码示例来源:origin: Nike-Inc/wingtips

@Test
public void constructor_does_not_register_WingtipsToZipkinLifecycleListener_when_props_shouldApplyWingtipsToZipkinLifecycleListener_returns_false() {
  // given
  WingtipsZipkinProperties props = mock(WingtipsZipkinProperties.class);
  doReturn(false).when(props).shouldApplyWingtipsToZipkinLifecycleListener();
  // when
  new WingtipsWithZipkinSpringBootConfiguration(props, null);
  // then
  assertThat(Tracer.getInstance().getSpanLifecycleListeners()).isEmpty();
  verify(props).shouldApplyWingtipsToZipkinLifecycleListener();
  verifyNoMoreInteractions(props);
}

相关文章