diff options
author | sourabh_sourabh <sourabh.sourabh@est.tech> | 2024-07-19 17:27:54 +0100 |
---|---|---|
committer | sourabh_sourabh <sourabh.sourabh@est.tech> | 2024-07-31 14:28:40 +0100 |
commit | e2527ea17f2e70f6b27655666e928314bdc56a22 (patch) | |
tree | e3badb0c2abe17fbe476ddb4b5cbe224fd7f5cdc /cps-ncmp-service | |
parent | b66c993cc2ab670db0f62afe22bb0b5020e5dc40 (diff) |
Effect changes on scheduled tasks (don't want scheduled tasks to be monitored)
- Applied a filter on tasks.scheduled.execution.
- It is configurable from application.ymal into cps.scheduled-task-names: - tasks.scheduled.execution by default.
- Added a new property configuration bean for open telemetry.
- Introduced new propery attribute that takes list of task names to be
filtered.
Issue-ID: CPS-2250
Change-Id: I8e5334cd166343181df6dd1985471f5465f14f0e
Signed-off-by: sourabh_sourabh <sourabh.sourabh@est.tech>
Diffstat (limited to 'cps-ncmp-service')
4 files changed, 173 insertions, 102 deletions
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/OpenTelemetryConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/OpenTelemetryConfig.java index cff3187966..a6a82b7936 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/OpenTelemetryConfig.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/OpenTelemetryConfig.java @@ -26,7 +26,11 @@ import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter; import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; import io.opentelemetry.sdk.extension.trace.jaeger.sampler.JaegerRemoteSampler; import io.opentelemetry.sdk.trace.samplers.Sampler; +import jakarta.annotation.PostConstruct; import java.time.Duration; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryCustomizer; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; @@ -37,11 +41,14 @@ import org.springframework.http.server.observation.ServerRequestObservationConte import org.springframework.util.AntPathMatcher; import org.springframework.util.PathMatcher; +/** + * Configuration class for setting up OpenTelemetry tracing in a Spring Boot application. + * This class provides beans for OTLP exporters (gRPC and HTTP), a Jaeger remote sampler, + * and customizes the ObservationRegistry to exclude certain endpoints from being observed. + */ @Configuration public class OpenTelemetryConfig { - public static final int JAEGER_REMOTE_SAMPLER_POLLING_INTERVAL_IN_SECOND = 30; - @Value("${spring.application.name:cps-application}") private String serviceId; @@ -51,9 +58,29 @@ public class OpenTelemetryConfig { @Value("${cps.tracing.sampler.jaeger_remote.endpoint:http://onap-otel-collector:14250}") private String jaegerRemoteSamplerUrl; + @Value("${cps.tracing.excluded-observation-names:tasks.scheduled.execution}") + private String excludedObservationNamesAsCsv; + + private static final int JAEGER_REMOTE_SAMPLER_POLLING_INTERVAL_IN_SECONDS = 30; + + private List<String> excludedObservationNames; + /** - * OTLP Exporter with Grpc exporter protocol. - */ + * Initializes the excludedObservationNames after the bean's properties have been set. + * This method is called by the Spring container during bean initialization. + */ + @PostConstruct + public void init() { + excludedObservationNames = Arrays.stream(excludedObservationNamesAsCsv.split(",")) + .map(String::trim) + .collect(Collectors.toList()); + } + + /** + * Creates an OTLP Exporter with gRPC protocol. + * + * @return OtlpGrpcSpanExporter bean if tracing is enabled and the exporter protocol is gRPC + */ @Bean @ConditionalOnExpression( "${cps.tracing.enabled} && 'grpc'.equals('${cps.tracing.exporter.protocol}')") @@ -62,7 +89,9 @@ public class OpenTelemetryConfig { } /** - * OTLP Exporter with HTTP exporter protocol. + * Creates an OTLP Exporter with HTTP protocol. + * + * @return OtlpHttpSpanExporter bean if tracing is enabled and the exporter protocol is HTTP */ @Bean @ConditionalOnExpression( @@ -72,39 +101,40 @@ public class OpenTelemetryConfig { } /** - * Jaeger Remote Sampler. + * Creates a Jaeger Remote Sampler. + * + * @return JaegerRemoteSampler bean if tracing is enabled */ @Bean @ConditionalOnProperty("cps.tracing.enabled") public JaegerRemoteSampler createJaegerRemoteSampler() { return JaegerRemoteSampler.builder() - .setEndpoint(jaegerRemoteSamplerUrl) - .setPollingInterval(Duration.ofSeconds(JAEGER_REMOTE_SAMPLER_POLLING_INTERVAL_IN_SECOND)) - .setInitialSampler(Sampler.alwaysOff()) - .setServiceName(serviceId) - .build(); + .setEndpoint(jaegerRemoteSamplerUrl) + .setPollingInterval(Duration.ofSeconds(JAEGER_REMOTE_SAMPLER_POLLING_INTERVAL_IN_SECONDS)) + .setInitialSampler(Sampler.alwaysOff()) + .setServiceName(serviceId) + .build(); } /** - * Excluding /actuator/** endpoints. - */ + * Customizes the ObservationRegistry to exclude /actuator/** endpoints from being observed. + * + * @return ObservationRegistryCustomizer bean if tracing is enabled + */ @Bean @ConditionalOnProperty("cps.tracing.enabled") - ObservationRegistryCustomizer<ObservationRegistry> skipActuatorEndpointsFromObservation() { + public ObservationRegistryCustomizer<ObservationRegistry> skipActuatorEndpointsFromObservation() { final PathMatcher pathMatcher = new AntPathMatcher("/"); return registry -> - registry.observationConfig().observationPredicate(observationPredicate(pathMatcher)); + registry.observationConfig().observationPredicate(observationPredicate(pathMatcher)); } - /** - * Excluding /actuator/** endpoints. - */ - static ObservationPredicate observationPredicate(final PathMatcher pathMatcher) { - return (name, context) -> { + private ObservationPredicate observationPredicate(final PathMatcher pathMatcher) { + return (observationName, context) -> { if (context instanceof ServerRequestObservationContext observationContext) { return !pathMatcher.match("/actuator/**", observationContext.getCarrier().getRequestURI()); } else { - return true; + return !excludedObservationNames.contains(observationName); } }; } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/OpenTelemetryCmNotificationSubscriptionConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/OpenTelemetryCmNotificationSubscriptionConfigSpec.groovy deleted file mode 100644 index 0f6906942f..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/OpenTelemetryCmNotificationSubscriptionConfigSpec.groovy +++ /dev/null @@ -1,81 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2024 Nordix Foundation - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.config - -import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter -import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter -import io.opentelemetry.sdk.extension.trace.jaeger.sampler.JaegerRemoteSampler -import org.spockframework.spring.SpringBean -import org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryCustomizer -import spock.lang.Shared -import spock.lang.Specification - -class OpenTelemetryConfigSpec extends Specification{ - - @Shared - @SpringBean - OpenTelemetryConfig openTelemetryConfig = new OpenTelemetryConfig() - - def setupSpec() { - openTelemetryConfig.tracingExporterEndpointUrl="http://tracingExporterEndpointUrl" - openTelemetryConfig.jaegerRemoteSamplerUrl="http://jaegerremotesamplerurl" - openTelemetryConfig.serviceId ="cps-application" - } - - def 'OpenTelemetryConfig Construction.'() { - expect: 'the system can create an instance' - new OpenTelemetryConfig() != null - } - - def 'OTLP Exporter creation with Grpc protocol'(){ - when: 'an OTLP exporter is created' - def result = openTelemetryConfig.createOtlpExporterGrpc() - then: 'an OTLP Exporter is created' - assert result instanceof OtlpGrpcSpanExporter - } - - def 'OTLP Exporter creation with HTTP protocol'(){ - when: 'an OTLP exporter is created' - def result = openTelemetryConfig.createOtlpExporterHttp() - then: 'an OTLP Exporter is created' - assert result instanceof OtlpHttpSpanExporter - and: - assert result.builder.endpoint=="http://tracingExporterEndpointUrl" - } - - def 'Jaeger Remote Sampler Creation'(){ - when: 'an OTLP exporter is created' - def result = openTelemetryConfig.createJaegerRemoteSampler() - then: 'an OTLP Exporter is created' - assert result instanceof JaegerRemoteSampler - and: - assert result.delegate.type=="remoteSampling" - and: - assert result.delegate.url.toString().startsWith("http://jaegerremotesamplerurl") - } - - def 'Skipping Acutator endpoints'(){ - when: 'an OTLP exporter is created' - def result = openTelemetryConfig.skipActuatorEndpointsFromObservation() - then: 'an OTLP Exporter is created' - assert result instanceof ObservationRegistryCustomizer - } -} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/OpenTelemetryConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/OpenTelemetryConfigSpec.groovy new file mode 100644 index 0000000000..cbff73113e --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/OpenTelemetryConfigSpec.groovy @@ -0,0 +1,113 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.config + +import io.micrometer.observation.ObservationPredicate +import io.micrometer.observation.ObservationRegistry +import io.micrometer.observation.ObservationRegistry.ObservationConfig +import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter +import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter +import io.opentelemetry.sdk.extension.trace.jaeger.sampler.JaegerRemoteSampler +import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.http.server.observation.ServerRequestObservationContext +import org.springframework.mock.web.MockHttpServletRequest +import org.springframework.util.AntPathMatcher +import spock.lang.Specification + +@SpringBootTest(classes = [OpenTelemetryConfig]) +class OpenTelemetryConfigSpec extends Specification { + + def objectUnderTest + + @Value('${cps.tracing.exporter.endpoint}') + def tracingExporterEndpointUrl + + @Value('${cps.tracing.sampler.jaeger_remote.endpoint}') + def jaegerRemoteSamplerUrl + + def setup() { + objectUnderTest = new OpenTelemetryConfig( + serviceId: 'sample-app', + tracingExporterEndpointUrl: tracingExporterEndpointUrl, + jaegerRemoteSamplerUrl: jaegerRemoteSamplerUrl, + excludedObservationNames: ['excluded-task-name']) + } + + def 'OTLP exporter creation with Grpc protocol'() { + when: 'an OTLP exporter is created' + def result = objectUnderTest.createOtlpExporterGrpc() + then: 'expected an instance of OtlpGrpcSpanExporter' + assert result instanceof OtlpGrpcSpanExporter + } + + def 'OTLP exporter creation with HTTP protocol'() { + when: 'an OTLP exporter is created' + def result = objectUnderTest.createOtlpExporterHttp() + then: 'an OTLP Exporter is created' + assert result instanceof OtlpHttpSpanExporter + and: 'the endpoint is correctly set' + assert result.builder.endpoint == 'http://exporter-test-url' + } + + def 'Jaeger Remote Sampler Creation'() { + when: 'a Jaeger remote sampler is created' + def result = objectUnderTest.createJaegerRemoteSampler() + then: 'a Jaeger remote sampler is created' + assert result instanceof JaegerRemoteSampler + and: 'the sampler type is correct' + assert result.delegate.type == 'remoteSampling' + and: 'the sampler endpoint is correctly set' + assert result.delegate.url.toString().startsWith('http://jaeger-remote-test-url') + } + + def 'Skipping actuator endpoints'() { + given: 'a mocked observation registry and config' + def observationRegistry = Mock(ObservationRegistry.class) + def observationConfig = Mock(ObservationConfig.class) + observationRegistry.observationConfig() >> observationConfig + when: 'an observation registry customizer is created and applied' + def result = objectUnderTest.skipActuatorEndpointsFromObservation() + result.customize(observationRegistry) + then: 'the observation predicate is set correctly' + 1 * observationConfig.observationPredicate(_) >> { ObservationPredicate observationPredicate -> + def mockedHttpServletRequest = new MockHttpServletRequest(_ as String, requestUrl) + def serverRequestObservationContext = new ServerRequestObservationContext(mockedHttpServletRequest, null) + and: 'expected predicate for endpoint' + assert observationPredicate.test('some-name', serverRequestObservationContext) == expectedPredicate + } + where: 'the following parameters are used' + scenario | requestUrl || expectedPredicate + 'an actuator' | '/actuator' || false + 'a non actuator' | '/some-api' || true + } + + def 'Observation predicate is configured to filter out excluded tasks by name'() { + when: 'a path matcher and observation predicate' + def observationPredicate = objectUnderTest.observationPredicate(new AntPathMatcher('/')) + then: 'a task name is provided' + assert observationPredicate.test(taskName, null) == expectedPredicate + where: 'the following parameters are used' + taskName || expectedPredicate + 'excluded-task-name' || false + 'non-excluded-task-name' || true + } +} diff --git a/cps-ncmp-service/src/test/resources/application.yml b/cps-ncmp-service/src/test/resources/application.yml index f0790dda4b..759de834ab 100644 --- a/cps-ncmp-service/src/test/resources/application.yml +++ b/cps-ncmp-service/src/test/resources/application.yml @@ -16,6 +16,15 @@ # SPDX-License-Identifier: Apache-2.0 # ============LICENSE_END========================================================= +cps: + tracing: + sampler: + jaeger_remote: + endpoint: http://jaeger-Remote-test-url + exporter: + endpoint: http://exporter-test-url + enabled: true + spring: kafka: producer: |