diff --git a/agent/agent-tooling/build.gradle.kts b/agent/agent-tooling/build.gradle.kts index 81f9ccb0578..e367277e1c8 100644 --- a/agent/agent-tooling/build.gradle.kts +++ b/agent/agent-tooling/build.gradle.kts @@ -62,10 +62,13 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter") testImplementation("org.assertj:assertj-core") + testImplementation("org.awaitility:awaitility") testImplementation("org.mockito:mockito-core") testImplementation("uk.org.webcompere:system-stubs-jupiter:1.1.0") testImplementation("io.github.hakky54:logcaptor") + testImplementation("io.opentelemetry:opentelemetry-sdk-testing") + testImplementation("com.microsoft.jfr:jfr-streaming") testImplementation("com.azure:azure-storage-blob") } diff --git a/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/configuration/Configuration.java b/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/configuration/Configuration.java index 31d50ace04a..c94a3688ed0 100644 --- a/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/configuration/Configuration.java +++ b/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/configuration/Configuration.java @@ -30,6 +30,7 @@ import com.microsoft.applicationinsights.agent.bootstrap.diagnostics.DiagnosticsHelper; import com.microsoft.applicationinsights.agent.bootstrap.diagnostics.status.StatusFile; import com.microsoft.applicationinsights.agent.internal.common.FriendlyException; +import io.opentelemetry.api.common.AttributeKey; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -195,11 +196,59 @@ public static class PreviewConfiguration { public LiveMetrics liveMetrics = new LiveMetrics(); public LegacyRequestIdPropagation legacyRequestIdPropagation = new LegacyRequestIdPropagation(); + public List inheritedAttributes = new ArrayList<>(); + public ProfilerConfiguration profiler = new ProfilerConfiguration(); public GcEventConfiguration gcEvents = new GcEventConfiguration(); public AadAuthentication authentication = new AadAuthentication(); } + public static class InheritedAttribute { + public String key; + public SpanAttributeType type; + + public AttributeKey getAttributeKey() { + switch (type) { + case STRING: + return AttributeKey.stringKey(key); + case BOOLEAN: + return AttributeKey.booleanKey(key); + case LONG: + return AttributeKey.longKey(key); + case DOUBLE: + return AttributeKey.doubleKey(key); + case STRING_ARRAY: + return AttributeKey.stringArrayKey(key); + case BOOLEAN_ARRAY: + return AttributeKey.booleanArrayKey(key); + case LONG_ARRAY: + return AttributeKey.longArrayKey(key); + case DOUBLE_ARRAY: + return AttributeKey.doubleArrayKey(key); + } + throw new IllegalStateException("Unexpected attribute key type: " + type); + } + } + + public enum SpanAttributeType { + @JsonProperty("string") + STRING, + @JsonProperty("boolean") + BOOLEAN, + @JsonProperty("long") + LONG, + @JsonProperty("double") + DOUBLE, + @JsonProperty("string-array") + STRING_ARRAY, + @JsonProperty("boolean-array") + BOOLEAN_ARRAY, + @JsonProperty("long-array") + LONG_ARRAY, + @JsonProperty("double-array") + DOUBLE_ARRAY + } + public static class LegacyRequestIdPropagation { public boolean enabled; } @@ -904,6 +953,7 @@ public void validate() { } public enum AuthenticationType { + // TODO (kyralama) should these use @JsonProperty to bind lowercase like other enums? UAMI, SAMI, VSCODE, diff --git a/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/init/InheritedAttributesSpanProcessor.java b/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/init/InheritedAttributesSpanProcessor.java new file mode 100644 index 00000000000..fc491d27f73 --- /dev/null +++ b/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/init/InheritedAttributesSpanProcessor.java @@ -0,0 +1,78 @@ +/* + * ApplicationInsights-Java + * Copyright (c) Microsoft Corporation + * All rights reserved. + * + * MIT License + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the ""Software""), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package com.microsoft.applicationinsights.agent.internal.init; + +import com.microsoft.applicationinsights.agent.internal.configuration.Configuration; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.trace.ReadWriteSpan; +import io.opentelemetry.sdk.trace.ReadableSpan; +import io.opentelemetry.sdk.trace.SpanProcessor; +import java.util.List; +import java.util.stream.Collectors; + +public class InheritedAttributesSpanProcessor implements SpanProcessor { + + private final List> inheritAttributeKeys; + + public InheritedAttributesSpanProcessor( + List inheritedAttributes) { + this.inheritAttributeKeys = + inheritedAttributes.stream() + .map(Configuration.InheritedAttribute::getAttributeKey) + .collect(Collectors.toList()); + } + + @Override + @SuppressWarnings("unchecked") + public void onStart(Context parentContext, ReadWriteSpan span) { + Span parentSpan = Span.fromContextOrNull(parentContext); + if (parentSpan == null) { + return; + } + if (!(parentSpan instanceof ReadableSpan)) { + return; + } + ReadableSpan parentReadableSpan = (ReadableSpan) parentSpan; + + for (AttributeKey inheritAttributeKey : inheritAttributeKeys) { + Object value = TempGetAttribute.getAttribute(parentReadableSpan, inheritAttributeKey); + if (value != null) { + span.setAttribute((AttributeKey) inheritAttributeKey, value); + } + } + } + + @Override + public boolean isStartRequired() { + return true; + } + + @Override + public void onEnd(ReadableSpan span) {} + + @Override + public boolean isEndRequired() { + return false; + } +} diff --git a/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/init/OpenTelemetryConfigurer.java b/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/init/OpenTelemetryConfigurer.java index 8ede39356b0..1de3ade6fe3 100644 --- a/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/init/OpenTelemetryConfigurer.java +++ b/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/init/OpenTelemetryConfigurer.java @@ -107,20 +107,23 @@ public void configure(SdkTracerProviderBuilder tracerProvider) { } } - // using BatchSpanProcessor in order to get off of the application thread as soon as possible - // using batch size 1 because need to convert to SpanData as soon as possible to grab data for - // live metrics - // real batching is done at a lower level - batchSpanProcessor = BatchSpanProcessor.builder(currExporter).setMaxExportBatchSize(1).build(); - tracerProvider.addSpanProcessor(batchSpanProcessor); // operation name span processor is only applied on span start, so doesn't need to be chained - // with above span processors + // with the batch span processor tracerProvider.addSpanProcessor(new AiOperationNameSpanProcessor()); - // legacy span processor is only applied on span start, so doesn't need to be chained with above - // span processors + // inherited attributes span processor is only applied on span start, so doesn't need to be + // chained with the batch span processor + tracerProvider.addSpanProcessor( + new InheritedAttributesSpanProcessor(config.preview.inheritedAttributes)); + // legacy span processor is only applied on span start, so doesn't need to be chained with the + // batch span processor // it is used to pass legacy attributes from the context (extracted by the AiLegacyPropagator) // to the span attributes (since there is no way to update attributes on span directly from // propagator) tracerProvider.addSpanProcessor(new AiLegacyHeaderSpanProcessor()); + // using BatchSpanProcessor in order to get off of the application thread as soon as possible + // using batch size 1 because need to convert to SpanData as soon as possible to grab data for + // live metrics. the real batching is done at a lower level + batchSpanProcessor = BatchSpanProcessor.builder(currExporter).setMaxExportBatchSize(1).build(); + tracerProvider.addSpanProcessor(batchSpanProcessor); } } diff --git a/agent/agent-tooling/src/test/java/com/microsoft/applicationinsights/agent/internal/init/InheritedAttributesSpanProcessorTest.java b/agent/agent-tooling/src/test/java/com/microsoft/applicationinsights/agent/internal/init/InheritedAttributesSpanProcessorTest.java new file mode 100644 index 00000000000..7eca8d6cd37 --- /dev/null +++ b/agent/agent-tooling/src/test/java/com/microsoft/applicationinsights/agent/internal/init/InheritedAttributesSpanProcessorTest.java @@ -0,0 +1,170 @@ +/* + * ApplicationInsights-Java + * Copyright (c) Microsoft Corporation + * All rights reserved. + * + * MIT License + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the ""Software""), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package com.microsoft.applicationinsights.agent.internal.init; + +import static io.opentelemetry.api.trace.SpanKind.INTERNAL; +import static io.opentelemetry.sdk.testing.assertj.TracesAssert.assertThat; +import static org.assertj.core.api.Assertions.entry; +import static org.awaitility.Awaitility.await; + +import com.microsoft.applicationinsights.agent.internal.configuration.Configuration; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; +import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +public class InheritedAttributesSpanProcessorTest { + + private final InMemorySpanExporter exporter = InMemorySpanExporter.create(); + + private final AttributeKey oneStringKey = AttributeKey.stringKey("one"); + private final AttributeKey oneLongKey = AttributeKey.longKey("one"); + + @AfterEach + public void afterEach() { + exporter.reset(); + } + + @Test + public void shouldNotInheritAttribute() { + Tracer tracer = newTracer(Collections.emptyList()); + Span span = + tracer.spanBuilder("parent").setNoParent().setAttribute(oneStringKey, "1").startSpan(); + Context context = Context.root().with(span); + try { + tracer.spanBuilder("child").setParent(context).startSpan().end(); + } finally { + span.end(); + } + + await().until(() -> exporter.getFinishedSpanItems().size() == 2); + + assertThat(Collections.singleton(exporter.getFinishedSpanItems())) + .hasTracesSatisfyingExactly( + trace -> + trace.hasSpansSatisfyingExactly( + childSpan -> + childSpan.hasName("child").hasKind(INTERNAL).hasTotalAttributeCount(0), + parentSpan -> + parentSpan + .hasName("parent") + .hasKind(INTERNAL) + .hasAttributesSatisfying( + attributes -> + OpenTelemetryAssertions.assertThat(attributes) + .containsOnly(entry(oneStringKey, "1"))))); + } + + @Test + public void shouldInheritAttribute() { + Configuration.InheritedAttribute inheritedAttribute = new Configuration.InheritedAttribute(); + inheritedAttribute.key = "one"; + inheritedAttribute.type = Configuration.SpanAttributeType.STRING; + + Tracer tracer = newTracer(Collections.singletonList(inheritedAttribute)); + Span span = + tracer.spanBuilder("parent").setNoParent().setAttribute(oneStringKey, "1").startSpan(); + Context context = Context.root().with(span); + try { + tracer.spanBuilder("child").setParent(context).startSpan().end(); + } finally { + span.end(); + } + + await().until(() -> exporter.getFinishedSpanItems().size() == 2); + + assertThat(Collections.singleton(exporter.getFinishedSpanItems())) + .hasTracesSatisfyingExactly( + trace -> + trace.hasSpansSatisfyingExactly( + childSpan -> + childSpan + .hasName("child") + .hasKind(INTERNAL) + .hasAttributesSatisfying( + attributes -> + OpenTelemetryAssertions.assertThat(attributes) + .containsOnly(entry(oneStringKey, "1"))), + parentSpan -> + parentSpan + .hasName("parent") + .hasKind(INTERNAL) + .hasAttributesSatisfying( + attributes -> + OpenTelemetryAssertions.assertThat(attributes) + .containsOnly(entry(oneStringKey, "1"))))); + } + + @Test + public void shouldNotInheritAttributeWithSameNameButDifferentType() { + Configuration.InheritedAttribute inheritedAttribute = new Configuration.InheritedAttribute(); + inheritedAttribute.key = "one"; + inheritedAttribute.type = Configuration.SpanAttributeType.STRING; + + Tracer tracer = newTracer(Collections.singletonList(inheritedAttribute)); + Span span = tracer.spanBuilder("parent").setNoParent().setAttribute(oneLongKey, 1L).startSpan(); + Context context = Context.root().with(span); + try { + tracer.spanBuilder("child").setParent(context).startSpan().end(); + } finally { + span.end(); + } + + await().until(() -> exporter.getFinishedSpanItems().size() == 2); + + assertThat(Collections.singleton(exporter.getFinishedSpanItems())) + .hasTracesSatisfyingExactly( + trace -> + trace.hasSpansSatisfyingExactly( + childSpan -> + childSpan.hasName("child").hasKind(INTERNAL).hasTotalAttributeCount(0), + parentSpan -> + parentSpan + .hasName("parent") + .hasKind(INTERNAL) + .hasAttributesSatisfying( + attributes -> + OpenTelemetryAssertions.assertThat(attributes) + .containsOnly(entry(oneLongKey, 1L))))); + } + + private Tracer newTracer(List inheritedAttributes) { + OpenTelemetrySdk sdk = + OpenTelemetrySdk.builder() + .setTracerProvider( + SdkTracerProvider.builder() + .addSpanProcessor(new InheritedAttributesSpanProcessor(inheritedAttributes)) + .addSpanProcessor(SimpleSpanProcessor.create(exporter)) + .build()) + .build(); + return sdk.getTracer("test"); + } +} diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index e14468cb5e3..a67f5604bda 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -102,6 +102,7 @@ val DEPENDENCIES = listOf( "com.azure:azure-storage-blob:12.13.0", "com.github.oshi:oshi-core:5.8.0", "org.assertj:assertj-core:3.19.0", + "org.awaitility:awaitility:4.1.0", "io.github.hakky54:logcaptor:2.5.0", "com.microsoft.jfr:jfr-streaming:1.2.0", "org.checkerframework:checker-qual:3.14.0" diff --git a/settings.gradle b/settings.gradle index 9c73c65ba84..11964a51f5d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -88,6 +88,7 @@ include ':test:smoke:testApps:CustomDimensions' include ':test:smoke:testApps:gRPC' include ':test:smoke:testApps:HeartBeat' include ':test:smoke:testApps:HttpClients' +include ':test:smoke:testApps:InheritedAttributes' include ':test:smoke:testApps:Jdbc' include ':test:smoke:testApps:Jedis' include ':test:smoke:testApps:JettyNativeHandler' diff --git a/test/smoke/appServers/global-resources/inheritedattributes_applicationinsights.json b/test/smoke/appServers/global-resources/inheritedattributes_applicationinsights.json new file mode 100644 index 00000000000..cd2586a33cd --- /dev/null +++ b/test/smoke/appServers/global-resources/inheritedattributes_applicationinsights.json @@ -0,0 +1,11 @@ +{ + "connectionString": "InstrumentationKey=00000000-0000-0000-0000-0FEEDDADBEEF;IngestionEndpoint=http://host.docker.internal:6060/", + "preview": { + "inheritedAttributes": [ + { + "key": "tenant", + "type": "string" + } + ] + } +} diff --git a/test/smoke/testApps/InheritedAttributes/build.gradle.kts b/test/smoke/testApps/InheritedAttributes/build.gradle.kts new file mode 100644 index 00000000000..60461f7e049 --- /dev/null +++ b/test/smoke/testApps/InheritedAttributes/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + id("ai.smoke-test-jar") +} + +dependencies { + implementation("org.springframework.boot:spring-boot-starter-web:2.1.7.RELEASE") + + implementation("com.microsoft.azure:applicationinsights-web") +} diff --git a/test/smoke/testApps/InheritedAttributes/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java b/test/smoke/testApps/InheritedAttributes/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java new file mode 100644 index 00000000000..d42f3864b42 --- /dev/null +++ b/test/smoke/testApps/InheritedAttributes/src/main/java/com/microsoft/applicationinsights/smoketestapp/SpringBootApp.java @@ -0,0 +1,34 @@ +/* + * ApplicationInsights-Java + * Copyright (c) Microsoft Corporation + * All rights reserved. + * + * MIT License + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the ""Software""), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package com.microsoft.applicationinsights.smoketestapp; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringBootApp { + + public static void main(String[] args) { + + SpringApplication.run(SpringBootApp.class, args); + } +} diff --git a/test/smoke/testApps/InheritedAttributes/src/main/java/com/microsoft/applicationinsights/smoketestapp/TestController.java b/test/smoke/testApps/InheritedAttributes/src/main/java/com/microsoft/applicationinsights/smoketestapp/TestController.java new file mode 100644 index 00000000000..8a56b01c4e5 --- /dev/null +++ b/test/smoke/testApps/InheritedAttributes/src/main/java/com/microsoft/applicationinsights/smoketestapp/TestController.java @@ -0,0 +1,44 @@ +/* + * ApplicationInsights-Java + * Copyright (c) Microsoft Corporation + * All rights reserved. + * + * MIT License + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the ""Software""), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package com.microsoft.applicationinsights.smoketestapp; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class TestController { + + private static final Logger logger = LoggerFactory.getLogger("smoketestapp"); + + @GetMapping("/") + public String root() { + return "OK"; + } + + @GetMapping("/test") + public String test() { + logger.info("hello"); + return "OK!"; + } +} diff --git a/test/smoke/testApps/InheritedAttributes/src/main/java/com/microsoft/applicationinsights/smoketestapp/TracingFilter.java b/test/smoke/testApps/InheritedAttributes/src/main/java/com/microsoft/applicationinsights/smoketestapp/TracingFilter.java new file mode 100644 index 00000000000..068cbe87d10 --- /dev/null +++ b/test/smoke/testApps/InheritedAttributes/src/main/java/com/microsoft/applicationinsights/smoketestapp/TracingFilter.java @@ -0,0 +1,25 @@ +package com.microsoft.applicationinsights.smoketestapp; + +import com.microsoft.applicationinsights.web.internal.ThreadContext; +import java.io.IOException; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import org.springframework.stereotype.Component; + +@Component +public class TracingFilter implements Filter { + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + + ThreadContext.getRequestTelemetryContext() + .getHttpRequestTelemetry() + .getProperties() + .put("tenant", "z"); + + chain.doFilter(request, response); + } +} diff --git a/test/smoke/testApps/InheritedAttributes/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/SpringBootAutoTest.java b/test/smoke/testApps/InheritedAttributes/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/SpringBootAutoTest.java new file mode 100644 index 00000000000..e8681fad5f3 --- /dev/null +++ b/test/smoke/testApps/InheritedAttributes/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/SpringBootAutoTest.java @@ -0,0 +1,65 @@ +/* + * ApplicationInsights-Java + * Copyright (c) Microsoft Corporation + * All rights reserved. + * + * MIT License + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the ""Software""), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package com.microsoft.applicationinsights.smoketest; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import com.microsoft.applicationinsights.smoketest.schemav2.Data; +import com.microsoft.applicationinsights.smoketest.schemav2.Envelope; +import com.microsoft.applicationinsights.smoketest.schemav2.MessageData; +import com.microsoft.applicationinsights.smoketest.schemav2.RequestData; +import com.microsoft.applicationinsights.smoketest.schemav2.SeverityLevel; +import java.util.List; +import org.junit.Test; + +@UseAgent("inheritedattributes") +public class SpringBootAutoTest extends AiSmokeTest { + + @Test + @TargetUri("/test") + public void test() throws Exception { + List rdList = mockedIngestion.waitForItems("RequestData", 1); + List mdList = mockedIngestion.waitForMessageItemsInRequest(1); + + Envelope rdEnvelope = rdList.get(0); + Envelope mdEnvelope = mdList.get(0); + RequestData rd = (RequestData) ((Data) rdEnvelope.getData()).getBaseData(); + MessageData md = (MessageData) ((Data) mdEnvelope.getData()).getBaseData(); + + assertEquals("GET /test", rd.getName()); + assertEquals("200", rd.getResponseCode()); + assertEquals("z", rd.getProperties().get("tenant")); + assertEquals(1, rd.getProperties().size()); + assertTrue(rd.getSuccess()); + + assertEquals("hello", md.getMessage()); + assertEquals(SeverityLevel.Information, md.getSeverityLevel()); + assertEquals("Logger", md.getProperties().get("SourceType")); + assertEquals("INFO", md.getProperties().get("LoggingLevel")); + assertEquals("smoketestapp", md.getProperties().get("LoggerName")); + assertNotNull(md.getProperties().get("ThreadName")); + assertEquals("z", rd.getProperties().get("tenant")); + assertEquals(5, md.getProperties().size()); + } +} diff --git a/test/smoke/testApps/InheritedAttributes/src/smokeTest/resources/appServers.txt b/test/smoke/testApps/InheritedAttributes/src/smokeTest/resources/appServers.txt new file mode 100644 index 00000000000..ff8ac2f8dca --- /dev/null +++ b/test/smoke/testApps/InheritedAttributes/src/smokeTest/resources/appServers.txt @@ -0,0 +1 @@ +javase diff --git a/test/smoke/testApps/InheritedAttributes/src/smokeTest/resources/logback-test.xml b/test/smoke/testApps/InheritedAttributes/src/smokeTest/resources/logback-test.xml new file mode 100644 index 00000000000..0b02019cda0 --- /dev/null +++ b/test/smoke/testApps/InheritedAttributes/src/smokeTest/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{36} - %msg%n + + + + + + + + + + +