7

I am looking for a way to export spring boot metrics to file in Spring Boot 2.

In Spring Boot 1.5.10, we used a Custom MetricsExporter class which implements MetricWriter and overrides set and increment methods to write the metrics using loggers. We used a log file because we have a different mechanism to process the log file later for metrics analysis.

We also used MetricsConfig class, which uses the bean MetricsEndpointMetricReader to read metrics from the metrics endpoint reader in a custom config class.

But, when we upgraded to Spring Boot 2.0.1 those are not working as there was a breaking change in the existing metrics classes.

Can someone help us with how we can export the metrics and write them using loggers when using Spring Boot 2.0?

@ExportMetricWriter public class MetricsExporter implements MetricWriter { private static Logger LOGGER = LoggerFactory.getLogger("metrics"); @Override public void set(Metric<?> value) { // Write the Gauge metrics to log file LOGGER.info("timestamp={}, name={}, value={}", value.getTimestamp(), value.getName(),value.getValue()); } @Override public void increment(Delta<?> delta) { //Write the Counter metrics to log file LOGGER.info("timestamp={}, name={}, value={}", delta.getTimestamp(), delta.getName(),delta.getValue()); } @Override public void reset(String metricName) { } } 

The MetricsConfig Class is as below:

@Configuration public class MetricsConfig { //Define the MetricsExporter bean to export metrics at regular interval to a log file @Bean public MetricsExporter metricsExporter() { return new MetricsExporter(); } //Define the MetricsEndpointMetricReader bean to export both push(counters and gauges) and pull(public) metrics @Bean public MetricsEndpointMetricReader metricsEndpointMetricReader(MetricsEndpoint metricsEndpoint) { return new MetricsEndpointMetricReader(metricsEndpoint); } } 
1
  • I am also using spring boot 1.5 with same configuration. Are you logging this periodically into some file? LoggingMeterRegistry is not available with v1.5. How did you achieve this? Commented Aug 8, 2020 at 8:15

3 Answers 3

5

You can implement a custom MeterRegistry and wire it as a @Bean. One of the roles of MeterRegistry implementations are to define the exposition format to a particular monitoring system (in your case, a log).

Here's a start:

public class LogMeterRegistry extends StepMeterRegistry { private final Logger logger = LoggerFactory.getLogger(LogMeterRegistry.class); /** * @param step Governs on what frequency metrics are logged */ public LogMeterRegistry(Duration step) { super(new StepRegistryConfig() { @Override public String prefix() { return "log"; } @Override public String get(String key) { return null; } @Override public Duration step() { return step; } }, Clock.SYSTEM); } @Override protected void publish() { for (Meter meter : getMeters()) { logger.info(meter.getId().toString()); for (Measurement measurement : meter.measure()) { logger.info(measurement.getStatistic().toString() + "=" + measurement.getValue()); } } } @Override protected TimeUnit getBaseTimeUnit() { return TimeUnit.SECONDS; } } 

An issue is open for Micrometer 1.1 to provide an out-of-the-box LogMeterRegistry.

Sign up to request clarification or add additional context in comments.

Comments

4

If you are using Spring Boot 2.x which means version of Micrometer >= 1.1.0, you could simply configure a bean like

@Bean LoggingMeterRegistry loggingMeterRegistry() { return new LoggingMeterRegistry();//by default, it will log metrics every 1m } 

You can also configure a different period cycle like :

@Bean LoggingMeterRegistry loggingMeterRegistry() { return new LoggingMeterRegistry(new LoggingRegistryConfig() { @Override public Duration step() { return Duration.ofSeconds(10); // log every 10 seconds } @Override public String get(String key) { return null; } }, Clock.SYSTEM); } 

If you don't use a higher enough spring boot version, try declare a version of 1.1.x of in micrometer-core your pom.xml

 <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-core</artifactId> <version>1.1.3</version> </dependency> 

Comments

0

I wanted to do something similar to what Jonathan suggests, but I wanted to publish all metrics on one log line using Slf4j MDCs.

I output my logs as json (and in my case to AWS CloudWatch) so I can draw queries and visualise the data.

import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.step.StepMeterRegistry; import io.micrometer.core.instrument.step.StepRegistryConfig; import io.micrometer.core.instrument.util.NamedThreadFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; import java.text.DecimalFormat; import java.util.concurrent.TimeUnit; public class LogMeterRegistry extends StepMeterRegistry { private static final Logger logger = LoggerFactory.getLogger(LogMeterRegistry.class); private final DecimalFormat df; public LogMeterRegistry(CustomRegistryConfig config, Clock clock) { super(config, clock); start(new NamedThreadFactory("log-meter-registry-publisher")); df = new DecimalFormat("0"); df.setMaximumFractionDigits(3); } @Override protected void publish() { MDC.put("server", getHostName()); getMeters().forEach(meter -> meter.measure().forEach( measurement -> MDC.put(meter.getId().getName(), df.format(measurement.getValue())))); logger.info("metrics"); MDC.clear(); } @Override protected TimeUnit getBaseTimeUnit() { return TimeUnit.MILLISECONDS; } public interface CustomRegistryConfig extends StepRegistryConfig { CustomRegistryConfig DEFAULT = k -> null; @Override default String prefix() { return ""; } } } 

So using the following Metrics:

 new JvmHeapPressureMetrics().bindTo(meterRegistry); new JvmThreadMetrics().bindTo(meterRegistry); new UptimeMetrics().bindTo(meterRegistry); new ProcessorMetrics().bindTo(meterRegistry); 

The json output looks like the following:

{ "@timestamp": "2022-xx-xxT11:03:00.027Z", "level": "INFO", "message": "metrics", "process.thread.name": "log-meter-registry-publisher", "log.logger": "MyClass", "jvm.gc.overhead": "0", "jvm.memory.usage.after.gc": "0.036", "jvm.threads.daemon": "46", "jvm.threads.live": "47", "jvm.threads.peak": "54", "jvm.threads.states": "23", "process.cpu.usage": "0.001", "process.start.time": "1663065827068", "process.uptime": "1152989", "server": "MyServer.localdomain", "system.cpu.count": "12", "system.cpu.usage": "0.051", "system.load.average.1m": "2.351" } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.