Replace LegacyMockZipkinCollector with MockSpansCollector (#1471)

This commit is contained in:
Robert Pająk 2022-10-20 14:20:37 +02:00 committed by GitHub
parent 0168588f1b
commit e8bcd8d01e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 96 additions and 163 deletions

View File

@ -47,10 +47,7 @@ public class StrongNamedValidation
internal static CallTargetState OnMethodBegin<TTarget>(TTarget instance)
{
using var activity = ValidationActivitySource.StartActivity(nameof(StrongNamedValidation));
activity.AddTag("validation", nameof(StrongNamedValidation));
Console.WriteLine($"Validation: {nameof(StrongNamedValidation)}");
return CallTargetState.GetDefault();
}
}

View File

@ -15,12 +15,9 @@
// </copyright>
#if NETFRAMEWORK
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using FluentAssertions;
using FluentAssertions.Execution;
using IntegrationTests.Helpers;
using Xunit;
using Xunit.Abstractions;
@ -40,6 +37,9 @@ public class DomainNeutralTests : TestHelper
{
EnvironmentTools.IsWindowsAdministrator().Should().BeTrue();
using var collector = await MockSpansCollector.Start(Output);
collector.Expect("TestApplication.StrongNamedValidation");
// Add the necessary assembly to the GAC so it can be loaded as domain-neutral.
var instrumentationAssembly = Path.Combine(
EnvironmentTools.GetSolutionDirectory(),
@ -55,22 +55,10 @@ public class DomainNeutralTests : TestHelper
var assemblyPath = GetTestAssemblyPath();
var integrationsFile = Path.Combine(assemblyPath, "StrongNamedTestsIntegrations.json");
File.Exists(integrationsFile).Should().BeTrue();
SetEnvironmentVariable("OTEL_DOTNET_AUTO_INTEGRATIONS_FILE", integrationsFile);
RunTestApplication(otlpTraceCollectorPort: collector.Port);
using var agent = await LegacyMockZipkinCollector.Start(Output);
RunTestApplication(agent.Port);
const int expectedSpansCount = 1;
var spans = await agent.WaitForSpansAsync(expectedSpansCount);
using (new AssertionScope())
{
spans.Count.Should().Be(expectedSpansCount);
spans.Count(s => s.Tags["validation"] == "StrongNamedValidation").Should().Be(1);
}
collector.AssertExpectations();
}
}
#endif

View File

@ -14,11 +14,7 @@
// limitations under the License.
// </copyright>
using System;
using System.Linq;
using System.Threading.Tasks;
using FluentAssertions;
using FluentAssertions.Execution;
using IntegrationTests.Helpers;
using Xunit;
using Xunit.Abstractions;
@ -36,22 +32,14 @@ public class GrpcNetClientTests : TestHelper
[Trait("Category", "EndToEnd")]
public async Task SubmitsTraces()
{
using var agent = await LegacyMockZipkinCollector.Start(Output);
using var collector = await MockSpansCollector.Start(Output);
collector.Expect("OpenTelemetry.Instrumentation.GrpcNetClient");
// Grpc.Net.Client is using various version of http communication under the hood.
// Disabling HttpClient instrumentation to have consistent set of spans.
SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_DISABLED_INSTRUMENTATIONS", "HttpClient");
// Enabling only GrpcNetClient instrumentation to have consistent set of spans.
SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_ENABLED_INSTRUMENTATIONS", "GrpcNetClient");
RunTestApplication(otlpTraceCollectorPort: collector.Port, enableClrProfiler: !IsCoreClr());
RunTestApplication(agent.Port, enableClrProfiler: !IsCoreClr());
const int expectedSpansCount = 1;
var spans = await agent.WaitForSpansAsync(expectedSpansCount);
using (new AssertionScope())
{
spans.Count.Should().Be(expectedSpansCount);
spans.Count(s => s.Tags["otel.library.name"] == "OpenTelemetry.Instrumentation.GrpcNetClient").Should().Be(1);
}
collector.AssertExpectations();
}
}

View File

