Commit 0ab9723f authored by Ananas Orange's avatar Ananas Orange Committed by Framboise Orange
Browse files

feat(metrics): add functional metrics

parent d878c89c
Pipeline #327265 passed with stages
in 40 minutes
......@@ -2,6 +2,7 @@ package fr.gouv.clea.consumer.service;
import fr.gouv.clea.consumer.configuration.VenueConsumerProperties;
import fr.gouv.clea.consumer.repository.visits.ExposedVisitRepository;
import fr.gouv.clea.consumer.utils.MetricsService;
import fr.inria.clea.lsp.utils.TimeUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
......@@ -21,6 +22,8 @@ public class ExposedVisitEntityService {
private final VenueConsumerProperties properties;
private final MetricsService metricsService;
@Transactional
@Scheduled(cron = "${clea.conf.scheduling.purge.cron}")
public void deleteOutdatedExposedVisits() {
......@@ -32,8 +35,10 @@ public class ExposedVisitEntityService {
);
long end = System.currentTimeMillis();
log.info("successfully purged {} entries from DB in {} seconds", count, (end - start) / 1000);
metricsService.getPurgedCounter().increment(count);
} catch (Exception e) {
log.error("error during purge");
metricsService.getFailedPurgeCounter().increment();
throw e;
}
}
......
package fr.gouv.clea.consumer.utils;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@Data
public class MetricsService {
private static final String PURGE_LABEL = "consumer.purge";
private MeterRegistry meterRegistry;
private Counter purgedCounter;
private Counter failedPurgeCounter;
@Autowired
public MetricsService(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.purgedCounter = Counter.builder(PURGE_LABEL)
.tag("type", "purged")
.description("The number of purged entries")
.register(meterRegistry);
this.failedPurgeCounter = Counter.builder(PURGE_LABEL)
.tag("type", "purgeFailed")
.description("The number of failed purge operations")
.register(meterRegistry);
}
}
......@@ -32,7 +32,9 @@ management:
endpoints:
web:
exposure:
include: health,metrics
include: health,metrics,prometheus
server:
port: 8081
server:
port: 8080
......
......@@ -7,6 +7,7 @@ import fr.gouv.clea.ws.api.model.ValidationError;
import fr.gouv.clea.ws.exception.CleaBadRequestException;
import fr.gouv.clea.ws.service.ReportService;
import fr.gouv.clea.ws.service.model.Visit;
import fr.gouv.clea.ws.utils.MetricsService;
import fr.inria.clea.lsp.utils.TimeUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
......@@ -25,6 +26,8 @@ public class CleaController implements CleaApi {
private final ReportService reportService;
private final MetricsService metricsService;
@Override
public ResponseEntity<ReportResponse> reportUsingPOST(ReportRequest reportRequest) {
nonNullPivotDateOrThrowBadRequest(reportRequest);
......@@ -37,7 +40,10 @@ public class CleaController implements CleaApi {
.collect(toList());
final var acceptedVisits = reportService.report(pivotDate, visits);
final var rejectedVisits = reportRequest.getVisits().size() - acceptedVisits;
final var message = String.format("%d/%d accepted visits", acceptedVisits, reportRequest.getVisits().size());
metricsService.getProcessedCounter().increment(acceptedVisits);
metricsService.getRejectedCounter().increment(rejectedVisits);
log.info(message);
return ResponseEntity.ok(
......
......@@ -5,6 +5,7 @@ import fr.gouv.clea.ws.model.DecodedVisit;
import fr.gouv.clea.ws.model.ReportStat;
import fr.gouv.clea.ws.service.model.Visit;
import fr.gouv.clea.ws.utils.MessageFormatter;
import fr.gouv.clea.ws.utils.MetricsService;
import fr.inria.clea.lsp.LocationSpecificPartDecoder;
import fr.inria.clea.lsp.exception.CleaEncodingException;
import fr.inria.clea.lsp.utils.TimeUtils;
......@@ -32,6 +33,8 @@ public class ReportService {
private final ProducerService producerService;
private final MetricsService metricsService;
public int report(Instant pivotDate, List<Visit> visits) {
final var now = Instant.now();
final var validatedPivotDate = this.validatePivotDate(pivotDate, now);
......@@ -97,6 +100,7 @@ public class ReportService {
final var daysBetweenScanTimeAndNow = DAYS.between(visit.getQrCodeScanTime(), now);
if (daysBetweenScanTimeAndNow > properties.getRetentionDurationInDays()) {
log.warn("report: {} rejected: Outdated", MessageFormatter.truncateQrCode(visit.getLocationSpecificPart()));
metricsService.getOutdatedCounter().increment();
return true;
}
return false;
......@@ -108,6 +112,7 @@ public class ReportService {
log.warn(
"report: {} rejected: In future", MessageFormatter.truncateQrCode(visit.getLocationSpecificPart())
);
metricsService.getFutureCounter().increment();
}
return future;
}
......@@ -129,6 +134,7 @@ public class ReportService {
"report: {} {} rejected: Duplicate",
MessageFormatter.truncateUUID(one.getStringLocationTemporaryPublicId()), one.getQrCodeScanTime()
);
metricsService.getDuplicateCounter().increment();
return true;
}
return false;
......@@ -152,6 +158,7 @@ public class ReportService {
"pivotDate: {} not between retentionLimitDate: {} and now: {}", pivotDate,
retentionDateLimit, now
);
metricsService.getNotCurrentCounter().increment();
return retentionDateLimit;
} else {
return pivotDate;
......
package fr.gouv.clea.ws.utils;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@Getter
public class MetricsService {
private static final String VISIT_LABEL = "report.visit";
private static final String TOKEN_LABEL = "report.token";
private static final String PIVOT_LABEL = "report.pivotDate";
//
private final MeterRegistry meterRegistry;
private final Counter processedCounter;
private final Counter rejectedCounter;
private final Counter duplicateCounter;
private final Counter outdatedCounter;
private final Counter notCurrentCounter;
private final Counter futureCounter;
private final Counter missingTokenCounter;
private final Counter invalidTokenCounter;
public MetricsService(
@Autowired MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.processedCounter = Counter.builder(VISIT_LABEL)
.tag("type", "processed")
.description("The number of processed reports")
.register(meterRegistry);
this.rejectedCounter = Counter.builder(VISIT_LABEL)
.tag("type", "rejected")
.description("The number of rejected reports")
.register(meterRegistry);
this.duplicateCounter = Counter.builder(VISIT_LABEL)
.tag("type", "duplicate")
.description("The number of duplicated reports")
.register(meterRegistry);
this.outdatedCounter = Counter.builder(VISIT_LABEL)
.tag("type", "outdated")
.description("The number of outdated reports")
.register(meterRegistry);
this.futureCounter = Counter.builder(VISIT_LABEL)
.tag("type", "future")
.description("The number of reports in future")
.register(meterRegistry);
this.notCurrentCounter = Counter.builder(PIVOT_LABEL)
.tag("type", "notCurrent")
.description("The number of non current pivotDates")
.register(meterRegistry);
this.missingTokenCounter = Counter.builder(TOKEN_LABEL)
.tag("type", "missing")
.description("The number of requests with a missing JWT token")
.register(meterRegistry);
this.invalidTokenCounter = Counter.builder(TOKEN_LABEL)
.tag("type", "invalid")
.description("The number of requests with an invalid JWT token")
.register(meterRegistry);
}
}
......@@ -4,11 +4,13 @@ import fr.gouv.clea.ws.configuration.CleaWsProperties;
import fr.gouv.clea.ws.model.DecodedVisit;
import fr.gouv.clea.ws.model.ReportStat;
import fr.gouv.clea.ws.service.model.Visit;
import fr.gouv.clea.ws.utils.MetricsService;
import fr.inria.clea.lsp.CleaEciesEncoder;
import fr.inria.clea.lsp.LocationSpecificPart;
import fr.inria.clea.lsp.LocationSpecificPartDecoder;
import fr.inria.clea.lsp.LocationSpecificPartEncoder;
import fr.inria.clea.lsp.utils.TimeUtils;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import org.apache.tomcat.util.codec.binary.Base64;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayNameGeneration;
......@@ -43,6 +45,8 @@ class ReportServiceTest {
@Captor
private ArgumentCaptor<List<DecodedVisit>> acceptedVisits;
private MetricsService metricsService;
private ReportService reportService;
private Instant now;
......@@ -55,7 +59,11 @@ class ReportServiceTest {
cleaWsProperties.setExposureTimeUnitInSeconds(Duration.ofMinutes(30).getSeconds());
cleaWsProperties.setDuplicateScanThresholdInSeconds(Duration.ofHours(3).getSeconds());
reportService = new ReportService(cleaWsProperties, new LocationSpecificPartDecoder(), producerService);
metricsService = new MetricsService(new SimpleMeterRegistry());
reportService = new ReportService(
cleaWsProperties, new LocationSpecificPartDecoder(), producerService, metricsService
);
}
@Test
......@@ -102,6 +110,8 @@ class ReportServiceTest {
.doesNotContain("33333333-3333-3333-3333-333333333333");
assertThat(acceptedCount)
.isEqualTo(2);
assertThat(metricsService.getFutureCounter().count()).isEqualTo(1.0);
}
@Test
......@@ -127,6 +137,8 @@ class ReportServiceTest {
.doesNotContain("11111111-1111-1111-1111-111111111111");
assertThat(acceptedCount)
.isEqualTo(3);
assertThat(metricsService.getOutdatedCounter().count()).isEqualTo(1.0);
}
@Test
......@@ -146,6 +158,7 @@ class ReportServiceTest {
.doesNotContain("22222222-2222-2222-2222-222222222222");
assertThat(acceptedCount)
.isEqualTo(1);
assertThat(metricsService.getFutureCounter().count()).isEqualTo(1.0);
}
@Test
......@@ -180,6 +193,8 @@ class ReportServiceTest {
);
assertThat(acceptedCount)
.isEqualTo(4);
assertThat(metricsService.getDuplicateCounter().count()).isEqualTo(4.0);
assertThat(metricsService.getFutureCounter().count()).isEqualTo(1.0);
}
private Visit newVisit(String uuid, Instant scanTime) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment