How to setup Java Melody in a Clojure Ring application
I use Java Melody to monitor my Java web applications. If you do not boast a dedicated monitoring infrastructure, Java Melody offers a convenient solution. It’s a Java library that gets embedded within your web app and connects to a servlet container to monitor HTTP calls and to a JDBC datasource to monitor JDBC calls and can be attached to application functionality in between by the means of an aspect.
Once setup properly, it collects long-term data about your application and presents a graphical overview on your backend app’s URL with
/monitoring appended. You can check out a demo here. You can drill down from an HTTP request through business logic calls down to the database (SQL) level, thus pinpointing any culprit in case of performance issues. You can easily find SQL queries that take the most time to perform and so e.g. identify candidates for adding a database index. You can also show a number of useful graphs over varying time intervals which could highlight a potential problem that might be building up in your application.
Overall, it’s a pretty handy, all-in-one “poor man’s monitoring solution.”
While the setup of Java Melody in a standard Java application running in a servlet container is well documented on the wiki, I haven’t found any instructions for how to set it up in a Clojure application running on Ring, so I decided to note down the way I made it work, for the sake of future reference.
Let’s start by adding the dependency to
project.clj. At the time of writing, the current version of Java Melody is 1.82.0:
This by itself already makes Java Melody functional. You can verify it by running the application in a servlet container (e.g. Tomcat or Jetty) and accessing the app’s URL with
Be aware that running the Ring handler by itself it not enough. So if you for example use
mount/start in your REPL to start the Ring handler, Java Melody will not run, as by default it needs a proper servlet container for its
MonitoringFilter to kick in. You will have to run the app within the servlet container.
Customizing setup parameters
So, Java Melody is running, alas with default parameters only. It already shows fully functional HTTP statistics, SQL statistics that are still empty though and the standard graphs.
The recommended way to setup Java Melody parameters is via the
web.xml file. As this is not available to us with Ring, we can setup Java Melody parameters by overriding the system properties instead. Let’s say we don’t like the localization of the UI that is performed by default, we can force a fixed English locale by setting the
locale parameter. We can create a function to setup Java Melody and call it at the start of the application:
Monitoring SQL queries
The next thing we want to monitor is the SQL queries, or JDBC calls. We can do that by wrapping our app’s datasource with a wrapper provided by Java Melody that will enable it to intercept and monitor JDBC queries.
This assumes that
config is app’s configuration (in Mount) in which there is a
:db key with database connection config.
To wrap the datasource for Java Melody we pass it to
Note that we can’t use the Hikari-provided
close-datasource function anymore as it expects the argument to be a Hikari datasource while now we have a proxy that wraps it. We can however simply
.close it as it is still a Java
After restarting the app and issuing a couple of calls we can verify that the SQL queries are now being monitored.
Monitoring business logic
To monitor the function calls in app’s business logic in-between an HTTP call and the related SQL queries, we have to wrap those business functions by an aspect (an interceptor). There is a Clojure library to achieve exactly this: Robert Hooke. How to set it up within an application is a topic for an article of its own, so here I’m going to assume you already have it in place and show how Java Melody fits in.
Java Melody has a list of predefined counters that is unfortunately not extensible. That means we have to use one of those, rather than define our own (say one named
clj, for example). I decided to pick the counter named
ejb, but that’s an arbitrary decision and you can try your luck in picking one with a better name :-)
The next issue is that the
MonitoringProxy.getEjbCounter() method that we have to use to access the counter instance is package-private. We can however overcome this by setting the method accessible:
Note that we have set the counter to be “displayed” and “used.”
With the counter defined we can plug it into our monitoring hook (aspect):
Note that the calls to
.addRequestForCurrentContext must be strictly paired to make it work properly.
->fn-name are utility custom functions that extract the name of the namespace and the name of the function being wrapped by the hook to form a meaningful name to be reported within Java Melody and unambiguously refer to our business functions.
Obviously, for this hook to work, it now has to be properly applied to all our business functions, which is out of scope of this article as I said.
We can also define the displayed counters via the
displayed-counters parameter to make the list explicit, but calling the
.setDisplayed method as we did above is enough.
After app restart we can now drill all the way from an HTTP request through all our business logic functions down to the SQL queries issued by our app to the database.
Java Melody offers nice visibility into how your web application works in production, for no extra cost apart from setting it up. I hope this article helps people to achieve this with web applications written in Clojure.