@ -146,6 +146,15 @@ public class MockSpansCollector : IDisposable
}
}
public void AssertEmpty(TimeSpan? timeout = null)
{
timeout ??= DefaultWaitTimeout;
if (_spans.TryTake(out var resourceSpan, timeout.Value))
{
Assert.Fail($"Expected nothing, but got: {resourceSpan}");
}
}
private static void FailExpectations(
List<Expectation> missingExpectations,
List<Collected> expectationsMet,

View File

@ -14,10 +14,7 @@
// limitations under the License.
// </copyright>
using System;
using System.Linq;
using System.Threading.Tasks;
using FluentAssertions;
using IntegrationTests.Helpers;
using Xunit;
using Xunit.Abstractions;
@ -27,14 +24,12 @@ namespace IntegrationTests;
[Collection(PostgresCollection.Name)]
public class NpqsqlTests : TestHelper
{
private const string ServiceName = "TestApplication.Npgsql";
private readonly PostgresFixture _postgres;
public NpqsqlTests(ITestOutputHelper output, PostgresFixture postgres)
: base("Npgsql", output)
{
_postgres = postgres;
SetEnvironmentVariable("OTEL_SERVICE_NAME", ServiceName);
}
[Fact]
@ -42,12 +37,11 @@ public class NpqsqlTests : TestHelper
[Trait("Containers", "Linux")]
public async Task SubmitsTraces()
{
using var agent = await LegacyMockZipkinCollector.Start(Output);
using var collector = await MockSpansCollector.Start(Output);
collector.Expect("Npgsql");
RunTestApplication(agent.Port, arguments: $"--postgres {_postgres.Port}", enableClrProfiler: !IsCoreClr());
var spans = await agent.WaitForSpansAsync(1);
RunTestApplication(otlpTraceCollectorPort: collector.Port, arguments: $"--postgres {_postgres.Port}", enableClrProfiler: !IsCoreClr());
spans.Count.Should().Be(1);
spans.First().Tags["db.statement"].Should().Be("SELECT 123;");
collector.AssertExpectations();
}
}

View File

@ -15,7 +15,6 @@
// </copyright>
using System.Threading.Tasks;
using FluentAssertions;
using IntegrationTests.Helpers;
using Xunit;
using Xunit.Abstractions;
@ -33,13 +32,13 @@ public class PluginsTests : TestHelper
[Trait("Category", "EndToEnd")]
public async Task SubmitsTraces()
{
using var collector = await MockSpansCollector.Start(Output);
collector.Expect("MyCompany.MyProduct.MyLibrary");
SetEnvironmentVariable("OTEL_DOTNET_AUTO_PLUGINS", "TestApplication.Plugins.Plugin, TestApplication.Plugins, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
RunTestApplication(otlpTraceCollectorPort: collector.Port);
using var collector = await LegacyMockZipkinCollector.Start(Output);
RunTestApplication(collector.Port);
var spans = await collector.WaitForSpansAsync(1);
spans.Should().Contain(x => x.Name == "SayHello");
collector.AssertExpectations();
}
[Fact]

View File

@ -14,11 +14,7 @@
// limitations under the License.
// </copyright>
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
@ -26,13 +22,14 @@ using System.Threading.Tasks;
using FluentAssertions;
using FluentAssertions.Extensions;
using IntegrationTests.Helpers;
using IntegrationTests.Helpers.Mocks;
using IntegrationTests.Helpers.Models;
using Xunit;
using Xunit.Abstractions;
#if NETFRAMEWORK
using IntegrationTests.Helpers.Compatibility;
#else
using System;
using System.Collections.Generic;
#endif
namespace IntegrationTests;
@ -52,22 +49,18 @@ public class SmokeTests : TestHelper
[Trait("Category", "EndToEnd")]
public async Task SubmitsTraces()
{
var spans = await RunTestApplicationAsync();
AssertAllSpansReceived(spans);
await VerifyTestApplicationInstrumented();
}
[Fact]
[Trait("Category", "EndToEnd")]
public async Task WhenStartupHookIsNotEnabled()
{
var spans = await RunTestApplicationAsync(enableStartupHook: false);
#if NETFRAMEWORK
AssertAllSpansReceived(spans);
await VerifyTestApplicationInstrumented(enableStartupHook: false);
#else
// on .NET Core it is required to set DOTNET_STARTUP_HOOKS
AssertNoSpansReceived(spans);
await VerifyTestApplicationNotInstrumented(enableStartupHook: false);
#endif
}
@ -75,13 +68,11 @@ public class SmokeTests : TestHelper
[Trait("Category", "EndToEnd")]
public async Task WhenClrProfilerIsNotEnabled()
{
var spans = await RunTestApplicationAsync(enableClrProfiler: false);
#if NETFRAMEWORK
// on .NET Framework it is required to set the CLR .NET Profiler
AssertNoSpansReceived(spans);
await VerifyTestApplicationNotInstrumented(enableClrProfiler: false);
#else
AssertAllSpansReceived(spans);
await VerifyTestApplicationInstrumented(enableClrProfiler: false);
#endif
}
@ -91,9 +82,7 @@ public class SmokeTests : TestHelper
{
SetEnvironmentVariable("OTEL_DOTNET_AUTO_EXCLUDE_PROCESSES", "dotnet,dotnet.exe");
var spans = await RunTestApplicationAsync();
AssertAllSpansReceived(spans);
await VerifyTestApplicationInstrumented();
}
[Fact]
@ -102,9 +91,7 @@ public class SmokeTests : TestHelper
{
SetEnvironmentVariable("OTEL_DOTNET_AUTO_EXCLUDE_PROCESSES", $"dotnet,dotnet.exe,{EnvironmentHelper.FullTestApplicationName},{EnvironmentHelper.FullTestApplicationName}.exe");
var spans = await RunTestApplicationAsync();
AssertNoSpansReceived(spans);
await VerifyTestApplicationNotInstrumented();
}
[Fact]
@ -113,14 +100,12 @@ public class SmokeTests : TestHelper
{
SetEnvironmentVariable("OTEL_DOTNET_AUTO_INCLUDE_PROCESSES", "dotnet,dotnet.exe");
var spans = await RunTestApplicationAsync();
#if NETFRAMEWORK
AssertNoSpansReceived(spans);
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
AssertAllSpansReceived(spans);
await VerifyTestApplicationInstrumented();
#endif
}
@ -130,9 +115,7 @@ public class SmokeTests : TestHelper
{
SetEnvironmentVariable("OTEL_DOTNET_AUTO_INCLUDE_PROCESSES", $"{EnvironmentHelper.FullTestApplicationName},{EnvironmentHelper.FullTestApplicationName}.exe");
var spans = await RunTestApplicationAsync();
AssertAllSpansReceived(spans);
await VerifyTestApplicationInstrumented();
}
[Fact]
@ -291,41 +274,29 @@ public class SmokeTests : TestHelper
}
#endif
private static void AssertNoSpansReceived(IImmutableList<IMockSpan> spans)
private async Task VerifyTestApplicationInstrumented(bool enableStartupHook = true, bool enableClrProfiler = true)
{
Assert.True(spans.Count == 0, $"Expecting no spans, received {spans.Count}");
}
private static void AssertAllSpansReceived(IImmutableList<IMockSpan> spans)
{
var expectedSpanCount = 2;
Assert.True(spans.Count() == expectedSpanCount, $"Expecting {expectedSpanCount} spans, received {spans.Count()}");
if (expectedSpanCount > 0)
{
Assert.Single(spans.Select(s => s.Service).Distinct());
var expectations = new List<SpanExpectation>();
expectations.Add(new SpanExpectation(ServiceName, "1.0.0", "SayHello", "MyCompany.MyProduct.MyLibrary", ActivityKind.Internal));
using var collector = await MockSpansCollector.Start(Output);
collector.Expect("MyCompany.MyProduct.MyLibrary");
#if NETFRAMEWORK
expectations.Add(new SpanExpectation(ServiceName, "1.0.0.0", "HTTP GET", "OpenTelemetry.HttpWebRequest", ActivityKind.Client));
collector.Expect("OpenTelemetry.HttpWebRequest");
#else
expectations.Add(new SpanExpectation(ServiceName, "1.0.0.0", "HTTP GET", "OpenTelemetry.Instrumentation.Http", ActivityKind.Client));
collector.Expect("OpenTelemetry.Instrumentation.Http");
#endif
SpanTestHelpers.AssertExpectationsMet(expectations, spans);
}
SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_ADDITIONAL_SOURCES", "MyCompany.MyProduct.MyLibrary");
RunTestApplication(otlpTraceCollectorPort: collector.Port, enableStartupHook: enableStartupHook, enableClrProfiler: enableClrProfiler);
collector.AssertExpectations();
}
private async Task<IImmutableList<IMockSpan>> RunTestApplicationAsync(bool enableStartupHook = true, bool enableClrProfiler = true)
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);
using var agent = await LegacyMockZipkinCollector.Start(Output);
RunTestApplication(agent.Port, enableStartupHook: enableStartupHook, enableClrProfiler: enableClrProfiler);
// The process emitting the spans already finished by this time, there is no need to wait more,
// just get the spans received so far.
return await agent.WaitForSpansAsync(count: 2, timeout: TimeSpan.Zero);
collector.AssertEmpty(5.Seconds());
}
}

