[JAVA-9604] Refactor and code clean-up for Metrics article
This commit is contained in:
parent
30c54f2623
commit
01ca4575ba
|
@ -12,3 +12,4 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring
|
||||||
- [Health Indicators in Spring Boot](https://www.baeldung.com/spring-boot-health-indicators)
|
- [Health Indicators in Spring Boot](https://www.baeldung.com/spring-boot-health-indicators)
|
||||||
- [How to Enable All Endpoints in Spring Boot Actuator](https://www.baeldung.com/spring-boot-actuator-enable-endpoints)
|
- [How to Enable All Endpoints in Spring Boot Actuator](https://www.baeldung.com/spring-boot-actuator-enable-endpoints)
|
||||||
- [Spring Boot Startup Actuator Endpoint](https://www.baeldung.com/spring-boot-actuator-startup)
|
- [Spring Boot Startup Actuator Endpoint](https://www.baeldung.com/spring-boot-actuator-startup)
|
||||||
|
- [Metrics for your Spring REST API](https://www.baeldung.com/spring-rest-api-metrics)
|
||||||
|
|
|
@ -23,6 +23,10 @@
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tomcat.embed</groupId>
|
||||||
|
<artifactId>tomcat-embed-jasper</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
|
@ -35,6 +39,16 @@
|
||||||
<groupId>com.h2database</groupId>
|
<groupId>com.h2database</groupId>
|
||||||
<artifactId>h2</artifactId>
|
<artifactId>h2</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>javax.servlet-api</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>jstl</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
@ -51,6 +65,10 @@
|
||||||
<artifactId>spring-security-test</artifactId>
|
<artifactId>spring-security-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.awaitility</groupId>
|
||||||
|
<artifactId>awaitility</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
package com.baeldung.metrics;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration;
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.security.servlet.SecurityRequestMatchersManagementContextConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
|
||||||
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
|
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
import org.springframework.web.context.request.RequestContextListener;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
|
||||||
|
@EnableScheduling
|
||||||
|
@ComponentScan("com.baeldung.metrics")
|
||||||
|
@SpringBootApplication
|
||||||
|
public class MetricsApplication extends SpringBootServletInitializer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
|
||||||
|
return application.sources(MetricsApplication.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartup(ServletContext sc) {
|
||||||
|
// Manages the lifecycle of the root application context
|
||||||
|
sc.addListener(new RequestContextListener());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(final String[] args) {
|
||||||
|
// only load properties for this application
|
||||||
|
System.setProperty("spring.config.location", "classpath:application-metrics.properties");
|
||||||
|
SpringApplication.run(MetricsApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.baeldung.spring;
|
package com.baeldung.metrics;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
@ -10,14 +10,10 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
import org.springframework.web.servlet.view.InternalResourceViewResolver;
|
import org.springframework.web.servlet.view.InternalResourceViewResolver;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ComponentScan("com.baeldung.web")
|
@ComponentScan("com.baeldung.metrics")
|
||||||
@EnableWebMvc
|
@EnableWebMvc
|
||||||
public class WebConfig implements WebMvcConfigurer {
|
public class WebConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
public WebConfig() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ViewResolver viewResolver() {
|
public ViewResolver viewResolver() {
|
||||||
final InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
|
final InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
|
||||||
|
@ -26,11 +22,10 @@ public class WebConfig implements WebMvcConfigurer {
|
||||||
return viewResolver;
|
return viewResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
// API
|
|
||||||
@Override
|
@Override
|
||||||
public void addViewControllers(final ViewControllerRegistry registry) {
|
public void addViewControllers(final ViewControllerRegistry registry) {
|
||||||
registry.addViewController("/graph.html");
|
registry.addViewController("/metrics/graph.html");
|
||||||
registry.addViewController("/homepage.html");
|
registry.addViewController("/metrics/homepage.html");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.baeldung.metrics.controller;
|
||||||
|
|
||||||
|
import com.baeldung.metrics.service.InMemoryMetricService;
|
||||||
|
import com.baeldung.metrics.service.MetricService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
@RequestMapping(value = "/metrics")
|
||||||
|
@ResponseBody
|
||||||
|
public class MetricsController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private InMemoryMetricService metricService;
|
||||||
|
|
||||||
|
// change the qualifier to use the in-memory implementation
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("customActuatorMetricService")
|
||||||
|
private MetricService graphMetricService;
|
||||||
|
|
||||||
|
@GetMapping(value = "/metric")
|
||||||
|
public Map<String, Map<Integer, Integer>> getMetric() {
|
||||||
|
return metricService.getFullMetric();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = "/status-metric")
|
||||||
|
public Map<Integer, Integer> getStatusMetric() {
|
||||||
|
return metricService.getStatusMetric();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = "/metric-graph-data")
|
||||||
|
public Object[][] getMetricData() {
|
||||||
|
return graphMetricService.getGraphData();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,11 @@
|
||||||
package com.baeldung.web.metric;
|
package com.baeldung.metrics.filter;
|
||||||
|
|
||||||
|
import com.baeldung.metrics.service.CustomActuatorMetricService;
|
||||||
|
import com.baeldung.metrics.service.InMemoryMetricService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||||
|
|
||||||
import javax.servlet.Filter;
|
import javax.servlet.Filter;
|
||||||
import javax.servlet.FilterChain;
|
import javax.servlet.FilterChain;
|
||||||
|
@ -9,24 +16,23 @@ import javax.servlet.ServletResponse;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class MetricFilter implements Filter {
|
public class MetricFilter implements Filter {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private IMetricService metricService;
|
private InMemoryMetricService metricService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ICustomActuatorMetricService actMetricService;
|
private CustomActuatorMetricService actMetricService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(final FilterConfig config) throws ServletException {
|
public void init(final FilterConfig config) {
|
||||||
if (metricService == null || actMetricService == null) {
|
if (metricService == null || actMetricService == null) {
|
||||||
metricService = (IMetricService) WebApplicationContextUtils.getRequiredWebApplicationContext(config.getServletContext()).getBean("metricService");
|
WebApplicationContext appContext = WebApplicationContextUtils
|
||||||
actMetricService = WebApplicationContextUtils.getRequiredWebApplicationContext(config.getServletContext()).getBean(CustomActuatorMetricService.class);
|
.getRequiredWebApplicationContext(config.getServletContext());
|
||||||
|
|
||||||
|
metricService = appContext.getBean(InMemoryMetricService.class);
|
||||||
|
actMetricService = appContext.getBean(CustomActuatorMetricService.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,8 +48,4 @@ public class MetricFilter implements Filter {
|
||||||
actMetricService.increaseCount(status);
|
actMetricService.increaseCount(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void destroy() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,32 +1,31 @@
|
||||||
package com.baeldung.web.metric;
|
package com.baeldung.metrics.service;
|
||||||
|
|
||||||
|
import io.micrometer.core.instrument.Counter;
|
||||||
|
import io.micrometer.core.instrument.Meter;
|
||||||
|
import io.micrometer.core.instrument.MeterRegistry;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import io.micrometer.core.instrument.Counter;
|
|
||||||
import io.micrometer.core.instrument.Meter;
|
|
||||||
import io.micrometer.core.instrument.MeterRegistry;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class ActuatorMetricService implements IActuatorMetricService {
|
public class ActuatorMetricService implements MetricService {
|
||||||
|
|
||||||
|
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private MeterRegistry publicMetrics;
|
private MeterRegistry publicMetrics;
|
||||||
|
|
||||||
private final List<ArrayList<Integer>> statusMetricsByMinute;
|
private final List<List<Integer>> statusMetricsByMinute;
|
||||||
private final List<String> statusList;
|
private final List<String> statusList;
|
||||||
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
|
||||||
|
|
||||||
public ActuatorMetricService() {
|
public ActuatorMetricService() {
|
||||||
super();
|
statusMetricsByMinute = new ArrayList<>();
|
||||||
statusMetricsByMinute = new ArrayList<ArrayList<Integer>>();
|
statusList = new ArrayList<>();
|
||||||
statusList = new ArrayList<String>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -36,19 +35,19 @@ public class ActuatorMetricService implements IActuatorMetricService {
|
||||||
final int rowCount = statusMetricsByMinute.size() + 1;
|
final int rowCount = statusMetricsByMinute.size() + 1;
|
||||||
final Object[][] result = new Object[rowCount][colCount];
|
final Object[][] result = new Object[rowCount][colCount];
|
||||||
result[0][0] = "Time";
|
result[0][0] = "Time";
|
||||||
int j = 1;
|
|
||||||
|
|
||||||
|
int j = 1;
|
||||||
for (final String status : statusList) {
|
for (final String status : statusList) {
|
||||||
result[0][j] = status;
|
result[0][j] = status;
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 1; i < rowCount; i++) {
|
for (int i = 1; i < rowCount; i++) {
|
||||||
result[i][0] = dateFormat.format(new Date(current.getTime() - (60000 * (rowCount - i))));
|
result[i][0] = DATE_FORMAT.format(new Date(current.getTime() - (60000L * (rowCount - i))));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Integer> minuteOfStatuses;
|
List<Integer> minuteOfStatuses;
|
||||||
List<Integer> last = new ArrayList<Integer>();
|
List<Integer> last = new ArrayList<>();
|
||||||
|
|
||||||
for (int i = 1; i < rowCount; i++) {
|
for (int i = 1; i < rowCount; i++) {
|
||||||
minuteOfStatuses = statusMetricsByMinute.get(i - 1);
|
minuteOfStatuses = statusMetricsByMinute.get(i - 1);
|
||||||
|
@ -64,11 +63,9 @@ public class ActuatorMetricService implements IActuatorMetricService {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non - API
|
@Scheduled(fixedDelayString = "${fixedDelay.in.milliseconds:60000}")
|
||||||
|
|
||||||
@Scheduled(fixedDelay = 60000)
|
|
||||||
private void exportMetrics() {
|
private void exportMetrics() {
|
||||||
final ArrayList<Integer> lastMinuteStatuses = initializeStatuses(statusList.size());
|
final List<Integer> lastMinuteStatuses = initializeStatuses(statusList.size());
|
||||||
|
|
||||||
for (final Meter counterMetric : publicMetrics.getMeters()) {
|
for (final Meter counterMetric : publicMetrics.getMeters()) {
|
||||||
updateMetrics(counterMetric, lastMinuteStatuses);
|
updateMetrics(counterMetric, lastMinuteStatuses);
|
||||||
|
@ -77,34 +74,32 @@ public class ActuatorMetricService implements IActuatorMetricService {
|
||||||
statusMetricsByMinute.add(lastMinuteStatuses);
|
statusMetricsByMinute.add(lastMinuteStatuses);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArrayList<Integer> initializeStatuses(final int size) {
|
private List<Integer> initializeStatuses(int size) {
|
||||||
final ArrayList<Integer> counterList = new ArrayList<Integer>();
|
List<Integer> counterList = new ArrayList<>();
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
counterList.add(0);
|
counterList.add(0);
|
||||||
}
|
}
|
||||||
return counterList;
|
return counterList;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateMetrics(final Meter counterMetric, final ArrayList<Integer> statusCount) {
|
private void updateMetrics(Meter counterMetric, List<Integer> statusCount) {
|
||||||
String status = "";
|
|
||||||
int index = -1;
|
|
||||||
int oldCount = 0;
|
|
||||||
|
|
||||||
if (counterMetric.getId().getName().contains("counter.status.")) {
|
String metricName = counterMetric.getId().getName();
|
||||||
status = counterMetric.getId().getName().substring(15, 18); // example 404, 200
|
|
||||||
|
if (metricName.contains("counter.status.")) {
|
||||||
|
// example 404, 200
|
||||||
|
String status = metricName.substring(15, 18);
|
||||||
appendStatusIfNotExist(status, statusCount);
|
appendStatusIfNotExist(status, statusCount);
|
||||||
index = statusList.indexOf(status);
|
int index = statusList.indexOf(status);
|
||||||
oldCount = statusCount.get(index) == null ? 0 : statusCount.get(index);
|
int oldCount = statusCount.get(index) == null ? 0 : statusCount.get(index);
|
||||||
statusCount.set(index, (int)((Counter) counterMetric).count() + oldCount);
|
statusCount.set(index, (int)((Counter) counterMetric).count() + oldCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendStatusIfNotExist(final String status, final ArrayList<Integer> statusCount) {
|
private void appendStatusIfNotExist(String status, List<Integer> statusCount) {
|
||||||
if (!statusList.contains(status)) {
|
if (!statusList.contains(status)) {
|
||||||
statusList.add(status);
|
statusList.add(status);
|
||||||
statusCount.add(0);
|
statusCount.add(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
//
|
|
||||||
}
|
|
|
@ -1,40 +1,36 @@
|
||||||
package com.baeldung.web.metric;
|
package com.baeldung.metrics.service;
|
||||||
|
|
||||||
|
import io.micrometer.core.instrument.Counter;
|
||||||
|
import io.micrometer.core.instrument.MeterRegistry;
|
||||||
|
import io.micrometer.core.instrument.search.Search;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import io.micrometer.core.instrument.Counter;
|
|
||||||
import io.micrometer.core.instrument.MeterRegistry;
|
|
||||||
import io.micrometer.core.instrument.search.Search;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class CustomActuatorMetricService implements ICustomActuatorMetricService {
|
public class CustomActuatorMetricService implements MetricService {
|
||||||
|
|
||||||
|
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private MeterRegistry registry;
|
private MeterRegistry registry;
|
||||||
|
|
||||||
private final List<ArrayList<Integer>> statusMetricsByMinute;
|
private final List<List<Integer>> statusMetricsByMinute;
|
||||||
private final List<String> statusList;
|
private final List<String> statusList;
|
||||||
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
|
||||||
|
|
||||||
public CustomActuatorMetricService() {
|
public CustomActuatorMetricService() {
|
||||||
super();
|
statusMetricsByMinute = new ArrayList<>();
|
||||||
statusMetricsByMinute = new ArrayList<ArrayList<Integer>>();
|
statusList = new ArrayList<>();
|
||||||
statusList = new ArrayList<String>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// API
|
public void increaseCount(int status) {
|
||||||
|
|
||||||
@Override
|
|
||||||
public void increaseCount(final int status) {
|
|
||||||
String counterName = "counter.status." + status;
|
String counterName = "counter.status." + status;
|
||||||
registry.counter(counterName).increment(1);
|
registry.counter(counterName).increment();
|
||||||
if (!statusList.contains(counterName)) {
|
if (!statusList.contains(counterName)) {
|
||||||
statusList.add(counterName);
|
statusList.add(counterName);
|
||||||
}
|
}
|
||||||
|
@ -55,7 +51,7 @@ public class CustomActuatorMetricService implements ICustomActuatorMetricService
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 1; i < rowCount; i++) {
|
for (int i = 1; i < rowCount; i++) {
|
||||||
result[i][0] = dateFormat.format(new Date(current.getTime() - (60000 * (rowCount - i))));
|
result[i][0] = DATE_FORMAT.format(new Date(current.getTime() - (60000L * (rowCount - i))));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Integer> minuteOfStatuses;
|
List<Integer> minuteOfStatuses;
|
||||||
|
@ -72,19 +68,17 @@ public class CustomActuatorMetricService implements ICustomActuatorMetricService
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non - API
|
@Scheduled(fixedDelayString = "${fixedDelay.in.milliseconds:60000}")
|
||||||
|
|
||||||
@Scheduled(fixedDelay = 60000)
|
|
||||||
private void exportMetrics() {
|
private void exportMetrics() {
|
||||||
final ArrayList<Integer> statusCount = new ArrayList<Integer>();
|
List<Integer> statusCount = new ArrayList<>();
|
||||||
for (final String status : statusList) {
|
for (final String status : statusList) {
|
||||||
Search search = registry.find(status);
|
Search search = registry.find(status);
|
||||||
if (search != null) {
|
Counter counter = search.counter();
|
||||||
Counter counter = search.counter();
|
if (counter == null) {
|
||||||
statusCount.add(counter != null ? ((int) counter.count()) : 0);
|
|
||||||
registry.remove(counter);
|
|
||||||
} else {
|
|
||||||
statusCount.add(0);
|
statusCount.add(0);
|
||||||
|
} else {
|
||||||
|
statusCount.add((int) counter.count());
|
||||||
|
registry.remove(counter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
statusMetricsByMinute.add(statusCount);
|
statusMetricsByMinute.add(statusCount);
|
|
@ -0,0 +1,112 @@
|
||||||
|
package com.baeldung.metrics.service;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class InMemoryMetricService implements MetricService {
|
||||||
|
|
||||||
|
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
||||||
|
|
||||||
|
private final Map<String, Map<Integer, Integer>> metricMap;
|
||||||
|
private final Map<Integer, Integer> statusMetric;
|
||||||
|
private final Map<String, Map<Integer, Integer>> timeMap;
|
||||||
|
|
||||||
|
public InMemoryMetricService() {
|
||||||
|
metricMap = new ConcurrentHashMap<>();
|
||||||
|
statusMetric = new ConcurrentHashMap<>();
|
||||||
|
timeMap = new ConcurrentHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void increaseCount(String request, int status) {
|
||||||
|
increaseMainMetric(request, status);
|
||||||
|
increaseStatusMetric(status);
|
||||||
|
updateTimeMap(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Map<Integer, Integer>> getFullMetric() {
|
||||||
|
return metricMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Integer, Integer> getStatusMetric() {
|
||||||
|
return statusMetric;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object[][] getGraphData() {
|
||||||
|
final int colCount = statusMetric.keySet().size() + 1;
|
||||||
|
final Set<Integer> allStatus = statusMetric.keySet();
|
||||||
|
final int rowCount = timeMap.keySet().size() + 1;
|
||||||
|
|
||||||
|
final Object[][] result = new Object[rowCount][colCount];
|
||||||
|
result[0][0] = "Time";
|
||||||
|
|
||||||
|
int j = 1;
|
||||||
|
for (final int status : allStatus) {
|
||||||
|
result[0][j] = status;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
int i = 1;
|
||||||
|
Map<Integer, Integer> tempMap;
|
||||||
|
for (final Entry<String, Map<Integer, Integer>> entry : timeMap.entrySet()) {
|
||||||
|
result[i][0] = entry.getKey();
|
||||||
|
tempMap = entry.getValue();
|
||||||
|
for (j = 1; j < colCount; j++) {
|
||||||
|
result[i][j] = tempMap.get((Integer) result[0][j]);
|
||||||
|
if (result[i][j] == null) {
|
||||||
|
result[i][j] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int k = 1; k < result[0].length; k++) {
|
||||||
|
result[0][k] = result[0][k].toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void increaseMainMetric(String request, int status) {
|
||||||
|
Map<Integer, Integer> statusMap = metricMap.get(request);
|
||||||
|
if (statusMap == null) {
|
||||||
|
statusMap = new ConcurrentHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer count = statusMap.get(status);
|
||||||
|
if (count == null) {
|
||||||
|
count = 1;
|
||||||
|
} else {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
statusMap.put(status, count);
|
||||||
|
metricMap.put(request, statusMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void increaseStatusMetric(int status) {
|
||||||
|
statusMetric.merge(status, 1, Integer::sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTimeMap(int status) {
|
||||||
|
final String time = DATE_FORMAT.format(new Date());
|
||||||
|
Map<Integer, Integer> statusMap = timeMap.get(time);
|
||||||
|
if (statusMap == null) {
|
||||||
|
statusMap = new ConcurrentHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer count = statusMap.get(status);
|
||||||
|
if (count == null) {
|
||||||
|
count = 1;
|
||||||
|
} else {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
statusMap.put(status, count);
|
||||||
|
timeMap.put(time, statusMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.baeldung.metrics.service;
|
||||||
|
|
||||||
|
public interface MetricService {
|
||||||
|
|
||||||
|
Object[][] getGraphData();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
management.endpoints.web.exposure.include=info,health,metrics
|
||||||
|
|
||||||
|
# JPA and Security is not required for Metrics application
|
||||||
|
spring.autoconfigure.exclude= org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, \
|
||||||
|
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, \
|
||||||
|
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, \
|
||||||
|
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, \
|
||||||
|
org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration, \
|
||||||
|
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration
|
|
@ -11,7 +11,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
function drawChart() {
|
function drawChart() {
|
||||||
$.get("<c:url value="/metric-graph-data"/>",
|
$.get("<c:url value="/metrics/metric-graph-data"/>",
|
||||||
function(mydata) {
|
function(mydata) {
|
||||||
|
|
||||||
var data = google.visualization.arrayToDataTable(mydata);
|
var data = google.visualization.arrayToDataTable(mydata);
|
|
@ -16,7 +16,7 @@
|
||||||
</context-param>
|
</context-param>
|
||||||
<context-param>
|
<context-param>
|
||||||
<param-name>contextConfigLocation</param-name>
|
<param-name>contextConfigLocation</param-name>
|
||||||
<param-value>com.baeldung.spring</param-value>
|
<param-value>com.baeldung.metrics</param-value>
|
||||||
</context-param>
|
</context-param>
|
||||||
|
|
||||||
<listener>
|
<listener>
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
<!-- Metric filter -->
|
<!-- Metric filter -->
|
||||||
<filter>
|
<filter>
|
||||||
<filter-name>metricFilter</filter-name>
|
<filter-name>metricFilter</filter-name>
|
||||||
<filter-class>com.baeldung.web.metric.MetricFilter</filter-class>
|
<filter-class>com.baeldung.metrics.filter.MetricFilter</filter-class>
|
||||||
</filter>
|
</filter>
|
||||||
|
|
||||||
<filter-mapping>
|
<filter-mapping>
|
|
@ -0,0 +1,64 @@
|
||||||
|
package com.baeldung.metrics;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.awaitility.Awaitility.await;
|
||||||
|
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||||
|
|
||||||
|
@SpringBootTest(
|
||||||
|
classes = MetricsApplication.class,
|
||||||
|
webEnvironment = RANDOM_PORT,
|
||||||
|
properties = {"fixedDelay.in.milliseconds=2000"}
|
||||||
|
)
|
||||||
|
@ActiveProfiles("metrics")
|
||||||
|
class MetricsApplicationIntegrationTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TestRestTemplate restTemplate;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenStatuses_WhenScheduledMethodExecuted_ExpectCountsAreAggregated() {
|
||||||
|
restTemplate.getForObject("/metrics/metric/notFound", String.class);
|
||||||
|
restTemplate.getForObject("/metrics/metric", String.class);
|
||||||
|
|
||||||
|
await().untilAsserted(() -> {
|
||||||
|
Object[][] statusCounts = restTemplate.getForObject("/metrics/metric-graph-data", Object[][].class);
|
||||||
|
|
||||||
|
assertThat(statusCounts[0]).contains("counter.status.200", "counter.status.404");
|
||||||
|
|
||||||
|
List<Integer> requestCounts = getRequestCounts(statusCounts);
|
||||||
|
verify404RequestFrom(requestCounts);
|
||||||
|
verify200RequestsFrom(requestCounts);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void verify200RequestsFrom(List<Integer> requestCounts) {
|
||||||
|
assertThat(requestCounts.size()).isGreaterThan(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void verify404RequestFrom(List<Integer> requestCounts) {
|
||||||
|
assertThat(requestCounts).contains(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Integer> getRequestCounts(Object[][] statusCounts) {
|
||||||
|
List<Integer> requestCounts = new ArrayList<>();
|
||||||
|
for (int i = 1; i < statusCounts.length; i++) {
|
||||||
|
for (int j = 1; j < statusCounts[i].length; j++) {
|
||||||
|
Integer count = (Integer) statusCounts[i][j];
|
||||||
|
if (count >= 1) {
|
||||||
|
requestCounts.add(count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return requestCounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -11,7 +11,6 @@ The "Learn Spring Security" Classes: http://github.learnspringsecurity.com
|
||||||
### Relevant Articles:
|
### Relevant Articles:
|
||||||
|
|
||||||
- [Integration Testing with the Maven Cargo plugin](https://www.baeldung.com/integration-testing-with-the-maven-cargo-plugin)
|
- [Integration Testing with the Maven Cargo plugin](https://www.baeldung.com/integration-testing-with-the-maven-cargo-plugin)
|
||||||
- [Metrics for your Spring REST API](https://www.baeldung.com/spring-rest-api-metrics)
|
|
||||||
- [Testing Exceptions with Spring MockMvc](https://www.baeldung.com/spring-mvc-test-exceptions)
|
- [Testing Exceptions with Spring MockMvc](https://www.baeldung.com/spring-mvc-test-exceptions)
|
||||||
|
|
||||||
### Build the Project
|
### Build the Project
|
||||||
|
|
|
@ -21,19 +21,10 @@
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.aspectj</groupId>
|
<groupId>org.aspectj</groupId>
|
||||||
<artifactId>aspectjweaver</artifactId>
|
<artifactId>aspectjweaver</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.tomcat.embed</groupId>
|
|
||||||
<artifactId>tomcat-embed-jasper</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<!-- Spring -->
|
<!-- Spring -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
|
@ -69,14 +60,6 @@
|
||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
<artifactId>spring-expression</artifactId>
|
<artifactId>spring-expression</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework</groupId>
|
|
||||||
<artifactId>spring-web</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework</groupId>
|
|
||||||
<artifactId>spring-webmvc</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.data</groupId>
|
<groupId>org.springframework.data</groupId>
|
||||||
<artifactId>spring-data-commons</artifactId>
|
<artifactId>spring-data-commons</artifactId>
|
||||||
|
@ -137,17 +120,6 @@
|
||||||
<groupId>net.bytebuddy</groupId>
|
<groupId>net.bytebuddy</groupId>
|
||||||
<artifactId>byte-buddy</artifactId>
|
<artifactId>byte-buddy</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- web -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>javax.servlet</groupId>
|
|
||||||
<artifactId>javax.servlet-api</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>javax.servlet</groupId>
|
|
||||||
<artifactId>jstl</artifactId>
|
|
||||||
<scope>runtime</scope>
|
|
||||||
</dependency>
|
|
||||||
<!-- marshalling -->
|
<!-- marshalling -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
|
|
@ -1,38 +1,19 @@
|
||||||
package com.baeldung.spring;
|
package com.baeldung.spring;
|
||||||
|
|
||||||
import javax.servlet.ServletContext;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
|
||||||
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
|
||||||
import org.springframework.web.context.request.RequestContextListener;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main Application Class - uses Spring Boot. Just run this as a normal Java
|
* Main Application Class - uses Spring Boot. Just run this as a normal Java
|
||||||
* class to run up a Jetty Server (on http://localhost:8082/spring-rest-full)
|
* class to run up a Jetty Server (on http://localhost:8082/spring-rest-full)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@EnableScheduling
|
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
@ComponentScan("com.baeldung")
|
@ComponentScan("com.baeldung")
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
public class Application extends SpringBootServletInitializer {
|
public class Application {
|
||||||
|
|
||||||
@Override
|
|
||||||
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
|
|
||||||
return application.sources(Application.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStartup(ServletContext sc) throws ServletException {
|
|
||||||
// Manages the lifecycle of the root application context
|
|
||||||
sc.addListener(new RequestContextListener());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(final String[] args) {
|
public static void main(final String[] args) {
|
||||||
SpringApplication.run(Application.class, args);
|
SpringApplication.run(Application.class, args);
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
package com.baeldung.web.controller;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.baeldung.web.metric.IActuatorMetricService;
|
|
||||||
import com.baeldung.web.metric.IMetricService;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
|
||||||
|
|
||||||
@Controller
|
|
||||||
@RequestMapping(value = "/auth/")
|
|
||||||
public class RootController {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private IMetricService metricService;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private IActuatorMetricService actMetricService;
|
|
||||||
|
|
||||||
public RootController() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
// API
|
|
||||||
|
|
||||||
@RequestMapping(value = "/metric", method = RequestMethod.GET)
|
|
||||||
@ResponseBody
|
|
||||||
public Map getMetric() {
|
|
||||||
return metricService.getFullMetric();
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequestMapping(value = "/status-metric", method = RequestMethod.GET)
|
|
||||||
@ResponseBody
|
|
||||||
public Map getStatusMetric() {
|
|
||||||
return metricService.getStatusMetric();
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequestMapping(value = "/metric-graph", method = RequestMethod.GET)
|
|
||||||
@ResponseBody
|
|
||||||
public Object[][] drawMetric() {
|
|
||||||
final Object[][] result = metricService.getGraphData();
|
|
||||||
for (int i = 1; i < result[0].length; i++) {
|
|
||||||
result[0][i] = result[0][i].toString();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
package com.baeldung.web.metric;
|
|
||||||
|
|
||||||
public interface IActuatorMetricService {
|
|
||||||
Object[][] getGraphData();
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
package com.baeldung.web.metric;
|
|
||||||
|
|
||||||
public interface ICustomActuatorMetricService {
|
|
||||||
|
|
||||||
void increaseCount(final int status);
|
|
||||||
|
|
||||||
Object[][] getGraphData();
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
package com.baeldung.web.metric;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public interface IMetricService {
|
|
||||||
|
|
||||||
void increaseCount(final String request, final int status);
|
|
||||||
|
|
||||||
Map getFullMetric();
|
|
||||||
|
|
||||||
Map getStatusMetric();
|
|
||||||
|
|
||||||
Object[][] getGraphData();
|
|
||||||
}
|
|
|
@ -1,122 +0,0 @@
|
||||||
package com.baeldung.web.metric;
|
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
public class MetricService implements IMetricService {
|
|
||||||
|
|
||||||
private ConcurrentMap<String, ConcurrentHashMap<Integer, Integer>> metricMap;
|
|
||||||
private ConcurrentMap<Integer, Integer> statusMetric;
|
|
||||||
private ConcurrentMap<String, ConcurrentHashMap<Integer, Integer>> timeMap;
|
|
||||||
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
|
||||||
|
|
||||||
public MetricService() {
|
|
||||||
super();
|
|
||||||
metricMap = new ConcurrentHashMap<String, ConcurrentHashMap<Integer, Integer>>();
|
|
||||||
statusMetric = new ConcurrentHashMap<Integer, Integer>();
|
|
||||||
timeMap = new ConcurrentHashMap<String, ConcurrentHashMap<Integer, Integer>>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// API
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void increaseCount(final String request, final int status) {
|
|
||||||
increaseMainMetric(request, status);
|
|
||||||
increaseStatusMetric(status);
|
|
||||||
updateTimeMap(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map getFullMetric() {
|
|
||||||
return metricMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map getStatusMetric() {
|
|
||||||
return statusMetric;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object[][] getGraphData() {
|
|
||||||
final int colCount = statusMetric.keySet().size() + 1;
|
|
||||||
final Set<Integer> allStatus = statusMetric.keySet();
|
|
||||||
final int rowCount = timeMap.keySet().size() + 1;
|
|
||||||
|
|
||||||
final Object[][] result = new Object[rowCount][colCount];
|
|
||||||
result[0][0] = "Time";
|
|
||||||
|
|
||||||
int j = 1;
|
|
||||||
for (final int status : allStatus) {
|
|
||||||
result[0][j] = status;
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
int i = 1;
|
|
||||||
ConcurrentMap<Integer, Integer> tempMap;
|
|
||||||
for (final Entry<String, ConcurrentHashMap<Integer, Integer>> entry : timeMap.entrySet()) {
|
|
||||||
result[i][0] = entry.getKey();
|
|
||||||
tempMap = entry.getValue();
|
|
||||||
for (j = 1; j < colCount; j++) {
|
|
||||||
result[i][j] = tempMap.get(result[0][j]);
|
|
||||||
if (result[i][j] == null) {
|
|
||||||
result[i][j] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NON-API
|
|
||||||
|
|
||||||
private void increaseMainMetric(final String request, final int status) {
|
|
||||||
ConcurrentHashMap<Integer, Integer> statusMap = metricMap.get(request);
|
|
||||||
if (statusMap == null) {
|
|
||||||
statusMap = new ConcurrentHashMap<Integer, Integer>();
|
|
||||||
}
|
|
||||||
|
|
||||||
Integer count = statusMap.get(status);
|
|
||||||
if (count == null) {
|
|
||||||
count = 1;
|
|
||||||
} else {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
statusMap.put(status, count);
|
|
||||||
metricMap.put(request, statusMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void increaseStatusMetric(final int status) {
|
|
||||||
final Integer statusCount = statusMetric.get(status);
|
|
||||||
if (statusCount == null) {
|
|
||||||
statusMetric.put(status, 1);
|
|
||||||
} else {
|
|
||||||
statusMetric.put(status, statusCount + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateTimeMap(final int status) {
|
|
||||||
final String time = dateFormat.format(new Date());
|
|
||||||
ConcurrentHashMap<Integer, Integer> statusMap = timeMap.get(time);
|
|
||||||
if (statusMap == null) {
|
|
||||||
statusMap = new ConcurrentHashMap<Integer, Integer>();
|
|
||||||
}
|
|
||||||
|
|
||||||
Integer count = statusMap.get(status);
|
|
||||||
if (count == null) {
|
|
||||||
count = 1;
|
|
||||||
} else {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
statusMap.put(status, count);
|
|
||||||
timeMap.put(time, statusMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,3 +1,2 @@
|
||||||
server.port=8082
|
server.port=8082
|
||||||
server.servlet.context-path=/spring-rest-full
|
server.servlet.context-path=/spring-rest-full
|
||||||
endpoints.metrics.enabled=true
|
|
Loading…
Reference in New Issue