diff --git a/pom.xml b/pom.xml index b1a0504..a7a5640 100644 --- a/pom.xml +++ b/pom.xml @@ -163,6 +163,21 @@ spring-boot-starter-actuator + + org.springframework.boot + spring-boot-starter-aop + + + + io.micrometer + micrometer-core + + + + io.micrometer + micrometer-registry-prometheus + + eu.dnetlib.dhp diff --git a/src/main/java/eu/openaire/api/OpenaireRestApiApplication.java b/src/main/java/eu/openaire/api/OpenaireRestApiApplication.java index ef5d1cf..0aeea35 100644 --- a/src/main/java/eu/openaire/api/OpenaireRestApiApplication.java +++ b/src/main/java/eu/openaire/api/OpenaireRestApiApplication.java @@ -2,8 +2,10 @@ package eu.openaire.api; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.EnableAspectJAutoProxy; @SpringBootApplication +@EnableAspectJAutoProxy public class OpenaireRestApiApplication { public static void main(String[] args) { diff --git a/src/main/java/eu/openaire/api/config/metrics/MetricsAspect.java b/src/main/java/eu/openaire/api/config/metrics/MetricsAspect.java new file mode 100644 index 0000000..4f947dd --- /dev/null +++ b/src/main/java/eu/openaire/api/config/metrics/MetricsAspect.java @@ -0,0 +1,50 @@ +package eu.openaire.api.config.metrics; + +import io.micrometer.core.annotation.Timed; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; + +@Aspect +@Component +public class MetricsAspect { + + private final MeterRegistry meterRegistry; + + @Autowired + public MetricsAspect(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + + /*** + * Advice for timing methods annotated with {@link Timed}. + */ + @Around("@annotation(io.micrometer.core.annotation.Timed)") + public Object timeAnnotatedMethods(ProceedingJoinPoint joinPoint) throws Throwable { + Method method = ((MethodSignature) joinPoint.getSignature()).getMethod(); + + String className = joinPoint.getTarget().getClass().getSimpleName(); + String methodName = method.getName(); + String timerName = className + "." + methodName; + + Timer.Sample sample = Timer.start(meterRegistry); + + try { + return joinPoint.proceed(); + } finally { + sample.stop(Timer.builder(timerName) + .tags("application", "openaire-search-api") + .tags("class", className) + .tags("method", methodName) + .publishPercentiles(0.5, 0.95) + .register(meterRegistry)); + } + } +} diff --git a/src/main/java/eu/openaire/api/services/DataSourceService.java b/src/main/java/eu/openaire/api/services/DataSourceService.java index 9c075bf..2f1b60a 100644 --- a/src/main/java/eu/openaire/api/services/DataSourceService.java +++ b/src/main/java/eu/openaire/api/services/DataSourceService.java @@ -11,6 +11,7 @@ import eu.openaire.api.mappers.response.ResponseResultsMapper; import eu.openaire.api.mappers.response.entities.DatasourceMapper; import eu.openaire.api.repositories.SolrRepository; import eu.openaire.api.solr.SolrQueryParams; +import io.micrometer.core.annotation.Timed; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import org.apache.logging.log4j.LogManager; @@ -37,6 +38,7 @@ public class DataSourceService { private final Logger log = LogManager.getLogger(this.getClass()); @SneakyThrows + @Timed public Datasource getById(String id) { var doc = solrRepository.getById(id); @@ -48,6 +50,7 @@ public class DataSourceService { } @SneakyThrows + @Timed public SearchResponse search(DataSourceRequest request) { SolrQueryParams solrQueryParams = dataSourceRequestMapper.toSolrQuery(request); diff --git a/src/main/java/eu/openaire/api/services/OrganizationService.java b/src/main/java/eu/openaire/api/services/OrganizationService.java index 602e431..e549ad3 100644 --- a/src/main/java/eu/openaire/api/services/OrganizationService.java +++ b/src/main/java/eu/openaire/api/services/OrganizationService.java @@ -11,6 +11,7 @@ import eu.openaire.api.mappers.response.ResponseResultsMapper; import eu.openaire.api.mappers.response.entities.OrganizationMapper; import eu.openaire.api.repositories.SolrRepository; import eu.openaire.api.solr.SolrQueryParams; +import io.micrometer.core.annotation.Timed; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import org.apache.logging.log4j.LogManager; @@ -37,6 +38,7 @@ public class OrganizationService { private final Logger log = LogManager.getLogger(this.getClass()); @SneakyThrows + @Timed public Organization getById(String id) { var doc = solrRepository.getById(id); @@ -48,6 +50,7 @@ public class OrganizationService { } @SneakyThrows + @Timed public SearchResponse search(OrganizationRequest request) { SolrQueryParams solrQueryParams = organizationRequestMapper.toSolrQuery(request); diff --git a/src/main/java/eu/openaire/api/services/ProjectService.java b/src/main/java/eu/openaire/api/services/ProjectService.java index 191554e..fbecfc6 100644 --- a/src/main/java/eu/openaire/api/services/ProjectService.java +++ b/src/main/java/eu/openaire/api/services/ProjectService.java @@ -11,6 +11,7 @@ import eu.openaire.api.mappers.response.ResponseResultsMapper; import eu.openaire.api.mappers.response.entities.ProjectMapper; import eu.openaire.api.repositories.SolrRepository; import eu.openaire.api.solr.SolrQueryParams; +import io.micrometer.core.annotation.Timed; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import org.apache.logging.log4j.LogManager; @@ -37,6 +38,7 @@ public class ProjectService { private final Logger log = LogManager.getLogger(this.getClass()); @SneakyThrows + @Timed public Project getById(String id) { var doc = solrRepository.getById(id); @@ -48,6 +50,7 @@ public class ProjectService { } @SneakyThrows + @Timed public SearchResponse search(ProjectRequest request) { SolrQueryParams solrQueryParams = projectRequestMapper.toSolrQuery(request); diff --git a/src/main/java/eu/openaire/api/services/ResearchProductService.java b/src/main/java/eu/openaire/api/services/ResearchProductService.java index 84a9d09..5e6318c 100644 --- a/src/main/java/eu/openaire/api/services/ResearchProductService.java +++ b/src/main/java/eu/openaire/api/services/ResearchProductService.java @@ -11,6 +11,7 @@ import eu.openaire.api.mappers.response.ResponseResultsMapper; import eu.openaire.api.mappers.response.entities.ResearchProductMapper; import eu.openaire.api.repositories.SolrRepository; import eu.openaire.api.solr.SolrQueryParams; +import io.micrometer.core.annotation.Timed; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import org.apache.logging.log4j.LogManager; @@ -37,6 +38,7 @@ public class ResearchProductService { private final Logger log = LogManager.getLogger(this.getClass()); @SneakyThrows + @Timed public GraphResult getById(String id) { var doc = solrRepository.getById(id); @@ -49,6 +51,7 @@ public class ResearchProductService { } @SneakyThrows + @Timed public SearchResponse search(ResearchProductsRequest request) { SolrQueryParams solrQueryParams = researchProductsRequestMapper.toSolrQuery(request); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 78f42bf..d62d9de 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -15,8 +15,10 @@ server.error.include-stacktrace=never # Enable the health endpoint management.endpoint.health.enabled=true -# Expose the health endpoint -management.endpoints.web.exposure.include=health,info +# Expose health, info and metrics endpoint +management.endpoints.web.exposure.include=health,info,prometheus +management.endpoint.prometheus.enabled=true +management.metrics.export.prometheus.enabled=true # Customize health endpoint settings management.endpoint.health.show-details=always