From 1163c9754c1c19f6c296d3e15426ef91880e89ad Mon Sep 17 00:00:00 2001 From: yasmin-aumeeruddy Date: Tue, 18 Jul 2023 09:05:59 +0100 Subject: [PATCH] Add SPI tests to TCK (#104) --- .../tracing/tck/rest/PropagatorSpiTest.java | 155 ++++++++++++++ .../tracing/tck/rest/TestPropagator.java | 189 ++++++++++++++++++ .../tck/rest/TestPropagatorProvider.java | 43 ++++ .../tracing/tck/spi/CustomizerSpiTest.java | 83 ++++++++ .../tracing/tck/spi/ExporterSpiTest.java | 77 +++++++ .../tracing/tck/spi/ResourceSpiTest.java | 83 ++++++++ .../tracing/tck/spi/SamplerSpiTest.java | 77 +++++++ .../tracing/tck/spi/TestCustomizer.java | 84 ++++++++ .../tracing/tck/spi/TestResourceProvider.java | 43 ++++ .../tracing/tck/spi/TestSampler.java | 58 ++++++ .../tracing/tck/spi/TestSamplerProvider.java | 43 ++++ 11 files changed, 935 insertions(+) create mode 100644 tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/rest/PropagatorSpiTest.java create mode 100644 tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/rest/TestPropagator.java create mode 100644 tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/rest/TestPropagatorProvider.java create mode 100644 tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/CustomizerSpiTest.java create mode 100644 tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/ExporterSpiTest.java create mode 100644 tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/ResourceSpiTest.java create mode 100644 tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/SamplerSpiTest.java create mode 100644 tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/TestCustomizer.java create mode 100644 tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/TestResourceProvider.java create mode 100644 tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/TestSampler.java create mode 100644 tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/TestSamplerProvider.java diff --git a/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/rest/PropagatorSpiTest.java b/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/rest/PropagatorSpiTest.java new file mode 100644 index 0000000..cb10aa4 --- /dev/null +++ b/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/rest/PropagatorSpiTest.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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 org.eclipse.microprofile.telemetry.tracing.tck.rest; + +import static java.net.HttpURLConnection.HTTP_OK; +import static org.eclipse.microprofile.telemetry.tracing.tck.rest.PropagationHelper.SpanResourceClient; + +import java.net.URL; + +import org.eclipse.microprofile.telemetry.tracing.tck.TestLibraries; +import org.eclipse.microprofile.telemetry.tracing.tck.exporter.InMemorySpanExporter; +import org.eclipse.microprofile.telemetry.tracing.tck.exporter.InMemorySpanExporterProvider; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.jboss.arquillian.testng.Arquillian; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import io.opentelemetry.api.baggage.Baggage; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.context.Scope; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigurablePropagatorProvider; +import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider; +import io.opentelemetry.sdk.trace.data.SpanData; +import jakarta.inject.Inject; +import jakarta.ws.rs.ApplicationPath; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.Response; + +public class PropagatorSpiTest extends Arquillian { + public static final String TEST_VALUE = "test-value"; + + public static final String TEST_KEY = "test-key"; + + @Deployment + public static WebArchive createDeployment() { + return ShrinkWrap.create(WebArchive.class) + .addClasses(TestPropagator.class, TestPropagatorProvider.class, InMemorySpanExporter.class, + InMemorySpanExporterProvider.class, PropagationHelper.class, + SpanResourceClient.class) + .addAsServiceProvider(ConfigurableSpanExporterProvider.class, InMemorySpanExporterProvider.class) + .addAsServiceProvider(ConfigurablePropagatorProvider.class, TestPropagatorProvider.class) + .addAsLibrary(TestLibraries.AWAITILITY_LIB) + .addAsResource( + new StringAsset("otel.sdk.disabled=false\notel.propagators=" + TestPropagatorProvider.NAME + + "\notel.traces.exporter=in-memory"), + "META-INF/microprofile-config.properties") + .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); + + } + + SpanResourceClient client; + + @ArquillianResource + URL url; + + @Inject + InMemorySpanExporter exporter; + + @Inject + Baggage baggage; + + @BeforeMethod + void setUp() { + // Only want to run on server + if (exporter != null) { + exporter.reset(); + } + } + + @Test + void testSPIPropagator() { + try (Scope s = baggage.toBuilder().put(TEST_KEY, TEST_VALUE).build().makeCurrent()) { + WebTarget target = ClientBuilder.newClient().target(url.toString()).path("baggage"); + Response response = target.request().get(); + Assert.assertEquals(response.getStatus(), HTTP_OK); + } + + exporter.assertSpanCount(2); + + SpanData server = exporter.getFirst(SpanKind.SERVER); + Assert.assertEquals(TEST_VALUE, server.getAttributes().get(AttributeKey.stringKey(TEST_KEY))); + + SpanData client = exporter.getFirst(SpanKind.CLIENT); + // Check that trace context propagation worked by checking that the parent was set correctly + Assert.assertEquals(server.getParentSpanId(), client.getSpanId());; + } + + @Path("/baggage") + public static class BaggageResource { + @Inject + Baggage baggage; + + @Inject + private Span span; + + @GET + public Response get(@Context HttpHeaders headers) { + try { + // Check the TestPropagator headers were used + Assert.assertNotNull(headers.getHeaderString(TestPropagator.TRACE_KEY)); + Assert.assertNotNull(headers.getHeaderString(TestPropagator.BAGGAGE_KEY)); + + // Test that the default W3C headers were not used + Assert.assertNull(headers.getHeaderString("traceparent")); + Assert.assertNull(headers.getHeaderString("tracestate")); + Assert.assertNull(headers.getHeaderString("baggage")); + + // Copy TEST_KEY from baggage into a span attribute + span.setAttribute(TEST_KEY, baggage.getEntryValue(TEST_KEY)); + return Response.ok().build(); + } catch (Throwable e) { + // An error here won't get reported back fully, so output it to the log as well + System.err.println("Baggage Resource Exception:"); + e.printStackTrace(); + throw e; + } + } + } + + @ApplicationPath("/") + public static class RestApplication extends Application { + + } +} diff --git a/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/rest/TestPropagator.java b/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/rest/TestPropagator.java new file mode 100644 index 0000000..9c70931 --- /dev/null +++ b/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/rest/TestPropagator.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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 org.eclipse.microprofile.telemetry.tracing.tck.rest; + +import static java.util.Collections.unmodifiableList; + +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map.Entry; + +import io.opentelemetry.api.baggage.Baggage; +import io.opentelemetry.api.baggage.BaggageBuilder; +import io.opentelemetry.api.baggage.BaggageEntry; +import io.opentelemetry.api.baggage.BaggageEntryMetadata; +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.TraceStateBuilder; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.propagation.TextMapGetter; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.context.propagation.TextMapSetter; + +/** + * A basic propagator for span context and baggage + *

+ * Span information is passed in the TEST-SPAN key with format + * {@code traceId;spanId;flags;statekey1=value,statekey2=value} + *

+ * Baggage information is passed in the TEST-BAGGAGE key with format + * {@code key,value,metadata;key2,value,metadata;key3,value,metadata;...} + *

+ * All individual values are urlencoded to make parsing easy (don't have to worry about values containing separator + * characters) + */ +public class TestPropagator implements TextMapPropagator { + + public static final String BAGGAGE_KEY = "TEST-BAGGAGE"; + public static final String TRACE_KEY = "TEST-SPAN"; + + private static final List FIELDS = unmodifiableList(Arrays.asList(BAGGAGE_KEY, TRACE_KEY)); + + /** {@inheritDoc} */ + @Override + public Collection fields() { + return FIELDS; + } + + /** {@inheritDoc} */ + @Override + public Context extract(Context context, C carrier, TextMapGetter getter) { + // extract data from carrier using getter and put it into context + + String baggageString = getter.get(carrier, BAGGAGE_KEY); + if (baggageString != null && !baggageString.isEmpty()) { + Baggage baggage = deserializeBaggage(baggageString); + context = context.with(baggage); + } + + String traceString = getter.get(carrier, TRACE_KEY); + if (traceString != null && !traceString.isEmpty()) { + Span span = deserializeSpan(traceString); + context = context.with(span); + } + + return context; + } + + /** {@inheritDoc} */ + @Override + public void inject(Context context, C carrier, TextMapSetter setter) { + // take data from context and inject it into carrier using setter + Baggage baggage = Baggage.fromContextOrNull(context); + if (baggage != null && !baggage.isEmpty()) { + setter.set(carrier, BAGGAGE_KEY, serializeBaggage(baggage)); + } + + Span span = Span.fromContextOrNull(context); + if (span != null && span.getSpanContext().isValid()) { + setter.set(carrier, TRACE_KEY, serializeSpan(span.getSpanContext())); + } + } + + private String serializeBaggage(Baggage baggage) { + StringBuffer baggageString = new StringBuffer(); + boolean first = true; + for (Entry entry : baggage.asMap().entrySet()) { + if (!first) { + baggageString.append(';'); + first = false; + } + baggageString.append(encode(entry.getKey())) + .append(',') + .append(encode(entry.getValue().getValue())) + .append(',') + .append(encode(entry.getValue().getMetadata().getValue())); + } + return baggageString.toString(); + } + + private Baggage deserializeBaggage(String string) { + BaggageBuilder builder = Baggage.empty().toBuilder(); + for (String entry : string.split(";")) { + if (entry.isEmpty()) { + continue; + } + String[] parts = entry.split(",", -1); // -1 -> keep trailing empty strings + builder.put(decode(parts[0]), + decode(parts[1]), + BaggageEntryMetadata.create(decode(parts[2]))); + } + return builder.build(); + } + + private String serializeSpan(SpanContext span) { + StringBuffer spanString = new StringBuffer(); + spanString.append(span.getTraceId()) + .append(';') + .append(span.getSpanId()) + .append(';') + .append(span.getTraceFlags().asHex()) + .append(';'); + boolean first = true; + for (Entry entry : span.getTraceState().asMap().entrySet()) { + if (first) { + spanString.append(','); + first = false; + } + + spanString.append(encode(entry.getKey())) + .append('=') + .append(encode(entry.getValue())); + } + + return spanString.toString(); + } + + private Span deserializeSpan(String string) { + String[] parts = string.split(";", -1); + String traceId = decode(parts[0]); + String spanId = decode(parts[1]); + TraceFlags flags = TraceFlags.fromHex(decode(parts[2]), 0); + + TraceStateBuilder stateBuilder = TraceState.builder(); + for (String entry : parts[3].split(",")) { + if (entry.isEmpty()) { + continue; + } + String[] entryParts = entry.split("="); + stateBuilder.put(decode(entryParts[0]), + decode(entryParts[1])); + } + + SpanContext spanContext = SpanContext.create(traceId, spanId, flags, stateBuilder.build()); + return Span.wrap(spanContext); + } + + private String encode(String s) { + return URLEncoder.encode(s, StandardCharsets.UTF_8); + } + + private String decode(String s) { + return URLDecoder.decode(s, StandardCharsets.UTF_8); + } + +} diff --git a/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/rest/TestPropagatorProvider.java b/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/rest/TestPropagatorProvider.java new file mode 100644 index 0000000..64a201c --- /dev/null +++ b/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/rest/TestPropagatorProvider.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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 org.eclipse.microprofile.telemetry.tracing.tck.rest; + +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigurablePropagatorProvider; + +public class TestPropagatorProvider implements ConfigurablePropagatorProvider { + + public static final String NAME = "test-propagator"; + + /** {@inheritDoc} */ + @Override + public String getName() { + return NAME; + } + + /** {@inheritDoc} */ + @Override + public TextMapPropagator getPropagator(ConfigProperties arg0) { + return new TestPropagator(); + } + +} diff --git a/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/CustomizerSpiTest.java b/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/CustomizerSpiTest.java new file mode 100644 index 0000000..0e5bcc8 --- /dev/null +++ b/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/CustomizerSpiTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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 org.eclipse.microprofile.telemetry.tracing.tck.spi; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.List; + +import org.eclipse.microprofile.telemetry.tracing.tck.TestLibraries; +import org.eclipse.microprofile.telemetry.tracing.tck.exporter.InMemorySpanExporter; +import org.eclipse.microprofile.telemetry.tracing.tck.exporter.InMemorySpanExporterProvider; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.testng.Arquillian; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.testng.annotations.Test; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; +import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider; +import io.opentelemetry.sdk.trace.data.SpanData; +import jakarta.inject.Inject; + +public class CustomizerSpiTest extends Arquillian { + @Deployment + public static WebArchive createDeployment() { + return ShrinkWrap.create(WebArchive.class) + .addClasses(InMemorySpanExporter.class, InMemorySpanExporterProvider.class, TestCustomizer.class) + .addAsServiceProvider(ConfigurableSpanExporterProvider.class, InMemorySpanExporterProvider.class) + .addAsServiceProvider(AutoConfigurationCustomizerProvider.class, TestCustomizer.class) + .addAsLibrary(TestLibraries.AWAITILITY_LIB) + .addAsResource(new StringAsset("otel.sdk.disabled=false\notel.traces.exporter=in-memory"), + "META-INF/microprofile-config.properties") + .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); + + } + + @Inject + private Tracer tracer; + + @Inject + private InMemorySpanExporter exporter; + + @Test + public void testCustomizer() { + Span span = tracer.spanBuilder("span").startSpan(); + span.end(); + + List spanItems = exporter.getFinishedSpanItems(1); + assertEquals(spanItems.size(), 1); + SpanData spanData = spanItems.get(0); + assertEquals(spanData.getResource().getAttribute(TestCustomizer.TEST_KEY), TestCustomizer.TEST_VALUE); + + // Check that the other customizers added were called + // Note: propagator listed twice since by default there are two propagators (W3C trace and W3C baggage) + assertTrue(TestCustomizer.loggedEvents.contains("propagator")); + assertTrue(TestCustomizer.loggedEvents.contains("properties")); + assertTrue(TestCustomizer.loggedEvents.contains("sampler")); + assertTrue(TestCustomizer.loggedEvents.contains("exporter")); + assertTrue(TestCustomizer.loggedEvents.contains("tracer")); + } +} diff --git a/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/ExporterSpiTest.java b/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/ExporterSpiTest.java new file mode 100644 index 0000000..b90d684 --- /dev/null +++ b/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/ExporterSpiTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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 org.eclipse.microprofile.telemetry.tracing.tck.spi; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.List; + +import org.eclipse.microprofile.telemetry.tracing.tck.TestLibraries; +import org.eclipse.microprofile.telemetry.tracing.tck.exporter.InMemorySpanExporter; +import org.eclipse.microprofile.telemetry.tracing.tck.exporter.InMemorySpanExporterProvider; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.testng.Arquillian; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.testng.annotations.Test; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider; +import io.opentelemetry.sdk.trace.data.SpanData; +import jakarta.inject.Inject; + +public class ExporterSpiTest extends Arquillian { + @Deployment + public static WebArchive createDeployment() { + return ShrinkWrap.create(WebArchive.class) + .addClasses(InMemorySpanExporter.class, InMemorySpanExporterProvider.class, TestCustomizer.class) + .addAsServiceProvider(ConfigurableSpanExporterProvider.class, InMemorySpanExporterProvider.class) + .addAsLibrary(TestLibraries.AWAITILITY_LIB) + .addAsResource(new StringAsset("otel.sdk.disabled=false\notel.traces.exporter=in-memory"), + "META-INF/microprofile-config.properties") + .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); + + } + + @Inject + private Tracer tracer; + + @Inject + private InMemorySpanExporter exporter; + + @Test + public void testExporter() { + AttributeKey FOO_KEY = AttributeKey.stringKey("foo"); + Span span = tracer.spanBuilder("test span").setAttribute(FOO_KEY, "bar").startSpan(); + span.end(); + System.out.println("Hello" + span); + List spanItems = exporter.getFinishedSpanItems(1); + System.out.println("Hello" + spanItems); + assertEquals(spanItems.size(), 1); + SpanData spanData = spanItems.get(0); + assertTrue(spanData.getName().contains("test span")); + assertTrue(spanData.getAttributes().get(FOO_KEY).contains("bar")); + } +} diff --git a/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/ResourceSpiTest.java b/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/ResourceSpiTest.java new file mode 100644 index 0000000..0bb742f --- /dev/null +++ b/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/ResourceSpiTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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 org.eclipse.microprofile.telemetry.tracing.tck.spi; + +import static org.testng.Assert.assertEquals; + +import java.util.List; + +import org.eclipse.microprofile.telemetry.tracing.tck.TestLibraries; +import org.eclipse.microprofile.telemetry.tracing.tck.exporter.InMemorySpanExporter; +import org.eclipse.microprofile.telemetry.tracing.tck.exporter.InMemorySpanExporterProvider; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.testng.Arquillian; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.testng.annotations.Test; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; +import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider; +import io.opentelemetry.sdk.trace.data.SpanData; +import jakarta.inject.Inject; + +public class ResourceSpiTest extends Arquillian { + + public static final String TEST_VALUE1 = "test1"; + public static final String TEST_VALUE2 = "test2"; + + @Deployment + public static WebArchive createDeployment() { + return ShrinkWrap.create(WebArchive.class) + .addClasses(InMemorySpanExporter.class, InMemorySpanExporterProvider.class, TestResourceProvider.class) + .addAsServiceProvider(ConfigurableSpanExporterProvider.class, InMemorySpanExporterProvider.class) + .addAsServiceProvider(ResourceProvider.class, TestResourceProvider.class) + .addAsLibrary(TestLibraries.AWAITILITY_LIB) + .addAsResource(new StringAsset("otel.sdk.disabled=false\notel.traces.exporter=in-memory\n" + + TestResourceProvider.TEST_KEY1.getKey() + "=" + TEST_VALUE1 + "\notel.test.key2=" + + TEST_VALUE2), + "META-INF/microprofile-config.properties") + .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); + + } + + @Inject + private Tracer tracer; + + @Inject + private InMemorySpanExporter exporter; + + @Test + public void testResource() { + Span span = tracer.spanBuilder("span").startSpan(); + span.end(); + + List spanItems = exporter.getFinishedSpanItems(1); + assertEquals(spanItems.size(), 1); + SpanData spanData = spanItems.get(0); + assertEquals(spanData.getResource().getAttribute(TestResourceProvider.TEST_KEY1), TEST_VALUE1); + assertEquals(spanData.getResource().getAttribute(TestResourceProvider.TEST_KEY2), TEST_VALUE2); + } + +} diff --git a/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/SamplerSpiTest.java b/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/SamplerSpiTest.java new file mode 100644 index 0000000..5bd9e04 --- /dev/null +++ b/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/SamplerSpiTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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 org.eclipse.microprofile.telemetry.tracing.tck.spi; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import org.eclipse.microprofile.telemetry.tracing.tck.TestLibraries; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.testng.Arquillian; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.testng.annotations.Test; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider; +import jakarta.inject.Inject; + +public class SamplerSpiTest extends Arquillian { + + @Inject + private Tracer tracer; + + @Deployment + public static WebArchive createDeployment() { + return ShrinkWrap.create(WebArchive.class) + .addClasses(TestSampler.class, TestSamplerProvider.class) + .addAsServiceProvider(ConfigurableSamplerProvider.class, TestSamplerProvider.class) + .addAsLibrary(TestLibraries.AWAITILITY_LIB) + .addAsResource( + new StringAsset("otel.sdk.disabled=false\notel.traces.sampler=" + TestSamplerProvider.NAME), + "META-INF/microprofile-config.properties") + .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); + } + + @Test + public void testSampler() { + // Span 1 does not set SAMPLE_ME, so it should not be sampled + Span span1 = tracer.spanBuilder("span1").startSpan(); + try { + assertFalse(span1.isRecording()); + assertFalse(span1.getSpanContext().isSampled()); + } finally { + span1.end(); + } + + Span span2 = tracer.spanBuilder("span2").setAttribute(TestSampler.SAMPLE_ME, true).startSpan(); + try { + // assertTrue(span2.isRecording()); + assertTrue(span2.getSpanContext().isSampled()); + } finally { + span2.end(); + } + + } +} diff --git a/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/TestCustomizer.java b/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/TestCustomizer.java new file mode 100644 index 0000000..6d06933 --- /dev/null +++ b/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/TestCustomizer.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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 org.eclipse.microprofile.telemetry.tracing.tck.spi; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import io.opentelemetry.sdk.trace.samplers.Sampler; + +public class TestCustomizer implements AutoConfigurationCustomizerProvider { + + public static final AttributeKey TEST_KEY = AttributeKey.stringKey("test-key"); + public static final String TEST_VALUE = "test-value"; + + public static final List loggedEvents = new ArrayList<>(); + + /** {@inheritDoc} */ + @Override + public void customize(AutoConfigurationCustomizer autoConfiguration) { + // Do an actual customization of the resource + autoConfiguration.addResourceCustomizer((r, c) -> r.toBuilder().put(TEST_KEY, TEST_VALUE).build()); + + // Just check that we can add the other customizers that relate to tracing and that they get called + // Use actual methods so that we know we're really loading every class and not missing some due to generic + // erasure + autoConfiguration.addPropagatorCustomizer(this::customizePropagator); + autoConfiguration.addPropertiesCustomizer(this::customizeProperties); + autoConfiguration.addSamplerCustomizer(this::customizeSampler); + autoConfiguration.addSpanExporterCustomizer(this::customizeExporter); + autoConfiguration.addTracerProviderCustomizer(this::customizeTracer); + } + + private TextMapPropagator customizePropagator(TextMapPropagator propagator, ConfigProperties config) { + loggedEvents.add("propagator"); + return propagator; + } + + private Map customizeProperties(ConfigProperties config) { + loggedEvents.add("properties"); + return Collections.emptyMap(); + } + + private Sampler customizeSampler(Sampler sampler, ConfigProperties config) { + loggedEvents.add("sampler"); + return sampler; + } + + private SpanExporter customizeExporter(SpanExporter exporter, ConfigProperties config) { + loggedEvents.add("exporter"); + return exporter; + } + + private SdkTracerProviderBuilder customizeTracer(SdkTracerProviderBuilder tracerBuilder, ConfigProperties config) { + loggedEvents.add("tracer"); + return tracerBuilder; + } + +} diff --git a/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/TestResourceProvider.java b/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/TestResourceProvider.java new file mode 100644 index 0000000..d9b7781 --- /dev/null +++ b/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/TestResourceProvider.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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 org.eclipse.microprofile.telemetry.tracing.tck.spi; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; +import io.opentelemetry.sdk.resources.Resource; + +public class TestResourceProvider implements ResourceProvider { + + public static final AttributeKey TEST_KEY1 = AttributeKey.stringKey("otel.test.key1"); + public static final AttributeKey TEST_KEY2 = AttributeKey.stringKey("otel.test.key2"); + + /** {@inheritDoc} */ + @Override + public Resource createResource(ConfigProperties config) { + // Read two test values from config and add them + return Resource.builder() + .put(TEST_KEY1, config.getString(TEST_KEY1.getKey())) + .put(TEST_KEY2, config.getString(TEST_KEY2.getKey())) + .build(); + } + +} diff --git a/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/TestSampler.java b/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/TestSampler.java new file mode 100644 index 0000000..5c984b7 --- /dev/null +++ b/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/TestSampler.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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 org.eclipse.microprofile.telemetry.tracing.tck.spi; + +import java.util.List; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.trace.data.LinkData; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import io.opentelemetry.sdk.trace.samplers.SamplingResult; + +/** + * A test sampler which looks for the "test.sample.me" attribute to decide whether a span should be sampled + */ +public class TestSampler implements Sampler { + + public static final AttributeKey SAMPLE_ME = AttributeKey.booleanKey("test.sample.me"); + + /** {@inheritDoc} */ + @Override + public String getDescription() { + return "Test sampler, samples if test.sample.me is true"; + } + + /** {@inheritDoc} */ + @Override + public SamplingResult shouldSample(Context context, String traceId, String name, SpanKind spanKind, + Attributes attributes, List parentLinks) { + + if (attributes.get(SAMPLE_ME) == Boolean.TRUE) { + return SamplingResult.recordAndSample(); + } else { + return SamplingResult.drop(); + } + } + +} diff --git a/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/TestSamplerProvider.java b/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/TestSamplerProvider.java new file mode 100644 index 0000000..5a52662 --- /dev/null +++ b/tracing/tck/src/main/java/org/eclipse/microprofile/telemetry/tracing/tck/spi/TestSamplerProvider.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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 org.eclipse.microprofile.telemetry.tracing.tck.spi; + +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider; +import io.opentelemetry.sdk.trace.samplers.Sampler; + +public class TestSamplerProvider implements ConfigurableSamplerProvider { + + public static final String NAME = "test-sampler"; + + /** {@inheritDoc} */ + @Override + public Sampler createSampler(ConfigProperties config) { + return new TestSampler(); + } + + /** {@inheritDoc} */ + @Override + public String getName() { + return NAME; + } + +} \ No newline at end of file