-
Notifications
You must be signed in to change notification settings - Fork 713
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds JDK Flight Recorder context correlation #843
Changes from 6 commits
cbb8d36
f38796b
612154d
74ce6f0
524bcb5
7ec977b
d40c8ba
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,7 +10,7 @@ cache: | |
language: java | ||
|
||
jdk: | ||
- oraclejdk9 | ||
- oraclejdk11 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. openjdk11 |
||
|
||
before_install: | ||
# Parameters used during release | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<parent> | ||
<groupId>io.zipkin.brave</groupId> | ||
<artifactId>brave-context-parent</artifactId> | ||
<version>5.5.3-SNAPSHOT</version> | ||
</parent> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<artifactId>brave-context-jfr</artifactId> | ||
<name>Brave Context: JDK Flight Recorder</name> | ||
|
||
<properties> | ||
<main.basedir>${project.basedir}/../..</main.basedir> | ||
<main.java.version>11</main.java.version> | ||
</properties> | ||
|
||
<build> | ||
<plugins> | ||
<plugin> | ||
<artifactId>maven-jar-plugin</artifactId> | ||
<configuration> | ||
<archive> | ||
<manifestEntries> | ||
<Automatic-Module-Name>brave.context.jfr</Automatic-Module-Name> | ||
</manifestEntries> | ||
</archive> | ||
</configuration> | ||
</plugin> | ||
<plugin> | ||
<groupId>net.orfjackal.retrolambda</groupId> | ||
<artifactId>retrolambda-maven-plugin</artifactId> | ||
<executions> | ||
<execution> | ||
<phase>none</phase> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
<plugin> | ||
<groupId>org.codehaus.mojo</groupId> | ||
<artifactId>animal-sniffer-maven-plugin</artifactId> | ||
<executions> | ||
<execution> | ||
<phase>none</phase> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
</project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package brave.context.jfr; | ||
|
||
import brave.internal.Nullable; | ||
import brave.propagation.CurrentTraceContext.Scope; | ||
import brave.propagation.CurrentTraceContext.ScopeDecorator; | ||
import brave.propagation.TraceContext; | ||
import jdk.jfr.Category; | ||
import jdk.jfr.Description; | ||
import jdk.jfr.Event; | ||
import jdk.jfr.Label; | ||
|
||
/** | ||
* Adds {@linkplain Event} properties "traceId", "parentId" and "spanId" when a {@link | ||
* brave.Tracer#currentSpan() span is current}. These can be used to correlate JDK Flight recorder | ||
* events with logs or Zipkin. | ||
* | ||
* <p>Ex. | ||
* <pre>{@code | ||
* tracing = Tracing.newBuilder() | ||
* .currentTraceContext(ThreadLocalCurrentTraceContext.newBuilder() | ||
* .addScopeDecorator(JfrScopeDecorator.create()) | ||
* .build() | ||
* ) | ||
* ... | ||
* .build(); | ||
* }</pre> | ||
*/ | ||
public final class JfrScopeDecorator implements ScopeDecorator { | ||
|
||
@Category("Zipkin") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. super cool that from here down is less than 50loc. it is very easy to implement this, almost copy/pasteable! |
||
@Label("Scope") | ||
@Description("Zipkin event representing a span being placed in scope") | ||
static final class ScopeEvent extends Event { | ||
@Label("Trace Id") String traceId; | ||
@Label("Parent Id") String parentId; | ||
@Label("Span Id") String spanId; | ||
} | ||
|
||
public static ScopeDecorator create() { | ||
return new JfrScopeDecorator(); | ||
} | ||
|
||
@Override public Scope decorateScope(@Nullable TraceContext currentSpan, Scope scope) { | ||
ScopeEvent event = new ScopeEvent(); | ||
if (!event.isEnabled()) return scope; | ||
|
||
if (currentSpan != null) { | ||
event.traceId = currentSpan.traceIdString(); | ||
event.parentId = currentSpan.parentIdString(); | ||
event.spanId = currentSpan.spanIdString(); | ||
} | ||
|
||
event.begin(); | ||
|
||
class JfrCurrentTraceContextScope implements Scope { | ||
@Override public void close() { | ||
scope.close(); | ||
if (!event.shouldCommit()) return; | ||
event.end(); | ||
event.commit(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to check shouldCommit, no need to end before commit (in JDK 11). Simply just call event.commit() |
||
} | ||
} | ||
return new JfrCurrentTraceContextScope(); | ||
} | ||
|
||
JfrScopeDecorator() { | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package brave.context.jfr; | ||
|
||
import brave.propagation.CurrentTraceContext; | ||
import brave.propagation.CurrentTraceContext.Scope; | ||
import brave.propagation.StrictScopeDecorator; | ||
import brave.propagation.ThreadLocalCurrentTraceContext; | ||
import brave.propagation.TraceContext; | ||
import java.nio.file.Path; | ||
import java.util.List; | ||
import java.util.concurrent.CountDownLatch; | ||
import java.util.concurrent.Executor; | ||
import java.util.concurrent.ExecutorService; | ||
import java.util.concurrent.Executors; | ||
import java.util.concurrent.TimeUnit; | ||
import jdk.jfr.Recording; | ||
import jdk.jfr.consumer.RecordedEvent; | ||
import jdk.jfr.consumer.RecordingFile; | ||
import org.junit.After; | ||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.junit.rules.TemporaryFolder; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.tuple; | ||
|
||
public class JfrScopeDecoratorTest { | ||
@Rule public TemporaryFolder folder = new TemporaryFolder(); | ||
|
||
ExecutorService wrappedExecutor = Executors.newSingleThreadExecutor(); | ||
CurrentTraceContext currentTraceContext = ThreadLocalCurrentTraceContext.newBuilder() | ||
.addScopeDecorator(StrictScopeDecorator.create()) | ||
.addScopeDecorator(JfrScopeDecorator.create()) | ||
.build(); | ||
|
||
Executor executor = currentTraceContext.executor(wrappedExecutor); | ||
TraceContext context = TraceContext.newBuilder().traceId(1).spanId(1).build(); | ||
TraceContext context2 = TraceContext.newBuilder().traceId(1).parentId(1).spanId(2).build(); | ||
TraceContext context3 = TraceContext.newBuilder().traceId(2).spanId(3).build(); | ||
|
||
@After public void shutdownExecutor() throws InterruptedException { | ||
wrappedExecutor.shutdown(); | ||
wrappedExecutor.awaitTermination(1, TimeUnit.SECONDS); | ||
} | ||
|
||
@Test public void endToEndTest() throws Exception { | ||
Path destination = folder.newFile("execute.jfr").toPath(); | ||
|
||
try (Recording recording = new Recording()) { | ||
recording.start(); | ||
|
||
makeFiveScopes(); | ||
|
||
recording.dump(destination); | ||
} | ||
|
||
List<RecordedEvent> events = RecordingFile.readAllEvents(destination); | ||
assertThat(events).extracting(e -> | ||
tuple(e.getString("traceId"), e.getString("parentId"), e.getString("spanId"))) | ||
.containsExactlyInAnyOrder( | ||
tuple("0000000000000001", null, "0000000000000001"), | ||
tuple("0000000000000001", null, "0000000000000001"), | ||
tuple(null, null, null), | ||
tuple("0000000000000001", "0000000000000001", "0000000000000002"), | ||
tuple("0000000000000002", null, "0000000000000003") | ||
); | ||
} | ||
|
||
/** | ||
* This makes five scopes: | ||
* | ||
* <pre><ol> | ||
* <li>Explicit scope 1</li> | ||
* <li>Implicit scope 1 with a scoping executor</li> | ||
* <li>Explicit scope 2 inside an executor thread</li> | ||
* <li>Explicit clearing scope inside an executor thread</li> | ||
* <li>Explicit scope 3 outside the executor thread</li> | ||
* </ol></pre> | ||
*/ | ||
void makeFiveScopes() throws InterruptedException { | ||
CountDownLatch latch = new CountDownLatch(1); | ||
|
||
try (Scope ws = currentTraceContext.newScope(context)) { | ||
executor.execute(() -> { | ||
try (Scope clear = currentTraceContext.newScope(null)) { | ||
} | ||
try (Scope child = currentTraceContext.newScope(context2)) { | ||
latch.countDown(); | ||
} | ||
}); | ||
} | ||
|
||
try (Scope ws = currentTraceContext.newScope(context3)) { | ||
latch.countDown(); | ||
shutdownExecutor(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
appenders = console | ||
appender.console.type = Console | ||
appender.console.name = STDOUT | ||
appender.console.layout.type = PatternLayout | ||
appender.console.layout.pattern = %d{ABSOLUTE} %-5p [%t] %C{2} (%F:%L) [%X{traceId}/%X{spanId}] - %m%n | ||
rootLogger.level = info | ||
rootLogger.appenderRefs = stdout | ||
rootLogger.appenderRef.stdout.ref = STDOUT |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wondering, should we make this openjdk11? I guess it might be causing the confusion about commercial features. Targeting the baseline seems reasonable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Definitely.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah yeah that was unintentional.. I just needed something 11 :P can try to change to openjdk11 presuming travis has it