View File

@ -14,11 +14,7 @@
// limitations under the License.
// </copyright>
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using FluentAssertions;
using FluentAssertions.Execution;
using IntegrationTests.Helpers;
using Xunit;
using Xunit.Abstractions;
@ -28,14 +24,12 @@ namespace IntegrationTests;
[Collection(SqlServerCollection.Name)]
public class SqlClientTests : TestHelper
{
private const string ServiceName = "TestApplication.SqlClient";
private readonly SqlServerFixture _sqlServerFixture;
public SqlClientTests(ITestOutputHelper output, SqlServerFixture sqlServerFixture)
: base("SqlClient", output)
{
_sqlServerFixture = sqlServerFixture;
SetEnvironmentVariable("OTEL_SERVICE_NAME", ServiceName);
}
[Fact]
@ -43,21 +37,11 @@ public class SqlClientTests : TestHelper
[Trait("Containers", "Linux")]
public async Task SubmitTraces()
{
using var agent = await LegacyMockZipkinCollector.Start(Output);
using var collector = await MockSpansCollector.Start(Output);
collector.Expect("OpenTelemetry.SqlClient");
const int expectedSpanCount = 8;
RunTestApplication(otlpTraceCollectorPort: collector.Port, arguments: $"{_sqlServerFixture.Password} {_sqlServerFixture.Port}", enableClrProfiler: !IsCoreClr());
RunTestApplication(agent.Port, arguments: $"{_sqlServerFixture.Password} {_sqlServerFixture.Port}", enableClrProfiler: !IsCoreClr());
var spans = await agent.WaitForSpansAsync(expectedSpanCount);
using (new AssertionScope())
{
spans.Count.Should().Be(expectedSpanCount);
foreach (var span in spans)
{
span.Tags.Should().Contain(new KeyValuePair<string, string>("otel.library.name", "OpenTelemetry.SqlClient"));
}
}
collector.AssertExpectations();
}
}

