// // Copyright The OpenTelemetry Authors // // 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. // using System.Net; using System.Net.Http; using System.Reflection; using System.Threading.Tasks; using FluentAssertions; using FluentAssertions.Extensions; using IntegrationTests.Helpers; using Xunit; using Xunit.Abstractions; #if NETFRAMEWORK using IntegrationTests.Helpers.Compatibility; #else using System; using System.Collections.Generic; #endif namespace IntegrationTests; public class SmokeTests : TestHelper { private const string ServiceName = "TestApplication.Smoke"; public SmokeTests(ITestOutputHelper output) : base("Smoke", output) { SetEnvironmentVariable("OTEL_SERVICE_NAME", ServiceName); SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_ENABLED_INSTRUMENTATIONS", "HttpClient"); } [Fact] [Trait("Category", "EndToEnd")] public async Task SubmitsTraces() { await VerifyTestApplicationInstrumented(); } [Fact] [Trait("Category", "EndToEnd")] public async Task WhenStartupHookIsNotEnabled() { #if NETFRAMEWORK await VerifyTestApplicationInstrumented(enableStartupHook: false); #else // on .NET Core it is required to set DOTNET_STARTUP_HOOKS await VerifyTestApplicationNotInstrumented(enableStartupHook: false); #endif } [Fact] [Trait("Category", "EndToEnd")] public async Task WhenClrProfilerIsNotEnabled() { #if NETFRAMEWORK // on .NET Framework it is required to set the CLR .NET Profiler await VerifyTestApplicationNotInstrumented(enableClrProfiler: false); #else await VerifyTestApplicationInstrumented(enableClrProfiler: false); #endif } [Fact] [Trait("Category", "EndToEnd")] public async Task ApplicationIsNotExcluded() { SetEnvironmentVariable("OTEL_DOTNET_AUTO_EXCLUDE_PROCESSES", "dotnet,dotnet.exe"); await VerifyTestApplicationInstrumented(); } [Fact] [Trait("Category", "EndToEnd")] public async Task ApplicationIsExcluded() { SetEnvironmentVariable("OTEL_DOTNET_AUTO_EXCLUDE_PROCESSES", $"dotnet,dotnet.exe,{EnvironmentHelper.FullTestApplicationName},{EnvironmentHelper.FullTestApplicationName}.exe"); await VerifyTestApplicationNotInstrumented(); } [Fact] [Trait("Category", "EndToEnd")] public async Task ApplicationIsNotIncluded() { SetEnvironmentVariable("OTEL_DOTNET_AUTO_INCLUDE_PROCESSES", "dotnet,dotnet.exe"); #if NETFRAMEWORK await VerifyTestApplicationNotInstrumented(); #else // FIXME: OTEL_DOTNET_AUTO_INCLUDE_PROCESSES does not work on .NET Core. // https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/issues/895 await VerifyTestApplicationInstrumented(); #endif } [Fact] [Trait("Category", "EndToEnd")] public async Task ApplicationIsIncluded() { SetEnvironmentVariable("OTEL_DOTNET_AUTO_INCLUDE_PROCESSES", $"{EnvironmentHelper.FullTestApplicationName},{EnvironmentHelper.FullTestApplicationName}.exe"); await VerifyTestApplicationInstrumented(); } [Fact] [Trait("Category", "EndToEnd")] public async Task SubmitMetrics() { using var collector = await MockMetricsCollector.Start(Output); collector.Expect("MyCompany.MyProduct.MyLibrary", metric => metric.Name == "MyFruitCounter"); SetEnvironmentVariable("OTEL_DOTNET_AUTO_METRICS_ADDITIONAL_SOURCES", "MyCompany.MyProduct.MyLibrary"); RunTestApplication(metricsAgentPort: collector.Port); collector.AssertExpectations(); } [Fact] [Trait("Category", "EndToEnd")] public async Task TracesResource() { using var collector = await MockSpansCollector.Start(Output); collector.ResourceExpector.Expect("service.name", ServiceName); collector.ResourceExpector.Expect("telemetry.sdk.name", "opentelemetry"); collector.ResourceExpector.Expect("telemetry.sdk.language", "dotnet"); collector.ResourceExpector.Expect("telemetry.sdk.version", typeof(OpenTelemetry.Resources.Resource).Assembly.GetCustomAttribute().Version); collector.ResourceExpector.Expect("telemetry.auto.version", OpenTelemetry.AutoInstrumentation.Constants.Tracer.Version); SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_ADDITIONAL_SOURCES", "MyCompany.MyProduct.MyLibrary"); RunTestApplication(otlpTraceCollectorPort: collector.Port); collector.ResourceExpector.AssertExpectations(); } [Fact] [Trait("Category", "EndToEnd")] public async Task MetricsResource() { using var collector = await MockMetricsCollector.Start(Output); collector.ResourceExpector.Expect("service.name", ServiceName); collector.ResourceExpector.Expect("telemetry.sdk.name", "opentelemetry"); collector.ResourceExpector.Expect("telemetry.sdk.language", "dotnet"); collector.ResourceExpector.Expect("telemetry.sdk.version", typeof(OpenTelemetry.Resources.Resource).Assembly.GetCustomAttribute().Version); collector.ResourceExpector.Expect("telemetry.auto.version", OpenTelemetry.AutoInstrumentation.Constants.Tracer.Version); SetEnvironmentVariable("OTEL_DOTNET_AUTO_METRICS_ADDITIONAL_SOURCES", "MyCompany.MyProduct.MyLibrary"); RunTestApplication(metricsAgentPort: collector.Port); collector.ResourceExpector.AssertExpectations(); } #if !NETFRAMEWORK // The feature is not supported on .NET Framework [Fact] [Trait("Category", "EndToEnd")] public async Task LogsResource() { using var collector = await MockLogsCollector.Start(Output); collector.ResourceExpector.Expect("service.name", ServiceName); collector.ResourceExpector.Expect("telemetry.sdk.name", "opentelemetry"); collector.ResourceExpector.Expect("telemetry.sdk.language", "dotnet"); collector.ResourceExpector.Expect("telemetry.sdk.version", typeof(OpenTelemetry.Resources.Resource).Assembly.GetCustomAttribute().Version); collector.ResourceExpector.Expect("telemetry.auto.version", OpenTelemetry.AutoInstrumentation.Constants.Tracer.Version); RunTestApplication(logsAgentPort: collector.Port); collector.ResourceExpector.AssertExpectations(); } #endif [Fact] [Trait("Category", "EndToEnd")] public async Task OtlpTracesExporter() { using var collector = await MockSpansCollector.Start(Output); collector.Expect("MyCompany.MyProduct.MyLibrary", span => span.Name == "SayHello"); SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_ADDITIONAL_SOURCES", "MyCompany.MyProduct.MyLibrary"); RunTestApplication(otlpTraceCollectorPort: collector.Port); collector.AssertExpectations(); } [Fact] [Trait("Category", "EndToEnd")] public async Task ZipkinExporter() { using var collector = await MockZipkinCollector.Start(Output); collector.Expect(span => span.Name == "SayHello" && span.Tags.GetValueOrDefault("otel.library.name") == "MyCompany.MyProduct.MyLibrary"); SetEnvironmentVariable("OTEL_TRACES_EXPORTER", "zipkin"); SetEnvironmentVariable("OTEL_EXPORTER_ZIPKIN_ENDPOINT", $"http://localhost:{collector.Port}/api/v2/spans"); SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_ADDITIONAL_SOURCES", "MyCompany.MyProduct.MyLibrary"); RunTestApplication(); collector.AssertExpectations(); } #if NETFRAMEWORK // The test is flaky on Linux and macOS, becasue of https://github.com/dotnet/runtime/issues/28658#issuecomment-462062760 [Fact] [Trait("Category", "EndToEnd")] public async Task PrometheusExporter() { SetEnvironmentVariable("LONG_RUNNING", "true"); SetEnvironmentVariable("OTEL_METRICS_EXPORTER", "prometheus"); SetEnvironmentVariable("OTEL_DOTNET_AUTO_METRICS_ADDITIONAL_SOURCES", "MyCompany.MyProduct.MyLibrary"); const string defaultPrometheusMetricsEndpoint = "http://localhost:9464/metrics"; using var process = StartTestApplication(); using var helper = new ProcessHelper(process); try { var assert = async () => { var httpClient = new HttpClient { Timeout = 5.Seconds() }; var response = await httpClient.GetAsync(defaultPrometheusMetricsEndpoint); response.StatusCode.Should().Be(HttpStatusCode.OK); var content = await response.Content.ReadAsStringAsync(); Output.WriteLine("Raw metrics from Prometheus:"); Output.WriteLine(content); content.Should().Contain("TYPE ", "should export any metric"); }; await assert.Should().NotThrowAfterAsync( waitTime: 1.Minutes(), pollInterval: 1.Seconds()); } finally { if (!helper.Process.HasExited) { helper.Process.Kill(); helper.Process.WaitForExit(); } Output.WriteLine("ProcessId: " + helper.Process.Id); Output.WriteLine("Exit Code: " + helper.Process.ExitCode); Output.WriteResult(helper); } } #endif #if !NETFRAMEWORK // The feature is not supported on .NET Framework [Fact] [Trait("Category", "EndToEnd")] public async Task SubmitLogs() { using var collector = await MockLogsCollector.Start(Output); collector.Expect(logRecord => Convert.ToString(logRecord.Body) == "{ \"stringValue\": \"Example log message\" }"); SetEnvironmentVariable("OTEL_DOTNET_AUTO_LOGS_INCLUDE_FORMATTED_MESSAGE", "true"); RunTestApplication(logsAgentPort: collector.Port, enableClrProfiler: true); collector.AssertExpectations(); } [Fact] [Trait("Category", "EndToEnd")] public async Task LogsNoneInstrumentations() { using var collector = await MockLogsCollector.Start(Output); SetEnvironmentVariable("OTEL_DOTNET_AUTO_LOGS_ENABLED_INSTRUMENTATIONS", "none"); RunTestApplication(logsAgentPort: collector.Port, enableClrProfiler: true); collector.AssertEmpty(5.Seconds()); } #endif private async Task VerifyTestApplicationInstrumented(bool enableStartupHook = true, bool enableClrProfiler = true) { using var collector = await MockSpansCollector.Start(Output); collector.Expect("MyCompany.MyProduct.MyLibrary"); #if NETFRAMEWORK collector.Expect("OpenTelemetry.HttpWebRequest"); #else collector.Expect("OpenTelemetry.Instrumentation.Http"); #endif SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_ADDITIONAL_SOURCES", "MyCompany.MyProduct.MyLibrary"); RunTestApplication(otlpTraceCollectorPort: collector.Port, enableStartupHook: enableStartupHook, enableClrProfiler: enableClrProfiler); collector.AssertExpectations(); } private async Task VerifyTestApplicationNotInstrumented(bool enableStartupHook = true, bool enableClrProfiler = true) { using var collector = await MockSpansCollector.Start(Output); SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_ADDITIONAL_SOURCES", "MyCompany.MyProduct.MyLibrary"); RunTestApplication(otlpTraceCollectorPort: collector.Port, enableStartupHook: enableStartupHook, enableClrProfiler: enableClrProfiler); collector.AssertEmpty(5.Seconds()); } }