opentelemetry-dotnet-instru.../test/IntegrationTests/SmokeTests.cs

423 lines
16 KiB
C#

// <copyright file="SmokeTests.cs" company="OpenTelemetry Authors">
// 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.
// </copyright>
using System.Reflection;
using FluentAssertions;
using IntegrationTests.Helpers;
using Xunit.Abstractions;
#if NETFRAMEWORK
using System.Net;
using FluentAssertions.Extensions;
using IntegrationTests.Helpers.Compatibility;
#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);
}
[Fact]
[Trait("Category", "EndToEnd")]
public void SubmitsTraces()
{
VerifyTestApplicationInstrumented();
}
[Fact]
[Trait("Category", "EndToEnd")]
public void WhenStartupHookIsNotEnabled()
{
SetEnvironmentVariable("DOTNET_STARTUP_HOOKS", null);
#if NETFRAMEWORK
VerifyTestApplicationInstrumented();
#else
// on .NET it is required to set DOTNET_STARTUP_HOOKS
VerifyTestApplicationNotInstrumented();
#endif
}
[Fact]
[Trait("Category", "EndToEnd")]
public void WhenClrProfilerIsNotEnabled()
{
SetEnvironmentVariable("COR_ENABLE_PROFILING", "0");
SetEnvironmentVariable("CORECLR_ENABLE_PROFILING", "0");
#if NETFRAMEWORK
// on .NET Framework it is required to set the CLR .NET Profiler
VerifyTestApplicationNotInstrumented();
#else
VerifyTestApplicationInstrumented();
#endif
}
[Fact]
[Trait("Category", "EndToEnd")]
public void ApplicationIsNotExcluded()
{
SetEnvironmentVariable("OTEL_DOTNET_AUTO_EXCLUDE_PROCESSES", "dotnet,dotnet.exe");
VerifyTestApplicationInstrumented();
}
[Fact]
[Trait("Category", "EndToEnd")]
public void ApplicationIsExcluded()
{
SetEnvironmentVariable("OTEL_DOTNET_AUTO_EXCLUDE_PROCESSES", $"dotnet,dotnet.exe,{EnvironmentHelper.FullTestApplicationName},{EnvironmentHelper.FullTestApplicationName}.exe");
VerifyTestApplicationNotInstrumented();
}
[Fact]
[Trait("Category", "EndToEnd")]
public void SubmitMetrics()
{
using var collector = new MockMetricsCollector(Output);
SetExporter(collector);
collector.Expect("MyCompany.MyProduct.MyLibrary", metric => metric.Name == "MyFruitCounter");
SetEnvironmentVariable("OTEL_DOTNET_AUTO_METRICS_ADDITIONAL_SOURCES", "MyCompany.MyProduct.MyLibrary");
RunTestApplication();
collector.AssertExpectations();
}
[Fact]
[Trait("Category", "EndToEnd")]
public void TracesResource()
{
using var collector = new MockSpansCollector(Output);
SetExporter(collector);
collector.ResourceExpector.Expect("service.name", ServiceName); // this is set via env var and App.config, but env var has precedence
#if NETFRAMEWORK
collector.ResourceExpector.Expect("deployment.environment", "test"); // this is set via App.config
#endif
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<AssemblyInformationalVersionAttribute>()!.InformationalVersion.Split('+')[0]);
collector.ResourceExpector.Expect("telemetry.auto.version", OpenTelemetry.AutoInstrumentation.Constants.Tracer.Version);
EnableOnlyHttpClientTraceInstrumentation();
SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_ADDITIONAL_SOURCES", "MyCompany.MyProduct.MyLibrary");
RunTestApplication();
collector.ResourceExpector.AssertExpectations();
}
[Fact]
[Trait("Category", "EndToEnd")]
public void MetricsResource()
{
using var collector = new MockMetricsCollector(Output);
SetExporter(collector);
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<AssemblyInformationalVersionAttribute>()!.InformationalVersion.Split('+')[0]);
collector.ResourceExpector.Expect("telemetry.auto.version", OpenTelemetry.AutoInstrumentation.Constants.Tracer.Version);
EnableOnlyHttpClientTraceInstrumentation();
SetEnvironmentVariable("OTEL_DOTNET_AUTO_METRICS_ADDITIONAL_SOURCES", "MyCompany.MyProduct.MyLibrary");
RunTestApplication();
collector.ResourceExpector.AssertExpectations();
}
#if NET6_0_OR_GREATER // The feature is not supported on .NET Framework
[Fact]
[Trait("Category", "EndToEnd")]
public void LogsResource()
{
using var collector = new MockLogsCollector(Output);
SetExporter(collector);
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<AssemblyInformationalVersionAttribute>()!.InformationalVersion.Split('+')[0]);
collector.ResourceExpector.Expect("telemetry.auto.version", OpenTelemetry.AutoInstrumentation.Constants.Tracer.Version);
EnableOnlyHttpClientTraceInstrumentation();
EnableBytecodeInstrumentation();
RunTestApplication();
collector.ResourceExpector.AssertExpectations();
}
#endif
[Fact]
[Trait("Category", "EndToEnd")]
public void OtlpTracesExporter()
{
using var collector = new MockSpansCollector(Output);
SetExporter(collector);
collector.Expect("MyCompany.MyProduct.MyLibrary", span => span.Name == "SayHello");
EnableOnlyHttpClientTraceInstrumentation();
SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_ADDITIONAL_SOURCES", "MyCompany.MyProduct.MyLibrary");
RunTestApplication();
collector.AssertExpectations();
}
[Fact]
[Trait("Category", "EndToEnd")]
public void ZipkinExporter()
{
using var collector = new MockZipkinCollector(Output);
collector.Expect(span => span.Name == "SayHello" && span.Tags?.GetValueOrDefault("otel.library.name") == "MyCompany.MyProduct.MyLibrary");
EnableOnlyHttpClientTraceInstrumentation();
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, because of https://github.com/dotnet/runtime/issues/28658#issuecomment-462062760
[Fact]
[Trait("Category", "EndToEnd")]
public void PrometheusExporter()
{
EnableOnlyHttpClientTraceInstrumentation();
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");
};
assert.Should().NotThrowAfterAsync(
waitTime: 1.Minutes(),
pollInterval: 1.Seconds());
}
finally
{
if (helper?.Process != null && !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 NET6_0_OR_GREATER // The feature is not supported on .NET Framework
[Fact]
[Trait("Category", "EndToEnd")]
public void SubmitLogs()
{
using var collector = new MockLogsCollector(Output);
SetExporter(collector);
collector.Expect(logRecord => Convert.ToString(logRecord.Body) == "{ \"stringValue\": \"Example log message\" }");
EnableOnlyHttpClientTraceInstrumentation();
SetEnvironmentVariable("OTEL_DOTNET_AUTO_LOGS_INCLUDE_FORMATTED_MESSAGE", "true");
EnableBytecodeInstrumentation();
RunTestApplication();
collector.AssertExpectations();
}
[Theory]
[InlineData("OTEL_DOTNET_AUTO_INSTRUMENTATION_ENABLED", "false")]
[InlineData("OTEL_DOTNET_AUTO_LOGS_INSTRUMENTATION_ENABLED", "false")]
[InlineData("OTEL_DOTNET_AUTO_LOGS_ENABLED", "false")]
[InlineData("OTEL_LOGS_EXPORTER", "none")]
[Trait("Category", "EndToEnd")]
public void LogsNoneInstrumentations(string envVarName, string envVarVal)
{
using var collector = new MockLogsCollector(Output);
SetExporter(collector);
EnableOnlyHttpClientTraceInstrumentation();
SetEnvironmentVariable(envVarName, envVarVal);
EnableBytecodeInstrumentation();
RunTestApplication();
collector.AssertEmpty();
}
#endif
[Theory]
[InlineData("OTEL_DOTNET_AUTO_INSTRUMENTATION_ENABLED", "false")]
[InlineData("OTEL_DOTNET_AUTO_TRACES_INSTRUMENTATION_ENABLED", "false")]
[InlineData("OTEL_TRACES_EXPORTER", "none")]
[Trait("Category", "EndToEnd")]
public void TracesNoneInstrumentations(string envVarName, string envVarVal)
{
using var collector = new MockSpansCollector(Output);
SetExporter(collector);
SetEnvironmentVariable(envVarName, envVarVal);
RunTestApplication();
collector.AssertEmpty();
}
[Theory]
[InlineData("OTEL_DOTNET_AUTO_INSTRUMENTATION_ENABLED", "false")]
[InlineData("OTEL_DOTNET_AUTO_METRICS_INSTRUMENTATION_ENABLED", "false")]
[InlineData("OTEL_DOTNET_AUTO_METRICS_ENABLED", "false")]
[InlineData("OTEL_METRICS_EXPORTER", "none")]
[Trait("Category", "EndToEnd")]
public void MetricsNoneInstrumentations(string envVarName, string envVarVal)
{
using var collector = new MockMetricsCollector(Output);
SetExporter(collector);
EnableOnlyHttpClientTraceInstrumentation();
SetEnvironmentVariable(envVarName, envVarVal);
RunTestApplication();
collector.AssertEmpty();
}
[Fact]
[Trait("Category", "EndToEnd")]
public void LogsDisabledInstrumentation()
{
using var collector = new MockLogsCollector(Output);
SetEnvironmentVariable("OTEL_DOTNET_AUTO_LOGS_DISABLED_INSTRUMENTATIONS", "ILogger");
EnableOnlyHttpClientTraceInstrumentation();
EnableBytecodeInstrumentation();
RunTestApplication();
collector.AssertEmpty();
}
[Fact]
[Trait("Category", "EndToEnd")]
public void MetricsDisabledInstrumentation()
{
using var collector = new MockMetricsCollector(Output);
SetEnvironmentVariable("OTEL_DOTNET_AUTO_METRICS_HTTPCLIENT_INSTRUMENTATION_ENABLED", "false");
EnableOnlyHttpClientTraceInstrumentation();
EnableBytecodeInstrumentation();
RunTestApplication();
collector.AssertEmpty();
}
[Fact]
[Trait("Category", "EndToEnd")]
public void TracesDisabledInstrumentation()
{
using var collector = new MockSpansCollector(Output);
SetExporter(collector);
SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_HTTPCLIENT_INSTRUMENTATION_ENABLED", "false");
RunTestApplication();
collector.AssertEmpty();
}
[Fact]
[Trait("Category", "EndToEnd")]
public void ApplicationFailFastDisabled()
{
SetEnvironmentVariable("OTEL_DOTNET_AUTO_EXCLUDE_PROCESSES", $"dotnet,dotnet.exe,{EnvironmentHelper.FullTestApplicationName},{EnvironmentHelper.FullTestApplicationName}.exe");
SetEnvironmentVariable("OTEL_DOTNET_AUTO_FAIL_FAST_ENABLED", "false");
VerifyTestApplicationNotInstrumented();
}
[Theory]
[Trait("Category", "EndToEnd")]
[InlineData("OTEL_TRACES_EXPORTER", "non-supported")]
[InlineData("OTEL_DOTNET_AUTO_EXCLUDE_PROCESSES", $"dotnet,dotnet.exe,TestApplication.Smoke,TestApplication.Smoke.exe")]
public void ApplicationFailFastEnabled(string additionalVariableKey, string additionalVariableValue)
{
SetEnvironmentVariable("OTEL_DOTNET_AUTO_FAIL_FAST_ENABLED", "true");
SetEnvironmentVariable(additionalVariableKey, additionalVariableValue);
using var process = StartTestApplication();
using var helper = new ProcessHelper(process);
process.Should().NotBeNull();
var processTimeout = !process!.WaitForExit((int)TestTimeout.ProcessExit.TotalMilliseconds);
if (processTimeout)
{
process.Kill();
}
Output.WriteResult(helper);
processTimeout.Should().BeFalse();
process!.ExitCode.Should().NotBe(0);
}
private void VerifyTestApplicationInstrumented()
{
using var collector = new MockSpansCollector(Output);
SetExporter(collector);
collector.Expect("MyCompany.MyProduct.MyLibrary");
#if NETFRAMEWORK
collector.Expect("OpenTelemetry.Instrumentation.Http.HttpWebRequest");
#elif NET7_0_OR_GREATER
collector.Expect("System.Net.Http");
#else
collector.Expect("OpenTelemetry.Instrumentation.Http.HttpClient");
#endif
EnableOnlyHttpClientTraceInstrumentation();
SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_ADDITIONAL_SOURCES", "MyCompany.MyProduct.MyLibrary");
RunTestApplication();
collector.AssertExpectations();
}
private void VerifyTestApplicationNotInstrumented()
{
using var collector = new MockSpansCollector(Output);
SetExporter(collector);
EnableOnlyHttpClientTraceInstrumentation();
SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_ADDITIONAL_SOURCES", "MyCompany.MyProduct.MyLibrary");
RunTestApplication();
collector.AssertEmpty();
}
private void EnableOnlyHttpClientTraceInstrumentation()
{
SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_INSTRUMENTATION_ENABLED", "false");
SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_HTTPCLIENT_INSTRUMENTATION_ENABLED", "true");
}
}