How to configure LoggingMeterRegistry step duration in Spring Boot 2.x? - spring-boot

I am trying to configure the LoggingMeterRegistry to log metrics for my Spring Boot 2.1.6 application. I want the metrics to be logged every hour.
In my application.yml, I've the following configured
management:
metrics:
export:
logging:
enabled: true
step: 60m
But in the logs I see the metrics being logged every minute. I've tried the other variation for the property key as well e.g.
management.metrics.export.logging:
enabled: true
step: 60m
I have also tried various formats for the duration string e.g. 1h, PT60M but with no success. The metrics are logged at 1 minute intervals.
I was looking at the code here StepDurationConverterTest and here StepDurationConverter that converts the step duration String to a Duration object and looks like both formats 60m and 1h should work.
Any ideas why I can't seem to change the logging interval?

I think the problem here is there's no
org.springframework.boot.actuate.autoconfigure.metrics.export.logging
package like there is for other MeterRegistrys (eg org.springframework.boot.actuate.autoconfigure.metrics.export.jmx).
Ie theres no auto configuration for the properties in Spring Boot. This is probably because the LoggingMeterRegistry is marked as #Incubating
You need to manually configure the LoggingMeterRegistry as a bean and create your own #ConfigurationProperties LoggingProperties and LoggingPropertiesConfigAdapter to get this to work. Or just hardcode the step period you want.

To configure step count duration in micrometer:
Please follow below step:
#Configuration
public class LoggingMeterRegistryConfig {
#Bean
public LoggingMeterRegistry loggingMeterRegistry() {
LoggingRegistryConfig config = new LoggingRegistryConfig() {
#Override
public String get(String s) {
return null;
}
#Override
public Duration step() {
return Duration.ofMinutes(2);
}
};
return LoggingMeterRegistry.builder(config).clock(Clock.SYSTEM).threadFactory(new NamedThreadFactory("logging-metrics-publisher")).build();
}
}

The following #Bean supplies config from Spring Environment allowing you to specify a property logging.step: 1h to get your desired period.
#Bean
LoggingMeterRegistry loggingMeterRegistry(Environment env) {
LoggingRegistryConfig springBasedConfig = prop -> env.getProperty(prop, String.class);
return new LoggingMeterRegistry(springBasedConfig, Clock.SYSTEM);
}

Related

Spring Integration - WIth AWS S3 ( Retry Strategy)

I am creating a simple integration service with AWS S3. I am facing some difficulties when an exception occurs.
My requirement is to poll an S3 Bucket periodically and to apply some transformation whenever a file is newly placed into S3 Bucket. The below code snippet works fine, but when an exception occurs it continues to retry again and again. I do not want that to happen. Can someone help me here.,
The IntegrationFlow is defined as below.,
#Configuration
public class S3Routes {
#Bean
public IntegrationFlow downloadFlow(MessageSource<InputStream> s3InboundStreamingMessageSource) {
return IntegrationFlows.from(s3InboundStreamingMessageSource)
.channel("s3Channel")
.handle("QueryServiceImpl", "processFile")
.get();
}
}
Configuration file is as below.,
#Service
public class S3AppConfiguration {
#Bean
#InboundChannelAdapter(value = "s3Channel")
public MessageSource<InputStream> s3InboundStreamingMessageSource(S3RemoteFileTemplate template) {
S3StreamingMessageSource messageSource = new S3StreamingMessageSource(template);
messageSource.setRemoteDirectory("my-bucket-name");
messageSource.setFilter(new S3PersistentAcceptOnceFileListFilter(new SimpleMetadataStore(),
"streaming"));
return messageSource;
}
#Bean
public PollableChannel s3Channel() {
return new QueueChannel();
}
#Bean
public S3RemoteFileTemplate template(AmazonS3 amazonS3) {
return new S3RemoteFileTemplate(new S3SessionFactory(amazonS3));
}
#Bean(name = "amazonS3")
public AmazonS3 nonProdAmazonS3(BasicAWSCredentials basicAWSCredentials) {
ClientConfiguration config = new ClientConfiguration();
config.setProxyHost("localhost");
config.setProxyPort(3128);
return AmazonS3ClientBuilder.standard().withRegion(Regions.fromName("ap-southeast-1"))
.withCredentials(new AWSStaticCredentialsProvider(basicAWSCredentials))
.withClientConfiguration(config)
.build();
}
#Bean
public BasicAWSCredentials basicAWSCredentials() {
return new BasicAWSCredentials("access_key", "secret_key");
}
#Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata nonProdPoller() {
return Pollers.cron("* */2 * * * *")
.get();
}
}
AcceptOnceFileList filter that I have used here, helps me to prevent handling the same file for continuous retries. But, I do not want to use AcceptOnceFileList filter, because when a file is not processed on 1st attempt, I wish to retry on next Poll (usually it happens every 1 hour in Prod region). I tried to use filter.remove() method whenever the processing fails(in case of any exception), it again results in continuous retries.
I am not sure how to disable the continuous retries on failure. Where should I configure it?
I took a look at Spring Integration ( Retry Strategy). Same scenario, but a different integration. I am not sure how to set up this for my IntegrationFlow. Can someone help here? Thanks in advance
That story is different: it talks about a listener container for AMQP. You use a source polling channel adapter - the approach might be different.
You create two source polling channel adapters: one via that #InboundChannelAdapter, another via IntegrationFlows.from(s3InboundStreamingMessageSource). Both of them produces data to the same channel. Not sure if that is really intentional.
It is not clear what is that retry in your case unless you really do that manual filter.remove() call. In this case indeed it is going to retry. But this is a single, not controlled retry. It is going to retry again only if you call that filter.remove() again. So, if you do everything yourself, why is the question?
Consider to use a RequestHandlerRetryAdvice configured for that your handle() instead: https://docs.spring.io/spring-integration/docs/current/reference/html/messaging-endpoints.html#message-handler-advice-chain. This way you really going to pull the remote file only once and retry is going to be managed by the Spring Retry API.
UPDATE
So, after some Cron Expression learning I realized that your one is wrong:
* */2 * * * * - means every second of every even minute
Must be like this:
0 */2 * * * * - at the beginning of every even minute
Perhaps something similar is with your hourly cron expression on the prod...

