Skip to content

Ratpack instrumentation support#276

Merged
realark merged 9 commits into
DataDog:masterfrom
jonmort:ratpack-support
Apr 24, 2018
Merged

Ratpack instrumentation support#276
realark merged 9 commits into
DataDog:masterfrom
jonmort:ratpack-support

Conversation

@jonmort
Copy link
Copy Markdown
Contributor

@jonmort jonmort commented Mar 30, 2018

I've been working on this for quite a while and been round and round on it. This is finally ready for some feedback so please provide it.

There are several areas I'm not sure about

  • Naming of the instrumentation
  • Component Tag- There are two here, the main handler that implements the http response and the http client
  • Resource Tag - I've left this out because I couldn't see how to build it well. I also had no idea what it should be
  • Use of Java 8 Features. Ratpack only supports Java 8 so I used them
  • Bytebuddy - I've not worked with this before so I tried to copy the patterns I saw. I'm particularly unsure about how the classLoaderHasClasses() calls are supposed to be used
  • Version plugin in the gradle file - I declared all of the classes that have no compile dependency on them in the gradle file - those classes that are instrumented. This appeared to be what the other classes instrumentations did but I was not sure
  • Logging - I felt like I wanted to add some more, but there isn't much elsewhere so I didn't
  • Versions of Ratpack to support and test. I chose 1.4.6+ as the API is good for the integration. In Ratpack 1.6 there will be a much better way to instrument HttpClient, what I have works but it would be much cleaner when HttpClient interceptors are available
  • Appropriate level of comments - I added a bit more than I found elsewhere, but they aren't extensive

Also, is there a contributors agreement I need to sign?

@realark
Copy link
Copy Markdown
Contributor

realark commented Mar 30, 2018

@jonmort Awesome work!

I'll get back to you about that contributors agreement and the rest of your questions early next week.

One blocker is this instrumentation is compiled against java 8. We have to support java 1.7, and attempting to load java 8 bytecode crashes the app. We'll put out a solution for this next week so we can unblock this PR and #254.

Copy link
Copy Markdown
Contributor

@tylerbenson tylerbenson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice job. Certainly more thorough in some aspects than what we normally do, and something for us to aspire to.

I added a lot of questions and comments, mainly for discussion. The main things that need to be resolved are:

  • Resource Naming - This is rather datadog specific, so let us know if you need more explanation about this.
  • Proper JVM Handling - You can add the test specific handling as mentioned in the comments, but we either need to change the way the project builds, or change the instrumentation back to JVM 7 compatible bytecode. Do you have any ideas for how we could handle this?

One other question to consider... in the future, if/when we add Netty support, how would we need to change this instrumentation?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t think it is safe for advice to reference this method. It should be moved to a utility class that is injected into the class loader.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, yes. Looks sketchy

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of this is done automatically when setting the error object, so you don’t need to do it again here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How much of it? I copied this from the AWS implementation

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually name the project after the earliest version supported by the instrumentation. So this should be ratpack-1.4.6. Is this the earliest version the instrumentation can work with?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Earliest tested with, I can try with 1.3 too

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice strategy for sharing the span between classes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was inspired from the ratpack tracing for zipkin

Copy link
Copy Markdown
Contributor

@tylerbenson tylerbenson Apr 2, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this isn’t the actual span, can you rename consistently to spanRef (like you did in other places) to avoid confusion?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, only 5XX status should be errors.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you expand on why that is? 4xx responses are, by definition, also errors...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jdbevan 4xx errors are generally client side errors and thus not something you'd usually want to page someone for. In certain cases where it is something you consider more critical, you can add a TraceInterceptor to change it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed. The super class has a tracer you can reference directly that already uses ListWriter.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a test that uses path variables.
You should also set the resource name in the instrumentation and assert on it here.