View File

@ -14,12 +14,9 @@
// limitations under the License.
// </copyright>
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using FluentAssertions;
using FluentAssertions.Execution;
using IntegrationTests.Helpers;
using Xunit;
using Xunit.Abstractions;
@ -36,24 +33,15 @@ public class StrongNamedTests : TestHelper
[Fact]
public async Task SubmitsTraces()
{
using var collector = await MockSpansCollector.Start(Output);
collector.Expect("TestApplication.StrongNamedValidation");
var assemblyPath = GetTestAssemblyPath();
var integrationsFile = Path.Combine(assemblyPath, "StrongNamedTestsIntegrations.json");
File.Exists(integrationsFile).Should().BeTrue();
SetEnvironmentVariable("OTEL_DOTNET_AUTO_INTEGRATIONS_FILE", integrationsFile);
RunTestApplication(otlpTraceCollectorPort: collector.Port);
using var agent = await LegacyMockZipkinCollector.Start(Output);
RunTestApplication(agent.Port);
const int expectedSpansCount = 1;
var spans = await agent.WaitForSpansAsync(expectedSpansCount);
using (new AssertionScope())
{
spans.Count.Should().Be(expectedSpansCount);
spans.Count(s => s.Tags["validation"] == "StrongNamedValidation").Should().Be(1);
}
collector.AssertExpectations();
}
}

