From 4f534d4986d9a2f20f9476df70f8c4fd6f1f9bb1 Mon Sep 17 00:00:00 2001 From: amvanbaren Date: Wed, 7 Feb 2024 12:51:41 +0200 Subject: [PATCH] Configure tracing Configure tracing Add @Observed for more detailed observation Add loki4j for log indexing Add OpenTelemetry resource attributes --- server/build.gradle | 7 +- server/src/dev/resources/application.yml | 11 ++- .../eclipse/openvsx/LocalRegistryService.java | 4 + .../java/org/eclipse/openvsx/RegistryAPI.java | 4 + .../eclipse/openvsx/RegistryApplication.java | 8 +- .../openvsx/UpstreamRegistryService.java | 4 + .../openvsx/metrics/MetricsConfiguration.java | 40 ++++++++++ .../RegistryObservationConvention.java | 75 +++++++++++++++++++ .../ExtensionVersionJooqRepository.java | 2 + .../repositories/RepositoryService.java | 2 + .../openvsx/search/DatabaseSearchService.java | 2 + .../openvsx/search/ElasticSearchService.java | 2 + .../openvsx/search/ISearchService.java | 3 + .../openvsx/search/SearchUtilService.java | 2 + 14 files changed, 158 insertions(+), 8 deletions(-) create mode 100644 server/src/main/java/org/eclipse/openvsx/metrics/MetricsConfiguration.java create mode 100644 server/src/main/java/org/eclipse/openvsx/metrics/RegistryObservationConvention.java diff --git a/server/build.gradle b/server/build.gradle index 2eafae447..e079c2faf 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -39,7 +39,8 @@ def versions = [ httpclient5: '5.2.1', jaxb_api: '2.3.1', jaxb_impl: '2.3.8', - gatling: '3.9.5' + gatling: '3.9.5', + loki4j: '1.4.2' ] ext['junit-jupiter.version'] = versions.junit sourceCompatibility = versions.java @@ -105,6 +106,10 @@ dependencies { exclude group: 'org.antlr', module: 'antlr' exclude group: 'org.apache.logging.log4j', module: 'log4j-slf4j-impl' } + implementation "com.github.loki4j:loki-logback-appender:${versions.loki4j}" + implementation "io.micrometer:micrometer-tracing" + implementation "io.micrometer:micrometer-tracing-bridge-otel" + implementation "io.opentelemetry:opentelemetry-exporter-zipkin" runtimeOnly "io.micrometer:micrometer-registry-prometheus" runtimeOnly "org.postgresql:postgresql" jooqGenerator "org.postgresql:postgresql" diff --git a/server/src/dev/resources/application.yml b/server/src/dev/resources/application.yml index 624660c2a..e8d39846b 100644 --- a/server/src/dev/resources/application.yml +++ b/server/src/dev/resources/application.yml @@ -1,7 +1,16 @@ +logging: + pattern: + level: '%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]' + server: port: 8080 spring: + application: + name: openvsx-server + autoconfigure: + # don't send traces to Zipkin in development + exclude: org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinAutoConfiguration profiles: include: ovsx cache: @@ -132,4 +141,4 @@ ovsx: publisher-agreement: timezone: US/Eastern integrity: - key-pair: create # create, renew, delete, 'undefined' \ No newline at end of file + key-pair: create # create, renew, delete, 'undefined' diff --git a/server/src/main/java/org/eclipse/openvsx/LocalRegistryService.java b/server/src/main/java/org/eclipse/openvsx/LocalRegistryService.java index 6596fd3fc..a3e68ff44 100644 --- a/server/src/main/java/org/eclipse/openvsx/LocalRegistryService.java +++ b/server/src/main/java/org/eclipse/openvsx/LocalRegistryService.java @@ -10,6 +10,7 @@ package org.eclipse.openvsx; import com.google.common.collect.Maps; +import io.micrometer.observation.annotation.Observed; import org.apache.commons.lang3.StringUtils; import org.eclipse.openvsx.cache.CacheService; import org.eclipse.openvsx.eclipse.EclipseService; @@ -259,6 +260,7 @@ public ReviewListJson getReviews(String namespace, String extensionName) { } @Override + @Observed public SearchResultJson search(ISearchService.Options options) { var json = new SearchResultJson(); var size = options.requestedSize; @@ -280,6 +282,7 @@ public SearchResultJson search(ISearchService.Options options) { } @Override + @Observed public QueryResultJson query(QueryRequest request) { if (!StringUtils.isEmpty(request.extensionId)) { var split = request.extensionId.split("\\."); @@ -331,6 +334,7 @@ public QueryResultJson query(QueryRequest request) { } @Override + @Observed public QueryResultJson queryV2(QueryRequestV2 request) { if (!StringUtils.isEmpty(request.extensionId)) { var split = request.extensionId.split("\\."); diff --git a/server/src/main/java/org/eclipse/openvsx/RegistryAPI.java b/server/src/main/java/org/eclipse/openvsx/RegistryAPI.java index c4ba9596a..ca816f72f 100644 --- a/server/src/main/java/org/eclipse/openvsx/RegistryAPI.java +++ b/server/src/main/java/org/eclipse/openvsx/RegistryAPI.java @@ -10,6 +10,7 @@ package org.eclipse.openvsx; import com.google.common.collect.Iterables; +import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.headers.Header; @@ -904,6 +905,7 @@ public ResponseEntity getReviews( return new ResponseEntity<>(json, HttpStatus.NOT_FOUND); } + @Observed @GetMapping( path = "/api/-/search", produces = MediaType.APPLICATION_JSON_VALUE @@ -1030,6 +1032,7 @@ private int mergeSearchResults(SearchResultJson result, List en return mergedEntries; } + @Observed @GetMapping( path = "/api/v2/-/query", produces = MediaType.APPLICATION_JSON_VALUE @@ -1142,6 +1145,7 @@ public ResponseEntity getQueryV2( .body(result); } + @Observed @GetMapping( path = "/api/-/query", produces = MediaType.APPLICATION_JSON_VALUE diff --git a/server/src/main/java/org/eclipse/openvsx/RegistryApplication.java b/server/src/main/java/org/eclipse/openvsx/RegistryApplication.java index b4e531642..5a3745d63 100644 --- a/server/src/main/java/org/eclipse/openvsx/RegistryApplication.java +++ b/server/src/main/java/org/eclipse/openvsx/RegistryApplication.java @@ -15,7 +15,6 @@ import org.eclipse.openvsx.web.ShallowEtagHeaderFilter; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; -import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.web.servlet.FilterRegistrationBean; @@ -65,6 +64,8 @@ public FilterRegistrationBean shallowEtagHeaderFilter() public RequestRejectedHandler requestRejectedHandler() { return new HttpStatusRequestRejectedHandler(); } + + @Bean @ConditionalOnProperty(value = "ovsx.data.mirror.enabled", havingValue = "true") public FilterRegistrationBean readOnlyRequestFilter( @Value("${ovsx.data.mirror.read-only.allowed-endpoints}") String[] allowedEndpoints, @@ -76,9 +77,4 @@ public FilterRegistrationBean readOnlyRequestFilter( return registrationBean; } - - @Bean - public MeterRegistryCustomizer metricsCommonTags() { - return registry -> registry.config().commonTags("application", "openvsx-server"); - } } diff --git a/server/src/main/java/org/eclipse/openvsx/UpstreamRegistryService.java b/server/src/main/java/org/eclipse/openvsx/UpstreamRegistryService.java index 68c66e8f0..d243d9c4e 100644 --- a/server/src/main/java/org/eclipse/openvsx/UpstreamRegistryService.java +++ b/server/src/main/java/org/eclipse/openvsx/UpstreamRegistryService.java @@ -9,6 +9,7 @@ ********************************************************************************/ package org.eclipse.openvsx; +import io.micrometer.observation.annotation.Observed; import org.apache.commons.lang3.StringUtils; import org.eclipse.openvsx.json.*; import org.eclipse.openvsx.search.ISearchService; @@ -259,6 +260,7 @@ public ReviewListJson getReviews(String namespace, String extension) { } @Override + @Observed public SearchResultJson search(ISearchService.Options options) { var urlTemplate = urlConfigService.getUpstreamUrl() + "/api/-/search"; var uriVariables = new HashMap(); @@ -293,6 +295,7 @@ public SearchResultJson search(ISearchService.Options options) { } @Override + @Observed public QueryResultJson query(QueryRequest request) { var urlTemplate = urlConfigService.getUpstreamUrl() + "/api/-/query"; var queryParams = new HashMap(); @@ -330,6 +333,7 @@ public QueryResultJson query(QueryRequest request) { } @Override + @Observed public QueryResultJson queryV2(QueryRequestV2 request) { var urlTemplate = urlConfigService.getUpstreamUrl() + "/api/v2/-/query"; var queryParams = new HashMap(); diff --git a/server/src/main/java/org/eclipse/openvsx/metrics/MetricsConfiguration.java b/server/src/main/java/org/eclipse/openvsx/metrics/MetricsConfiguration.java new file mode 100644 index 000000000..979d82460 --- /dev/null +++ b/server/src/main/java/org/eclipse/openvsx/metrics/MetricsConfiguration.java @@ -0,0 +1,40 @@ +/** ****************************************************************************** + * Copyright (c) 2024 Precies. Software Ltd and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * ****************************************************************************** */ +package org.eclipse.openvsx.metrics; + +import io.micrometer.common.KeyValue; +import io.micrometer.observation.ObservationFilter; +import io.micrometer.observation.ObservationRegistry; +import io.micrometer.observation.aop.ObservedAspect; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +@Configuration +@Profile("!test") +public class MetricsConfiguration { + @Bean + public ObservedAspect observedAspect(ObservationRegistry observationRegistry) { + return new ObservedAspect(observationRegistry, new RegistryObservationConvention()); + } + + @Bean + public ObservationFilter observationFilter( + @Value("${management.metrics.tags.application:app}") String service, + @Value("${management.metrics.tags.environment:development}") String environment, + @Value("${management.metrics.tags.instance:local}") String instance + ) { + return context -> context + .addLowCardinalityKeyValue(KeyValue.of("service.name", service)) + .addLowCardinalityKeyValue(KeyValue.of("deployment.environment", environment)) + .addLowCardinalityKeyValue(KeyValue.of("service.instance.id", instance)); + } +} diff --git a/server/src/main/java/org/eclipse/openvsx/metrics/RegistryObservationConvention.java b/server/src/main/java/org/eclipse/openvsx/metrics/RegistryObservationConvention.java new file mode 100644 index 000000000..aa8c71991 --- /dev/null +++ b/server/src/main/java/org/eclipse/openvsx/metrics/RegistryObservationConvention.java @@ -0,0 +1,75 @@ +/** ****************************************************************************** + * Copyright (c) 2024 Precies. Software Ltd and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * ****************************************************************************** */ +package org.eclipse.openvsx.metrics; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.micrometer.common.KeyValue; +import io.micrometer.common.KeyValues; +import io.micrometer.observation.Observation; +import io.micrometer.observation.ObservationConvention; +import io.micrometer.observation.aop.ObservedAspect; +import org.aspectj.lang.reflect.MethodSignature; + +public class RegistryObservationConvention implements ObservationConvention { + + private ObjectMapper mapper; + + public RegistryObservationConvention() { + this.mapper = new ObjectMapper(); + } + + @Override + public KeyValues getHighCardinalityKeyValues(ObservedAspect.ObservedAspectContext context) { + var joinPoint = context.getProceedingJoinPoint(); + var args = joinPoint.getArgs(); + var methodSignature = (MethodSignature) joinPoint.getSignature(); + var parameterNames = methodSignature.getParameterNames(); + var argKeyValues = new KeyValue[args.length]; + for(var i = 0; i < args.length; i++) { + var key = "args." + parameterNames[i]; + var value = convertObjectToString(args[i]); + argKeyValues[i] = KeyValue.of(key, value); + } + + return ObservationConvention.super.getHighCardinalityKeyValues(context).and(argKeyValues); + } + + private String convertObjectToString(Object arg) { + if(arg instanceof String) { + return (String) arg; + } else if(arg instanceof Number || arg instanceof Boolean) { + return String.valueOf(arg); + } else { + try { + return mapper.writeValueAsString(arg); + } catch (JsonProcessingException e) { + return ""; + } + } + } + + @Override + public boolean supportsContext(Observation.Context context) { + return context instanceof ObservedAspect.ObservedAspectContext; + } + + @Override + public String getName() { + return "org.eclipse.openvsx.observed"; + } + + @Override + public String getContextualName(ObservedAspect.ObservedAspectContext context) { + var methodSignature = (MethodSignature) context.getProceedingJoinPoint().getSignature(); + var method = methodSignature.getMethod(); + return method.getDeclaringClass().getSimpleName() + "#" + method.getName(); + } +} diff --git a/server/src/main/java/org/eclipse/openvsx/repositories/ExtensionVersionJooqRepository.java b/server/src/main/java/org/eclipse/openvsx/repositories/ExtensionVersionJooqRepository.java index 40f319d18..47637919b 100644 --- a/server/src/main/java/org/eclipse/openvsx/repositories/ExtensionVersionJooqRepository.java +++ b/server/src/main/java/org/eclipse/openvsx/repositories/ExtensionVersionJooqRepository.java @@ -9,6 +9,7 @@ * ****************************************************************************** */ package org.eclipse.openvsx.repositories; +import io.micrometer.observation.annotation.Observed; import org.apache.commons.lang3.StringUtils; import org.eclipse.openvsx.entities.*; import org.eclipse.openvsx.json.QueryRequest; @@ -267,6 +268,7 @@ private List findVersionStringsSorted(List conditions, Pageab return versionsQuery.fetch(record -> record.get(EXTENSION_VERSION.VERSION)); } + @Observed public Page findActiveVersions(QueryRequest request) { var conditions = new ArrayList(); if (!StringUtils.isEmpty(request.namespaceUuid)) { diff --git a/server/src/main/java/org/eclipse/openvsx/repositories/RepositoryService.java b/server/src/main/java/org/eclipse/openvsx/repositories/RepositoryService.java index 247c0682c..ec998210c 100644 --- a/server/src/main/java/org/eclipse/openvsx/repositories/RepositoryService.java +++ b/server/src/main/java/org/eclipse/openvsx/repositories/RepositoryService.java @@ -9,6 +9,7 @@ ********************************************************************************/ package org.eclipse.openvsx.repositories; +import io.micrometer.observation.annotation.Observed; import org.eclipse.openvsx.entities.*; import org.eclipse.openvsx.json.QueryRequest; import org.eclipse.openvsx.util.NamingUtil; @@ -317,6 +318,7 @@ public List findActiveExtensionsById(Collection ids) { return extensionJooqRepo.findAllActiveById(ids); } + @Observed public Page findActiveVersions(QueryRequest request) { return extensionVersionJooqRepo.findActiveVersions(request); } diff --git a/server/src/main/java/org/eclipse/openvsx/search/DatabaseSearchService.java b/server/src/main/java/org/eclipse/openvsx/search/DatabaseSearchService.java index 192e18670..28054fe34 100644 --- a/server/src/main/java/org/eclipse/openvsx/search/DatabaseSearchService.java +++ b/server/src/main/java/org/eclipse/openvsx/search/DatabaseSearchService.java @@ -14,6 +14,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import io.micrometer.observation.annotation.Observed; import org.eclipse.openvsx.util.TargetPlatform; import org.eclipse.openvsx.util.VersionService; import org.springframework.beans.factory.annotation.Autowired; @@ -54,6 +55,7 @@ public boolean isEnabled() { @Autowired VersionService versions; + @Observed @Transactional @Cacheable(CACHE_DATABASE_SEARCH) @CacheEvict(value = CACHE_AVERAGE_REVIEW_RATING, allEntries = true) diff --git a/server/src/main/java/org/eclipse/openvsx/search/ElasticSearchService.java b/server/src/main/java/org/eclipse/openvsx/search/ElasticSearchService.java index e91711bff..82f4b6be1 100644 --- a/server/src/main/java/org/eclipse/openvsx/search/ElasticSearchService.java +++ b/server/src/main/java/org/eclipse/openvsx/search/ElasticSearchService.java @@ -14,6 +14,7 @@ import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery; import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders; import co.elastic.clients.util.ObjectBuilder; +import io.micrometer.observation.annotation.Observed; import org.apache.commons.lang3.StringUtils; import org.eclipse.openvsx.entities.Extension; import org.eclipse.openvsx.migration.HandlerJobRequest; @@ -250,6 +251,7 @@ public void removeSearchEntry(Extension extension) { } } + @Observed public SearchHits search(Options options) { var resultWindow = options.requestedOffset + options.requestedSize; if(resultWindow > getMaxResultWindow()) { diff --git a/server/src/main/java/org/eclipse/openvsx/search/ISearchService.java b/server/src/main/java/org/eclipse/openvsx/search/ISearchService.java index 9f6d1e6ae..3fcd76b92 100644 --- a/server/src/main/java/org/eclipse/openvsx/search/ISearchService.java +++ b/server/src/main/java/org/eclipse/openvsx/search/ISearchService.java @@ -13,6 +13,8 @@ import java.util.Collection; import java.util.List; import java.util.Objects; + +import io.micrometer.observation.annotation.Observed; import org.springframework.data.elasticsearch.core.SearchHits; import org.eclipse.openvsx.entities.Extension; @@ -29,6 +31,7 @@ public interface ISearchService { /** * Search with given options */ + @Observed SearchHits search(Options options); /** diff --git a/server/src/main/java/org/eclipse/openvsx/search/SearchUtilService.java b/server/src/main/java/org/eclipse/openvsx/search/SearchUtilService.java index f9d7b0cfb..bc86d25c7 100644 --- a/server/src/main/java/org/eclipse/openvsx/search/SearchUtilService.java +++ b/server/src/main/java/org/eclipse/openvsx/search/SearchUtilService.java @@ -10,6 +10,7 @@ package org.eclipse.openvsx.search; +import io.micrometer.observation.annotation.Observed; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.data.elasticsearch.core.SearchHits; @@ -54,6 +55,7 @@ protected ISearchService getImplementation() { } + @Observed public SearchHits search(ElasticSearchService.Options options) { return getImplementation().search(options); }