Resource names should be based on the PathBinding which should be available on the context.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hadn't worked out how to do this when i first looked at it, now I see we can do this beforeSend and pull the path bindings then. I wasn't quite sure what the resource name was in ratpack, but the path binding description appears to be perfectly good

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you feel it complicates the tests too much, you can remove this so okhttp doesn’t get instrumented.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll take a look. Again I think I copied from servlet

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is how we’ve been restricting the tests from running specific JVM versions for Ratpack.
https://github.com/DataDog/dd-trace-java/blob/master/dd-java-agent/instrumentation/okhttp-3/okhttp-3.gradle

This won’t help the need to compile the instrumentation with Java 8 though.
As Andrew said. Let us figure out a strategy for that.

@tylerbenson
Copy link
Copy Markdown
Contributor

tylerbenson commented Apr 2, 2018

Looks like this is something we could potentially do with custom sourceSets: https://stackoverflow.com/a/18191391, though that doesn't mean it will work with the java 7 compiler.

Maybe instead, we could save the artifacts from the java 8 compile, then use them in the java 7 test run. Changing the JVM might invalidate the gradle Up-to-date detection though.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Earliest tested with, I can try with 1.3 too

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need datadog.opentracing.scopemanager.ContextualScopeManager to be able to call addScopeContext

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll take a look. Again I think I copied from servlet

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, yes. Looks sketchy

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How much of it? I copied this from the AWS implementation

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was inspired from the ratpack tracing for zipkin

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it won't. It will add to anything else already there so when calling execution.getAll it will return all RatpackScopes. I'll modify to clear it out first

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hadn't worked out how to do this when i first looked at it, now I see we can do this beforeSend and pull the path bindings then. I wasn't quite sure what the resource name was in ratpack, but the path binding description appears to be perfectly good

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tylerbenson is this span name ok?

@jonmort
Copy link
Copy Markdown
Contributor Author

jonmort commented Apr 4, 2018

I have addressed all the comments. I'm waiting on a solution for Java 7. I realised there was a Java 8 method reference in the instrumentation which i have replaced with a new class to ensure Java 7 byte code compatibility for the instrumentation class. The other classes have significant use of Java 8 language features.

Regarding Netty, I think it might be possible to integrate this instrumentation, but mostly when using Ratpack you don't want Netty details as the abstractions Ratpack provides are more functional

Copy link
Copy Markdown
Contributor

@tylerbenson tylerbenson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see new comments below.

As for the Java 7 issue, we are looking into how to fix that. Thanks for your patience.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I had you move this to a utility class... This whole method can be replaced with a simple call to:
span.log(Collections.singletonMap(ERROR_OBJECT, throwable));

We do all the rest inside the tracer.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think ratpack.request would be better.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jdbevan 4xx errors are generally client side errors and thus not something you'd usually want to page someone for. In certain cases where it is something you consider more critical, you can add a TraceInterceptor to change it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason is spans are added to the front of the list as they complete: https://github.com/DataDog/dd-trace-java/blob/master/dd-trace-ot/src/main/java/datadog/opentracing/PendingTrace.java#L87
This means the first span is usually the root span.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any idea in what cases this would happen?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will happen when there is no handler bound to the path (I think the response will be 404 or 405).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I didn't mention this before, but the resource name should ideally be prefixed with the HTTP verb, so in this case, the resource name would be GET /:foo/:bar?/baz. Is that possible?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that makes sense. I'm also missing the leading slash. I'll put that in too

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These classes should mirror what is listed in the versionScan.verifyPresent block. This is how we ensure we don't try to instrument something in 1.3

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ok restricting it to 1.4 as long as it doesn't try to inadvertently instrument any unsupported classes. We ensure this by including the below verifyPresent classes in the classLoaderHasClasses list in the instrumentation.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is where it gets complex! The reason 1.3 isn't supported is because the interfaces have different methods, they are the same classes but method signatures changed. Specifically it is the RequestSpec that has the changes. I could rely on a class that is only in 1.4 to force this, but one that isn't instrumented

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that is ok. The classes in that list aren't the ones we're explicitly instrumenting. They're intended as a "fingerprint" to identify the supported versions.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've sorted out the fingerprint now. It is pretty much just that method on that class that identifies 1.4 from 1.3

