mirror of
https://gitlab.com/restcountries/restcountries.git
synced 2026-03-31 15:07:46 +01:00
feat: testing metrics
This commit is contained in:
@@ -0,0 +1,113 @@
|
|||||||
|
package com.restcountries.log;
|
||||||
|
|
||||||
|
import io.micronaut.scheduling.annotation.Scheduled;
|
||||||
|
import jakarta.inject.Singleton;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class MetricsLogger {
|
||||||
|
|
||||||
|
private final RequestCounter counter;
|
||||||
|
private final AtomicLong globalCounter = new AtomicLong(0);
|
||||||
|
|
||||||
|
private final AtomicLong dailyCounter = new AtomicLong(0);
|
||||||
|
private LocalDate currentDay = LocalDate.now();
|
||||||
|
|
||||||
|
public MetricsLogger(RequestCounter counter) {
|
||||||
|
this.counter = counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Scheduled(fixedRate = "6h")
|
||||||
|
void logStats() {
|
||||||
|
long requests = counter.getTotalRequests();
|
||||||
|
|
||||||
|
if (requests == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long totalRequests = globalCounter.addAndGet(requests);
|
||||||
|
dailyCounter.addAndGet(requests);
|
||||||
|
|
||||||
|
int windowMinutes = 360;
|
||||||
|
double avgRps = requests / (windowMinutes * 60.0);
|
||||||
|
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
|
||||||
|
writeLine(
|
||||||
|
now,
|
||||||
|
windowMinutes,
|
||||||
|
requests,
|
||||||
|
totalRequests,
|
||||||
|
avgRps,
|
||||||
|
""
|
||||||
|
);
|
||||||
|
|
||||||
|
counter.reset();
|
||||||
|
|
||||||
|
checkDailySummary(now);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkDailySummary(LocalDateTime now) {
|
||||||
|
LocalDate today = now.toLocalDate();
|
||||||
|
|
||||||
|
if (!today.equals(currentDay)) {
|
||||||
|
writeDailySummary();
|
||||||
|
dailyCounter.set(0);
|
||||||
|
currentDay = today;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeDailySummary() {
|
||||||
|
long dailyTotal = dailyCounter.get();
|
||||||
|
|
||||||
|
double avgRps = dailyTotal / 86400.0; // 24h
|
||||||
|
|
||||||
|
LocalDateTime endOfDay = currentDay.atTime(23, 59, 59);
|
||||||
|
|
||||||
|
writeLine(
|
||||||
|
endOfDay,
|
||||||
|
1440,
|
||||||
|
dailyTotal,
|
||||||
|
null,
|
||||||
|
avgRps,
|
||||||
|
"DAILY_SUMMARY"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeLine(
|
||||||
|
LocalDateTime timestamp,
|
||||||
|
int windowMinutes,
|
||||||
|
Long requests,
|
||||||
|
Long totalRequests,
|
||||||
|
double avgRps,
|
||||||
|
String notes
|
||||||
|
) {
|
||||||
|
String line = String.join(",",
|
||||||
|
timestamp.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
|
||||||
|
String.valueOf(windowMinutes),
|
||||||
|
String.valueOf(requests),
|
||||||
|
totalRequests != null ? String.valueOf(totalRequests) : "",
|
||||||
|
String.format("%.2f", avgRps),
|
||||||
|
notes
|
||||||
|
) + "\n";
|
||||||
|
|
||||||
|
File file = new File("/tmp/request-stats.csv");
|
||||||
|
boolean writeHeader = !file.exists();
|
||||||
|
|
||||||
|
try (FileWriter fw = new FileWriter(file, true)) {
|
||||||
|
if (writeHeader) {
|
||||||
|
fw.write("timestamp,window_minutes,requests,total_requests,avg_rps,notes\n");
|
||||||
|
}
|
||||||
|
fw.write(line);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.restcountries.log;
|
||||||
|
|
||||||
|
import jakarta.inject.Singleton;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class RequestCounter {
|
||||||
|
|
||||||
|
private final AtomicLong totalRequests = new AtomicLong(0);
|
||||||
|
private final ConcurrentHashMap<String, AtomicLong> endpointCounts = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public void increment(String path) {
|
||||||
|
totalRequests.incrementAndGet();
|
||||||
|
endpointCounts
|
||||||
|
.computeIfAbsent(path, k -> new AtomicLong(0))
|
||||||
|
.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTotalRequests() {
|
||||||
|
return totalRequests.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConcurrentHashMap<String, AtomicLong> getEndpointCounts() {
|
||||||
|
return endpointCounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Map.Entry<String, Long>> getTopEndpoints() {
|
||||||
|
return endpointCounts.entrySet()
|
||||||
|
.stream()
|
||||||
|
.map(e -> Map.entry(e.getKey(), e.getValue().get()))
|
||||||
|
.sorted((a, b) -> Long.compare(b.getValue(), a.getValue())) // DESC
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
totalRequests.set(0);
|
||||||
|
endpointCounts.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.restcountries.log;
|
||||||
|
|
||||||
|
import io.micronaut.http.HttpRequest;
|
||||||
|
import io.micronaut.http.MutableHttpResponse;
|
||||||
|
import io.micronaut.http.annotation.Filter;
|
||||||
|
import io.micronaut.http.filter.HttpServerFilter;
|
||||||
|
import io.micronaut.http.filter.ServerFilterChain;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import org.reactivestreams.Publisher;
|
||||||
|
|
||||||
|
@Filter("/**")
|
||||||
|
public class RequestLoggingFilter implements HttpServerFilter {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
RequestCounter counter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request, ServerFilterChain chain) {
|
||||||
|
counter.increment(request.getPath());
|
||||||
|
return chain.proceed(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user