From c58fd6a8aa90e68dd69bd9b47675a1f5b52bf2ed Mon Sep 17 00:00:00 2001 From: Staffan Friberg Date: Sat, 8 Dec 2018 19:38:41 -0600 Subject: [PATCH] add tests some renames --- .gitignore | 6 +- README.md | 2 +- build.gradle | 71 ++++++++- ...pperFactory.java => JfrTracerFactory.java} | 38 +++-- ...itterImpl.java => AbstractJfrEmitter.java} | 5 +- ...rImpl.java => AbstractJfrSpanEmitter.java} | 31 ++-- .../jfrtracer/impl/jfr/JfrEmitterFactory.java | 2 - .../impl/jfr/JfrScopeEmitterImpl.java | 127 ++++++++-------- .../impl/jfr/JfrSpanEmitterImpl.java | 116 ++++++++------- .../impl/wrapper/ScopeManagerWrapper.java | 15 +- .../jfrtracer/impl/wrapper/ScopeWrapper.java | 26 +++- .../impl/wrapper/SpanBuilderWrapper.java | 35 ++--- ...atingJfrTracer.java => TracerWrapper.java} | 31 ++-- .../impl/jfr/JfrScopeEmitterImpl.java | 22 +-- .../impl/jfr/JfrSpanEmitterImpl.java | 33 +++-- ...tracing.contrib.jfrtracer.ContextExtractor | 3 - .../contrib/jfrtracer/DifferentSpanTest.java | 135 +++++++++++++++++ .../jfrtracer/ImplementationsJFRTest.java | 135 +++++++++++++++++ .../contrib/jfrtracer/JFRTestUtils.java | 112 ++++++++++++++ .../contrib/jfrtracer/JFRTracerTest.java | 90 ++++++++++++ .../contrib/jfrtracer/DifferentSpanTest.java | 129 ++++++++++++++++ .../jfrtracer/ImplementationsJFRTest.java | 132 +++++++++++++++++ .../contrib/jfrtracer/JfrTestUtils.java} | 19 +-- .../contrib/jfrtracer/JfrTracerTest.java | 138 ++++++++++++++++++ .../contrib/jfrtracer/opentracing.jfc | 27 ++++ 25 files changed, 1230 insertions(+), 250 deletions(-) rename src/main/java/io/opentracing/contrib/jfrtracer/{WrapperFactory.java => JfrTracerFactory.java} (60%) rename src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/{AbstractJfrEmitterImpl.java => AbstractJfrEmitter.java} (84%) rename src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/{AbstractJfrSpanEmitterImpl.java => AbstractJfrSpanEmitter.java} (55%) rename src/main/java/io/opentracing/contrib/jfrtracer/impl/wrapper/{DelegatingJfrTracer.java => TracerWrapper.java} (74%) delete mode 100644 src/main/resources/META-INF/services/io.opentracing.contrib.jfrtracer.ContextExtractor create mode 100644 src/test/java/io/opentracing/contrib/jfrtracer/DifferentSpanTest.java create mode 100644 src/test/java/io/opentracing/contrib/jfrtracer/ImplementationsJFRTest.java create mode 100644 src/test/java/io/opentracing/contrib/jfrtracer/JFRTestUtils.java create mode 100644 src/test/java/io/opentracing/contrib/jfrtracer/JFRTracerTest.java create mode 100644 src/test/java11/io/opentracing/contrib/jfrtracer/DifferentSpanTest.java create mode 100644 src/test/java11/io/opentracing/contrib/jfrtracer/ImplementationsJFRTest.java rename src/test/{java/io/opentracing/contrib/jfrtracer/DelegatingJfrTracerTest.java => java11/io/opentracing/contrib/jfrtracer/JfrTestUtils.java} (65%) create mode 100644 src/test/java11/io/opentracing/contrib/jfrtracer/JfrTracerTest.java create mode 100644 src/test/resources/io/opentracing/contrib/jfrtracer/opentracing.jfc diff --git a/.gitignore b/.gitignore index 5d2908e..6b1ee87 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,8 @@ /bin/ /build/ *.bak -/.nb-gradle/ \ No newline at end of file +/.nb-gradle/ +nbproject/ +nb-configuration.xml +nbactions.xml +.attach_pid* diff --git a/README.md b/README.md index 34fd774..710837e 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ An example app with OpenTracing enabled, and which is using the JFR tracer, can ## Building To build the JFR Tracer, and install it into the local maven repo, first ensure that you -have installed an Oracle JDK 7, and an Open JDK 11. These will be required to build the +have installed an Oracle JDK 8, and an Open JDK 11. These will be required to build the tracer. Once built, the tracer can be used with Oracle JDK 8+ (except Oracle JDK 9 and 10), and OpenJDK 11+. Ensure that the following two environment variables are set to the JAVA_HOME of the JDKs: diff --git a/build.gradle b/build.gradle index 96328d5..3b11317 100644 --- a/build.gradle +++ b/build.gradle @@ -12,31 +12,86 @@ sourceSets { java11 { java { srcDirs = ['src/main/java11'] + compileClasspath += main.output.classesDirs + runtimeClasspath += main.output.classesDirs + } + } + + testJava11 { + java { + srcDirs = ['src/test/java11'] + compileClasspath += java11.output.classesDirs + main.output.classesDirs + runtimeClasspath += java11.output.classesDirs + main.output.classesDirs } } } +configurations { + java11Compile { + extendsFrom compile + } + + testJava11Compile { + extendsFrom testCompile + } +} dependencies { - compile group: 'io.opentracing', name: 'opentracing-api', version: '0.32.0-RC2-SNAPSHOT' - compile group: 'io.opentracing', name: 'opentracing-noop', version: '0.32.0-RC2-SNAPSHOT' - java11Compile group: 'io.opentracing', name: 'opentracing-api', version: '0.32.0-RC2-SNAPSHOT' - java11Compile group: 'io.opentracing', name: 'opentracing-noop', version: '0.32.0-RC2-SNAPSHOT' - java11Implementation files(sourceSets.main.output.classesDirs) { builtBy compileJava } - testImplementation 'junit:junit:4.12' + compile 'io.opentracing:opentracing-api:0.32.0-RC1' + testCompile 'io.opentracing:opentracing-mock:0.32.0-RC1' + testCompile 'io.jaegertracing:jaeger-core:0.32.0' + testCompile 'io.opentracing.brave:brave-opentracing:0.31.2' + testCompile 'io.opentracing.contrib:opentracing-concurrent:0.2.0' + testCompile 'org.awaitility:awaitility:3.1.3' + testCompile 'org.junit.jupiter:junit-jupiter-api:5.3.1' + testCompile 'org.junit.jupiter:junit-jupiter-engine:5.3.1' } compileJava { - options.compilerArgs.addAll(['-Xlint:all,-deprecation']) + options.compilerArgs.addAll(['-Xlint:all,-deprecation', '-Werror']) sourceCompatibility = 1.8 targetCompatibility = 1.8 } +compileTestJava { + options.compilerArgs.addAll(['-Xlint:all,-deprecation']) + sourceCompatibility = compileJava.sourceCompatibility + targetCompatibility = compileJava.targetCompatibility +} + +test { + useJUnitPlatform() + executable = "${JAVA_8}/bin/java" + forkEvery = 1 + testLogging { + events "passed", "skipped", "failed" + } +} + compileJava11Java { - options.compilerArgs.addAll(['--release', '11', '-Xlint:all']) + options.compilerArgs.addAll(['--release', '11', '-Xlint:all', '-Werror']) sourceCompatibility = 11 targetCompatibility = 11 } +compileTestJava11Java { + options.compilerArgs.addAll(['--release', '11', '-Xlint:all']) + sourceCompatibility = compileJava11Java.sourceCompatibility + targetCompatibility = compileJava11Java.targetCompatibility +} + +task testJava11(type: Test) { + dependsOn testJava11Classes + testClassesDirs = sourceSets.testJava11.output.classesDirs + classpath = sourceSets.testJava11.runtimeClasspath + useJUnitPlatform() + executable = "${JAVA_11}/bin/java" + testLogging { + events "passed", "skipped", "failed" + } +} + +test.dependsOn testJava11 + jar { into('META-INF/versions/11') { from sourceSets.java11.output diff --git a/src/main/java/io/opentracing/contrib/jfrtracer/WrapperFactory.java b/src/main/java/io/opentracing/contrib/jfrtracer/JfrTracerFactory.java similarity index 60% rename from src/main/java/io/opentracing/contrib/jfrtracer/WrapperFactory.java rename to src/main/java/io/opentracing/contrib/jfrtracer/JfrTracerFactory.java index 5eb5715..445eb77 100644 --- a/src/main/java/io/opentracing/contrib/jfrtracer/WrapperFactory.java +++ b/src/main/java/io/opentracing/contrib/jfrtracer/JfrTracerFactory.java @@ -16,28 +16,38 @@ package io.opentracing.contrib.jfrtracer; import io.opentracing.Tracer; -import io.opentracing.contrib.jfrtracer.impl.wrapper.DelegatingJfrTracer; +import io.opentracing.contrib.jfrtracer.impl.wrapper.TracerWrapper; + +import java.util.logging.Logger; /** - * Factory responsible for creating the wrapper tracer used to emit the flight - * recorder events. + * Factory responsible for creating the wrapper tracer used to emit the flight recorder events. *

