Micrometer and Spring (Non-Boot)

Almost all of the tutorials and blog posts I found on this topic were focused on Spring Boot because, starting with version 2, it uses Micrometer as its metrics framework. However, in a particular project at work we do not have access to Spring Boot let alone a recent Spring version. Therefore, I’m explaining how to include Micrometer in your non-Boot Spring application using XML configuration.

In this tutorial I will be using Spring 5 and Java 11, so not exactly the versions I’m dealing with at work, but the concepts are the same and everything can probably be copied exactly as shown here.

How-To

First thing that needs to be done is to add the dependencies. 

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.6.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-registry-atlas</artifactId>
        <version>1.1.4</version>
    </dependency>
</dependencies>

For this example, I’m content with the Spring Context only, as that is enough to explain the necessary steps. Micrometer is split into multiple modules as well, one being the core and the others are the bindings for the specific APM systems. Because Maven resolves the required dependencies on its own, only the Micrometer-Atlas binding is specified.

(Keep the pom.xml as clean as possible)

Depending on your requirements, you will change this to use New Relic, Prometheus or anything else that is supported by Micrometer. The way how you include the framework into your code and work with it will stay the same. Simply change the dependency and you’re good to go.

(I’m using Atlas because it can be executed as a standalone runnable without complex dependencies)

Next, we need to define the necessary beans. I use a separate configuration class and a factory to create the “AtlasMeterRegistry” bean.

Micrometer employs the concept of a MeterRegistry that enables an application to make use of the individual metric classes the framework provides. You can work with only one back-end, or combine more of them with a CompositeMeterRegistry. This example will keep things simple and use only one back-end.

Here’s the configuration (standard Spring).

@Configuration
@PropertySource("classpath:application.properties")
public class AppConfiguration {

    @Value("${atlas.uri}")
    private String atlasUri;

    public String getAtlasUri() {
        return atlasUri;
    }
}

Here’s the factory that uses the configuration.

public class MeterRegistryFactory {

    public static AtlasMeterRegistry createRegistry(final AppConfiguration config) {
        final var meterConfig = new AtlasConfig() {
            @Override
            public Duration step() {
                return Duration.ofSeconds(2);
            }

            @Override
            public String get(String k) {
                if ("atlas.step".equalsIgnoreCase(k)) {
                    return "PT2S";
                }
                if ("atlas.uri".equalsIgnoreCase(k)) {
                    return config.getAtlasUri();
                }
                return null; // accept the rest of the defaults
            }
        };

        return new AtlasMeterRegistry(meterConfig, Clock.SYSTEM);
    }
}

I have set the interval to publish new metrics to two seconds in order to quickly see results. The basics of the configuration can be found in the Micrometer configuration documentation. There’s a page for every supported APM back-end. Unfortunately, the documentation is missing the list of key-value pairs that get used in the “get(String)” method.

Now, to glue this all together via XML.

<context:annotation-config />

<bean id="appConfiguration" class="com.thecodeslinger.metrics.AppConfiguration" />

<bean id="meterRegistry"
      class="com.thecodeslinger.metrics.MeterRegistryFactory"
      factory-method="createRegistry">
    <constructor-arg ref="appConfiguration" />
</bean>

The first line makes sure our Java @Configuration class is picked up properly by Spring and contains the configuration from the “application.properties” file that can be found on the classpath. The following two bean definitions are exactly this configuration class and the factory that requires this configuration and spits out the “MeterRegistry” as a result.

Time to use it, right? First, let’s create a dummy class to showcase the integration.

public class DataGenerator {

    private MeterRegistry registry;
    private Counter counter;

    public DataGenerator(MeterRegistry registry) {
        this.registry = registry;
        this.counter = registry.counter(
            "custom.beast.counter", "generator", "metrics");
    }

    public void createMetricsLikeABeast() {
        counter.increment();
    }
}

As can be seen, the “MeterRegistry” is simply wired into the classes where needed. In this rather stupid example, it is merely used to create a counter. Let’s write some more XML and feel like a relic, shall we?

<bean id="dataGenerator" class="com.thecodeslinger.metrics.DataGenerator">
    <constructor-arg ref="meterRegistry" />
</bean>

<task:scheduled-tasks>
    <task:scheduled ref="dataGenerator" 
        method="createMetricsLikeABeast" 
        fixed-delay="250"/>
</task:scheduled-tasks>

First of all, we need a bean for this “DataGenerator” which is then fed into a Spring scheduled task that runs every 250 ms. Creative use of computing resources, right?

The last step is to load the application context to set things in motion.

new ClassPathXmlApplicationContext("application-context.xml");

To prove that it is working, here is a screenshot of the output of Atlas.

The source code can be found on GitHub.

Additional Notes

As mentioned earlier, Netflix Atlas is easy to use for testing purposes. Have a look at the GitHub repo for more information. One important thing to note is that Atlas does not run on Java 11 (yet). The most recent version 1.5.3 at the time of writing was run with Java 8 for the purpose of this blog post.

I’m not an expert on metrics in general or Micrometer in particular. The way how you name a metric and how it shows up in the APM is still a mystery to me since practical experience is non-existent. Hence this very simple example that only shows how to get going.

2 thoughts on “Micrometer and Spring (Non-Boot)

  1. I am using exact project shared by you but I am getting c.n.spectator.atlas.AtlasRegistry – failed to send metrics
    java.net.SocketTimeoutException: connect timed out.
    Do you have any idea why that is happening

    Like

    • Sorry for the delayed response. Are you certain that Atlas is running on localhost:7101? Have you tried telnet localhost 7101 to verify something is listening on that port? Maybe you configured another port value when you started Atlas, or maybe the defaults have changed since I wrote this blog post. These are a few things that quickly come to mind.

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.