package org.stagemonitor.tracing.elasticsearch; import com.codahale.metrics.Timer; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; import org.stagemonitor.core.util.JsonUtils; import org.stagemonitor.tracing.NoopSpan; import org.stagemonitor.tracing.RequestMonitor; import org.stagemonitor.tracing.SpanContextInformation; import org.stagemonitor.tracing.metrics.ExternalRequestMetricsSpanEventListener; import org.stagemonitor.tracing.reporter.ReadbackSpan; import org.stagemonitor.tracing.reporter.ReadbackSpanEventListener; import org.stagemonitor.tracing.reporter.ReportingSpanEventListener; import org.stagemonitor.tracing.utils.SpanUtils; import org.stagemonitor.tracing.wrapper.SpanWrapper; import java.util.Arrays; import java.util.Collections; import java.util.concurrent.TimeUnit; import io.opentracing.Span; import io.opentracing.tag.Tags; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.stagemonitor.core.metrics.metrics2.MetricName.name; public class ElasticsearchExternalRequestReporterTest extends AbstractElasticsearchSpanReporterTest { private ElasticsearchSpanReporter reporter; private ReportingSpanEventListener reportingSpanEventListener; @Override @Before public void setUp() throws Exception { super.setUp(); JsonUtils.getMapper().registerModule(new ReadbackSpan.SpanJsonModule()); when(tracingPlugin.getRateLimitClientSpansPerMinute()).thenReturn(1000000d); reporter = new ElasticsearchSpanReporter(spanLogger); reporter.init(configuration); when(tracingPlugin.getOnlyReportSpansWithName()).thenReturn(Collections.emptyList()); final RequestMonitor requestMonitor = mock(RequestMonitor.class); when(tracingPlugin.getRequestMonitor()).thenReturn(requestMonitor); reportingSpanEventListener = mock(ReportingSpanEventListener.class); } @Test public void reportSpan() throws Exception { when(elasticsearchTracingPlugin.isOnlyLogElasticsearchSpanReports()).thenReturn(false); final ReadbackSpan span = getSpan(); report(span); Mockito.verify(elasticsearchClient).index(ArgumentMatchers.startsWith("stagemonitor-spans-"), ArgumentMatchers.eq("spans"), ArgumentMatchers.any()); Assert.assertTrue(reporter.isActive(null)); verifyTimerCreated(1); } @Test public void doNotReportSpan() throws Exception { when(elasticsearchTracingPlugin.isOnlyLogElasticsearchSpanReports()).thenReturn(false); when(elasticsearchClient.isElasticsearchAvailable()).thenReturn(false); when(corePlugin.getElasticsearchUrl()).thenReturn(null); final ReadbackSpan span = getSpan(); report(span); Mockito.verify(elasticsearchClient, Mockito.times(0)).index(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.any()); Mockito.verify(spanLogger, Mockito.times(0)).info(ArgumentMatchers.anyString()); Assert.assertFalse(reporter.isActive(null)); verifyTimerCreated(1); } @Test public void testLogReportSpan() throws Exception { when(elasticsearchTracingPlugin.isOnlyLogElasticsearchSpanReports()).thenReturn(true); try (final Span span = tracingPlugin.getTracer().buildSpan("test").start()) { report(SpanContextInformation.forSpan(span).getReadbackSpan()); Mockito.verify(elasticsearchClient, Mockito.times(0)).index(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.any()); Mockito.verify(spanLogger).info(ArgumentMatchers.startsWith("{\"index\":{\"_index\":\"stagemonitor-spans-")); Assert.assertTrue(reporter.isActive(SpanContextInformation.forSpan(span))); } } @Test public void reportSpanRateLimited() throws Exception { when(tracingPlugin.getRateLimitClientSpansPerMinute()).thenReturn(1d); report(getSpan()); Mockito.verify(elasticsearchClient).index(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.any()); Thread.sleep(5010); // the meter only updates every 5 seconds report(getSpan()); Mockito.verifyNoMoreInteractions(spanLogger); verifyTimerCreated(2); } @Test public void excludeExternalRequestsFasterThan() throws Exception { when(tracingPlugin.getExcludeExternalRequestsFasterThan()).thenReturn(100d); report(getSpan(100)); Mockito.verify(elasticsearchClient).index(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.any()); report(getSpan(99)); Mockito.verifyNoMoreInteractions(spanLogger); verifyTimerCreated(2); } @Test public void testElasticsearchExcludeFastCallTree() throws Exception { when(tracingPlugin.getExcludeExternalRequestsWhenFasterThanXPercent()).thenReturn(0.85d); report(getSpan(1000)); Mockito.verify(elasticsearchClient).index(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.any()); report(getSpan(250)); Mockito.verifyNoMoreInteractions(spanLogger); verifyTimerCreated(2); } private void report(ReadbackSpan span) { if (reporter.isActive(null)) { SpanContextInformation spanContextInformation = mock(SpanContextInformation.class); when(spanContextInformation.getReadbackSpan()).thenReturn(span); reporter.report(spanContextInformation); } } @Test public void testElasticsearchDontExcludeSlowCallTree() throws Exception { when(tracingPlugin.getExcludeExternalRequestsWhenFasterThanXPercent()).thenReturn(0.85d); report(getSpan(250)); report(getSpan(1000)); Mockito.verify(elasticsearchClient, Mockito.times(2)).index(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.any()); verifyTimerCreated(2); } private ReadbackSpan getSpan() { return getSpan(1); } private ReadbackSpan getSpan(long executionTimeMillis) { final Span span = new SpanWrapper(NoopSpan.INSTANCE, "External Request", 1, 1, Arrays.asList(new ExternalRequestMetricsSpanEventListener(registry), new ReadbackSpanEventListener(reportingSpanEventListener, tracingPlugin))); Tags.SPAN_KIND.set(span, Tags.SPAN_KIND_CLIENT); span.setTag(SpanUtils.OPERATION_TYPE, "jdbc"); span.setTag("method", "SELECT"); span.finish(TimeUnit.MILLISECONDS.toMicros(executionTimeMillis) + 1); return SpanContextInformation.forSpan(span).getReadbackSpan(); } private void verifyTimerCreated(int count) { final Timer timer = registry.getTimers().get(name("external_request_response_time") .tag("type", "jdbc") .tag("signature", "External Request") .tag("method", "SELECT") .build()); Assert.assertNotNull(registry.getTimers().keySet().toString(), timer); Assert.assertEquals(count, timer.getCount()); final Timer allTimer = registry.getTimers().get(name("external_request_response_time") .tag("type", "jdbc") .tag("signature", "All") .tag("method", "SELECT") .build()); Assert.assertNotNull(allTimer); Assert.assertEquals(count, allTimer.getCount()); } }