At getaroom.com we’ve used New Relic extensively for many years to monitor our Rails applications. We’ve recently began moving our backend infastructure to Clojure, maintaing the visibility New Relic gave us for our Rails environment was a must.
New Relic’s product is great, but the ability to add custom instrumention to a Clojure application is less than ideal. The primary method of custom instrumentation using New Relic’s Java agent is via custom annotations. Sean Corfield wrote an excellent post on how to get this to work using deftype.
Setup New Relic
Adding the New Relic API jar will permit custom metrics via the Java API. At getaroom we use it expose a number of metrics about our business of selling hotel rooms. These metrics can be displayed using custom dashboards along with many of the standard graphs available but they don’t provide the same insight that instrumentation of underlying function calls can provide.
Enabling New Relic requires launching the JVM using the
-javaagent switch. The startup time of the agent leaves much to be desired. Given this, I’ve found it useful to conditionally enable New Relic via a leiningen profile.
Instrumentation of AOT Compiled Code
Adding a custom annotation in Clojure isn’t always practical. Let’s consider the case where we wanted to instrument the custom JSON library cheshire. If we were calling parse-string we could create functions that call through a method containing the custom
Trace annotation as Sean Corfield describes here. This works but involves extra stubs used only to satisfy New Relic’s use of annotations.
If you’re willing to trade the downsides of AOT compilation for better instrumentation we can use New Relic’s custom XML extensions. We acheive this by using an AOT compiled uberjar. Currently, this trade-off is worth for us but YMMV.
Instrumenting Clojure Functions with Custom XML Extensions
In many cases the AOT compiled function has a name that is very easy to find. An anonymous function is not one of these cases.
The following is the roughly what the generated class looks like for the function cheshire.core/parse-string.
1 2 3 4 5 6 7 8 9 10 11 12
Now that we know what the method signature looks like, we can add the custom extension XML.
Add the following XML to a file called
cheshire.xml within the
extensions folder of your New Relic install.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
Once this is added and the application is restarted you should begin to see traces that include the generated class name for the
cheshire.core/parse-string function. Additionally, using the excellent New Relic thread profiler you can easily find new functions that would benefit from visibility in New Relic traces.