View File

@ -46,7 +46,7 @@ internal class WcfServerTestHelper : TestHelper
var testSettings = new TestSettings
{
TracesSettings = new TracesSettings { Port = traceAgentPort }
OtlpTracesSettings = new OtlpTracesSettings { Port = traceAgentPort }
};
// clear all relevant environment variables to start with a clean slate
@ -102,6 +102,24 @@ internal class WcfServerTestHelper : TestHelper
environmentVariables["OTEL_EXPORTER_ZIPKIN_ENDPOINT"] = $"http://localhost:{testSettings.TracesSettings.Port}/api/v2/spans";
}
if (testSettings.OtlpTracesSettings != null)
{
environmentVariables["OTEL_TRACES_EXPORTER"] = testSettings.OtlpTracesSettings.Exporter;
environmentVariables["OTEL_EXPORTER_OTLP_ENDPOINT"] = $"http://localhost:{testSettings.OtlpTracesSettings.Port}";
}
if (testSettings.MetricsSettings != null)
{
environmentVariables["OTEL_METRICS_EXPORTER"] = testSettings.MetricsSettings.Exporter;
environmentVariables["OTEL_EXPORTER_OTLP_ENDPOINT"] = $"http://localhost:{testSettings.MetricsSettings.Port}";
}
if (testSettings.LogSettings != null)
{
environmentVariables["OTEL_LOGS_EXPORTER"] = testSettings.LogSettings.Exporter;
environmentVariables["OTEL_EXPORTER_OTLP_ENDPOINT"] = $"http://localhost:{testSettings.LogSettings.Port}";
}
environmentVariables["OTEL_DOTNET_AUTO_TRACES_ADDITIONAL_SOURCES"] = "TestApplication.*";
foreach (var key in EnvironmentHelper.CustomEnvironmentVariables.Keys)

View File

@ -18,10 +18,10 @@ using System;
using System.Net.Sockets;
using System.Threading.Tasks;
using FluentAssertions;
using FluentAssertions.Execution;
using IntegrationTests.Helpers;
using Xunit;
using Xunit.Abstractions;
using static OpenTelemetry.Proto.Trace.V1.Span.Types;
namespace IntegrationTests;
public abstract class WcfTestsBase : TestHelper, IDisposable
@ -38,25 +38,22 @@ public abstract class WcfTestsBase : TestHelper, IDisposable
[Trait("Category", "EndToEnd")]
public async Task SubmitsTraces()
{
using var agent = await LegacyMockZipkinCollector.Start(Output);
EnvironmentTools.IsWindowsAdministrator().Should().BeTrue(); // WCF Server needs admin
using var collector = await MockSpansCollector.Start(Output);
// the test app makes 2 calls (therefore we exepct 4 spans)
collector.Expect("OpenTelemetry.Instrumentation.Wcf", span => span.Kind == SpanKind.Server, "Server 1");
collector.Expect("OpenTelemetry.Instrumentation.Wcf", span => span.Kind == SpanKind.Client, "Client 1");
collector.Expect("OpenTelemetry.Instrumentation.Wcf", span => span.Kind == SpanKind.Server, "Server 2");
collector.Expect("OpenTelemetry.Instrumentation.Wcf", span => span.Kind == SpanKind.Client, "Client 2");
var serverHelper = new WcfServerTestHelper(Output);
_serverProcess = serverHelper.RunWcfServer(agent.Port);
_serverProcess = serverHelper.RunWcfServer(collector.Port);
await WaitForServer();
RunTestApplication(agent.Port);
RunTestApplication(otlpTraceCollectorPort: collector.Port);
// wait so the spans from server are delivered
await Task.Delay(2000);
var spans = await agent.WaitForSpansAsync(4);
using var scope = new AssertionScope();
spans.Count.Should().Be(4);
foreach (var span in spans)
{
span.Tags["otel.library.name"].Should().Be("OpenTelemetry.Instrumentation.Wcf");
}
collector.AssertExpectations();
}
public void Dispose()