* Note that this is only supported API. */ -public final class WrapperFactory { +public final class JfrTracerFactory { + + private static final Logger LOG = Logger.getLogger(JfrTracerFactory.class.getName()); + + private JfrTracerFactory() { + } + /** - * Wraps a tracer in a tracer which will provide contextual JFR events The - * tracer will be small and the overhead small. - * - * @param delegate the tracer responsible for the normal open tracing work. This - * can, for example, be your usual Jaeger or Zipkin tracer. - * @return the wrapped tracer to use. You would normally register this tracer as - * your global tracer. + * Wraps a tracer in a tracer which will provide contextual JFR events The tracer will be small and the overhead + * small. + * + * @param delegate the tracer responsible for the normal open tracing work. This can, for example, be your usual + * Jaeger or Zipkin tracer. + * @return the wrapped tracer to use. You would normally register this tracer as your global tracer. */ - public static Tracer wrap(Tracer delegate) { - if (delegate instanceof DelegatingJfrTracer) { + public static Tracer create(Tracer delegate) { + + LOG.info("Using DelegatingJfrTracer to capture contextual information into JFR."); + + if (delegate instanceof TracerWrapper) { throw new IllegalArgumentException("You may not wrap a jfr tracer!"); } - return new DelegatingJfrTracer(delegate); + + return new TracerWrapper(delegate); } } diff --git a/src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/AbstractJfrEmitterImpl.java b/src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/AbstractJfrEmitter.java similarity index 84% rename from src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/AbstractJfrEmitterImpl.java rename to src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/AbstractJfrEmitter.java index 83adbd8..0755b7e 100644 --- a/src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/AbstractJfrEmitterImpl.java +++ b/src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/AbstractJfrEmitter.java @@ -18,16 +18,15 @@ import java.util.logging.Logger; import io.opentracing.Span; -import io.opentracing.contrib.jfrtracer.impl.jfr.JfrScopeEmitterImpl; /** * Abstract super class for emitters. */ -abstract class AbstractJfrEmitterImpl implements JfrEmitter { +abstract class AbstractJfrEmitter implements JfrEmitter { static final Logger LOGGER = Logger.getLogger(JfrScopeEmitterImpl.class.getName()); protected Span span; - AbstractJfrEmitterImpl(Span span) { + AbstractJfrEmitter(Span span) { this.span = span; } } diff --git a/src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/AbstractJfrSpanEmitterImpl.java b/src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/AbstractJfrSpanEmitter.java similarity index 55% rename from src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/AbstractJfrSpanEmitterImpl.java rename to src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/AbstractJfrSpanEmitter.java index 1c7ec29..54c0468 100644 --- a/src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/AbstractJfrSpanEmitterImpl.java +++ b/src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/AbstractJfrSpanEmitter.java @@ -16,8 +16,6 @@ package io.opentracing.contrib.jfrtracer.impl.jfr; import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.RejectedExecutionHandler; -import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -26,24 +24,21 @@ /** * Abstract super class for span emitters. */ -abstract class AbstractJfrSpanEmitterImpl extends AbstractJfrEmitterImpl { - protected final static ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(1, 1, 2, TimeUnit.SECONDS, - new ArrayBlockingQueue(50), new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - Thread thread = new Thread(r, "JfrTracer Span Events"); - thread.setDaemon(true); - return thread; - } - }, new RejectedExecutionHandler() { - @Override - public void rejectedExecution(Runnable runnable, ThreadPoolExecutor executor) { - // Seems very unlikely to happen, but just to be sure... - LOGGER.warning("Span Event queue full - dropped span event"); - } +abstract class AbstractJfrSpanEmitter extends AbstractJfrEmitter { + + protected final static ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(1, 1, Long.MAX_VALUE, TimeUnit.NANOSECONDS, + new ArrayBlockingQueue(50), + (r) -> { + Thread thread = new Thread(r, "JfrTracer Span Events"); + thread.setDaemon(true); + return thread; + }, + (r, e) -> { + // Seems very unlikely to happen, but just to be sure... + LOGGER.warning("Span Event queue full - dropped span event"); }); - AbstractJfrSpanEmitterImpl(Span span) { + AbstractJfrSpanEmitter(Span span) { super(span); } } diff --git a/src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/JfrEmitterFactory.java b/src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/JfrEmitterFactory.java index 13ea4e8..5563b70 100644 --- a/src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/JfrEmitterFactory.java +++ b/src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/JfrEmitterFactory.java @@ -16,8 +16,6 @@ package io.opentracing.contrib.jfrtracer.impl.jfr; import io.opentracing.Span; -import io.opentracing.contrib.jfrtracer.impl.jfr.JfrScopeEmitterImpl; -import io.opentracing.contrib.jfrtracer.impl.jfr.JfrSpanEmitterImpl; /** * For creating JfrEmitters. diff --git a/src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/JfrScopeEmitterImpl.java b/src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/JfrScopeEmitterImpl.java index aa887ce..9cb78d3 100644 --- a/src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/JfrScopeEmitterImpl.java +++ b/src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/JfrScopeEmitterImpl.java @@ -31,13 +31,13 @@ import io.opentracing.Span; /** - * This is the JDK 7/8 implementation. For the JDK 9 and later implementation, see src/main/java9. + * This is the JDK 8 implementation. For the JDK 11 and later implementation, see src/main/java11. */ @SuppressWarnings({"deprecation"}) -final class JfrScopeEmitterImpl extends AbstractJfrEmitterImpl { +final class JfrScopeEmitterImpl extends AbstractJfrEmitter { + private static final Producer PRODUCER; private static final EventToken SCOPE_EVENT_TOKEN; - private ScopeEvent currentEvent; static { URI producerURI = URI.create("http://opentracing.io/jfr-tracer"); @@ -46,21 +46,74 @@ final class JfrScopeEmitterImpl extends AbstractJfrEmitterImpl { SCOPE_EVENT_TOKEN = register(ScopeEvent.class); } - @EventDefinition(path = "jfrtracer/scopeevent", name = "ScopeEvent", description = "A thread local event triggered by scope activation", stacktrace = true, thread = true) - private static class ScopeEvent extends TimedEvent { - @ValueDefinition(name = "OperationName") + private ScopeEvent currentEvent; + + JfrScopeEmitterImpl(Span span) { + super(span); + } + + @Override + public void close() { + if (currentEvent != null) { + if (currentEvent.shouldWrite()) { + currentEvent.end(); + currentEvent.commit(); + } + currentEvent = null; + } + } + + @Override + public void start(String parentId, String operationName) { + currentEvent = new ScopeEvent(SCOPE_EVENT_TOKEN); + currentEvent.operationName = operationName; + currentEvent.parentId = parentId; + currentEvent.traceId = span.context().toTraceId(); + currentEvent.spanId = span.context().toSpanId(); + currentEvent.begin(); + } + + @Override + public String toString() { + return "JDK 8 JFR Scope Emitter"; + } + + /** + * Helper method to register an event class with the jfr-tracer producer. + * + * @param clazz the event class to register. + * @return the token associated with the event class. + */ + static EventToken register(Class clazz) { + try { + EventToken token = PRODUCER.addEvent(clazz); + LOGGER.fine("Registered EventType " + clazz.getName()); + return token; + } catch (InvalidEventDefinitionException | InvalidValueException e) { + LOGGER.log(Level.SEVERE, + "Failed to register the event class " + clazz.getName() + + ". Event will not be available. Please check your configuration.", + e); + } + return null; + } + + @EventDefinition(path = "opentracing/scopeevent", name = "ScopeEvent", description = "A thread local event triggered by scope activation", stacktrace = true, thread = true) + public static class ScopeEvent extends TimedEvent { + + @ValueDefinition(name = "Operation Name") private String operationName; - @ValueDefinition(name = "TraceId") + @ValueDefinition(name = "Trace Id") private String traceId; - @ValueDefinition(name = "SpanId") + @ValueDefinition(name = "Span Id") private String spanId; - @ValueDefinition(name = "ParentId") + @ValueDefinition(name = "Parent Id") private String parentId; - public ScopeEvent(EventToken eventToken) { + ScopeEvent(EventToken eventToken) { super(eventToken); } @@ -68,7 +121,7 @@ public ScopeEvent(EventToken eventToken) { public String getOperationName() { return operationName; } - + @SuppressWarnings("unused") public String getTraceId() { return traceId; @@ -84,56 +137,4 @@ public String getParentId() { return parentId; } } - - /** - * Helper method to register an event class with the jfr-tracer producer. - * - * @param clazz - * the event class to register. - * @return the token associated with the event class. - */ - static EventToken register(Class clazz) { - try { - EventToken token = PRODUCER.addEvent(clazz); - Logger.getLogger(JfrScopeEmitterImpl.class.getName()).log(Level.FINE, - "Registered EventType " + clazz.getName()); - return token; - } catch (InvalidEventDefinitionException | InvalidValueException e) { - Logger.getLogger(JfrScopeEmitterImpl.class.getName()).log(Level.SEVERE, - "Failed to register the event class " + clazz.getName() - + ". Event will not be available. Please check your configuration.", - e); - } - return null; - } - - JfrScopeEmitterImpl(Span span) { - super(span); - } - - @Override - public void close() { - if (currentEvent != null) { - currentEvent.end(); - currentEvent.commit(); - currentEvent = null; - } else { - LOGGER.warning("Close without start discovered!"); - } - } - - @Override - public void start(String parentId, String operationName) { - currentEvent = new ScopeEvent(SCOPE_EVENT_TOKEN); - currentEvent.operationName = operationName; - currentEvent.parentId = parentId; - currentEvent.traceId = span.context().toTraceId(); - currentEvent.spanId = span.context().toSpanId(); - currentEvent.begin(); - } - - @Override - public String toString() { - return "JDK 7 & JDK 8 JFR Scope Emitter"; - } } diff --git a/src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/JfrSpanEmitterImpl.java b/src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/JfrSpanEmitterImpl.java index 63fba02..2cdc177 100644 --- a/src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/JfrSpanEmitterImpl.java +++ b/src/main/java/io/opentracing/contrib/jfrtracer/impl/jfr/JfrSpanEmitterImpl.java @@ -21,42 +21,76 @@ import com.oracle.jrockit.jfr.ValueDefinition; import io.opentracing.Span; -import io.opentracing.contrib.jfrtracer.impl.jfr.JfrScopeEmitterImpl; /** - * This is the JDK 7/8 implementation for emitting Span events. For the JDK 9 and later - * implementation, see src/main/java9. + * This is the JDK 7/8 implementation for emitting Span events. For the JDK 9 and later implementation, see + * src/main/java9. */ @SuppressWarnings("deprecation") -final class JfrSpanEmitterImpl extends AbstractJfrSpanEmitterImpl { +final class JfrSpanEmitterImpl extends AbstractJfrSpanEmitter { + private static final EventToken SPAN_EVENT_TOKEN; - private SpanEvent currentEvent; static { SPAN_EVENT_TOKEN = JfrScopeEmitterImpl.register(SpanEvent.class); } - @EventDefinition(path = "jfrtracer/spanevent", name = "SpanEvent", description = "And event representing an OpenTracing span", stacktrace = false, thread = true) - private static class SpanEvent extends TimedEvent { - @ValueDefinition(name = "OperationName") + private SpanEvent currentEvent; + + JfrSpanEmitterImpl(Span span) { + super(span); + } + + @Override + public void start(String parentId, String operationName) { + currentEvent = new SpanEvent(SPAN_EVENT_TOKEN); + if (currentEvent.getEventInfo().isEnabled()) { + currentEvent.operationName = operationName; + currentEvent.traceId = span.context().toTraceId(); + currentEvent.spanId = span.context().toSpanId(); + currentEvent.parentId = parentId; + currentEvent.startThread = Thread.currentThread(); + } + EXECUTOR.execute(new BeginEventCommand(currentEvent)); + } + + @Override + public void close() { + if (currentEvent != null) { + currentEvent.endThread = Thread.currentThread(); + EXECUTOR.execute(new EndEventCommand(currentEvent)); + currentEvent = null; + } + } + + @Override + public String toString() { + return "JDK 8 JFR Span Emitter"; + } + + // Must be public for JFR to access it + @EventDefinition(path = "opentracing/spanevent", name = "SpanEvent", description = "And event representing an OpenTracing span", stacktrace = false, thread = true) + public static class SpanEvent extends TimedEvent { + + @ValueDefinition(name = "Operation Name") private String operationName; - - @ValueDefinition(name = "TraceId") + + @ValueDefinition(name = "Trace Id") private String traceId; - @ValueDefinition(name = "SpanId") + @ValueDefinition(name = "Span Id") private String spanId; - @ValueDefinition(name = "ParentId") + @ValueDefinition(name = "Parent Id") private String parentId; - @ValueDefinition(name = "StartThread", description = "The thread initiating the span") + @ValueDefinition(name = "Start Thread", description = "The thread initiating the span") private Thread startThread; - @ValueDefinition(name = "EndThread", description = "The thread ending the span") + @ValueDefinition(name = "End Thread", description = "The thread ending the span") private Thread endThread; - public SpanEvent(EventToken eventToken) { + SpanEvent(EventToken eventToken) { super(eventToken); } @@ -84,26 +118,35 @@ public Thread getStartThread() { public Thread getEndThread() { return endThread; } + + @SuppressWarnings("unused") + public String getOperationName() { + return operationName; + } } private static class EndEventCommand implements Runnable { + private final SpanEvent event; - public EndEventCommand(SpanEvent event) { + EndEventCommand(SpanEvent event) { this.event = event; } @Override public void run() { - event.end(); - event.commit(); + if (event.shouldWrite()) { + event.end(); + event.commit(); + } } } private static class BeginEventCommand implements Runnable { + private final SpanEvent event; - public BeginEventCommand(SpanEvent event) { + BeginEventCommand(SpanEvent event) { this.event = event; } @@ -112,39 +155,4 @@ public void run() { event.begin(); } } - - JfrSpanEmitterImpl(Span span) { - super(span); - } - - @Override - public void close() { - if (currentEvent != null) { - if (currentEvent.shouldWrite()) { - currentEvent.endThread = Thread.currentThread(); - EXECUTOR.execute(new EndEventCommand(currentEvent)); - currentEvent = null; - } - } else { - LOGGER.warning("Close without start discovered!"); - } - } - - @Override - public void start(String parentId, String operationName) { - currentEvent = new SpanEvent(SPAN_EVENT_TOKEN); - if (currentEvent.getEventInfo().isEnabled()) { - currentEvent.operationName = operationName; - currentEvent.parentId = parentId; - currentEvent.startThread = Thread.currentThread(); - currentEvent.traceId = span.context().toTraceId(); - currentEvent.spanId = span.context().toSpanId(); - } - EXECUTOR.execute(new BeginEventCommand(currentEvent)); - } - - @Override - public String toString() { - return "JDK 7 & JDK 8 JFR Span Emitter"; - } } diff --git a/src/main/java/io/opentracing/contrib/jfrtracer/impl/wrapper/ScopeManagerWrapper.java b/src/main/java/io/opentracing/contrib/jfrtracer/impl/wrapper/ScopeManagerWrapper.java index 588cdf5..cded712 100644 --- a/src/main/java/io/opentracing/contrib/jfrtracer/impl/wrapper/ScopeManagerWrapper.java +++ b/src/main/java/io/opentracing/contrib/jfrtracer/impl/wrapper/ScopeManagerWrapper.java @@ -30,17 +30,17 @@ final class ScopeManagerWrapper implements ScopeManager { this.delegate = delegate; } - @SuppressWarnings("deprecation") @Override + @Deprecated public Scope activate(Span span, boolean finishSpanOnClose) { ScopeWrapper wrapper; if (!(span instanceof SpanWrapper)) { // This should be rather unlikely... SpanWrapper spanWrapper = new SpanWrapper("", span, ""); - wrapper = new ScopeWrapper(spanWrapper, delegate.activate(span, finishSpanOnClose), finishSpanOnClose); + wrapper = new ScopeWrapper(this, spanWrapper, delegate.activate(span, finishSpanOnClose), finishSpanOnClose); } else { SpanWrapper spanWrapper = (SpanWrapper) span; - wrapper = new ScopeWrapper(spanWrapper, delegate.activate(spanWrapper.getDelegate(), finishSpanOnClose), finishSpanOnClose); + wrapper = new ScopeWrapper(this, spanWrapper, delegate.activate(spanWrapper.getDelegate(), finishSpanOnClose), finishSpanOnClose); } activeScope.set(wrapper); return wrapper; @@ -51,13 +51,20 @@ public Scope active() { return activeScope.get(); } + void setActive(ScopeWrapper scope) { + activeScope.set(scope); + } + @Override + @SuppressWarnings("deprecation") public Scope activate(Span arg) { return activate(arg, false); } @Override + @SuppressWarnings("deprecation") public Span activeSpan() { - return delegate.activeSpan(); + Scope scope = active(); + return scope == null ? null : scope.span(); } } diff --git a/src/main/java/io/opentracing/contrib/jfrtracer/impl/wrapper/ScopeWrapper.java b/src/main/java/io/opentracing/contrib/jfrtracer/impl/wrapper/ScopeWrapper.java index 4093c48..78b2f1a 100644 --- a/src/main/java/io/opentracing/contrib/jfrtracer/impl/wrapper/ScopeWrapper.java +++ b/src/main/java/io/opentracing/contrib/jfrtracer/impl/wrapper/ScopeWrapper.java @@ -19,42 +19,54 @@ import io.opentracing.Span; import io.opentracing.contrib.jfrtracer.impl.jfr.JfrEmitter; +import java.util.logging.Logger; +import java.util.logging.Level; + /** * Wrapper for {@link Scope}. */ final class ScopeWrapper implements Scope { + + private static final Logger LOG = Logger.getLogger(ScopeWrapper.class.getName()); + + private final ScopeManagerWrapper scopeManagerWrapper; private final Scope delegate; private final JfrEmitter emitter; private final SpanWrapper spanWrapper; - private final boolean finishOnClose; + private final boolean finishSpanOnClose; + private final ScopeWrapper parentScope; - ScopeWrapper(SpanWrapper spanWrapper, Scope delegate, boolean finishOnClose) { + ScopeWrapper(ScopeManagerWrapper scopeManagerWrapper, SpanWrapper spanWrapper, Scope delegate, boolean finishSpanOnClose) { + this.scopeManagerWrapper = scopeManagerWrapper; + this.parentScope = (ScopeWrapper) scopeManagerWrapper.active(); this.spanWrapper = spanWrapper; this.delegate = delegate; emitter = SpanWrapper.EMITTER_FACTORY.createScopeEmitter(spanWrapper); emitter.start(spanWrapper.getParentId(), spanWrapper.getOperationName()); - this.finishOnClose = finishOnClose; + this.finishSpanOnClose = finishSpanOnClose; } @Override public void close() { delegate.close(); closeEmitter(); - if (finishOnClose) { + if (finishSpanOnClose) { spanWrapper.closeEmitter(); } + scopeManagerWrapper.setActive(parentScope); } @Override + @Deprecated public Span span() { return spanWrapper; } - + private void closeEmitter() { try { emitter.close(); - } catch (Exception e) { - e.printStackTrace(); + } catch (Exception ex) { + LOG.log(Level.SEVERE, "Error closing JFR Span", ex); } } } diff --git a/src/main/java/io/opentracing/contrib/jfrtracer/impl/wrapper/SpanBuilderWrapper.java b/src/main/java/io/opentracing/contrib/jfrtracer/impl/wrapper/SpanBuilderWrapper.java index d7a9a2c..e6dd10f 100644 --- a/src/main/java/io/opentracing/contrib/jfrtracer/impl/wrapper/SpanBuilderWrapper.java +++ b/src/main/java/io/opentracing/contrib/jfrtracer/impl/wrapper/SpanBuilderWrapper.java @@ -25,16 +25,15 @@ * Wrapper for {@link SpanBuilder}. */ final class SpanBuilderWrapper implements SpanBuilder { - private final DelegatingJfrTracer owner; + + private final TracerWrapper owner; private final SpanBuilder delegate; private final String operationName; // Not sure how likely it is that these builders get passed around, // but assumption is the mother of all... private volatile String parentId; - private SpanWrapper spanWrapper; - - SpanBuilderWrapper(DelegatingJfrTracer owner, String operationName, SpanBuilder delegate) { + SpanBuilderWrapper(TracerWrapper owner, String operationName, SpanBuilder delegate) { this.owner = owner; this.delegate = delegate; this.operationName = operationName; @@ -84,14 +83,20 @@ public SpanBuilder withTag(String key, Number value) { return this; } + @Override + public SpanBuilder withTag(Tag key, T value) { + delegate.withTag(key, value); + return this; + } + @Override public SpanBuilder withStartTimestamp(long microseconds) { delegate.withStartTimestamp(microseconds); return this; } - @SuppressWarnings("deprecation") @Override + @Deprecated public Scope startActive(boolean finishSpanOnClose) { return owner.scopeManager().activate(start(), finishSpanOnClose); } @@ -99,24 +104,20 @@ public Scope startActive(boolean finishSpanOnClose) { @Override @Deprecated public Span startManual() { - if (spanWrapper == null) { - spanWrapper = new SpanWrapper(parentId, delegate.startManual(), operationName); - } - return spanWrapper; + return new SpanWrapper(getParentSpanId(), delegate.startManual(), operationName); } @Override public Span start() { - if (spanWrapper == null) { - spanWrapper = new SpanWrapper(parentId, delegate.start(), operationName); - spanWrapper.start(); - } + SpanWrapper spanWrapper = new SpanWrapper(getParentSpanId(), delegate.start(), operationName); + spanWrapper.start(); return spanWrapper; } - @Override - public SpanBuilder withTag(Tag key, T value) { - delegate.withTag(key, value); - return this; + private String getParentSpanId() { + Span activeSpan = owner.scopeManager().activeSpan(); + return parentId != null ? parentId + : activeSpan != null ? activeSpan.context().toSpanId() + : null; } } diff --git a/src/main/java/io/opentracing/contrib/jfrtracer/impl/wrapper/DelegatingJfrTracer.java b/src/main/java/io/opentracing/contrib/jfrtracer/impl/wrapper/TracerWrapper.java similarity index 74% rename from src/main/java/io/opentracing/contrib/jfrtracer/impl/wrapper/DelegatingJfrTracer.java rename to src/main/java/io/opentracing/contrib/jfrtracer/impl/wrapper/TracerWrapper.java index 0326650..329d0e5 100644 --- a/src/main/java/io/opentracing/contrib/jfrtracer/impl/wrapper/DelegatingJfrTracer.java +++ b/src/main/java/io/opentracing/contrib/jfrtracer/impl/wrapper/TracerWrapper.java @@ -15,16 +15,16 @@ */ package io.opentracing.contrib.jfrtracer.impl.wrapper; -import java.util.logging.Logger; - import io.opentracing.Scope; import io.opentracing.ScopeManager; import io.opentracing.Span; import io.opentracing.SpanContext; import io.opentracing.Tracer; -import io.opentracing.noop.NoopTracerFactory; import io.opentracing.propagation.Format; +import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; + /** * A tracer that records context information into the JDK Flight Recorder, making it possible to * correlate interesting findings in distributed traces with detailed information in the flight @@ -37,34 +37,26 @@ *

  • Parent Id
  • * */ -public final class DelegatingJfrTracer implements Tracer { +public final class TracerWrapper implements Tracer { + private final Tracer delegate; private final ScopeManagerWrapper scopeManager; - public DelegatingJfrTracer(Tracer delegate) { - this.delegate = initialize(delegate); + public TracerWrapper(Tracer delegate) { + this.delegate = requireNonNull(delegate); this.scopeManager = new ScopeManagerWrapper(delegate.scopeManager()); } - private static Tracer initialize(Tracer delegate) { - Logger.getLogger(DelegatingJfrTracer.class.getName()) - .info("Using DelegatingJfrTracer to capture contextual information into JFR."); - - if (delegate == null) { - Logger.getLogger(DelegatingJfrTracer.class.getName()).info("No delegate set - will only log to JFR."); - } - return delegate == null ? NoopTracerFactory.create() : delegate; - } - @Override public ScopeManager scopeManager() { return scopeManager; } - @SuppressWarnings("deprecation") @Override + @Deprecated public Span activeSpan() { - return scopeManager.active().span(); + Scope active = scopeManager.active(); + return isNull(active) ? null : active.span(); } @Override @@ -83,7 +75,8 @@ public SpanContext extract(Format format, C carrier) { } @Override + @SuppressWarnings("deprecation") public Scope activateSpan(Span span) { - return scopeManager.activate(span); + return scopeManager.activate(span, true); } } diff --git a/src/main/java11/io/opentracing/contrib/jfrtracer/impl/jfr/JfrScopeEmitterImpl.java b/src/main/java11/io/opentracing/contrib/jfrtracer/impl/jfr/JfrScopeEmitterImpl.java index e14f4ed..a1a6a13 100644 --- a/src/main/java11/io/opentracing/contrib/jfrtracer/impl/jfr/JfrScopeEmitterImpl.java +++ b/src/main/java11/io/opentracing/contrib/jfrtracer/impl/jfr/JfrScopeEmitterImpl.java @@ -25,13 +25,15 @@ /** * This is the JDK 9 or later implementation of the JfrEmitter. */ -public class JfrScopeEmitterImpl extends AbstractJfrEmitterImpl { - private Jdk9ScopeEvent currentEvent; +public class JfrScopeEmitterImpl extends AbstractJfrEmitter { + private ScopeEvent currentEvent; + + @Category("Open Tracing") @Label("Scope Event") @Description("Open tracing event corresponding to an activation scope") - @Category("Open Tracing") - private static class Jdk9ScopeEvent extends Event { + private static class ScopeEvent extends Event { + @Label("Operation Name") private String operationName; @@ -52,17 +54,17 @@ private static class Jdk9ScopeEvent extends Event { @Override public void close() { if (currentEvent != null) { - currentEvent.end(); - currentEvent.commit(); + if (currentEvent.shouldCommit()) { + currentEvent.end(); + currentEvent.commit(); + } currentEvent = null; - } else { - LOGGER.warning("Close without start discovered!"); } } @Override public void start(String parentId, String operationName) { - currentEvent = new Jdk9ScopeEvent(); + currentEvent = new ScopeEvent(); if (currentEvent.isEnabled()) { currentEvent.operationName = operationName; currentEvent.parentId = parentId; @@ -74,6 +76,6 @@ public void start(String parentId, String operationName) { @Override public String toString() { - return "JDK 9+ JFR Emitter"; + return "JDK 11 JFR Emitter"; } } diff --git a/src/main/java11/io/opentracing/contrib/jfrtracer/impl/jfr/JfrSpanEmitterImpl.java b/src/main/java11/io/opentracing/contrib/jfrtracer/impl/jfr/JfrSpanEmitterImpl.java index d6e938a..2b328c4 100644 --- a/src/main/java11/io/opentracing/contrib/jfrtracer/impl/jfr/JfrSpanEmitterImpl.java +++ b/src/main/java11/io/opentracing/contrib/jfrtracer/impl/jfr/JfrSpanEmitterImpl.java @@ -21,21 +21,21 @@ import jdk.jfr.Description; import jdk.jfr.StackTrace; -import javax.management.DescriptorKey; - import io.opentracing.Span; /** * This is the JDK 9 or later implementation of the JfrEmitter. */ -public class JfrSpanEmitterImpl extends AbstractJfrSpanEmitterImpl { - private Jdk9SpanEvent currentEvent; +public class JfrSpanEmitterImpl extends AbstractJfrSpanEmitter { + + private volatile SpanEvent currentEvent; @Label("Span Event") @Description("Open tracing event corresponding to a span.") @Category("Open Tracing") @StackTrace(false) - private static class Jdk9SpanEvent extends Event { + private static class SpanEvent extends Event { + @Label("Operation Name") private String operationName; @@ -58,22 +58,27 @@ private static class Jdk9SpanEvent extends Event { } private static class EndEventCommand implements Runnable { - private final Jdk9SpanEvent event; - public EndEventCommand(Jdk9SpanEvent event) { + private final SpanEvent event; + + EndEventCommand(SpanEvent event) { this.event = event; } @Override public void run() { - event.commit(); + if (event.shouldCommit()) { + event.end(); + event.commit(); + } } } private static class BeginEventCommand implements Runnable { - private final Jdk9SpanEvent event; - public BeginEventCommand(Jdk9SpanEvent event) { + private final SpanEvent event; + + BeginEventCommand(SpanEvent event) { this.event = event; } @@ -89,18 +94,16 @@ public void run() { @Override public void close() { - if (currentEvent != null && currentEvent.isEnabled()) { + if (currentEvent != null) { currentEvent.endThread = Thread.currentThread(); EXECUTOR.execute(new EndEventCommand(currentEvent)); currentEvent = null; - } else { - LOGGER.warning("Close without start discovered!"); } } @Override public void start(String parentId, String operationName) { - currentEvent = new Jdk9SpanEvent(); + currentEvent = new SpanEvent(); if (currentEvent.isEnabled()) { currentEvent.operationName = operationName; currentEvent.parentId = parentId; @@ -113,6 +116,6 @@ public void start(String parentId, String operationName) { @Override public String toString() { - return "JDK 9+ JFR Emitter"; + return "JDK 11 JFR Emitter"; } } diff --git a/src/main/resources/META-INF/services/io.opentracing.contrib.jfrtracer.ContextExtractor b/src/main/resources/META-INF/services/io.opentracing.contrib.jfrtracer.ContextExtractor deleted file mode 100644 index 0d36e9a..0000000 --- a/src/main/resources/META-INF/services/io.opentracing.contrib.jfrtracer.ContextExtractor +++ /dev/null @@ -1,3 +0,0 @@ -io.opentracing.contrib.jfrtracer.extractors.JaegerContextExtractor -io.opentracing.contrib.jfrtracer.extractors.ZipkinContextExtractor -io.opentracing.contrib.jfrtracer.extractors.NoOpContextExtractor diff --git a/src/test/java/io/opentracing/contrib/jfrtracer/DifferentSpanTest.java b/src/test/java/io/opentracing/contrib/jfrtracer/DifferentSpanTest.java new file mode 100644 index 0000000..7d404cf --- /dev/null +++ b/src/test/java/io/opentracing/contrib/jfrtracer/DifferentSpanTest.java @@ -0,0 +1,135 @@ +/* + * Copyright 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.jfrtracer; + +import io.opentracing.Span; +import io.opentracing.Tracer; +import io.opentracing.contrib.concurrent.TracedExecutorService; +import io.opentracing.mock.MockSpan; +import io.opentracing.mock.MockTracer; +import oracle.jrockit.jfr.parser.FLREvent; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@SuppressWarnings("deprecation") +public class DifferentSpanTest { + + @Test + public void spansInMultipleThreads() throws IOException, InterruptedException, ExecutionException, TimeoutException { + Path output = Files.createTempFile("test-recording", ".jfr"); + + try { + // Setup tracers + MockTracer mockTracer = new MockTracer(); + Tracer tracer = JfrTracerFactory.create(mockTracer); + + // Start JFR + JFRTestUtils.startJFR(); + + // Generate spans + Span span = tracer.buildSpan("test span").start(); + TracedExecutorService executor = new TracedExecutorService(Executors.newSingleThreadExecutor(), tracer); + executor.submit(() -> { + tracer.buildSpan("executor span").start().finish(); + }).get(5, TimeUnit.SECONDS); + span.finish(); + + // Stop recording + //Ugly Sleep for now + Thread.sleep(100); + List events = JFRTestUtils.stopJfr(output); + + // Validate span was created and recorded in JFR + assertEquals(2, mockTracer.finishedSpans().size()); + + Map finishedSpans = mockTracer.finishedSpans().stream().collect(Collectors.toMap(e -> e.operationName(), e -> e)); + assertEquals(finishedSpans.size(), events.size()); + events.stream() + .forEach(e -> { + MockSpan finishedSpan = finishedSpans.get(e.getValue("operationName").toString()); + assertNotNull(finishedSpan); + assertEquals(Long.toString(finishedSpan.context().traceId()), e.getValue("traceId")); + assertEquals(Long.toString(finishedSpan.context().spanId()), e.getValue("spanId")); + assertEquals(finishedSpan.operationName(), e.getValue("operationName")); + }); + + } finally { + Files.delete(output); + } + } + + @Test + public void passingSpanBetweenThreads() throws IOException, InterruptedException, TimeoutException, ExecutionException { + Path output = Files.createTempFile("test-recording", ".jfr"); + + try { + // Setup tracers + MockTracer mockTracer = new MockTracer(); + Tracer tracer = JfrTracerFactory.create(mockTracer); + + // Start JFR + JFRTestUtils.startJFR(); + + // Generate spans + TracedExecutorService executor = new TracedExecutorService(Executors.newSingleThreadExecutor(), tracer); + long expectedStartThread = Thread.currentThread().getId(); + Span span = tracer.buildSpan("test span").start(); + long expectedFinishThread = executor.submit(() -> { + span.finish(); + return Thread.currentThread().getId(); + }).get(5, TimeUnit.SECONDS); + + // Stop recording + Thread.sleep(100); + List events = JFRTestUtils.stopJfr(output); + + // Validate span was created and recorded in JFR + assertEquals(1, mockTracer.finishedSpans().size()); + + Map finishedSpans = mockTracer.finishedSpans().stream().collect(Collectors.toMap(e -> e.operationName(), e -> e)); + assertEquals(finishedSpans.size(), events.size()); + events.stream() + .forEach(e -> { + MockSpan finishedSpan = finishedSpans.get(e.getValue("operationName").toString()); + assertNotNull(finishedSpan); + assertEquals(Long.toString(finishedSpan.context().traceId()), e.getValue("traceId")); + assertEquals(Long.toString(finishedSpan.context().spanId()), e.getValue("spanId")); + assertEquals(finishedSpan.operationName(), e.getValue("operationName")); + assertNotEquals(expectedStartThread, e.getThread()); + assertNotEquals(expectedFinishThread, e.getThread()); + assertEquals(expectedStartThread, e.getValue("startThread")); + assertEquals(expectedFinishThread, e.getValue("endThread")); + }); + + } finally { + Files.delete(output); + } + } +} diff --git a/src/test/java/io/opentracing/contrib/jfrtracer/ImplementationsJFRTest.java b/src/test/java/io/opentracing/contrib/jfrtracer/ImplementationsJFRTest.java new file mode 100644 index 0000000..38514ac --- /dev/null +++ b/src/test/java/io/opentracing/contrib/jfrtracer/ImplementationsJFRTest.java @@ -0,0 +1,135 @@ +/* + * Copyright 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.jfrtracer; + +import brave.Tracing; +import brave.opentracing.BraveTracer; +import brave.propagation.B3Propagation; +import brave.propagation.ExtraFieldPropagation; +import brave.propagation.Propagation.Factory; +import io.jaegertracing.Configuration.CodecConfiguration; +import io.jaegertracing.Configuration.ReporterConfiguration; +import io.jaegertracing.Configuration.SamplerConfiguration; +import io.opentracing.Span; +import io.opentracing.Tracer; +import oracle.jrockit.jfr.parser.FLREvent; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; + +import static io.jaegertracing.Configuration.JAEGER_AGENT_HOST; +import static io.jaegertracing.Configuration.JAEGER_AGENT_PORT; +import static io.jaegertracing.Configuration.JAEGER_PROPAGATION; +import static io.jaegertracing.Configuration.JAEGER_SAMPLER_PARAM; +import static io.jaegertracing.Configuration.JAEGER_SAMPLER_TYPE; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@SuppressWarnings("deprecation") +@Disabled("Skip until Jaeger and Brave supports 0.32.0") +public class ImplementationsJFRTest { + + @Test + public void jaegerB3() throws IOException { + System.setProperty(JAEGER_SAMPLER_TYPE, "const"); + System.setProperty(JAEGER_SAMPLER_PARAM, "1"); + System.setProperty(JAEGER_AGENT_HOST, "localhost"); + System.setProperty(JAEGER_AGENT_PORT, "6831"); + System.setProperty(JAEGER_PROPAGATION, "B3"); + + Tracer jaegerTracer = new io.jaegertracing.Configuration("test") + .withSampler(SamplerConfiguration.fromEnv()) + .withCodec(CodecConfiguration.fromEnv()) + .withReporter(ReporterConfiguration.fromEnv()) + .getTracer(); + innerTest(jaegerTracer); + } + + @Test + public void jaegerUber() throws IOException { + System.setProperty(JAEGER_SAMPLER_TYPE, "const"); + System.setProperty(JAEGER_SAMPLER_PARAM, "1"); + System.setProperty(JAEGER_AGENT_HOST, "localhost"); + System.setProperty(JAEGER_AGENT_PORT, "6831"); + System.setProperty(JAEGER_PROPAGATION, "JAEGER"); + + Tracer jaegerTracer = new io.jaegertracing.Configuration("test") + .withSampler(SamplerConfiguration.fromEnv()) + .withCodec(CodecConfiguration.fromEnv()) + .withReporter(ReporterConfiguration.fromEnv()) + .getTracer(); + innerTest(jaegerTracer); + } + + @Test + public void brave() throws IOException { + + Factory propagationFactory = ExtraFieldPropagation.newFactoryBuilder(B3Propagation.FACTORY) + .addPrefixedFields("baggage-", Arrays.asList("country-code", "user-id")) + .build(); + + Tracing braveTracing = Tracing.newBuilder() + .localServiceName("my-service") + .propagationFactory(propagationFactory) + .build(); + innerTest(BraveTracer.create(braveTracing)); + } + + private void innerTest(Tracer testTracer) throws IOException { + Path output = Files.createTempFile("test-recording", ".jfr"); + try { + + Tracer tracer = JfrTracerFactory.create(testTracer); + + // Start JFR + JFRTestUtils.startJFR(); + + // Generate span + Span start = tracer.buildSpan("outer span").start(); + tracer.scopeManager().activate(start, false); + tracer.buildSpan("inner span").startActive(true).close(); + tracer.scopeManager().active().close(); + start.finish(); + + try { + // Stop recording + Thread.sleep(100); + } catch (InterruptedException ex) { + } + List events = JFRTestUtils.stopJfr(output); + + // Validate span was created and recorded in JFR + assertEquals(4, events.size()); + events.stream() + .forEach(e -> { + assertNotNull(e.getValue("operationName")); + if (e.getValue("operationName").equals("inner span")) { + assertNotNull(e.getValue("parentSpanId")); + } + assertNotNull(e.getValue("traceId")); + assertNotNull(e.getValue("spanId")); + }); + + } finally { + Files.delete(output); + } + } +} diff --git a/src/test/java/io/opentracing/contrib/jfrtracer/JFRTestUtils.java b/src/test/java/io/opentracing/contrib/jfrtracer/JFRTestUtils.java new file mode 100644 index 0000000..fa8809d --- /dev/null +++ b/src/test/java/io/opentracing/contrib/jfrtracer/JFRTestUtils.java @@ -0,0 +1,112 @@ +/* + * Copyright 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.jfrtracer; + +import oracle.jrockit.jfr.JFR; +import oracle.jrockit.jfr.parser.ChunkParser; +import oracle.jrockit.jfr.parser.FLREvent; +import oracle.jrockit.jfr.parser.Parser; + +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import javax.management.InstanceNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanServer; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.ReflectionException; + +import static java.util.Objects.nonNull; +import static org.junit.jupiter.api.Assertions.assertTimeout; +import static org.junit.jupiter.api.Assertions.fail; + +public final class JFRTestUtils { + + private JFRTestUtils() { + } + + private static Path getJfrConfig() throws IOException { + Path jfrConfig = Files.createTempFile("opentracing", ".jfc"); + Files.copy(JFRTestUtils.class.getResourceAsStream("opentracing.jfc"), jfrConfig, StandardCopyOption.REPLACE_EXISTING); + return jfrConfig; + } + + @SuppressWarnings("deprecation") + public static void startJFR() { + + Path jfrConfig = null; + try { + jfrConfig = getJfrConfig(); + } catch (IOException ex) { + fail(ex.getMessage()); + } + + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + try { + mbs.invoke(new ObjectName("com.sun.management:type=DiagnosticCommand"), "vmUnlockCommercialFeatures", new Object[0], new String[0]); + mbs.invoke(new ObjectName("com.sun.management:type=DiagnosticCommand"), "jfrStart", + new Object[] {new String[] {"name=opentracing-jfr", "settings=" + jfrConfig.toAbsolutePath().toString()}}, + new String[] {String[].class.getName()}); + + assertTimeout(Duration.ofSeconds(10), () -> { + while (JFR.get().getMBean().getRecordings().isEmpty()) { + System.out.println("Waiting for recording to start"); + Thread.sleep(10); + } + }); + } catch (InstanceNotFoundException | MBeanException | MalformedObjectNameException | ReflectionException ex) { + fail(ex.getMessage()); + } finally { + if (nonNull(jfrConfig)) { + try { + Files.delete(jfrConfig); + } catch (IOException ex) { + } + } + } + } + + @SuppressWarnings("deprecation") + public static List stopJfr(Path output) throws IOException { + try { + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + mbs.invoke(new ObjectName("com.sun.management:type=DiagnosticCommand"), "jfrStop", + new Object[] {new String[] {"name=opentracing-jfr", "filename=" + output.toAbsolutePath().toString()}}, + new String[] {String[].class.getName()}); + } catch (InstanceNotFoundException | MBeanException | MalformedObjectNameException | ReflectionException ex) { + fail(ex.getMessage()); + } + + try (Parser parser = new Parser(output.toFile())) { + List readAllEvents = new ArrayList<>(); + for (ChunkParser chunkParser : parser) { + for (FLREvent event : chunkParser) { + if (event.getPath().startsWith("opentracing")) { + readAllEvents.add(event); + } + } + } + return readAllEvents; + } + } +} diff --git a/src/test/java/io/opentracing/contrib/jfrtracer/JFRTracerTest.java b/src/test/java/io/opentracing/contrib/jfrtracer/JFRTracerTest.java new file mode 100644 index 0000000..a8a96de --- /dev/null +++ b/src/test/java/io/opentracing/contrib/jfrtracer/JFRTracerTest.java @@ -0,0 +1,90 @@ +/* + * Copyright 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.jfrtracer; + +import io.opentracing.Tracer; +import io.opentracing.mock.MockSpan; +import io.opentracing.mock.MockTracer; +import oracle.jrockit.jfr.parser.FLREvent; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@SuppressWarnings("deprecation") +public class JFRTracerTest { + + /** + * Test JFR gets the generated span + * + * @throws java.io.IOException on error + */ + @Test + public void basicEvent() throws IOException { + Path output = Files.createTempFile("opentracing", ".jfr"); + + try { + // Setup tracers + MockTracer mockTracer = new MockTracer(); + Tracer tracer = JfrTracerFactory.create(mockTracer); + + // Start JFR + JFRTestUtils.startJFR(); + // Generate span + tracer.buildSpan("test span").start().finish(); + + // Stop recording + List events = JFRTestUtils.stopJfr(output); + + // Validate span was created and recorded in JFR + assertEquals(1, mockTracer.finishedSpans().size()); + + Map finishedSpans = mockTracer.finishedSpans().stream().collect(Collectors.toMap(e -> e.operationName(), e -> e)); + assertEquals(finishedSpans.size(), events.size()); + events.stream() + .forEach(e -> { + MockSpan finishedSpan = finishedSpans.get(e.getValue("operationName").toString()); + assertNotNull(finishedSpan); + assertEquals(Long.toString(finishedSpan.context().traceId()), e.getValue("traceId")); + assertEquals(Long.toString(finishedSpan.context().spanId()), e.getValue("spanId")); + assertEquals(finishedSpan.operationName(), e.getValue("operationName")); + }); + + } finally { +// Files.delete(output); + } + } + + @Test + public void noJFR() throws IOException { + // Setup tracers + MockTracer mockTracer = new MockTracer(); + Tracer tracer = JfrTracerFactory.create(mockTracer); + + // Generate span + tracer.buildSpan("test span").start().finish(); + + // Validate span was created and recorded in JFR + assertEquals(1, mockTracer.finishedSpans().size()); + } +} diff --git a/src/test/java11/io/opentracing/contrib/jfrtracer/DifferentSpanTest.java b/src/test/java11/io/opentracing/contrib/jfrtracer/DifferentSpanTest.java new file mode 100644 index 0000000..112c5a4 --- /dev/null +++ b/src/test/java11/io/opentracing/contrib/jfrtracer/DifferentSpanTest.java @@ -0,0 +1,129 @@ +/* + * Copyright 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.jfrtracer; + +import io.opentracing.Scope; +import io.opentracing.Span; +import io.opentracing.Tracer; +import io.opentracing.contrib.concurrent.TracedExecutorService; +import io.opentracing.mock.MockSpan; +import io.opentracing.mock.MockTracer; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class DifferentSpanTest { + + @Test + public void spansInMultipleThreads() throws IOException, InterruptedException, ExecutionException, TimeoutException { + Path output = Files.createTempFile("test-recording", ".jfr"); + try { + // Setup tracers + MockTracer mockTracer = new MockTracer(); + Tracer tracer = JfrTracerFactory.create(mockTracer); + + try (Recording recording = JfrTestUtils.startJFR()) { + + // Generate spans + Scope scope = tracer.activateSpan(tracer.buildSpan("test span").start()); + TracedExecutorService executor = new TracedExecutorService(Executors.newSingleThreadExecutor(), tracer); + executor.submit(() -> { + tracer.activateSpan(tracer.buildSpan("executor span").start()).close(); + }).get(5, TimeUnit.SECONDS); + scope.close(); + + recording.dump(output); + } + + // Validate span was created and recorded in JFR + assertEquals(2, mockTracer.finishedSpans().size()); + + Map finishedSpans = mockTracer.finishedSpans().stream().collect(Collectors.toMap(e -> e.operationName(), e -> e)); + List events = RecordingFile.readAllEvents(output); + events.stream() + .forEach(e -> { + MockSpan finishedSpan = finishedSpans.get(e.getString("operationName")); + assertNotNull(finishedSpan); + assertEquals(Long.toString(finishedSpan.context().traceId()), e.getString("traceId")); + assertEquals(Long.toString(finishedSpan.context().spanId()), e.getString("spanId")); + assertEquals(finishedSpan.operationName(), e.getString("operationName")); + if ("executor span".equals(e.getString("operationName"))) { + assertNotNull(e.getString("parentId")); + } + }); + + } finally { + Files.delete(output); + } + } + + @Test + public void passingSpanBetweenThreads() throws IOException, InterruptedException, TimeoutException, ExecutionException { + Path output = Files.createTempFile("test-recording", ".jfr"); + try { + // Setup tracers + MockTracer mockTracer = new MockTracer(); + Tracer tracer = JfrTracerFactory.create(mockTracer); + + try (Recording recording = JfrTestUtils.startJFR()) { + + // Generate spans + Span span = tracer.buildSpan("test span").start(); + TracedExecutorService executor = new TracedExecutorService(Executors.newSingleThreadExecutor(), tracer); + executor.submit(() -> { + span.finish(); + }).get(5, TimeUnit.SECONDS); + + recording.dump(output); + } + + // Validate span was created and recorded in JFR + assertEquals(1, mockTracer.finishedSpans().size()); + + Map finishedSpans = mockTracer.finishedSpans().stream().collect(Collectors.toMap(e -> e.operationName(), e -> e)); + List events = RecordingFile.readAllEvents(output); + assertEquals(finishedSpans.size(), events.size()); + events.stream() + .forEach(e -> { + MockSpan finishedSpan = finishedSpans.get(e.getString("operationName")); + assertNotNull(finishedSpan); + assertEquals(Long.toString(finishedSpan.context().traceId()), e.getString("traceId")); + assertEquals(Long.toString(finishedSpan.context().spanId()), e.getString("spanId")); + assertEquals(finishedSpan.operationName(), e.getString("operationName")); + assertNotEquals(Thread.currentThread().getName(), e.getThread().getJavaName()); + }); + + } finally { + Files.delete(output); + } + } +} diff --git a/src/test/java11/io/opentracing/contrib/jfrtracer/ImplementationsJFRTest.java b/src/test/java11/io/opentracing/contrib/jfrtracer/ImplementationsJFRTest.java new file mode 100644 index 0000000..e8e78fd --- /dev/null +++ b/src/test/java11/io/opentracing/contrib/jfrtracer/ImplementationsJFRTest.java @@ -0,0 +1,132 @@ +/* + * Copyright 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.jfrtracer; + +import brave.Tracing; +import brave.opentracing.BraveTracer; +import brave.propagation.B3Propagation; +import brave.propagation.ExtraFieldPropagation; +import brave.propagation.Propagation.Factory; +import io.jaegertracing.Configuration.CodecConfiguration; +import io.jaegertracing.Configuration.ReporterConfiguration; +import io.jaegertracing.Configuration.SamplerConfiguration; +import io.opentracing.Span; +import io.opentracing.Tracer; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; + +import static io.jaegertracing.Configuration.JAEGER_AGENT_HOST; +import static io.jaegertracing.Configuration.JAEGER_AGENT_PORT; +import static io.jaegertracing.Configuration.JAEGER_PROPAGATION; +import static io.jaegertracing.Configuration.JAEGER_SAMPLER_PARAM; +import static io.jaegertracing.Configuration.JAEGER_SAMPLER_TYPE; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@Disabled("Skip until Jaeger and Brave supports 0.32.0") +public class ImplementationsJFRTest { + + @Test + public void jaegerB3() throws IOException { + System.setProperty(JAEGER_SAMPLER_TYPE, "const"); + System.setProperty(JAEGER_SAMPLER_PARAM, "1"); + System.setProperty(JAEGER_AGENT_HOST, "localhost"); + System.setProperty(JAEGER_AGENT_PORT, "6831"); + System.setProperty(JAEGER_PROPAGATION, "B3"); + + Tracer jaegerTracer = new io.jaegertracing.Configuration("test") + .withSampler(SamplerConfiguration.fromEnv()) + .withCodec(CodecConfiguration.fromEnv()) + .withReporter(ReporterConfiguration.fromEnv()) + .getTracer(); + innerTest(jaegerTracer); + } + + @Test + public void jaegerUber() throws IOException { + System.setProperty(JAEGER_SAMPLER_TYPE, "const"); + System.setProperty(JAEGER_SAMPLER_PARAM, "1"); + System.setProperty(JAEGER_AGENT_HOST, "localhost"); + System.setProperty(JAEGER_AGENT_PORT, "6831"); + System.setProperty(JAEGER_PROPAGATION, "JAEGER"); + + Tracer jaegerTracer = new io.jaegertracing.Configuration("test") + .withSampler(SamplerConfiguration.fromEnv()) + .withCodec(CodecConfiguration.fromEnv()) + .withReporter(ReporterConfiguration.fromEnv()) + .getTracer(); + innerTest(jaegerTracer); + } + + @Test + public void brave() throws IOException { + + Factory propagationFactory = ExtraFieldPropagation.newFactoryBuilder(B3Propagation.FACTORY) + .addPrefixedFields("baggage-", Arrays.asList("country-code", "user-id")) + .build(); + + Tracing braveTracing = Tracing.newBuilder() + .localServiceName("my-service") + .propagationFactory(propagationFactory) + .build(); + innerTest(BraveTracer.create(braveTracing)); + } + + private void innerTest(Tracer testTracer) throws IOException { + Path output = Files.createTempFile("test-recording", ".jfr"); + try { + + Tracer tracer = JfrTracerFactory.create(testTracer); + + try (Recording recording = JfrTestUtils.startJFR()) { + + // Generate span + Span start = tracer.buildSpan("outer span").start(); + tracer.scopeManager().activate(start); + tracer.activateSpan(tracer.buildSpan("inner span").start()).close(); + tracer.scopeManager().active().close(); + start.finish(); + + recording.dump(output); + } + + // Validate span was created and recorded in JFR + List events = RecordingFile.readAllEvents(output); + assertEquals(4, events.size()); + events.stream() + .forEach(e -> { + assertNotNull(e.getString("operationName")); + if (e.getString("operationName").equals("inner span")) { + assertNotNull(e.getString("parentSpanId")); + } + assertNotNull(e.getString("traceId")); + assertNotNull(e.getString("spanId")); + }); + + } finally { + Files.delete(output); + } + } +} diff --git a/src/test/java/io/opentracing/contrib/jfrtracer/DelegatingJfrTracerTest.java b/src/test/java11/io/opentracing/contrib/jfrtracer/JfrTestUtils.java similarity index 65% rename from src/test/java/io/opentracing/contrib/jfrtracer/DelegatingJfrTracerTest.java rename to src/test/java11/io/opentracing/contrib/jfrtracer/JfrTestUtils.java index da84932..b23e59f 100644 --- a/src/test/java/io/opentracing/contrib/jfrtracer/DelegatingJfrTracerTest.java +++ b/src/test/java11/io/opentracing/contrib/jfrtracer/JfrTestUtils.java @@ -15,19 +15,16 @@ */ package io.opentracing.contrib.jfrtracer; -import static org.junit.Assert.assertNotNull; +import jdk.jfr.Recording; -import org.junit.Ignore; -import org.junit.Test; +public final class JfrTestUtils { -import io.opentracing.contrib.jfrtracer.impl.wrapper.DelegatingJfrTracer; - -public class DelegatingJfrTracerTest { + private JfrTestUtils() { + } - @Test - @Ignore - public void testSomeLibraryMethod() { - DelegatingJfrTracer tracer = new DelegatingJfrTracer(null); - assertNotNull(tracer); + public static Recording startJFR() { + Recording recording = new Recording(); + recording.start(); + return recording; } } diff --git a/src/test/java11/io/opentracing/contrib/jfrtracer/JfrTracerTest.java b/src/test/java11/io/opentracing/contrib/jfrtracer/JfrTracerTest.java new file mode 100644 index 0000000..8052b3f --- /dev/null +++ b/src/test/java11/io/opentracing/contrib/jfrtracer/JfrTracerTest.java @@ -0,0 +1,138 @@ +/* + * Copyright 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.jfrtracer; + +import io.opentracing.Scope; +import io.opentracing.Tracer; +import io.opentracing.mock.MockSpan; +import io.opentracing.mock.MockTracer; +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static org.awaitility.Awaitility.await; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class JfrTracerTest { + + /** + * Test JFR gets the generated span + * + * @throws java.io.IOException on error + */ + @Test + public void basicEvent() throws IOException { + Path output = Files.createTempFile("test-recording", ".jfr"); + try { + // Setup tracers + MockTracer mockTracer = new MockTracer(); + Tracer tracer = JfrTracerFactory.create(mockTracer); + + try (Recording recording = JfrTestUtils.startJFR()) { + + // Generate span + tracer.buildSpan("test span").start().finish(); + + recording.dump(output); + } + + // Validate span was created and recorded in JFR + assertEquals(1, mockTracer.finishedSpans().size()); + + Map finishedSpans = mockTracer.finishedSpans().stream().collect(Collectors.toMap(e -> e.operationName(), e -> e)); + List events = RecordingFile.readAllEvents(output); + assertEquals(finishedSpans.size(), events.size()); + events.stream() + .forEach(e -> { + MockSpan finishedSpan = finishedSpans.get(e.getString("operationName")); + assertNotNull(finishedSpan); + assertEquals(Long.toString(finishedSpan.context().traceId()), e.getString("traceId")); + assertEquals(Long.toString(finishedSpan.context().spanId()), e.getString("spanId")); + assertEquals(finishedSpan.operationName(), e.getString("operationName")); + assertTrue(e.getEventType().getName().contains("SpanEvent")); + }); + + } finally { + Files.delete(output); + } + } + + @Test + public void noJFR() throws IOException { + // Setup tracers + MockTracer mockTracer = new MockTracer(); + Tracer tracer = JfrTracerFactory.create(mockTracer); + + // Generate span + assertNull(tracer.scopeManager().active()); + tracer.activateSpan(tracer.buildSpan("test span").start()).close(); + + // Validate span was created and recorded in JFR + assertEquals(1, mockTracer.finishedSpans().size()); + assertNull(tracer.scopeManager().active()); + } + + @Test + @SuppressWarnings("try") + public void noRunningJFR() throws IOException, InterruptedException { + // Setup tracers + MockTracer mockTracer = new MockTracer(); + Tracer tracer = JfrTracerFactory.create(mockTracer); + + Recording recording = JfrTestUtils.startJFR(); + + // Generate span + assertNull(tracer.scopeManager().active()); + try (Scope scope = tracer.activateSpan(tracer.buildSpan("outer span").start())) { + Scope activeScopeOuter = tracer.scopeManager().active(); + assertNotNull(activeScopeOuter); + recording.close(); + while (!FlightRecorder.getFlightRecorder().getRecordings().isEmpty()) { + System.out.println(FlightRecorder.getFlightRecorder().getRecordings().size()); + } + await().atMost(20, TimeUnit.SECONDS).until(() -> FlightRecorder.getFlightRecorder().getRecordings().isEmpty()); + try (Scope inner = tracer.activateSpan(tracer.buildSpan("inner span").start())) { + Scope activeScopeInner = tracer.scopeManager().active(); + assertNotNull(activeScopeInner); + assertNotEquals(activeScopeOuter, activeScopeInner); + } + } + try (Scope scope = tracer.activateSpan(tracer.buildSpan("separate span").start())) { + Scope activeScope = tracer.scopeManager().active(); + assertNotNull(activeScope); + assertFalse(activeScope.getClass().getSimpleName().contains("ScopeEvent")); + } + + // Validate span was created and recorded in JFR + assertEquals(3, mockTracer.finishedSpans().size()); + assertNull(tracer.scopeManager().active()); + } +} diff --git a/src/test/resources/io/opentracing/contrib/jfrtracer/opentracing.jfc b/src/test/resources/io/opentracing/contrib/jfrtracer/opentracing.jfc new file mode 100644 index 0000000..50c784f --- /dev/null +++ b/src/test/resources/io/opentracing/contrib/jfrtracer/opentracing.jfc @@ -0,0 +1,27 @@ + + + + + + true + false + + + true + true + + + + + + + true + + + + true + + + + +