added metrics for prometheus
All checks were successful
CD / build-and-push (push) Successful in 3m48s
CD / deploy (push) Successful in 10s

This commit is contained in:
RM
2026-10-15 11:54:45 +03:00
parent 7438e2b57a
commit 76aa1ab253
3 changed files with 56 additions and 14 deletions

View File

@@ -88,6 +88,10 @@
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.8.17</version> <version>2.8.17</version>
</dependency> </dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>

View File

@@ -8,6 +8,8 @@ import com.pledge.torgi.service.DownloadHeadlessService;
import com.pledge.torgi.service.Lot; import com.pledge.torgi.service.Lot;
import com.pledge.torgi.service.SendEmailService; import com.pledge.torgi.service.SendEmailService;
import com.pledge.torgi.utils.StartParams; import com.pledge.torgi.utils.StartParams;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
@@ -17,8 +19,10 @@ import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.time.Instant;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
@@ -34,12 +38,21 @@ public class DownloadHeadlessController {
private final TorgiEventRepository eventRepository; private final TorgiEventRepository eventRepository;
private final MissedDataRepository missedDataRepository; private final MissedDataRepository missedDataRepository;
private final DownloadHeadlessService downloadHeadlessService; private final DownloadHeadlessService downloadHeadlessService;
private final Counter diffAdded;
private final Counter missedAdded;
private final AtomicLong lastPollTimestamp;
public DownloadHeadlessController(TorgiEventRepository eventRepository, MissedDataRepository missedDataRepository, public DownloadHeadlessController(TorgiEventRepository eventRepository, MissedDataRepository missedDataRepository,
DownloadHeadlessService downloadHeadlessService) { DownloadHeadlessService downloadHeadlessService, MeterRegistry meterRegistry) {
this.eventRepository = eventRepository; this.eventRepository = eventRepository;
this.missedDataRepository = missedDataRepository; this.missedDataRepository = missedDataRepository;
this.downloadHeadlessService = downloadHeadlessService; this.downloadHeadlessService = downloadHeadlessService;
this.diffAdded = Counter.builder("torgi.events.added").tag("source", "diff")
.description("Torgi events added from scheduled diff").register(meterRegistry);
this.missedAdded = Counter.builder("torgi.events.added").tag("source", "missed")
.description("Torgi events added from missed-data queue").register(meterRegistry);
this.lastPollTimestamp = new AtomicLong(0);
meterRegistry.gauge("torgi.last.poll.timestamp", lastPollTimestamp);
eventRepository.findAllByOrderByCreatedAtDesc(PageRequest.of(0, 10000)).forEach(event -> content.add(event.getKey())); eventRepository.findAllByOrderByCreatedAtDesc(PageRequest.of(0, 10000)).forEach(event -> content.add(event.getKey()));
} }
@@ -57,9 +70,11 @@ public class DownloadHeadlessController {
newEvents.add(new TorgiEvent(key, currentContent.get(key))); newEvents.add(new TorgiEvent(key, currentContent.get(key)));
content.add(key); content.add(key);
businessLogger.info("added new diff content"); businessLogger.info("added new diff content");
diffAdded.increment();
} }
} }
eventRepository.saveAll(newEvents); eventRepository.saveAll(newEvents);
lastPollTimestamp.set(Instant.now().getEpochSecond());
businessLogger.info("completeProcessing readed from torgi = {}, added to DB = {}", currentContent.size(), newEvents.size()); businessLogger.info("completeProcessing readed from torgi = {}, added to DB = {}", currentContent.size(), newEvents.size());
} }
@@ -156,6 +171,7 @@ public class DownloadHeadlessController {
newEvents.add(new TorgiEvent(key, currentContent.get(key))); newEvents.add(new TorgiEvent(key, currentContent.get(key)));
content.add(key); content.add(key);
businessLogger.info("added new missed content"); businessLogger.info("added new missed content");
missedAdded.increment();
} }
} }
eventRepository.saveAll(newEvents); eventRepository.saveAll(newEvents);

View File

