Merge pull request #249 from biconou/biconou_develop_PR_metrics
Performance metrics
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
|
||||
The Libresonic framework contains a convenient class (called MetricsManager) to add inner metrics that constructs in real time some performance indicators.
|
||||
|
||||
The use of MetricsManager is illustrated in the org.libresonic.player.filter.MetricsFilter class.
|
||||
|
||||
The MetricsFilter adds a metric based on the time spent by each /main.view HTTP request. This is interesting as the main.view request is invoqued when something is displayed in the main Libresonic web frame.
|
||||
|
||||
By default, the MetricsManager is deactivated; it does nothing.
|
||||
It can be activated only by adding the following line inside the livresonic.properties configuration file :
|
||||
|
||||
```
|
||||
Metrics=
|
||||
```
|
||||
|
||||
Once the MetricsManager as been activated this way, each metric can be inspected using a jmx console like VisualVM.
|
||||
Each metric is registered as a MBean as shown below.
|
||||
|
||||

|
||||
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
@@ -38,7 +38,6 @@
|
||||
<groupId>io.dropwizard.metrics</groupId>
|
||||
<artifactId>metrics-core</artifactId>
|
||||
<version>${metrics.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- END Metrics -->
|
||||
|
||||
|
||||
@@ -153,6 +153,20 @@ public class Application extends SpringBootServletInitializer implements Embedde
|
||||
return registration;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Filter metricsFilter() {
|
||||
return new MetricsFilter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean metricsFilterRegistration() {
|
||||
FilterRegistrationBean registration = new FilterRegistrationBean();
|
||||
registration.setFilter(metricsFilter());
|
||||
registration.setOrder(7);
|
||||
return registration;
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public Filter noCacheFilter() {
|
||||
return new ResponseHeaderFilter();
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.libresonic.player.filter;
|
||||
|
||||
import org.libresonic.player.monitor.MetricsManager;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by remi on 12/01/17.
|
||||
*/
|
||||
public class MetricsFilter implements Filter {
|
||||
|
||||
@Autowired
|
||||
private MetricsManager metricsManager;
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
|
||||
|
||||
String timerName = httpServletRequest.getRequestURI();
|
||||
// Add a metric that measures the time spent for each http request for the /main.view url.
|
||||
try (MetricsManager.Timer t = metricsManager.condition(timerName.contains("main.view")).timer(this,timerName)) {
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package org.libresonic.player.monitor;
|
||||
|
||||
import com.codahale.metrics.JmxReporter;
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import org.libresonic.player.service.ApacheCommonsConfigurationService;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Created by remi on 17/01/17.
|
||||
*/
|
||||
public class MetricsManager {
|
||||
|
||||
private ApacheCommonsConfigurationService configurationService;
|
||||
|
||||
// Main metrics registry
|
||||
private static final MetricRegistry metrics = new MetricRegistry();
|
||||
|
||||
private static Boolean metricsActivatedByConfiguration = null;
|
||||
private static Object _lock = new Object();
|
||||
|
||||
// Potential metrics reporters
|
||||
private static JmxReporter reporter;
|
||||
|
||||
private void configureMetricsActivation() {
|
||||
if (configurationService.containsKey("Metrics")) {
|
||||
metricsActivatedByConfiguration = Boolean.TRUE;
|
||||
|
||||
// Start a Metrics JMX reporter
|
||||
reporter = JmxReporter.forRegistry(metrics)
|
||||
.convertRatesTo(TimeUnit.SECONDS.SECONDS)
|
||||
.convertDurationsTo(TimeUnit.MILLISECONDS)
|
||||
.build();
|
||||
reporter.start();
|
||||
} else {
|
||||
metricsActivatedByConfiguration = Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean metricsActivatedByConfiguration() {
|
||||
if (metricsActivatedByConfiguration == null) {
|
||||
synchronized (_lock) {
|
||||
if (metricsActivatedByConfiguration == null) {
|
||||
configureMetricsActivation();
|
||||
}
|
||||
}
|
||||
}
|
||||
return metricsActivatedByConfiguration.booleanValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Timer} whose name is based on a class name and a
|
||||
* qualified name.
|
||||
* @param clazz
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public Timer timer(Class clazz, String name) {
|
||||
if (metricsActivatedByConfiguration()) {
|
||||
return new TimerBuilder().timer(clazz, name);
|
||||
} else {
|
||||
return nullTimerSingleton;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Timer} whose name is based on an object's class name and a
|
||||
* qualified name.
|
||||
* @param ref
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public Timer timer(Object ref, String name) {
|
||||
return timer(ref.getClass(),name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate a {@link TimerBuilder} using a condition.
|
||||
* If the condition is false, a void {@link Timer} will finally be built thus
|
||||
* no timer will be registered in the Metrics registry.
|
||||
*
|
||||
* @param ifTrue
|
||||
* @return
|
||||
*/
|
||||
public TimerBuilder condition(boolean ifTrue) {
|
||||
if (metricsActivatedByConfiguration()) {
|
||||
if (ifTrue == false) {
|
||||
return conditionFalseTimerBuilderSingleton;
|
||||
}
|
||||
return new TimerBuilder();
|
||||
} else {
|
||||
return nullTimerBuilderSingleton;
|
||||
}
|
||||
}
|
||||
|
||||
public void setConfigurationService(ApacheCommonsConfigurationService configurationService) {
|
||||
this.configurationService = configurationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that builds a {@link Timer}
|
||||
*/
|
||||
public static class TimerBuilder {
|
||||
|
||||
public Timer timer(Class clazz, String name) {
|
||||
com.codahale.metrics.Timer t = metrics.timer(MetricRegistry.name(clazz,name));
|
||||
com.codahale.metrics.Timer.Context tContext = t.time();
|
||||
return new Timer(tContext);
|
||||
}
|
||||
|
||||
public Timer timer(Object ref, String name) {
|
||||
return timer(ref.getClass(),name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that holds a Metrics timer context implementing {@link AutoCloseable}
|
||||
* thus it can be used in a try-with-resources statement.
|
||||
*/
|
||||
public static class Timer implements AutoCloseable {
|
||||
|
||||
private com.codahale.metrics.Timer.Context timerContext;
|
||||
|
||||
protected Timer(com.codahale.metrics.Timer.Context timerContext) {
|
||||
this.timerContext = timerContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
timerContext.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Convenient singletons to avoid creating useless objects instances
|
||||
// -----------------------------------------------------------------
|
||||
private static final NullTimer nullTimerSingleton = new NullTimer(null);
|
||||
private static final NullTimerBuilder conditionFalseTimerBuilderSingleton = new NullTimerBuilder();
|
||||
private static final NullTimerBuilder nullTimerBuilderSingleton = new NullTimerBuilder();
|
||||
|
||||
private static class NullTimer extends Timer {
|
||||
|
||||
protected NullTimer(com.codahale.metrics.Timer.Context timerContext) {
|
||||
super(timerContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// Does nothing
|
||||
}
|
||||
}
|
||||
|
||||
private static class NullTimerBuilder extends TimerBuilder {
|
||||
@Override
|
||||
public Timer timer(Class clazz, String name) {
|
||||
return nullTimerSingleton;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -92,6 +92,10 @@
|
||||
|
||||
<bean id="configurationService" class="org.libresonic.player.service.ApacheCommonsConfigurationService" />
|
||||
|
||||
<bean id="metricsManager" class="org.libresonic.player.monitor.MetricsManager">
|
||||
<property name="configurationService" ref="configurationService" />
|
||||
</bean>
|
||||
|
||||
<bean id="settingsService" class="org.libresonic.player.service.SettingsService" init-method="init">
|
||||
<property name="internetRadioDao" ref="internetRadioDao"/>
|
||||
<property name="musicFolderDao" ref="musicFolderDao"/>
|
||||
|
||||
Reference in New Issue
Block a user