diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 1b2ef86..0593b5c 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -19,52 +19,52 @@ jobs: if: github.repository == 'release-engineering/opentelemetry-ext-cli-java' && github.event_name == 'pull_request' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v5 - name: Cache local Maven repository - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{ runner.os }}-maven- - - name: Set up JDK 11 - uses: actions/setup-java@v3 + - name: Set up JDK 17 + uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 11 + java-version: 17 - name: Build with Maven run: mvn -B -V install # - name: Setup tmate session # uses: mxschmitt/action-tmate@v3 - name: Codecov - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v5 snapshot: if: github.repository == 'release-engineering/opentelemetry-ext-cli-java' && github.event_name == 'push' && github.ref == 'refs/heads/main' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v5 - name: Cache local Maven repository - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{ runner.os }}-maven- - - name: Set up JDK 11 - uses: actions/setup-java@v3 + - name: Set up JDK 17 + uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 11 + java-version: 17 # https://github.com/marketplace/actions/maven-setings-action - name: Maven Settings - uses: s4u/maven-settings-action@v2 + uses: s4u/maven-settings-action@v4.0.0 with: sonatypeSnapshots: true githubServer: false servers: | [{ - "id": "sonatype-nexus-snapshots", + "id": "central", "username": "${{ secrets.SONATYPE_USERNAME }}", "password": "${{ secrets.SONATYPE_PASSWORD }}" }] diff --git a/pom.xml b/pom.xml index c1c8be3..b478bd5 100644 --- a/pom.xml +++ b/pom.xml @@ -1,22 +1,44 @@ + 4.0.0 + + com.redhat.rcm + redhat-releng-tools + 12 + + com.redhat.resilience.otel opentelemetry-ext-cli-java 1.3.1-SNAPSHOT opentelemetry-ext-cli-java - https://github.com/release-engineering/opentelemetry-ext-cli-java Opentelemetry CLI Extension for Java + https://github.com/release-engineering/opentelemetry-ext-cli-java 2022 Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt + https://www.apache.org/licenses/LICENSE-2.0.txt repo @@ -32,20 +54,18 @@ scm:git:https://github.com/release-engineering/opentelemetry-ext-cli-java.git scm:git:git@github.com:release-engineering/opentelemetry-ext-cli-java.git - http://github.com/release-engineering/opentelemetry-ext-cli-java HEAD + https://github.com/release-engineering/opentelemetry-ext-cli-java - 11 https://github.com/release-engineering/opentelemetry-ext-cli-java + 1.8 UTF-8 - 1.8 - 1.8 - 1.7.36 - 1.22.0 + 2.0.17 + 1.53.0 @@ -58,13 +78,42 @@ pom import + + io.opentelemetry + opentelemetry-semconv + 1.29.0-alpha + io.quarkus.platform quarkus-bom - 2.13.3.Final + 2.16.12.Final pom import + + org.projectlombok + lombok + 1.18.22 + provided + + + org.slf4j + slf4j-simple + ${slf4j.version} + test + + + uk.org.webcompere + system-stubs-core + 2.1.8 + test + + + io.rest-assured + rest-assured + 4.4.0 + test + @@ -93,36 +142,25 @@ org.projectlombok lombok - 1.18.22 provided org.slf4j slf4j-api - ${slf4j.version} org.slf4j slf4j-simple - ${slf4j.version} - test - - - org.junit.jupiter - junit-jupiter - 5.7.1 test - com.github.stefanbirkner - system-lambda - 1.2.1 + uk.org.webcompere + system-stubs-core test io.rest-assured rest-assured - 4.4.0 test @@ -143,29 +181,11 @@ - + - - - maven-clean-plugin - 3.1.0 - - - - maven-resources-plugin - 3.0.2 - - - maven-compiler-plugin - 3.8.0 - - true - true - - maven-surefire-plugin - 2.22.1 + 3.5.2 default-test @@ -178,128 +198,53 @@ - - maven-jar-plugin - 3.0.2 - - - maven-install-plugin - 2.5.2 - - - maven-deploy-plugin - 2.8.2 - - - - maven-site-plugin - 3.7.1 - - - maven-project-info-reports-plugin - 3.0.0 - org.apache.maven.plugins maven-release-plugin - 2.5.3 - false - -Prelease v@{project.version} - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - verify - - sign - - - - - - org.apache.maven.plugins - maven-source-plugin - 3.2.1 - - - attach-sources - - jar-no-fork - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.2.0 - - - attach-javadocs - - jar - - - false - none - - - - + + + com.diffplug.spotless + spotless-maven-plugin + 3.0.0 + + + + false + + + + + + java-import-order.txt + + + java-formatter.xml + + UNIX + + + + + org.jboss.pnc + ide-config + 1.1.0 + + + + + + apply + + compile + + + + - - - - release - - - - org.apache.maven.plugins - maven-source-plugin - - - org.apache.maven.plugins - maven-javadoc-plugin - - - org.apache.maven.plugins - maven-gpg-plugin - - - - - - - - - - sonatype-nexus-staging - Release Repository - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - sonatype-nexus-snapshots - Sonatype Nexus - https://oss.sonatype.org/content/repositories/snapshots/ - - - - - - sonatype-snapshots - https://oss.sonatype.org/content/repositories/snapshots/ - false - true - - - diff --git a/src/main/java/com/redhat/resilience/otel/OTelCLIHelper.java b/src/main/java/com/redhat/resilience/otel/OTelCLIHelper.java index b9e73a4..8c10667 100644 --- a/src/main/java/com/redhat/resilience/otel/OTelCLIHelper.java +++ b/src/main/java/com/redhat/resilience/otel/OTelCLIHelper.java @@ -1,11 +1,22 @@ /* - * Copyright Red Hat - * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2022 Red Hat, Inc. + * + * 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. */ - package com.redhat.resilience.otel; import com.redhat.resilience.otel.internal.EnvarExtractingPropagator; + import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.Span; @@ -27,8 +38,7 @@ */ @Slf4j @UtilityClass -public class OTelCLIHelper -{ +public class OTelCLIHelper { private SpanProcessor spanProcessor; private Span root = null; @@ -39,9 +49,8 @@ public class OTelCLIHelper * @param endpoint The gRPC endpoint for sending span data * @return The {@link OtlpGrpcSpanExporter} instance */ - public SpanExporter defaultSpanExporter( String endpoint ) - { - return OtlpGrpcSpanExporter.builder().setEndpoint( endpoint ).build(); + public SpanExporter defaultSpanExporter(String endpoint) { + return OtlpGrpcSpanExporter.builder().setEndpoint(endpoint).build(); } /** @@ -50,13 +59,13 @@ public SpanExporter defaultSpanExporter( String endpoint ) * @param exporter The {@link SpanExporter}, which MAY come from {@link OTelCLIHelper#defaultSpanExporter} * @return The {@link BatchSpanProcessor} instance */ - public SpanProcessor defaultSpanProcessor( SpanExporter exporter ) - { - return BatchSpanProcessor.builder( exporter ).build(); + public SpanProcessor defaultSpanProcessor(SpanExporter exporter) { + return BatchSpanProcessor.builder(exporter).build(); } /** - * Setup {@link GlobalOpenTelemetry} using the provided service name and span processor (which contains an exporter). + * Setup {@link GlobalOpenTelemetry} using the provided service name and span processor (which contains an + * exporter). * This will also ininitialize with the {@link EnvarExtractingPropagator} context propagator, which knows how to set * W3C HTTP headers for propagating context downstream, but reads similar fields from {@link System#getenv()}. *

