Skip to content

Commit 612bee4

Browse files
authored
add metrics (#68)
Signed-off-by: Abdelsalem <[email protected]>
1 parent e74a599 commit 612bee4

File tree

6 files changed

+118
-17
lines changed

6 files changed

+118
-17
lines changed

pom.xml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,11 @@
160160
<artifactId>powsybl-network-store-iidm-impl</artifactId>
161161
<scope>runtime</scope>
162162
</dependency>
163+
<dependency>
164+
<groupId>io.micrometer</groupId>
165+
<artifactId>micrometer-registry-prometheus</artifactId>
166+
<scope>runtime</scope>
167+
</dependency>
163168
<dependency>
164169
<groupId>org.liquibase</groupId>
165170
<artifactId>liquibase-core</artifactId>
@@ -178,7 +183,6 @@
178183
<dependency>
179184
<groupId>org.springframework.boot</groupId>
180185
<artifactId>spring-boot-starter-actuator</artifactId>
181-
<scope>runtime</scope>
182186
</dependency>
183187

184188
<!-- Test dependencies -->
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* Copyright (c) 2024, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
package org.gridsuite.shortcircuit.server.service;
8+
9+
import com.powsybl.shortcircuit.ShortCircuitAnalysis;
10+
import com.powsybl.shortcircuit.ShortCircuitAnalysisResult;
11+
import io.micrometer.core.instrument.Counter;
12+
import io.micrometer.core.instrument.MeterRegistry;
13+
import io.micrometer.observation.Observation;
14+
import io.micrometer.observation.ObservationRegistry;
15+
import lombok.NonNull;
16+
import org.springframework.stereotype.Service;
17+
18+
/**
19+
* @author Abdelsalem Hedhili <abdelsalem.hedhili at rte-france.com>
20+
*/
21+
@Service
22+
public class ShortCircuitObserver {
23+
private static final String OBSERVATION_PREFIX = "app.computation.";
24+
private static final String TYPE_TAG_NAME = "type";
25+
private static final String STATUS_TAG_NAME = "status";
26+
private static final String COMPUTATION_TYPE = "shortcircuit";
27+
private static final String COMPUTATION_COUNTER_NAME = OBSERVATION_PREFIX + "count";
28+
private static final String PROVIDER_TAG_NAME = "provider";
29+
private final ObservationRegistry observationRegistry;
30+
private final MeterRegistry meterRegistry;
31+
32+
public ShortCircuitObserver(@NonNull ObservationRegistry observationRegistry, @NonNull MeterRegistry meterRegistry) {
33+
this.observationRegistry = observationRegistry;
34+
this.meterRegistry = meterRegistry;
35+
}
36+
37+
public <E extends Throwable> void observe(String name, Observation.CheckedRunnable<E> runnable) throws E {
38+
createObservation(name).observeChecked(runnable);
39+
}
40+
41+
public <T extends ShortCircuitAnalysisResult, E extends Throwable> T observeRun(String name, Observation.CheckedCallable<T, E> callable) throws E {
42+
T result = createObservation(name).observeChecked(callable);
43+
incrementCount(result);
44+
return result;
45+
}
46+
47+
public <T, E extends Throwable> T observe(String name, Observation.CheckedCallable<T, E> callable) throws E {
48+
return createObservation(name).observeChecked(callable);
49+
}
50+
51+
private Observation createObservation(String name) {
52+
return Observation.createNotStarted(OBSERVATION_PREFIX + name, observationRegistry)
53+
.lowCardinalityKeyValue(PROVIDER_TAG_NAME, ShortCircuitAnalysis.find().getName())
54+
.lowCardinalityKeyValue(TYPE_TAG_NAME, COMPUTATION_TYPE);
55+
}
56+
57+
private void incrementCount(ShortCircuitAnalysisResult result) {
58+
Counter.builder(COMPUTATION_COUNTER_NAME)
59+
.tag(PROVIDER_TAG_NAME, ShortCircuitAnalysis.find().getName())
60+
.tag(TYPE_TAG_NAME, COMPUTATION_TYPE)
61+
.tag(STATUS_TAG_NAME, result != null ? "OK" : "NOK")
62+
.register(meterRegistry)
63+
.increment();
64+
}
65+
}

src/main/java/org/gridsuite/shortcircuit/server/service/ShortCircuitWorkerService.java

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ public class ShortCircuitWorkerService {
5858
private final ShortCircuitExecutionService shortCircuitExecutionService;
5959
private final ObjectMapper objectMapper;
6060
private final Collection<AbstractReportMapper> reportMappers;
61+
private final ShortCircuitObserver shortCircuitObserver;
6162

6263
private final Map<UUID, CompletableFuture<ShortCircuitAnalysisResult>> futures = new ConcurrentHashMap<>();
6364

@@ -70,14 +71,15 @@ public class ShortCircuitWorkerService {
7071
@Autowired
7172
public ShortCircuitWorkerService(NetworkStoreService networkStoreService, ReportService reportService, ShortCircuitExecutionService shortCircuitExecutionService,
7273
NotificationService notificationService, ShortCircuitAnalysisResultRepository resultRepository,
73-
ObjectMapper objectMapper, Collection<AbstractReportMapper> reportMappers) {
74+
ObjectMapper objectMapper, Collection<AbstractReportMapper> reportMappers, ShortCircuitObserver shortCircuitObserver) {
7475
this.networkStoreService = Objects.requireNonNull(networkStoreService);
7576
this.reportService = Objects.requireNonNull(reportService);
7677
this.shortCircuitExecutionService = Objects.requireNonNull(shortCircuitExecutionService);
7778
this.notificationService = Objects.requireNonNull(notificationService);
7879
this.resultRepository = Objects.requireNonNull(resultRepository);
7980
this.objectMapper = Objects.requireNonNull(objectMapper);
8081
this.reportMappers = Objects.requireNonNull(reportMappers);
82+
this.shortCircuitObserver = shortCircuitObserver;
8183
}
8284

8385
private Network getNetwork(UUID networkUuid, String variantId) {
@@ -92,34 +94,36 @@ private Network getNetwork(UUID networkUuid, String variantId) {
9294
return network;
9395
}
9496

95-
private ShortCircuitAnalysisResult run(ShortCircuitRunContext context, UUID resultUuid) throws ExecutionException, InterruptedException {
97+
private ShortCircuitAnalysisResult run(ShortCircuitRunContext context, UUID resultUuid) throws Exception {
9698
Objects.requireNonNull(context);
9799

98100
LOGGER.info("Run short circuit analysis...");
99-
Network network = getNetwork(context.getNetworkUuid(), context.getVariantId());
101+
Network network = shortCircuitObserver.observe("network.load", () -> getNetwork(context.getNetworkUuid(), context.getVariantId()));
100102

101-
Reporter rootReporter = Reporter.NO_OP;
103+
AtomicReference<Reporter> rootReporter = new AtomicReference<>(Reporter.NO_OP);
102104
Reporter reporter = Reporter.NO_OP;
103105
if (context.getReportUuid() != null) {
104-
String reportType = context.getReportType();
105-
if (StringUtils.isEmpty(reportType)) {
106-
reportType = StringUtils.isEmpty(context.getBusId()) ? SHORTCIRCUIT_ALL_BUSES_DEFAULT_TYPE_REPORT : SHORTCIRCUIT_ONE_BUS_DEFAULT_TYPE_REPORT;
106+
AtomicReference<String> reportType = new AtomicReference<>();
107+
reportType.set(context.getReportType());
108+
if (StringUtils.isEmpty(reportType.get())) {
109+
reportType.set(StringUtils.isEmpty(context.getBusId()) ? SHORTCIRCUIT_ALL_BUSES_DEFAULT_TYPE_REPORT : SHORTCIRCUIT_ONE_BUS_DEFAULT_TYPE_REPORT);
107110
}
108-
String rootReporterId = context.getReporterId() == null ? reportType : context.getReporterId() + "@" + reportType;
109-
rootReporter = new ReporterModel(rootReporterId, rootReporterId);
110-
reporter = rootReporter.createSubReporter(reportType, reportType + " (${providerToUse})", "providerToUse", ShortCircuitAnalysis.find().getName());
111+
String rootReporterId = context.getReporterId() == null ? reportType.get() : context.getReporterId() + "@" + reportType.get();
112+
rootReporter.set(new ReporterModel(rootReporterId, rootReporterId));
113+
reporter = rootReporter.get().createSubReporter(reportType.get(), reportType + " (${providerToUse})", "providerToUse", ShortCircuitAnalysis.find().getName());
111114
// Delete any previous short-circuit computation logs
112-
reportService.deleteReport(context.getReportUuid(), reportType);
115+
shortCircuitObserver.observe("report.delete", () -> reportService.deleteReport(context.getReportUuid(), reportType.get()));
113116
}
114117

115118
CompletableFuture<ShortCircuitAnalysisResult> future = runShortCircuitAnalysisAsync(context, network, reporter, resultUuid);
116119

117-
ShortCircuitAnalysisResult result = future == null ? null : future.get();
120+
ShortCircuitAnalysisResult result = future == null ? null : shortCircuitObserver.observeRun("run", future::get);
121+
118122
if (context.getReportUuid() != null) {
119123
for (final AbstractReportMapper reportMapper : reportMappers) {
120-
rootReporter = reportMapper.processReporter(rootReporter);
124+
rootReporter.set(reportMapper.processReporter(rootReporter.get()));
121125
}
122-
reportService.sendReport(context.getReportUuid(), rootReporter);
126+
shortCircuitObserver.observe("report.send", () -> reportService.sendReport(context.getReportUuid(), rootReporter.get()));
123127
}
124128
return result;
125129
}
@@ -231,7 +235,7 @@ public Consumer<Message<String>> consumeRun() {
231235
long nanoTime = System.nanoTime();
232236
LOGGER.info("Just run in {}s", TimeUnit.NANOSECONDS.toSeconds(nanoTime - startTime.getAndSet(nanoTime)));
233237

234-
resultRepository.insert(resultContext.getResultUuid(), result, resultContext.getRunContext().getShortCircuitLimits(), ShortCircuitAnalysisStatus.COMPLETED.name());
238+
shortCircuitObserver.observe("results.save", () -> resultRepository.insert(resultContext.getResultUuid(), result, resultContext.getRunContext().getShortCircuitLimits(), ShortCircuitAnalysisStatus.COMPLETED.name()));
235239
long finalNanoTime = System.nanoTime();
236240
LOGGER.info("Stored in {}s", TimeUnit.NANOSECONDS.toSeconds(finalNanoTime - startTime.getAndSet(finalNanoTime)));
237241

src/main/resources/application-local.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,13 @@ gridsuite:
1818
services:
1919
report-server:
2020
base-uri: http://localhost:5028
21+
22+
management:
23+
metrics:
24+
distribution:
25+
percentiles-histogram:
26+
http.server.requests: true
27+
endpoints:
28+
web:
29+
exposure:
30+
include: prometheus, health, info

src/test/java/org/gridsuite/shortcircuit/server/ShortCircuitAnalysisControllerTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
import static org.mockito.ArgumentMatchers.*;
6161
import static org.mockito.BDDMockito.given;
6262
import static org.mockito.Mockito.doAnswer;
63+
import static org.mockito.Mockito.when;
6364
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
6465
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
6566
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -168,6 +169,9 @@ public String getVersion() {
168169
@MockBean
169170
private UuidGeneratorService uuidGeneratorService;
170171

172+
@MockBean
173+
ShortCircuitAnalysis.Runner runner;
174+
171175
private final RestTemplateConfig restTemplateConfig = new RestTemplateConfig();
172176
private final ObjectMapper mapper = restTemplateConfig.objectMapper();
173177

@@ -279,8 +283,11 @@ public void runTest() throws Exception {
279283
//We need to limit the precision to avoid database precision storage limit issue (postgres has a precision of 6 digits while h2 can go to 9)
280284
LocalDateTime testTime = LocalDateTime.now().truncatedTo(ChronoUnit.MICROS);
281285
try (MockedStatic<ShortCircuitAnalysis> shortCircuitAnalysisMockedStatic = Mockito.mockStatic(ShortCircuitAnalysis.class)) {
286+
282287
shortCircuitAnalysisMockedStatic.when(() -> ShortCircuitAnalysis.runAsync(eq(network), anyList(), any(ShortCircuitParameters.class), any(ComputationManager.class), anyList(), any(Reporter.class)))
283288
.thenReturn(CompletableFuture.completedFuture(ShortCircuitAnalysisResultMock.RESULT_MAGNITUDE_FULL));
289+
shortCircuitAnalysisMockedStatic.when(ShortCircuitAnalysis::find).thenReturn(runner);
290+
when(runner.getName()).thenReturn("providerTest");
284291

285292
ShortCircuitParameters shortCircuitParameters = new ShortCircuitParameters();
286293
shortCircuitParameters.setWithFortescueResult(false);
@@ -394,6 +401,8 @@ public void runWithBusIdTest() throws Exception {
394401
try (MockedStatic<ShortCircuitAnalysis> shortCircuitAnalysisMockedStatic = Mockito.mockStatic(ShortCircuitAnalysis.class)) {
395402
shortCircuitAnalysisMockedStatic.when(() -> ShortCircuitAnalysis.runAsync(eq(network), anyList(), any(ShortCircuitParameters.class), any(ComputationManager.class), anyList(), any(Reporter.class)))
396403
.thenReturn(CompletableFuture.completedFuture(ShortCircuitAnalysisResultMock.RESULT_FORTESCUE_FULL));
404+
shortCircuitAnalysisMockedStatic.when(ShortCircuitAnalysis::find).thenReturn(runner);
405+
when(runner.getName()).thenReturn("providerTest");
397406

398407
ShortCircuitParameters shortCircuitParameters = new ShortCircuitParameters();
399408
shortCircuitParameters.setWithFortescueResult(true);
@@ -452,6 +461,8 @@ public void runWithBusBarSectionIdTest() throws Exception {
452461
try (MockedStatic<ShortCircuitAnalysis> shortCircuitAnalysisMockedStatic = Mockito.mockStatic(ShortCircuitAnalysis.class)) {
453462
shortCircuitAnalysisMockedStatic.when(() -> ShortCircuitAnalysis.runAsync(eq(nodeBreakerNetwork), anyList(), any(ShortCircuitParameters.class), any(ComputationManager.class), anyList(), any(Reporter.class)))
454463
.thenReturn(CompletableFuture.completedFuture(ShortCircuitAnalysisResultMock.RESULT_FORTESCUE_FULL));
464+
shortCircuitAnalysisMockedStatic.when(ShortCircuitAnalysis::find).thenReturn(runner);
465+
when(runner.getName()).thenReturn("providerTest");
455466

456467
ShortCircuitParameters shortCircuitParameters = new ShortCircuitParameters();
457468
shortCircuitParameters.setWithFortescueResult(true);
@@ -528,6 +539,8 @@ public void stopTest() throws Exception {
528539
try (MockedStatic<ShortCircuitAnalysis> shortCircuitAnalysisMockedStatic = Mockito.mockStatic(ShortCircuitAnalysis.class)) {
529540
shortCircuitAnalysisMockedStatic.when(() -> ShortCircuitAnalysis.runAsync(eq(network), anyList(), any(ShortCircuitParameters.class), any(ComputationManager.class), anyList(), any(Reporter.class)))
530541
.thenReturn(CompletableFuture.completedFuture(RESULT));
542+
shortCircuitAnalysisMockedStatic.when(ShortCircuitAnalysis::find).thenReturn(runner);
543+
when(runner.getName()).thenReturn("providerTest");
531544

532545
mockMvc.perform(post(
533546
"/" + VERSION + "/networks/{networkUuid}/run-and-save?reportType=AllBusesShortCircuitAnalysis&receiver=me&variantId=" + VARIANT_2_ID, NETWORK_UUID)
@@ -628,6 +641,8 @@ public void checkShortCircuitLimitsTest() throws Exception {
628641
.thenReturn(CompletableFuture.completedFuture(ShortCircuitAnalysisResultMock.RESULT_MAGNITUDE_FULL));
629642
shortCircuitAnalysisMockedStatic.when(() -> ShortCircuitAnalysis.runAsync(eq(network1), anyList(), any(ShortCircuitParameters.class), any(ComputationManager.class), anyList(), any(Reporter.class)))
630643
.thenReturn(CompletableFuture.completedFuture(ShortCircuitAnalysisResultMock.RESULT_MAGNITUDE_FULL));
644+
shortCircuitAnalysisMockedStatic.when(ShortCircuitAnalysis::find).thenReturn(runner);
645+
when(runner.getName()).thenReturn("providerTest");
631646

632647
ShortCircuitParameters shortCircuitParameters = new ShortCircuitParameters().setWithFortescueResult(false);
633648
String parametersJson = mapper.writeValueAsString(shortCircuitParameters);

src/test/java/org/gridsuite/shortcircuit/server/service/ShortCircuitServiceTest.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import com.powsybl.network.store.client.NetworkStoreService;
1616
import com.powsybl.network.store.client.PreloadingStrategy;
1717
import com.powsybl.shortcircuit.*;
18+
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
19+
import io.micrometer.observation.ObservationRegistry;
1820
import lombok.AllArgsConstructor;
1921
import lombok.extern.slf4j.Slf4j;
2022
import org.assertj.core.api.WithAssertions;
@@ -65,6 +67,7 @@ void testLogsMappersIsCalled() throws Exception {
6567
final VariantManager variantManagerMocked = Mockito.mock(VariantManager.class);
6668
final Network.BusView busViewMocked = Mockito.mock(Network.BusView.class);
6769
final Reporter reporter = new ReporterModel("test", "test");
70+
final ShortCircuitObserver shortCircuitObserver = new ShortCircuitObserver(ObservationRegistry.create(), new SimpleMeterRegistry());
6871

6972
try (final MockedStatic<ShortCircuitAnalysis> shortCircuitAnalysisMockedStatic = TestUtils.injectShortCircuitAnalysisProvider(providerMock);
7073
final MockedStatic<ShortCircuitResultContext> shortCircuitResultContextMockedStatic = Mockito.mockStatic(ShortCircuitResultContext.class)) {
@@ -78,7 +81,7 @@ void testLogsMappersIsCalled() throws Exception {
7881
Mockito.when(networkMocked.getBusView()).thenReturn(busViewMocked);
7982
Mockito.when(busViewMocked.getBusStream()).thenAnswer(invocation -> Stream.empty());
8083
Mockito.when(reportMapperMocked.processReporter(any(Reporter.class))).thenReturn(reporter);
81-
final ShortCircuitWorkerService workerService = new ShortCircuitWorkerService(networkStoreServiceMocked, reportServiceMocked, shortCircuitExecutionService, notificationServiceMocked, resultRepositoryMocked, objectMapperMocked, List.of(reportMapperMocked));
84+
final ShortCircuitWorkerService workerService = new ShortCircuitWorkerService(networkStoreServiceMocked, reportServiceMocked, shortCircuitExecutionService, notificationServiceMocked, resultRepositoryMocked, objectMapperMocked, List.of(reportMapperMocked), shortCircuitObserver);
8285
workerService.consumeRun().accept(message);
8386
shortCircuitAnalysisMockedStatic.verify(ShortCircuitAnalysis::find, atLeastOnce());
8487
Mockito.verify(reportMapperMocked, times(1)).processReporter(any(ReporterModel.class));

0 commit comments

Comments
 (0)