Change timeout for Spring Cloud Circuit Breaker at runtime?

I'm using Spring Cloud Circuit Breaker 2.0.0 (resilience4j implementation) for circuit breaking and timeouts in my application. I've created the following configuration:
#Bean
public Customizer<Resilience4JCircuitBreakerFactory> defaultCustomizer() {
return factory ->
factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
.timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(10)).build())
.circuitBreakerConfig(..)
.build());
}
Now I want to write an integration test to verify that my behavior is correct when a timeout occurs. For this to work, I'd like to temporarily change the timeout duration specified in the configuration above to something like 1 millisecond instead of 10 seconds.
So my question is: How can I change the value of the timeout of the TimeLimiterConfig(temporarily) when I'm writing a Spring Boot integration test?
You can use the #Value Spring annotation that retrieves the value at a configuration file from your resource folder src/main/resources/common.properties.
#Bean
public Customizer<Resilience4JCircuitBreakerFactory> defaultCustomizer(
#Value("${duration.milli:600}") int durationMilli) {
return factory ->
factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
.timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofMilli(durationMilli)).build())
.circuitBreakerConfig(..)
.build());
}
Then you set the value at src/main/resources/common.properties
duration.milli=600
When you are doing your test you can configure another resource file at the test folder src/test/resources/common.properties with a different value.
duration.milli=1

io.micrometer.core.instrument.config.MeterFilter : DENY is not working in spring boot

I want to expose all metrics on the metrics endpoint but publish some of them to a remote meter registry.
For doing so, I have a SimpleMeterRegistry for the metrics endpoint and added a MeterRegistryCustomizer for the remote meter registry(Datadog) to add some MeterFilter to avoid specific metrics using MeterFilter's DENY function. For example :
#Bean
public MeterRegistryCustomizer<StatsdMeterRegistry> meterRegistryCustomizer() {
return (registry) -> new StatsdMeterRegistry(config, Clock.SYSTEM).config().meterFilter(MeterFilter.denyNameStartsWith("jvm"));
}
However, all jvm related metrics are visible in Datadog. I tried MeterFilterReply but no use.
Please suggest how this can be achieved.
You are configuring the filter on a new StatsdMeterRegistry. When using a MeterRegistryCustomizer you need to operate on the registry that was passed in.
#Bean
public MeterRegistryCustomizer<StatsdMeterRegistry> meterRegistryCustomizer() {
return (registry) -> registry.config().meterFilter(MeterFilter.denyNameStartsWith("jvm"));
}
Since the customizer will be used against all registries, you also would need to add an if statement to only filter against the registry you want filtered.
#Bean
public MeterRegistryCustomizer<StatsdMeterRegistry> meterRegistryCustomizer() {
return (registry) -> {
if(registry instanceof StatsdMeterRegistry) {
registry.config().meterFilter(MeterFilter.denyNameStartsWith("jvm"));
}
}
}

Spring Boot auto-configured metrics not arriving to Librato

