diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..d8b3278 --- /dev/null +++ b/pom.xml @@ -0,0 +1,67 @@ + + 4.0.0 + + ru.simplex_software + web-statistics + 1.0 + jar + + Project Name + http://simplex-software.ru + + + 5.0.8.RELEASE + 1.7.25 + + + + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + + + + + org.slf4j + jcl-over-slf4j + ${slf4j.version} + + + + + + + org.apache.maven.plugins + maven-eclipse-plugin + 2.9 + + true + false + + + + + true + org.apache.maven.plugins + maven-compiler-plugin + 3.6.1 + + 1.8 + 1.8 + UTF-8 + true + true + + + -Xlint:unchecked + + + + + + diff --git a/src/java/ru/simplex_software/web_statistics/service/RequestStatisticService.java b/src/java/ru/simplex_software/web_statistics/service/RequestStatisticService.java new file mode 100644 index 0000000..25477a5 --- /dev/null +++ b/src/java/ru/simplex_software/web_statistics/service/RequestStatisticService.java @@ -0,0 +1,115 @@ +package ru.simplex_software.web_statistics.service; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Сервис для хранения статистики по запросам. + */ +public class RequestStatisticService { + private static Map statistic = new HashMap<>(); + + /** + * Добавление данных о запросе в статистику. + * + * @param url адрес запроса. + * @param executionTime время выполнения запроса. + */ + public static void addRequestData(String url, long executionTime) { + RequestStatisticRecord record = statistic.getOrDefault(url, new RequestStatisticRecord()); + record.setUrl(url); + record.setCount(record.getCount() + 1); + record.setTotal(record.getTotal() + executionTime); + + if (executionTime < record.getMin() || record.getMin() == 0) { + record.setMin(executionTime); + } + if (executionTime > record.getMax()) { + record.setMax(executionTime); + } + statistic.put(url, record); + } + + /** + * Получение статистики по запросам. + * + * @return статистика по запросам. + */ + public static Map getStatistic() { + Map result = new LinkedHashMap<>(); + statistic.values() + .forEach(r -> r.setAverage((float) r.getTotal() / r.getCount())); + statistic.entrySet().stream() + .sorted((e1, e2) -> Float.compare(e2.getValue().getAverage(), e1.getValue().getAverage())) + .forEachOrdered(e -> result.put(e.getKey(), e.getValue())); + return result; + } + + /** + * Сброс статистики. + */ + public static void resetStatistic() { + statistic.clear(); + } + + /** + * Запись о статистике по запросу. + */ + public static class RequestStatisticRecord { + private String url; + private long count; + private long max; + private long min; + private long total; + private float average; + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public long getCount() { + return count; + } + + public void setCount(long count) { + this.count = count; + } + + public long getMax() { + return max; + } + + public void setMax(long max) { + this.max = max; + } + + public long getMin() { + return min; + } + + public void setMin(long min) { + this.min = min; + } + + public long getTotal() { + return total; + } + + public void setTotal(long total) { + this.total = total; + } + + public float getAverage() { + return average; + } + + public void setAverage(float average) { + this.average = average; + } + } +} \ No newline at end of file diff --git a/src/java/ru/simplex_software/web_statistics/web/filters/StatisticFilter.java b/src/java/ru/simplex_software/web_statistics/web/filters/StatisticFilter.java new file mode 100644 index 0000000..9c7d0de --- /dev/null +++ b/src/java/ru/simplex_software/web_statistics/web/filters/StatisticFilter.java @@ -0,0 +1,38 @@ +package ru.simplex_software.web_statistics.web.filters; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ru.simplex_software.web_statistics.service.RequestStatisticService; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +/** + * Фильтр для замера времени выполнения запросов. + */ +public class StatisticFilter implements Filter { + private static final Logger LOG = LoggerFactory.getLogger(StatisticFilter.class); + + @Override + public void init(FilterConfig filterConfig) { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + long startTime = System.nanoTime(); + chain.doFilter(request, response); + + long endTime = System.nanoTime(); + String url = ((HttpServletRequest) request).getRequestURI(); + long elapsed = (endTime - startTime) / 1000_000; + + RequestStatisticService.addRequestData(url, elapsed); + LOG.debug("Request to url {} executed in {} ms.", url, elapsed); + } +} \ No newline at end of file diff --git a/src/java/ru/simplex_software/web_statistics/web/servlets/RequestStatisticServlet.java b/src/java/ru/simplex_software/web_statistics/web/servlets/RequestStatisticServlet.java new file mode 100644 index 0000000..8ec15e6 --- /dev/null +++ b/src/java/ru/simplex_software/web_statistics/web/servlets/RequestStatisticServlet.java @@ -0,0 +1,78 @@ +package ru.simplex_software.web_statistics.web.servlets; + +import ru.simplex_software.web_statistics.service.RequestStatisticService; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Map; + +/** + * Сервлет, отдающий статистику. + */ +public class RequestStatisticServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { + // Сброс статистики. + if ("reset=true".equals(request.getQueryString())) { + RequestStatisticService.resetStatistic(); + response.sendRedirect(getRequestPath(request)); + } + + // Получение статистики. + Map statistic = RequestStatisticService.getStatistic(); + + // Запись статистики в ответ. + response.setContentType("text/html; charset=UTF-8"); + response.setCharacterEncoding("UTF-8"); + response.getWriter().print(createStatisticPage(statistic, getRequestPath(request))); + } + + /** + * Создание строки страницы статистики. + * + * @param statistic статистика. + * @param requestPath путь запроса. + * @return страица статистики. + */ + private String createStatisticPage(Map statistic, String requestPath) { + StringBuilder builder = new StringBuilder(); + builder.append(""); + builder.append(""); + builder.append("Статистика по запросам"); + builder.append(""); + builder.append(""); + builder.append("Reset"); + builder.append(""); + builder.append(""); + + for (String key : statistic.keySet()) { + builder.append(""); + RequestStatisticService.RequestStatisticRecord record = statistic.get(key); + String average = String.format("%.2f", record.getAverage()); + + builder.append(""); + builder.append(""); + builder.append(""); + builder.append(""); + builder.append(""); + builder.append(""); + } + + builder.append("
UrlCountMinMaxAverage
").append(key).append("").append(record.getCount()).append("").append(record.getMin()).append(" ms").append(record.getMax()).append(" ms").append(average).append(" ms
"); + builder.append(""); + return builder.toString(); + } + + /** + * Получение пути запроса без параметров. + * + * @param request запрос. + * @return путь запроса. + */ + private String getRequestPath(HttpServletRequest request) { + return request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getServletPath(); + } +} \ No newline at end of file