@@ -8,6 +8,7 @@ import com.pledge.torgi.service.DownloadHeadlessService;
import com.pledge.torgi.service.Lot; import com.pledge.torgi.service.Lot;
import com.pledge.torgi.service.SendEmailService; import com.pledge.torgi.service.SendEmailService;
import com.pledge.torgi.utils.StartParams; import com.pledge.torgi.utils.StartParams;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
@@ -76,7 +77,7 @@ class DownloadHeadlessControllerTest {
.thenReturn(page); .thenReturn(page);
// when // when
new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService); new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService, new SimpleMeterRegistry());
// then // then
verify(eventRepository).findAllByOrderByCreatedAtDesc(any(PageRequest.class)); verify(eventRepository).findAllByOrderByCreatedAtDesc(any(PageRequest.class));
@@ -93,7 +94,7 @@ class DownloadHeadlessControllerTest {
.thenReturn(new PageImpl<>(List.of(event1, event2))); .thenReturn(new PageImpl<>(List.of(event1, event2)));
// when // when
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService); DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService, new SimpleMeterRegistry());
// then // then
assertThat(getContent(controller)).containsExactlyInAnyOrder("key-1", "key-2"); assertThat(getContent(controller)).containsExactlyInAnyOrder("key-1", "key-2");
@@ -110,7 +111,7 @@ class DownloadHeadlessControllerTest {
lot.setFullInfo("Full info"); lot.setFullInfo("Full info");
Map<String, Lot> downloaded = Map.of(EVENT_KEY, lot); Map<String, Lot> downloaded = Map.of(EVENT_KEY, lot);
when(downloadHeadlessService.downloadExcel(anyString())).thenReturn(downloaded); when(downloadHeadlessService.downloadExcel(anyString())).thenReturn(downloaded);
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService); DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService, new SimpleMeterRegistry());
// when // when
controller.process(); controller.process();
@@ -138,7 +139,7 @@ class DownloadHeadlessControllerTest {
.thenReturn(new PageImpl<>(List.of(existing))); .thenReturn(new PageImpl<>(List.of(existing)));
Map<String, Lot> downloaded = Map.of(EVENT_KEY, new Lot()); Map<String, Lot> downloaded = Map.of(EVENT_KEY, new Lot());
when(downloadHeadlessService.downloadExcel(anyString())).thenReturn(downloaded); when(downloadHeadlessService.downloadExcel(anyString())).thenReturn(downloaded);
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService); DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService, new SimpleMeterRegistry());
// when // when
controller.process(); controller.process();
@@ -159,7 +160,7 @@ class DownloadHeadlessControllerTest {
List<String> cadastral = List.of(CADASTRAL); List<String> cadastral = List.of(CADASTRAL);
try (MockedConstruction<SendEmailService> emailConstruction = mockConstruction(SendEmailService.class)) { try (MockedConstruction<SendEmailService> emailConstruction = mockConstruction(SendEmailService.class)) {
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService); DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService, new SimpleMeterRegistry());
// when // when
controller.sendNewContent(params, cadastral, "to@example.com"); controller.sendNewContent(params, cadastral, "to@example.com");
@@ -186,7 +187,7 @@ class DownloadHeadlessControllerTest {
StartParams params = new StartParams(); StartParams params = new StartParams();
params.setFrom("from@example.com"); params.setFrom("from@example.com");
params.setToken("token"); params.setToken("token");
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService); DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService, new SimpleMeterRegistry());
try (MockedConstruction<SendEmailService> ignored = mockConstruction(SendEmailService.class, try (MockedConstruction<SendEmailService> ignored = mockConstruction(SendEmailService.class,
(mock, context) -> doThrow(new RuntimeException("smtp failure")) (mock, context) -> doThrow(new RuntimeException("smtp failure"))
@@ -215,7 +216,7 @@ class DownloadHeadlessControllerTest {
lot.setCadastral(CADASTRAL); lot.setCadastral(CADASTRAL);
Map<String, Lot> downloaded = Map.of(EVENT_KEY, lot); Map<String, Lot> downloaded = Map.of(EVENT_KEY, lot);
when(downloadHeadlessService.downloadExcel(anyString())).thenReturn(downloaded); when(downloadHeadlessService.downloadExcel(anyString())).thenReturn(downloaded);
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService); DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService, new SimpleMeterRegistry());
// when // when
controller.richData(); controller.richData();
@@ -241,7 +242,7 @@ class DownloadHeadlessControllerTest {
Map<String, Lot> downloaded = Map.of(EVENT_KEY, new Lot()); Map<String, Lot> downloaded = Map.of(EVENT_KEY, new Lot());
when(downloadHeadlessService.downloadExcel(anyString())).thenReturn(downloaded); when(downloadHeadlessService.downloadExcel(anyString())).thenReturn(downloaded);
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService); DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService, new SimpleMeterRegistry());
// when // when
controller.richData(); controller.richData();
@@ -260,7 +261,7 @@ class DownloadHeadlessControllerTest {
missed.setCadastral(CADASTRAL); missed.setCadastral(CADASTRAL);
when(missedDataRepository.findAll(any(PageRequest.class))).thenReturn(new PageImpl<>(List.of(missed))); when(missedDataRepository.findAll(any(PageRequest.class))).thenReturn(new PageImpl<>(List.of(missed)));
when(downloadHeadlessService.downloadExcel(anyString())).thenReturn(Collections.emptyMap()); when(downloadHeadlessService.downloadExcel(anyString())).thenReturn(Collections.emptyMap());
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService); DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService, new SimpleMeterRegistry());
// when // when
controller.richData(); controller.richData();
@@ -281,7 +282,7 @@ class DownloadHeadlessControllerTest {
Map<String, Lot> downloaded = Map.of(EVENT_KEY, new Lot()); Map<String, Lot> downloaded = Map.of(EVENT_KEY, new Lot());
when(downloadHeadlessService.downloadExcel(anyString())).thenReturn(downloaded); when(downloadHeadlessService.downloadExcel(anyString())).thenReturn(downloaded);
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService); DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService, new SimpleMeterRegistry());
// when // when
controller.richData(); controller.richData();
@@ -295,7 +296,7 @@ class DownloadHeadlessControllerTest {
void richData_shouldHandleExceptionGracefully() { void richData_shouldHandleExceptionGracefully() {
// given // given
when(missedDataRepository.findAll(any(PageRequest.class))).thenThrow(new RuntimeException("Database error")); when(missedDataRepository.findAll(any(PageRequest.class))).thenThrow(new RuntimeException("Database error"));
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService); DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService, new SimpleMeterRegistry());
// when / then // when / then
assertThatCode(controller::richData).doesNotThrowAnyException(); assertThatCode(controller::richData).doesNotThrowAnyException();
@@ -308,7 +309,7 @@ class DownloadHeadlessControllerTest {
File deeper = new File(nested, "deeper"); File deeper = new File(nested, "deeper");
assertThat(deeper.mkdirs()).isTrue(); assertThat(deeper.mkdirs()).isTrue();
writeFile(new File(deeper, "file.txt"), "content"); writeFile(new File(deeper, "file.txt"), "content");
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService); DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService, new SimpleMeterRegistry());
// when // when
boolean result = invokeDeleteDirectory(controller, nested); boolean result = invokeDeleteDirectory(controller, nested);
@@ -371,7 +372,7 @@ class DownloadHeadlessControllerTest {
File nested = new File(sourceDir, "nested"); File nested = new File(sourceDir, "nested");
assertThat(nested.mkdirs()).isTrue(); assertThat(nested.mkdirs()).isTrue();
writeFile(new File(nested, "data.txt"), "archive-content"); writeFile(new File(nested, "data.txt"), "archive-content");
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService); DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService, new SimpleMeterRegistry());
try { try {
// when // when
@@ -388,6 +389,27 @@ class DownloadHeadlessControllerTest {
} }
} }
@Test
void process_shouldIncrementMetrics() throws Exception {
// given
Lot lot = new Lot();
lot.setStatus("PUBLISHED");
lot.setNotice("Notice1");
lot.setLotNumber(1);
lot.setCadastral(CADASTRAL);
Map<String, Lot> downloaded = Map.of(EVENT_KEY, lot);
when(downloadHeadlessService.downloadExcel(anyString())).thenReturn(downloaded);
SimpleMeterRegistry registry = new SimpleMeterRegistry();
DownloadHeadlessController controller = new DownloadHeadlessController(eventRepository, missedDataRepository, downloadHeadlessService, registry);
// when
controller.process();
// then
assertThat(registry.get("torgi.events.added").tag("source", "diff").counter().count()).isEqualTo(1.0);
assertThat(registry.get("torgi.last.poll.timestamp").gauge().value()).isGreaterThan(0.0);
}
// Helpers // Helpers
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")