314 lines
12 KiB
C#
314 lines
12 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.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<AssemblyFileVersionAttribute>().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<AssemblyFileVersionAttribute>().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<AssemblyFileVersionAttribute>().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());
|
|
}
|
|
}
|