@@ -66,13 +75,13 @@ public SpanProcessor defaultSpanProcessor( SpanExporter exporter ) * @param serviceName This translates into 'service.name' in the span, which is usually required for span validity * @param processor This is a span processor that determines how spans are exported */ - public void startOTel( String serviceName, SpanProcessor processor ) - { - startOTel( serviceName, "cli-execution", processor ); + public void startOTel(String serviceName, SpanProcessor processor) { + startOTel(serviceName, "cli-execution", processor); } /** - * Setup {@link GlobalOpenTelemetry} using the provided service name and span processor (which contains an exporter). + * Setup {@link GlobalOpenTelemetry} using the provided service name and span processor (which contains an + * exporter). * This will also ininitialize with the {@link EnvarExtractingPropagator} context propagator, which knows how to set * W3C HTTP headers for propagating context downstream, but reads similar fields from {@link System#getenv()}. *

@@ -83,45 +92,48 @@ public void startOTel( String serviceName, SpanProcessor processor ) * @param commandName This is used to name the new span * @param processor This is a span processor that determines how spans are exported */ - public void startOTel( String serviceName, String commandName, SpanProcessor processor) - { - if (spanProcessor != null) - { + public void startOTel(String serviceName, String commandName, SpanProcessor processor) { + if (spanProcessor != null) { throw new IllegalStateException("startOTel has already been called"); } - if (serviceName == null) - { + if (serviceName == null) { throw new RuntimeException("serviceName must be passed in"); } - if (commandName == null) - { + if (commandName == null) { commandName = serviceName; } spanProcessor = processor; Resource resource = Resource.getDefault() - .merge(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, serviceName))); + .merge(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, serviceName))); SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder() - .addSpanProcessor(processor) - .setResource(resource) - .build(); + .addSpanProcessor(processor) + .setResource(resource) + .build(); // NOTE the use of EnvarExtractingPropagator here OpenTelemetrySdk openTelemetry = OpenTelemetrySdk.builder() - .setTracerProvider(sdkTracerProvider) - .setPropagators(ContextPropagators.create(EnvarExtractingPropagator.getInstance())) - .buildAndRegisterGlobal(); + .setTracerProvider(sdkTracerProvider) + .setPropagators(ContextPropagators.create(EnvarExtractingPropagator.getInstance())) + .buildAndRegisterGlobal(); Context parentContext = EnvarExtractingPropagator.getInstance().extract(Context.current(), null, null); root = openTelemetry.getTracer(serviceName).spanBuilder(commandName).setParent(parentContext).startSpan(); root.makeCurrent(); - log.debug("Running with traceId {} spanId {}", - Span.current().getSpanContext().getTraceId(), Span.current().getSpanContext().getSpanId()); + log.debug( + "Running with traceId {} spanId {}", + Span.current().getSpanContext().getTraceId(), + Span.current().getSpanContext().getSpanId()); } + /** + * Return whether this is enabled + * + * @return a boolean with the current enabled status. + */ public boolean otelEnabled() { return spanProcessor != null; } @@ -129,13 +141,10 @@ public boolean otelEnabled() { /** * Shutdown the span processor, giving it some time to flush any pending spans out to the exporter. */ - public void stopOTel() - { - if ( otelEnabled() ) - { + public void stopOTel() { + if (otelEnabled()) { log.debug("Finishing OpenTelemetry instrumentation for {}", root); - if (root != null) - { + if (root != null) { root.end(); } spanProcessor.close(); diff --git a/src/main/java/com/redhat/resilience/otel/internal/EnvarExtractingConfigurablePropagator.java b/src/main/java/com/redhat/resilience/otel/internal/EnvarExtractingConfigurablePropagator.java index 3ff91a0..11fea38 100644 --- a/src/main/java/com/redhat/resilience/otel/internal/EnvarExtractingConfigurablePropagator.java +++ b/src/main/java/com/redhat/resilience/otel/internal/EnvarExtractingConfigurablePropagator.java @@ -1,8 +1,18 @@ /* - * Copyright Red Hat - * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2022 Red Hat, Inc. + * + * 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. */ - package com.redhat.resilience.otel.internal; import io.opentelemetry.context.propagation.TextMapPropagator; @@ -14,26 +24,25 @@ * 'envar' in Quarkus application.yaml. */ public class EnvarExtractingConfigurablePropagator - implements ConfigurablePropagatorProvider -{ + implements ConfigurablePropagatorProvider { /** * Return the propagator instance + * * @param configProperties Not used * @return The {@link EnvarExtractingPropagator} instance */ @Override - public TextMapPropagator getPropagator( ConfigProperties configProperties ) - { + public TextMapPropagator getPropagator(ConfigProperties configProperties) { return EnvarExtractingPropagator.getInstance(); } /** * Return the keyword used for autoconfiguring this context propagator. + * * @return 'envar' */ @Override - public String getName() - { + public String getName() { return "envar"; } } diff --git a/src/main/java/com/redhat/resilience/otel/internal/EnvarExtractingPropagator.java b/src/main/java/com/redhat/resilience/otel/internal/EnvarExtractingPropagator.java index 826ce8d..56dd6d3 100644 --- a/src/main/java/com/redhat/resilience/otel/internal/EnvarExtractingPropagator.java +++ b/src/main/java/com/redhat/resilience/otel/internal/EnvarExtractingPropagator.java @@ -1,20 +1,22 @@ /* - * Copyright Red Hat - * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2022 Red Hat, Inc. + * + * 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. */ - package com.redhat.resilience.otel.internal; -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.api.trace.SpanContext; -import io.opentelemetry.api.trace.TraceFlags; -import io.opentelemetry.api.trace.TraceState; -import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; -import io.opentelemetry.context.Context; -import io.opentelemetry.context.propagation.TextMapGetter; -import io.opentelemetry.context.propagation.TextMapPropagator; -import io.opentelemetry.context.propagation.TextMapSetter; -import lombok.extern.slf4j.Slf4j; +import static com.redhat.resilience.otel.internal.OTelContextUtil.extractContextFromTraceParent; +import static com.redhat.resilience.otel.internal.OTelContextUtil.extractTraceState; import java.io.BufferedReader; import java.io.IOException; @@ -26,8 +28,16 @@ import java.util.Map; import java.util.stream.Collectors; -import static com.redhat.resilience.otel.internal.OTelContextUtil.extractContextFromTraceParent; -import static com.redhat.resilience.otel.internal.OTelContextUtil.extractTraceState; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.TraceFlags; +import io.opentelemetry.api.trace.TraceState; +import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.propagation.TextMapGetter; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.context.propagation.TextMapSetter; +import lombok.extern.slf4j.Slf4j; /** * {@link TextMapPropagator} implementation (for propagating trace context) that consumes environment variables during @@ -40,12 +50,13 @@ * This also uses {@link W3CTraceContextPropagator} to inject trace state into downstream calls. *

* See also: - * Jenkins environment variables. + * Jenkins + * environment variables. */ @Slf4j public class EnvarExtractingPropagator - implements TextMapPropagator -{ + implements TextMapPropagator { private static final String ENVAR_TRACE_PARENT = "TRACEPARENT"; private static final String ENVAR_TRACE_STATE = "TRACESTATE"; @@ -56,16 +67,15 @@ public class EnvarExtractingPropagator private static final EnvarExtractingPropagator INSTANCE = new EnvarExtractingPropagator(); - private EnvarExtractingPropagator() - { + private EnvarExtractingPropagator() { } /** * Return the singleton instance + * * @return the singleton */ - public static EnvarExtractingPropagator getInstance() - { + public static EnvarExtractingPropagator getInstance() { return INSTANCE; } @@ -74,8 +84,7 @@ public static EnvarExtractingPropagator getInstance() * it delegates directly to {@link W3CTraceContextPropagator#fields()} */ @Override - public Collection fields() - { + public Collection fields() { return W3CTraceContextPropagator.getInstance().fields(); } @@ -89,9 +98,8 @@ public Collection fields() * @param The instance type */ @Override - public void inject( Context context, C c, TextMapSetter textMapSetter ) - { - W3CTraceContextPropagator.getInstance().inject( context, c, textMapSetter ); + public void inject(Context context, C c, TextMapSetter textMapSetter) { + W3CTraceContextPropagator.getInstance().inject(context, c, textMapSetter); } /** @@ -110,20 +118,17 @@ public void inject( Context context, C c, TextMapSetter textMapSetter ) * @return The updated trace context object */ @Override - public Context extract( Context context, C carrier, TextMapGetter getter ) - { - if ( context == null ) - { + public Context extract(Context context, C carrier, TextMapGetter getter) { + if (context == null) { context = Context.root(); } SpanContext spanContext = extractFromEnvars(); - if ( !spanContext.isValid() ) - { + if (!spanContext.isValid()) { return context; } - return context.with( Span.wrap( spanContext ) ); + return context.with(Span.wrap(spanContext)); } /** @@ -133,56 +138,51 @@ public Context extract( Context context, C carrier, TextMapGetter getter * @param The instance type; disregarded here * @return The {@link SpanContext} or {@link SpanContext#getInvalid()} one if no environment variables are found. */ - private static SpanContext extractFromEnvars() - { + private static SpanContext extractFromEnvars() { Map envMap = System.getenv(); SpanContext contextFromParent = null; - String traceParentValue = parseURL( envMap.get( ENVAR_TRACE_PARENT ) ); + String traceParentValue = parseURL(envMap.get(ENVAR_TRACE_PARENT)); log.info("Trace parent: {}", traceParentValue); - if ( traceParentValue != null ) - { - contextFromParent = extractContextFromTraceParent( traceParentValue ); + if (traceParentValue != null) { + contextFromParent = extractContextFromTraceParent(traceParentValue); } - if ( contextFromParent == null ) - { - String traceId = parseURL( envMap.get( ENVAR_TRACE_ID ) ); - String parentSpanId = parseURL( envMap.get( ENVAR_SPAN_ID ) ); + if (contextFromParent == null) { + String traceId = parseURL(envMap.get(ENVAR_TRACE_ID)); + String parentSpanId = parseURL(envMap.get(ENVAR_SPAN_ID)); log.debug("Trace ID: {}, Span ID: {}", traceId, parentSpanId); - if ( traceId != null && !traceId.isEmpty() && parentSpanId != null && !parentSpanId.isEmpty() ) - { - contextFromParent = SpanContext.createFromRemoteParent( traceId, parentSpanId, TraceFlags.getDefault(), - TraceState.getDefault() ); + if (traceId != null && !traceId.isEmpty() && parentSpanId != null && !parentSpanId.isEmpty()) { + contextFromParent = SpanContext.createFromRemoteParent( + traceId, + parentSpanId, + TraceFlags.getDefault(), + TraceState.getDefault()); } } - if ( contextFromParent == null ) - { + if (contextFromParent == null) { return SpanContext.getInvalid(); - } - else if ( !contextFromParent.isValid() ) - { + } else if (!contextFromParent.isValid()) { return contextFromParent; } - String traceStateValue = parseURL( envMap.get( ENVAR_TRACE_STATE ) ); + String traceStateValue = parseURL(envMap.get(ENVAR_TRACE_STATE)); log.debug("Trace state: {}", traceStateValue); - if ( traceStateValue == null || traceStateValue.isEmpty() ) - { + if (traceStateValue == null || traceStateValue.isEmpty()) { return contextFromParent; } - try - { - TraceState traceState = extractTraceState( traceStateValue ); - return SpanContext.createFromRemoteParent( contextFromParent.getTraceId(), contextFromParent.getSpanId(), - contextFromParent.getTraceFlags(), traceState ); - } - catch ( IllegalArgumentException e ) - { - log.debug( "Unparseable tracestate header. Returning span context without state." ); + try { + TraceState traceState = extractTraceState(traceStateValue); + return SpanContext.createFromRemoteParent( + contextFromParent.getTraceId(), + contextFromParent.getSpanId(), + contextFromParent.getTraceFlags(), + traceState); + } catch (IllegalArgumentException e) { + log.debug("Unparseable tracestate header. Returning span context without state."); return contextFromParent; } } @@ -194,23 +194,18 @@ else if ( !contextFromParent.isValid() ) * @param value the value to parse * @return either the original value or the new resolved value */ - public static String parseURL (String value) - { + public static String parseURL(String value) { String result = value; - if (result != null && (result.startsWith( "http" ) || result.startsWith( "file" ))) - { - try - { - URLConnection conn = new URL( result ).openConnection(); - try (BufferedReader reader = new BufferedReader( new InputStreamReader( conn.getInputStream(), StandardCharsets.UTF_8 ) )) - { - result = reader.lines().collect( Collectors.joining( System.lineSeparator() ) ); + if (result != null && (result.startsWith("http") || result.startsWith("file"))) { + try { + URLConnection conn = new URL(result).openConnection(); + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) { + result = reader.lines().collect(Collectors.joining(System.lineSeparator())); } - } - catch ( IOException e ) - { - throw new RuntimeException( e ); + } catch (IOException e) { + throw new RuntimeException(e); } } return result; diff --git a/src/main/java/com/redhat/resilience/otel/internal/OTelContextUtil.java b/src/main/java/com/redhat/resilience/otel/internal/OTelContextUtil.java index 7919aea..01f402d 100644 --- a/src/main/java/com/redhat/resilience/otel/internal/OTelContextUtil.java +++ b/src/main/java/com/redhat/resilience/otel/internal/OTelContextUtil.java @@ -1,10 +1,29 @@ /* - * Copyright Red Hat (derived from The OpenTelemetry Authors) - * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2022 Red Hat, Inc. + * + * 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. */ - package com.redhat.resilience.otel.internal; +import static io.opentelemetry.api.internal.Utils.checkArgument; + +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Pattern; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import io.opentelemetry.api.internal.OtelEncodingUtils; import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.api.trace.SpanId; @@ -13,14 +32,6 @@ import io.opentelemetry.api.trace.TraceState; import io.opentelemetry.api.trace.TraceStateBuilder; import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashSet; -import java.util.Set; -import java.util.regex.Pattern; - -import static io.opentelemetry.api.internal.Utils.checkArgument; /** * Rip off of the utility functions from {@link W3CTraceContextPropagator} (version 1.6.0), for parsing @@ -29,8 +40,7 @@ * * @see W3CTraceContextPropagator */ -public final class OTelContextUtil -{ +public final class OTelContextUtil { private static final int VERSION_SIZE = 2; private static final char TRACEPARENT_DELIMITER = '-'; private static final int TRACEPARENT_DELIMITER_SIZE = 1; @@ -38,16 +48,14 @@ public final class OTelContextUtil private static final int SPAN_ID_HEX_SIZE = SpanId.getLength(); private static final int TRACE_OPTION_HEX_SIZE = TraceFlags.getLength(); private static final int TRACE_ID_OFFSET = VERSION_SIZE + TRACEPARENT_DELIMITER_SIZE; - private static final int SPAN_ID_OFFSET = - TRACE_ID_OFFSET + TRACE_ID_HEX_SIZE + TRACEPARENT_DELIMITER_SIZE; - private static final int TRACE_OPTION_OFFSET = - SPAN_ID_OFFSET + SPAN_ID_HEX_SIZE + TRACEPARENT_DELIMITER_SIZE; + private static final int SPAN_ID_OFFSET = TRACE_ID_OFFSET + TRACE_ID_HEX_SIZE + TRACEPARENT_DELIMITER_SIZE; + private static final int TRACE_OPTION_OFFSET = SPAN_ID_OFFSET + SPAN_ID_HEX_SIZE + TRACEPARENT_DELIMITER_SIZE; private static final int TRACEPARENT_HEADER_SIZE = TRACE_OPTION_OFFSET + TRACE_OPTION_HEX_SIZE; private static final int TRACESTATE_MAX_MEMBERS = 32; private static final char TRACESTATE_KEY_VALUE_DELIMITER = '='; private static final char TRACESTATE_ENTRY_DELIMITER = ','; - private static final Pattern TRACESTATE_ENTRY_DELIMITER_SPLIT_PATTERN = - Pattern.compile("[ \t]*" + TRACESTATE_ENTRY_DELIMITER + "[ \t]*"); + private static final Pattern TRACESTATE_ENTRY_DELIMITER_SPLIT_PATTERN = Pattern + .compile("[ \t]*" + TRACESTATE_ENTRY_DELIMITER + "[ \t]*"); private static final Set VALID_VERSIONS; private static final String VERSION_00 = "00"; @@ -63,7 +71,8 @@ public final class OTelContextUtil } } - private OTelContextUtil(){} + private OTelContextUtil() { + } /** * Parse a W3C-compliant traceparent header value, and return a {@link SpanContext} with the details, or else @@ -73,48 +82,43 @@ private OTelContextUtil(){} * @param traceparent The traceparent 'header' to extract from...this doesn't have to come from HTTP headers! * @return The SpanContext parsed from the traceparent, or else {@link SpanContext#getInvalid()} */ - public static SpanContext extractContextFromTraceParent( String traceparent ) - { - final Logger logger = LoggerFactory.getLogger( OTelContextUtil.class ); + public static SpanContext extractContextFromTraceParent(String traceparent) { + final Logger logger = LoggerFactory.getLogger(OTelContextUtil.class); // TODO(bdrutu): Do we need to verify that version is hex and that // for the version the length is the expected one? - boolean isValid = ( traceparent.length() == TRACEPARENT_HEADER_SIZE || ( - traceparent.length() > TRACEPARENT_HEADER_SIZE - && traceparent.charAt( TRACEPARENT_HEADER_SIZE ) == TRACEPARENT_DELIMITER ) ) - && traceparent.charAt( TRACE_ID_OFFSET - 1 ) == TRACEPARENT_DELIMITER - && traceparent.charAt( SPAN_ID_OFFSET - 1 ) == TRACEPARENT_DELIMITER - && traceparent.charAt( TRACE_OPTION_OFFSET - 1 ) == TRACEPARENT_DELIMITER; - if ( !isValid ) - { - logger.debug( "Unparseable traceparent header. Returning INVALID span context." ); + boolean isValid = (traceparent.length() == TRACEPARENT_HEADER_SIZE + || (traceparent.length() > TRACEPARENT_HEADER_SIZE + && traceparent.charAt(TRACEPARENT_HEADER_SIZE) == TRACEPARENT_DELIMITER)) + && traceparent.charAt(TRACE_ID_OFFSET - 1) == TRACEPARENT_DELIMITER + && traceparent.charAt(SPAN_ID_OFFSET - 1) == TRACEPARENT_DELIMITER + && traceparent.charAt(TRACE_OPTION_OFFSET - 1) == TRACEPARENT_DELIMITER; + if (!isValid) { + logger.debug("Unparseable traceparent header. Returning INVALID span context."); return SpanContext.getInvalid(); } - String version = traceparent.substring( 0, 2 ); - if ( !VALID_VERSIONS.contains( version ) ) - { + String version = traceparent.substring(0, 2); + if (!VALID_VERSIONS.contains(version)) { return SpanContext.getInvalid(); } - if ( version.equals( VERSION_00 ) && traceparent.length() > TRACEPARENT_HEADER_SIZE ) - { + if (version.equals(VERSION_00) && traceparent.length() > TRACEPARENT_HEADER_SIZE) { return SpanContext.getInvalid(); } - String traceId = traceparent.substring( TRACE_ID_OFFSET, TRACE_ID_OFFSET + TraceId.getLength() ); - String spanId = traceparent.substring( SPAN_ID_OFFSET, SPAN_ID_OFFSET + SpanId.getLength() ); - char firstTraceFlagsChar = traceparent.charAt( TRACE_OPTION_OFFSET ); - char secondTraceFlagsChar = traceparent.charAt( TRACE_OPTION_OFFSET + 1 ); + String traceId = traceparent.substring(TRACE_ID_OFFSET, TRACE_ID_OFFSET + TraceId.getLength()); + String spanId = traceparent.substring(SPAN_ID_OFFSET, SPAN_ID_OFFSET + SpanId.getLength()); + char firstTraceFlagsChar = traceparent.charAt(TRACE_OPTION_OFFSET); + char secondTraceFlagsChar = traceparent.charAt(TRACE_OPTION_OFFSET + 1); - if ( !OtelEncodingUtils.isValidBase16Character( firstTraceFlagsChar ) - || !OtelEncodingUtils.isValidBase16Character( secondTraceFlagsChar ) ) - { + if (!OtelEncodingUtils.isValidBase16Character(firstTraceFlagsChar) + || !OtelEncodingUtils.isValidBase16Character(secondTraceFlagsChar)) { return SpanContext.getInvalid(); } TraceFlags traceFlags = TraceFlags.fromByte( - OtelEncodingUtils.byteFromBase16( firstTraceFlagsChar, secondTraceFlagsChar ) ); - return SpanContext.createFromRemoteParent( traceId, spanId, traceFlags, TraceState.getDefault() ); + OtelEncodingUtils.byteFromBase16(firstTraceFlagsChar, secondTraceFlagsChar)); + return SpanContext.createFromRemoteParent(traceId, spanId, traceFlags, TraceState.getDefault()); } /** @@ -125,23 +129,20 @@ public static SpanContext extractContextFromTraceParent( String traceparent ) * @param traceStateHeader The string containing tracestate information * @return The {@link TraceState} parsed, or else {@link TraceState#getDefault()} */ - public static TraceState extractTraceState( String traceStateHeader ) - { + public static TraceState extractTraceState(String traceStateHeader) { TraceStateBuilder traceStateBuilder = TraceState.builder(); - String[] listMembers = TRACESTATE_ENTRY_DELIMITER_SPLIT_PATTERN.split( traceStateHeader ); - checkArgument( listMembers.length <= TRACESTATE_MAX_MEMBERS, "TraceState has too many elements." ); + String[] listMembers = TRACESTATE_ENTRY_DELIMITER_SPLIT_PATTERN.split(traceStateHeader); + checkArgument(listMembers.length <= TRACESTATE_MAX_MEMBERS, "TraceState has too many elements."); // Iterate in reverse order because when call builder set the elements is added in the // front of the list. - for ( int i = listMembers.length - 1; i >= 0; i-- ) - { + for (int i = listMembers.length - 1; i >= 0; i--) { String listMember = listMembers[i]; - int index = listMember.indexOf( TRACESTATE_KEY_VALUE_DELIMITER ); - checkArgument( index != -1, "Invalid TraceState list-member format." ); - traceStateBuilder.put( listMember.substring( 0, index ), listMember.substring( index + 1 ) ); + int index = listMember.indexOf(TRACESTATE_KEY_VALUE_DELIMITER); + checkArgument(index != -1, "Invalid TraceState list-member format."); + traceStateBuilder.put(listMember.substring(0, index), listMember.substring(index + 1)); } TraceState traceState = traceStateBuilder.build(); - if ( traceState.size() != listMembers.length ) - { + if (traceState.size() != listMembers.length) { // Validation failure, drop the tracestate return TraceState.getDefault(); } diff --git a/src/test/java/com/redhat/resilience/otel/OTelCLiHelperTest.java b/src/test/java/com/redhat/resilience/otel/OTelCLiHelperTest.java index d539f27..b504ba2 100644 --- a/src/test/java/com/redhat/resilience/otel/OTelCLiHelperTest.java +++ b/src/test/java/com/redhat/resilience/otel/OTelCLiHelperTest.java @@ -1,80 +1,87 @@ +/* + * Copyright (C) 2022 Red Hat, Inc. + * + * 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. + */ package com.redhat.resilience.otel; -import com.redhat.resilience.otel.fixture.TestSpanExporter; -import com.redhat.resilience.otel.internal.EnvarExtractingPropagator; -import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; -public class OTelCLiHelperTest -{ - static - { - System.setProperty( org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "TRACE" ); +import com.redhat.resilience.otel.fixture.TestSpanExporter; +import com.redhat.resilience.otel.internal.EnvarExtractingPropagator; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; + +public class OTelCLiHelperTest { + static { + System.setProperty(org.slf4j.simple.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "TRACE"); } @AfterEach - public void otelTeardown() - { + public void otelTeardown() { OTelCLIHelper.stopOTel(); GlobalOpenTelemetry.resetForTest(); } @Test - public void verifyDoubleStart() - { - OTelCLIHelper.startOTel( "cli-test", SimpleSpanProcessor.create( new TestSpanExporter() ) ); + public void verifyDoubleStart() { + OTelCLIHelper.startOTel("cli-test", SimpleSpanProcessor.create(new TestSpanExporter())); IllegalStateException thrown = assertThrows( - IllegalStateException.class, - () -> OTelCLIHelper.startOTel( "traceparent-envar-cli-test", - SimpleSpanProcessor.create( new TestSpanExporter() ) ) - ); + IllegalStateException.class, + () -> OTelCLIHelper.startOTel( + "traceparent-envar-cli-test", + SimpleSpanProcessor.create(new TestSpanExporter()))); assertTrue(thrown.getMessage().contains("startOTel has already been called")); } @Test - public void verifyDoubleStop() - { - OTelCLIHelper.startOTel( "cli-test", SimpleSpanProcessor.create( new TestSpanExporter() ) ); + public void verifyDoubleStop() { + OTelCLIHelper.startOTel("cli-test", SimpleSpanProcessor.create(new TestSpanExporter())); OTelCLIHelper.stopOTel(); OTelCLIHelper.stopOTel(); } @Test public void testParseUrlWithFile(@TempDir Path tempDir) - throws IOException - { + throws IOException { Path numbers = tempDir.resolve("testFile.txt"); - Files.write( numbers, Collections.singletonList( "content" ) ); - assertEquals( "content", EnvarExtractingPropagator.parseURL( numbers.toFile().toURI().toString() ) ); + Files.write(numbers, Collections.singletonList("content")); + assertEquals("content", EnvarExtractingPropagator.parseURL(numbers.toFile().toURI().toString())); } - @Test - public void testParseUrlWithURL() - { - assertFalse( EnvarExtractingPropagator.parseURL( "http://www.google.com" ).isEmpty() ); + public void testParseUrlWithURL() { + assertFalse(EnvarExtractingPropagator.parseURL("http://www.google.com").isEmpty()); } - @Test - public void testParseUrlWithTraceID() - { + public void testParseUrlWithTraceID() { String trace = "0af7651916cd43dd8448eb211c80319c"; - assertEquals( EnvarExtractingPropagator.parseURL( trace ), trace ); + assertEquals(EnvarExtractingPropagator.parseURL(trace), trace); } } diff --git a/src/test/java/com/redhat/resilience/otel/TraceparentEnvarCLITest.java b/src/test/java/com/redhat/resilience/otel/TraceparentEnvarCLITest.java index e555c51..ecbd755 100644 --- a/src/test/java/com/redhat/resilience/otel/TraceparentEnvarCLITest.java +++ b/src/test/java/com/redhat/resilience/otel/TraceparentEnvarCLITest.java @@ -1,113 +1,129 @@ +/* + * Copyright (C) 2022 Red Hat, Inc. + * + * 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. + */ package com.redhat.resilience.otel; -import com.redhat.resilience.otel.fixture.TestSpanExporter; -import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.api.trace.TraceState; -import io.opentelemetry.sdk.trace.data.SpanData; -import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.util.List; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; -import java.util.List; +import com.redhat.resilience.otel.fixture.TestSpanExporter; -import static com.github.stefanbirkner.systemlambda.SystemLambda.withEnvironmentVariable; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.TraceState; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; -public class TraceparentEnvarCLITest -{ +public class TraceparentEnvarCLITest { private static final String TRACE_ID = "0af7651916cd43dd8448eb211c80319c"; private static final String SPAN_ID = "b9c7c989f97918e1"; + private EnvironmentVariables environmentVariables = new EnvironmentVariables(); + @BeforeEach - public void otelSetup() throws Exception - { + public void otelSetup() throws Exception { TestSpanExporter.clear(); // normally you'd probably call OtelCLIHelper.defaultSpanProcessor() and OtelCLIHelper.defaultSpanExporter() // We have to use a SimpleSpanProcessor and the TestSpanExporter to ensure the span data is recorded immediately // See https://www.w3.org/TR/trace-context/#relationship-between-the-headers - withEnvironmentVariable("TRACEPARENT", "00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01") - .and("TRACESTATE", "rojo=00f067aa0ba902b7,congo=t61rcWkgMzE") - .execute(() -> OTelCLIHelper.startOTel( "traceparent-envar-cli-test", - SimpleSpanProcessor.create( new TestSpanExporter() ) )); + environmentVariables.set("TRACEPARENT", "00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01") + .and("TRACESTATE", "rojo=00f067aa0ba902b7,congo=t61rcWkgMzE") + .execute( + () -> OTelCLIHelper.startOTel( + "traceparent-envar-cli-test", + SimpleSpanProcessor.create(new TestSpanExporter()))); } @AfterEach - public void otelTeardown() - { + public void otelTeardown() { OTelCLIHelper.stopOTel(); GlobalOpenTelemetry.resetForTest(); } @Test - public void createExecutionChildSpan( TestInfo testInfo ) - { - System.out.println( "\n\n" + testInfo.getDisplayName() ); + public void createExecutionChildSpan(TestInfo testInfo) { + System.out.println("\n\n" + testInfo.getDisplayName()); Span current = Span.current(); - assertNotNull( current ); + assertNotNull(current); - System.out.println( "Current span: " + current ); + System.out.println("Current span: " + current); - assertNotNull( current, "No current span!" ); + assertNotNull(current, "No current span!"); // Sample data taken from W3C trace context spec - assertEquals( TRACE_ID, current.getSpanContext().getTraceId(), "Wrong trace ID" ); + assertEquals(TRACE_ID, current.getSpanContext().getTraceId(), "Wrong trace ID"); // NOTE: This is the PARENT span, not the root span created in this execution! // assertEquals( "b9c7c989f97918e1", current.getSpanContext().getSpanId(), "Wrong span ID" ); TraceState traceState = current.getSpanContext().getTraceState(); - assertEquals( 2, traceState.size(), "Wrong trace state" ); - assertEquals( "t61rcWkgMzE", traceState.get( "congo" ), "Cannot find congo trace state" ); - assertEquals( "00f067aa0ba902b7", traceState.get( "rojo" ), "Cannot find rojo trace state" ); + assertEquals(2, traceState.size(), "Wrong trace state"); + assertEquals("t61rcWkgMzE", traceState.get("congo"), "Cannot find congo trace state"); + assertEquals("00f067aa0ba902b7", traceState.get("rojo"), "Cannot find rojo trace state"); // regular execution ends... current.end(); List spanData = TestSpanExporter.getSpans(); - assertEquals( 1, spanData.size(), "Incorrect span count!" ); - assertEquals( TRACE_ID, spanData.get( 0 ).getTraceId(), "Incorrect trace ID in span output" ); - assertEquals( SPAN_ID, spanData.get( 0 ).getParentSpanId(), "Incorrect parent span ID in span output" ); + assertEquals(1, spanData.size(), "Incorrect span count!"); + assertEquals(TRACE_ID, spanData.get(0).getTraceId(), "Incorrect trace ID in span output"); + assertEquals(SPAN_ID, spanData.get(0).getParentSpanId(), "Incorrect parent span ID in span output"); } @Test - public void createExecutionChildSpanWithStop( TestInfo testInfo ) - { - System.out.println( "\n\n" + testInfo.getDisplayName() ); + public void createExecutionChildSpanWithStop(TestInfo testInfo) { + System.out.println("\n\n" + testInfo.getDisplayName()); Span current = Span.current(); - assertNotNull( current ); + assertNotNull(current); - System.out.println( "Current span: " + current ); + System.out.println("Current span: " + current); - assertNotNull( current, "No current span!" ); + assertNotNull(current, "No current span!"); // Sample data taken from W3C trace context spec - assertEquals( TRACE_ID, current.getSpanContext().getTraceId(), "Wrong trace ID" ); + assertEquals(TRACE_ID, current.getSpanContext().getTraceId(), "Wrong trace ID"); // NOTE: This is the PARENT span, not the root span created in this execution! // assertEquals( "b9c7c989f97918e1", current.getSpanContext().getSpanId(), "Wrong span ID" ); TraceState traceState = current.getSpanContext().getTraceState(); - assertEquals( 2, traceState.size(), "Wrong trace state" ); - assertEquals( "t61rcWkgMzE", traceState.get( "congo" ), "Cannot find congo trace state" ); - assertEquals( "00f067aa0ba902b7", traceState.get( "rojo" ), "Cannot find rojo trace state" ); + assertEquals(2, traceState.size(), "Wrong trace state"); + assertEquals("t61rcWkgMzE", traceState.get("congo"), "Cannot find congo trace state"); + assertEquals("00f067aa0ba902b7", traceState.get("rojo"), "Cannot find rojo trace state"); // regular execution ends... OTelCLIHelper.stopOTel(); List spanData = TestSpanExporter.getSpans(); - assertEquals( 1, spanData.size(), "Incorrect span count!" ); - assertEquals( TRACE_ID, spanData.get( 0 ).getTraceId(), "Incorrect trace ID in span output" ); - assertEquals( SPAN_ID, spanData.get( 0 ).getParentSpanId(), "Incorrect parent span ID in span output" ); + assertEquals(1, spanData.size(), "Incorrect span count!"); + assertEquals(TRACE_ID, spanData.get(0).getTraceId(), "Incorrect trace ID in span output"); + assertEquals(SPAN_ID, spanData.get(0).getParentSpanId(), "Incorrect parent span ID in span output"); } } diff --git a/src/test/java/com/redhat/resilience/otel/TraceparentEnvarQuarkusTest.java b/src/test/java/com/redhat/resilience/otel/TraceparentEnvarQuarkusTest.java index e7f7e98..0c6b6a7 100644 --- a/src/test/java/com/redhat/resilience/otel/TraceparentEnvarQuarkusTest.java +++ b/src/test/java/com/redhat/resilience/otel/TraceparentEnvarQuarkusTest.java @@ -1,41 +1,57 @@ +/* + * Copyright (C) 2022 Red Hat, Inc. + * + * 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. + */ package com.redhat.resilience.otel; +import static io.restassured.RestAssured.given; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + import com.redhat.resilience.otel.fixture.CustomTestProfile; + import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.junit.TestProfile; import io.restassured.response.Response; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; - -import static com.github.stefanbirkner.systemlambda.SystemLambda.withEnvironmentVariable; -import static io.restassured.RestAssured.given; -import static org.junit.jupiter.api.Assertions.assertEquals; +import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; @QuarkusTest -@TestProfile( CustomTestProfile.class ) -public class TraceparentEnvarQuarkusTest -{ +@TestProfile(CustomTestProfile.class) +public class TraceparentEnvarQuarkusTest { private static final String TRACE_ID = "0af7651916cd43dd8448eb211c80319c"; private static final String SPAN_ID = "b9c7c989f97918e1"; + private EnvironmentVariables environmentVariables = new EnvironmentVariables(); + @Test - public void test( TestInfo testInfo ) throws Exception - { - System.out.println( testInfo.getDisplayName() ); - - withEnvironmentVariable("TRACEPARENT", "00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01") - .and("TRACESTATE", "rojo=00f067aa0ba902b7,congo=t61rcWkgMzE") - .execute(() -> { - given().when().get( "/test" ).then().statusCode( 200 ); - Response response = given().when().get( "/spans" ).thenReturn(); - String[] traceLines = response.getBody().print().split( "\n" ); - System.out.println( traceLines.length + " spans" ); - - for ( String line : traceLines ) - { - String[] parts = line.split( "," ); - assertEquals( TRACE_ID, parts[0], "Incorrect trace ID!" ); - } - }); + public void test(TestInfo testInfo) throws Exception { + System.out.println(testInfo.getDisplayName()); + + environmentVariables.set("TRACEPARENT", "00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01") + .and("TRACESTATE", "rojo=00f067aa0ba902b7,congo=t61rcWkgMzE") + .execute(() -> { + given().when().get("/test").then().statusCode(200); + Response response = given().when().get("/spans").thenReturn(); + String[] traceLines = response.getBody().print().split("\n"); + System.out.println(traceLines.length + " spans"); + + for (String line : traceLines) { + String[] parts = line.split(","); + assertEquals(TRACE_ID, parts[0], "Incorrect trace ID!"); + } + }); } } diff --git a/src/test/java/com/redhat/resilience/otel/fixture/CustomTestProfile.java b/src/test/java/com/redhat/resilience/otel/fixture/CustomTestProfile.java index 9157ccd..6fe4ac9 100644 --- a/src/test/java/com/redhat/resilience/otel/fixture/CustomTestProfile.java +++ b/src/test/java/com/redhat/resilience/otel/fixture/CustomTestProfile.java @@ -1,15 +1,28 @@ +/* + * Copyright (C) 2022 Red Hat, Inc. + * + * 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. + */ package com.redhat.resilience.otel.fixture; -import io.quarkus.test.junit.QuarkusTestProfile; - import java.util.HashMap; import java.util.Map; -public class CustomTestProfile implements QuarkusTestProfile -{ +import io.quarkus.test.junit.QuarkusTestProfile; + +public class CustomTestProfile implements QuarkusTestProfile { @Override - public Map getConfigOverrides() - { + public Map getConfigOverrides() { Map overrides = new HashMap<>(); overrides.put("quarkus.opentelemetry.enabled", "true"); overrides.put("quarkus.opentelemetry.propagators", "envar"); diff --git a/src/test/java/com/redhat/resilience/otel/fixture/QuarkusSpansResource.java b/src/test/java/com/redhat/resilience/otel/fixture/QuarkusSpansResource.java index 2735aad..1a1d8c8 100644 --- a/src/test/java/com/redhat/resilience/otel/fixture/QuarkusSpansResource.java +++ b/src/test/java/com/redhat/resilience/otel/fixture/QuarkusSpansResource.java @@ -1,45 +1,57 @@ +/* + * Copyright (C) 2022 Red Hat, Inc. + * + * 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. + */ package com.redhat.resilience.otel.fixture; -import io.opentelemetry.sdk.trace.data.SpanData; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import java.util.List; import javax.ws.rs.GET; import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import java.util.List; -@Path( "/spans" ) -public class QuarkusSpansResource -{ - private final Logger logger = LoggerFactory.getLogger( getClass() ); +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.opentelemetry.sdk.trace.data.SpanData; + +@Path("/spans") +public class QuarkusSpansResource { + private final Logger logger = LoggerFactory.getLogger(getClass()); @GET - public String get() - { + public String get() { StringBuilder sb = new StringBuilder(); List spans = TestSpanExporter.getSpans(); - logger.info( "Got {} spans:\n{}", spans.size(), spans ); + logger.info("Got {} spans:\n{}", spans.size(), spans); - spans.forEach( spanData -> { - if ( sb.length() > 0) - { - sb.append( "\n" ); + spans.forEach(spanData -> { + if (sb.length() > 0) { + sb.append("\n"); } - sb.append( spanData.getTraceId() ) - .append( ',' ) - .append( spanData.getParentSpanId() ) - .append( ',' ) - .append( spanData.getSpanId() ) - .append( ',' ) - .append( spanData.getName() ) - .append( ',' ) - .append( spanData.getKind().name() ); - } ); - -// logger.info( "In QuarkusSpansResource, returning spans string:\n {}", sb ); + sb.append(spanData.getTraceId()) + .append(',') + .append(spanData.getParentSpanId()) + .append(',') + .append(spanData.getSpanId()) + .append(',') + .append(spanData.getName()) + .append(',') + .append(spanData.getKind().name()); + }); + + // logger.info( "In QuarkusSpansResource, returning spans string:\n {}", sb ); return sb.toString(); } } diff --git a/src/test/java/com/redhat/resilience/otel/fixture/QuarkusTestResource.java b/src/test/java/com/redhat/resilience/otel/fixture/QuarkusTestResource.java index 77e49bc..3342cd6 100644 --- a/src/test/java/com/redhat/resilience/otel/fixture/QuarkusTestResource.java +++ b/src/test/java/com/redhat/resilience/otel/fixture/QuarkusTestResource.java @@ -1,37 +1,48 @@ +/* + * Copyright (C) 2022 Red Hat, Inc. + * + * 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. + */ package com.redhat.resilience.otel.fixture; -import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.sdk.trace.ReadableSpan; +import java.util.Arrays; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.core.Response; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.sdk.trace.ReadableSpan; @Path("/test") -public class QuarkusTestResource -{ +public class QuarkusTestResource { @GET - public Response test() - { + public Response test() { Span span = Span.current(); Span client = GlobalOpenTelemetry.get() - .getTracer( "test-client" ) - .spanBuilder( "get-test-resource" ) - .setSpanKind( SpanKind.CLIENT ) - .setAttribute( "service.name", "sidecar" ) - .startSpan(); - + .getTracer("test-client") + .spanBuilder("get-test-resource") + .setSpanKind(SpanKind.CLIENT) + .setAttribute("service.name", "sidecar") + .startSpan(); client.end(); TestSpanExporter.record( - Arrays.asList( ( (ReadableSpan) span ).toSpanData(), ( (ReadableSpan) client ).toSpanData() ) ); + Arrays.asList(((ReadableSpan) span).toSpanData(), ((ReadableSpan) client).toSpanData())); return Response.ok().build(); } diff --git a/src/test/java/com/redhat/resilience/otel/fixture/TestSpanExporter.java b/src/test/java/com/redhat/resilience/otel/fixture/TestSpanExporter.java index c6e1c8b..4e6a6f0 100644 --- a/src/test/java/com/redhat/resilience/otel/fixture/TestSpanExporter.java +++ b/src/test/java/com/redhat/resilience/otel/fixture/TestSpanExporter.java @@ -1,53 +1,61 @@ +/* + * Copyright (C) 2022 Red Hat, Inc. + * + * 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. + */ package com.redhat.resilience.otel.fixture; -import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.trace.data.SpanData; -import io.opentelemetry.sdk.trace.export.SpanExporter; - import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.export.SpanExporter; + /** * Just hold all the span data for later inspection at the end of a test. */ -public class TestSpanExporter implements SpanExporter -{ +public class TestSpanExporter implements SpanExporter { private static final List SPANS = new ArrayList<>(); @Override - public CompletableResultCode export( Collection spans ) - { - record( spans ); + public CompletableResultCode export(Collection spans) { + record(spans); return CompletableResultCode.ofSuccess(); } @Override - public CompletableResultCode flush() - { + public CompletableResultCode flush() { return CompletableResultCode.ofSuccess(); } @Override - public CompletableResultCode shutdown() - { + public CompletableResultCode shutdown() { return flush(); } - public static void record( Collection spans ) - { - SPANS.addAll( spans ); + public static void record(Collection spans) { + SPANS.addAll(spans); } - public static void clear() - { + public static void clear() { SPANS.clear(); } - public static List getSpans() - { - return Collections.unmodifiableList( SPANS ); + public static List getSpans() { + return Collections.unmodifiableList(SPANS); } }