Monitoring Clojure Ring Apps with Java Melody

Ivo Maixner
May 2, 2020 · 5 min read

How to setup Java Melody in a Clojure Ring application

Photo by Artem Sapegin on Unsplash

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 /monitoring appended.

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.

As an example, let’s say we use Hikari datasource and Mount to manage app’s state, then our datasource definition might be:

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 JdbcWrapper.createDataSourceProxy:

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 Closeable.

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 .bindContextIncludingCpu and .addRequestForCurrentContext must be strictly paired to make it work properly.

The ->ns-name and ->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.

The Startup

Get smarter at building your thing. Join The Startup’s +724K followers.

Ivo Maixner

Written by

Worked on sw dev projects in all the roles since way too long ago. Seen a fair share of languages and technologies. Always tries to help the team work better.

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +724K followers.

Ivo Maixner

Written by

Worked on sw dev projects in all the roles since way too long ago. Seen a fair share of languages and technologies. Always tries to help the team work better.

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +724K followers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store