I am using Spring Boot with auto-configure enabled (#EnableAutoConfiguration) and trying to send my Spring MVC metrics to Librato. Right now only my own created metrics are arriving to Librato but auto-configured metrics (CPU, file descriptors, etc) are not sent to my reporter.
If I access a metric endpoint I can see the info generated there, for instance http://localhost:8081/actuator/metrics/system.cpu.count
I based my code on this post for ConsoleReporter. so I have this:
public static MeterRegistry libratoRegistry() {
MetricRegistry dropwizardRegistry = new MetricRegistry();
String libratoApiAccount = "xx";
String libratoApiKey = "yy";
String libratoPrefix = "zz";
LibratoReporter reporter = Librato
.reporter(dropwizardRegistry, libratoApiAccount, libratoApiKey)
.setPrefix(libratoPrefix)
.build();
reporter.start(60, TimeUnit.SECONDS);
DropwizardConfig dropwizardConfig = new DropwizardConfig() {
#Override
public String prefix() {
return "myprefix";
}
#Override
public String get(String key) {
return null;
}
};
return new DropwizardMeterRegistry(dropwizardConfig, dropwizardRegistry, HierarchicalNameMapper.DEFAULT, Clock.SYSTEM) {
#Override
protected Double nullGaugeValue() {
return null;
}
};
}
and at my main function I added Metrics.addRegistry(SpringReporter.libratoRegistry());
For the Librato library I am using in my compile("com.librato.metrics:metrics-librato:5.1.2") build.gradle. Documentation here. I used this library before without any problem.
If I use the ConsoleReporter as in this post the same thing happens, only my own created metrics are printed to the console.
Any thoughts on what am I doing wrong? or what am I missing?
Also, I enabled debug mode to see the "CONDITIONS EVALUATION REPORT" printed in the console but not sure what to look for in there.
Try to make your MeterRegistry for Librato reporter as a Spring #Bean and let me know whether it works.
UPDATED:
I tested with ConsoleReporter you mentioned and confirmed it's working with a sample. Note that the sample is on the branch console-reporter, not the master branch. See the sample for details.

Sending System Metrics to Graphite with Spring-Boot

Spring-Boot actuator exposes many useful metrics at /metrics such as uptime, memory usage, GC count.
Only a subset of these are sent to Graphite when using the Dropwizard Metrics integration. In specific, only the counters and gauges
Is there any way to get these other metrics to be published to graphite?
The documentation suggests that it should be possible:
Users of the Dropwizard ‘Metrics’ library will find that Spring Boot metrics are automatically published to com.codahale.metrics.MetricRegistry
System Metrics created by Spring boot are not reported automatically because MetricsRegistry does not know anything about those Metrics.
You should register those metrics manually when your application boots up.
#Autowired
private SystemPublicMetrics systemPublicMetrics;
private void registerSystemMetrics(MetricRegistry metricRegistry) {
systemPublicMetrics.metrics().forEach(m -> {
Gauge<Long> metricGauge = () -> m.getValue().longValue();
metricRegistry.register(m.getName(), metricGauge);
});
}
I have defined Gauge, not all the system metrics should be added as gauge. e.g. the Counter should be used to capture count values.
If you don't want to use Spring boot. Use can include metrics-jvm out of the box to capture JVM level metrics.
Here's a solution that does update DropWizard metrics on Spring metrics change. It also does that without turning #EnableScheduling on:
#EnableMetrics
#Configuration
public class ConsoleMetricsConfig extends MetricsConfigurerAdapter {
#Autowired
private SystemPublicMetrics systemPublicMetrics;
#Override
public void configureReporters(MetricRegistry metricRegistry) {
metricRegistry.register("jvm.memory", new MemoryUsageGaugeSet());
metricRegistry.register("jvm.thread-states", new ThreadStatesGaugeSet());
metricRegistry.register("jvm.garbage-collector", new GarbageCollectorMetricSet());
metricRegistry.register("spring.boot", (MetricSet) () -> {
final Map<String, Metric> gauges = new HashMap<String, Metric>();
for (final org.springframework.boot.actuate.metrics.Metric<?> springMetric :
systemPublicMetrics.metrics()) {
gauges.put(springMetric.getName(), (Gauge<Object>) () -> {
return systemPublicMetrics.metrics().stream()
.filter(m -> StringUtils.equals(m.getName(), springMetric.getName()))
.map(m -> m.getValue())
.findFirst()
.orElse(null);
});
}
return Collections.unmodifiableMap(gauges);
});
registerReporter(ConsoleReporter
.forRegistry(metricRegistry)
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build())
.start(intervalSecs, TimeUnit.SECONDS);
}
}
It uses the com.ryantenney.metrics library for enabling additional Spring annotations support and DropWizard reporters:
<dependency>
<groupId>com.ryantenney.metrics</groupId>
<artifactId>metrics-spring</artifactId>
<version>3.1.3</version>
</dependency>
But it is actually not necessary in this particular case.

Resources