Comment thread settings.gradle Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please re-order alphabetically.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method (getDescription) is only available on Ratpack 1.4+. I don't instrument PathBinding but do the getDescription method on it. I couln't see a way to express that in the agent builder. Any suggestions?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only classes in this list should be the ones that are unique to version 1.4+. In some cases, just class name isn't enough to uniquely identify. That's why we also have the optional method name in there.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tylerbenson I went out on a limb here and added a way to check for a class that has a particular method as I could not see a way to do it otherwise. I looked at using classLoaderHasClassWithField but it felt too fragile to use on a Ratpack internal class.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tylerbenson this is where I use the new ClassLoader Matcher, which gets reused for all the instrumentation

@realark
Copy link
Copy Markdown
Contributor

realark commented Apr 6, 2018

@jonmort Thanks for your patience while we work on unblocking you. We're still working on a solution for Java 8 instrumentation, but we'll let you know as soon as we can get this merged.

@jonmort jonmort changed the title Ratpack instrumentaiton support Ratpack instrumentation support Apr 9, 2018
@jonmort
Copy link
Copy Markdown
Contributor Author

jonmort commented Apr 10, 2018

I notice that ClassLoaderMatcher just got updated to add the same functionality that I did, but with one key difference. The method matcher that was added uses Classes to pick out the method arg and mine uses Strings. I used Strings because if we are checking for the presence of a method with a signature as a fingerprint for versions the parameter classes may not be loaded when the check is done. In short I don't think it is safe to use Class in this instance

@tylerbenson
Copy link
Copy Markdown
Contributor

Good point... @realark you may want to reconsider this^

@realark
Copy link
Copy Markdown
Contributor

realark commented Apr 11, 2018

Thanks @jonmort That's a good point. I'm adding that fix to my next play pr.

@jonmort jonmort force-pushed the ratpack-support branch 2 times, most recently from c080a1a to 1d2f28b Compare April 18, 2018 10:36
@jonmort
Copy link
Copy Markdown
Contributor Author

jonmort commented Apr 18, 2018

@tylerbenson I've isolated the Java 8 code now and we have green builds. Is there anything further that needs doing here?

Copy link
Copy Markdown
Contributor

@tylerbenson tylerbenson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this looks great. You did a nice job and resolved all the comments I had. I'm ok merging this as-is. I left a few questions, but nothing blocking. Sorry for the delay in review. Let me know if you have anything else you want to do, otherwise I'll merge it.

Here we introduce a sourceSet for the java 8 code which needs to be compiled with a source and target of 1.8
The instrumentation classes must be compiled with java 7 and do nothing when ratpack is not on the classpath. The
java 8 classes are used lazily so there is no direct linking between the 1.7 and 1.8 bytecode.
*/
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the nice comment.


@Override
public void execute(RequestSpec requestSpec) throws Exception {
requestAction.execute(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need any logic to prevent double-wrapping here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The scenario in which requestSpec could have already been wrapped happens only if the client has some fancy logic to re-use requests, or chain the actions, this is quite unlikely but possible so the wise thing would be to guard against double wrapping

/*
This test is somewhat convoluted and it raises some questions about how this is supposed to work
*/
setup:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think there are potential problems that still need to be resolved?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should probably remove this comment. In a previous iteration there was some more setup code.

My concern about this test is the length of it. It has close to 100 lines of assertions but I think each assertion is necessary and I'd rather verbose tests than hiding them in a helper method

@tylerbenson
Copy link
Copy Markdown
Contributor

Ready to merge now?

@jonmort
Copy link
Copy Markdown
Contributor Author

jonmort commented Apr 24, 2018

Yes ready to merge

@realark realark merged commit f295b73 into DataDog:master Apr 24, 2018
@tylerbenson tylerbenson added this to the 0.7.0 milestone Apr 26, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants