[WCF] use wcf client instrumentation from contrib (#2876)

This commit is contained in:
Mateusz Łach 2023-08-28 23:20:47 +02:00 committed by GitHub
parent c0d389fbe2
commit afd8a8b4cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 467 additions and 716 deletions

View File

@ -10,6 +10,7 @@ This component adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.h
### Added
- Added support for Azure SDK traces instrumentation on .NET Framework.
- Added support for `WCFCLIENT` instrumentation on .NET.
### Changed

View File

@ -11,6 +11,6 @@
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.5.1" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.5.1" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.5.1-beta.1" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Wcf" Version="1.0.0-rc.10" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Wcf" Version="1.0.0-rc.11" />
</ItemGroup>
</Project>

View File

@ -201,6 +201,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApplication.OpenTracing
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApplication.Azure", "test\test-applications\integrations\TestApplication.Azure\TestApplication.Azure.csproj", "{3A125210-A784-4982-ACDB-C3442E414E44}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApplication.Wcf.Client.DotNet", "test\test-applications\integrations\TestApplication.Wcf.Client.DotNet\TestApplication.Wcf.Client.DotNet.csproj", "{EDE168E0-DBCD-4DE3-B55A-4B633ED6565E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -949,6 +951,18 @@ Global
{3A125210-A784-4982-ACDB-C3442E414E44}.Release|x64.Build.0 = Release|x64
{3A125210-A784-4982-ACDB-C3442E414E44}.Release|x86.ActiveCfg = Release|x86
{3A125210-A784-4982-ACDB-C3442E414E44}.Release|x86.Build.0 = Release|x86
{EDE168E0-DBCD-4DE3-B55A-4B633ED6565E}.Debug|Any CPU.ActiveCfg = Debug|x64
{EDE168E0-DBCD-4DE3-B55A-4B633ED6565E}.Debug|Any CPU.Build.0 = Debug|x64
{EDE168E0-DBCD-4DE3-B55A-4B633ED6565E}.Debug|x64.ActiveCfg = Debug|x64
{EDE168E0-DBCD-4DE3-B55A-4B633ED6565E}.Debug|x64.Build.0 = Debug|x64
{EDE168E0-DBCD-4DE3-B55A-4B633ED6565E}.Debug|x86.ActiveCfg = Debug|x86
{EDE168E0-DBCD-4DE3-B55A-4B633ED6565E}.Debug|x86.Build.0 = Debug|x86
{EDE168E0-DBCD-4DE3-B55A-4B633ED6565E}.Release|Any CPU.ActiveCfg = Release|x64
{EDE168E0-DBCD-4DE3-B55A-4B633ED6565E}.Release|Any CPU.Build.0 = Release|x64
{EDE168E0-DBCD-4DE3-B55A-4B633ED6565E}.Release|x64.ActiveCfg = Release|x64
{EDE168E0-DBCD-4DE3-B55A-4B633ED6565E}.Release|x64.Build.0 = Release|x64
{EDE168E0-DBCD-4DE3-B55A-4B633ED6565E}.Release|x86.ActiveCfg = Release|x86
{EDE168E0-DBCD-4DE3-B55A-4B633ED6565E}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1021,6 +1035,7 @@ Global
{BB720565-B83C-497F-ACF9-B0D13642DA19} = {E409ADD3-9574-465C-AB09-4324D205CC7C}
{C66927FD-ED1F-4079-8733-51AB7463060D} = {E409ADD3-9574-465C-AB09-4324D205CC7C}
{3A125210-A784-4982-ACDB-C3442E414E44} = {E409ADD3-9574-465C-AB09-4324D205CC7C}
{EDE168E0-DBCD-4DE3-B55A-4B633ED6565E} = {E409ADD3-9574-465C-AB09-4324D205CC7C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {160A1D00-1F5B-40F8-A155-621B4459D78F}

View File

@ -141,5 +141,13 @@ public static class LibraryVersion
new("2.6.122"),
}
},
{
"TestApplication.Wcf.Client.DotNet",
new List<PackageBuildInfo>
{
new("4.10.2"),
new("6.0.0"),
}
},
};
}

View File

@ -124,27 +124,27 @@ for example `TRACES`, and `{0}` is the case-sensitive name of the instrumentatio
Traces are stable, but particular instrumentation are in Experimental status
due to lack of stable semantic convention.
| ID | Instrumented library | Supported versions | Instrumentation type | Status |
|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------|
| `ASPNET` | ASP.NET (.NET Framework) MVC / WebApi \[1\] **Not supported on .NET** | * | source & bytecode | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `ASPNETCORE` | ASP.NET Core **Not supported on .NET Framework** | * | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `AZURE` | [Azure SDK](https://azure.github.io/azure-sdk/releases/latest/index.html) | \[2\] | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `ELASTICSEARCH` | [Elastic.Clients.Elasticsearch](https://www.nuget.org/packages/Elastic.Clients.Elasticsearch) | ≥8.0.0 | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `ENTITYFRAMEWORKCORE` | [Microsoft.EntityFrameworkCore](https://www.nuget.org/packages/) **Not supported on .NET Framework** | ≥6.0.12 | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `GRAPHQL` | [GraphQL](https://www.nuget.org/packages/GraphQL) **Not supported on .NET Framework** | ≥7.5.0 | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `GRPCNETCLIENT` | [Grpc.Net.Client](https://www.nuget.org/packages/Grpc.Net.Client) | ≥2.52.0 & < 3.0.0 | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `HTTPCLIENT` | [System.Net.Http.HttpClient](https://docs.microsoft.com/dotnet/api/system.net.http.httpclient) and [System.Net.HttpWebRequest](https://docs.microsoft.com/dotnet/api/system.net.httpwebrequest) | * | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `QUARTZ` | [Quartz](https://www.nuget.org/packages/Quartz) **Not supported on .NET Framework 4.7.1 and older** | ≥3.4.0 | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `MASSTRANSIT` | [MassTransit](https://www.nuget.org/packages/MassTransit) **Not supported on .NET Framework** | ≥8.0.0 | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `MONGODB` | [MongoDB.Driver.Core](https://www.nuget.org/packages/MongoDB.Driver.Core) | ≥2.13.3 & < 3.0.0 | source & bytecode | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `MYSQLCONNECTOR` | [MySqlConnector](https://www.nuget.org/packages/MySqlConnector) | ≥2.0.0 | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `MYSQLDATA` | [MySql.Data](https://www.nuget.org/packages/MySql.Data) **Not supported on .NET Framework** | ≥8.1.0 | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `NPGSQL` | [Npgsql](https://www.nuget.org/packages/Npgsql) | ≥6.0.0 | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `NSERVICEBUS` | [NServiceBus](https://www.nuget.org/packages/NServiceBus) | ≥8.0.0 | source & bytecode | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `SQLCLIENT` | [Microsoft.Data.SqlClient](https://www.nuget.org/packages/Microsoft.Data.SqlClient) and [System.Data.SqlClient](https://www.nuget.org/packages/System.Data.SqlClient) | * \[3\] | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `STACKEXCHANGEREDIS` | [StackExchange.Redis](https://www.nuget.org/packages/StackExchange.Redis) **Not supported on .NET Framework** | ≥2.0.405 < 3.0.0 | source & bytecode | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `WCFCLIENT` | [System.ServiceModel](https://www.nuget.org/packages/System.ServiceModel) **Not supported on .NET**. | ≥4.0.0.0 < 5.0.0.0 | source & bytecode | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `WCFSERVICE` | [System.ServiceModel](https://www.nuget.org/packages/System.ServiceModel) **Not supported on .NET**. | ≥4.0.0.0 < 5.0.0.0 | source & bytecode | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| ID | Instrumented library | Supported versions | Instrumentation type | Status |
|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------|
| `ASPNET` | ASP.NET (.NET Framework) MVC / WebApi \[1\] **Not supported on .NET** | * | source & bytecode | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `ASPNETCORE` | ASP.NET Core **Not supported on .NET Framework** | * | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `AZURE` | [Azure SDK](https://azure.github.io/azure-sdk/releases/latest/index.html) | \[2\] | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `ELASTICSEARCH` | [Elastic.Clients.Elasticsearch](https://www.nuget.org/packages/Elastic.Clients.Elasticsearch) | ≥8.0.0 | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `ENTITYFRAMEWORKCORE` | [Microsoft.EntityFrameworkCore](https://www.nuget.org/packages/) **Not supported on .NET Framework** | ≥6.0.12 | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `GRAPHQL` | [GraphQL](https://www.nuget.org/packages/GraphQL) **Not supported on .NET Framework** | ≥7.5.0 | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `GRPCNETCLIENT` | [Grpc.Net.Client](https://www.nuget.org/packages/Grpc.Net.Client) | ≥2.52.0 & < 3.0.0 | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `HTTPCLIENT` | [System.Net.Http.HttpClient](https://docs.microsoft.com/dotnet/api/system.net.http.httpclient) and [System.Net.HttpWebRequest](https://docs.microsoft.com/dotnet/api/system.net.httpwebrequest) | * | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `QUARTZ` | [Quartz](https://www.nuget.org/packages/Quartz) **Not supported on .NET Framework 4.7.1 and older** | ≥3.4.0 | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `MASSTRANSIT` | [MassTransit](https://www.nuget.org/packages/MassTransit) **Not supported on .NET Framework** | ≥8.0.0 | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `MONGODB` | [MongoDB.Driver.Core](https://www.nuget.org/packages/MongoDB.Driver.Core) | ≥2.13.3 & < 3.0.0 | source & bytecode | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `MYSQLCONNECTOR` | [MySqlConnector](https://www.nuget.org/packages/MySqlConnector) | ≥2.0.0 | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `MYSQLDATA` | [MySql.Data](https://www.nuget.org/packages/MySql.Data) **Not supported on .NET Framework** | ≥8.1.0 | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `NPGSQL` | [Npgsql](https://www.nuget.org/packages/Npgsql) | ≥6.0.0 | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `NSERVICEBUS` | [NServiceBus](https://www.nuget.org/packages/NServiceBus) | ≥8.0.0 | source & bytecode | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `SQLCLIENT` | [Microsoft.Data.SqlClient](https://www.nuget.org/packages/Microsoft.Data.SqlClient) and [System.Data.SqlClient](https://www.nuget.org/packages/System.Data.SqlClient) | * \[3\] | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `STACKEXCHANGEREDIS` | [StackExchange.Redis](https://www.nuget.org/packages/StackExchange.Redis) **Not supported on .NET Framework** | ≥2.0.405 < 3.0.0 | source & bytecode | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `WCFCLIENT` | WCF **Limited support on .NET, see [wcf-config.md](./wcf-config.md)** | * | source & bytecode | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
| `WCFSERVICE` | WCF **Not supported on .NET**. | * | source & bytecode | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
\[1\]: Only integrated pipeline mode is supported.

View File

@ -8,16 +8,11 @@ Example project available in
[test/test-applications/integrations/TestApplication.Wcf.Client.NetFramework](../test/test-applications/integrations/TestApplication.Wcf.Client.NetFramework/)
folder.
⚠️ **NOTICE:** Instrumentation of
[APM-style](https://learn.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/asynchronous-programming-model-apm)
calls is supported, but have known limitations.
It is recommended to convert them to
[TAP-style](https://learn.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap)
calls.
## WCF Client Configuration (.NET)
Instrumentation for WCF Client on .NET is not supported.
Example project available in
[test/test-applications/integrations/TestApplication.Wcf.Client.DotNet](../test/test-applications/integrations/TestApplication.Wcf.Client.DotNet/)
folder.
## WCF Server Configuration (.NET Framework)

View File

@ -52,7 +52,7 @@ void CorProfiler::InitNetFxAssemblyRedirectsMap()
{ L"OpenTelemetry.Instrumentation.Quartz", {1, 0, 0, 3} },
{ L"OpenTelemetry.Instrumentation.Runtime", {1, 5, 0, 0} },
{ L"OpenTelemetry.Instrumentation.SqlClient", {1, 0, 0, 0} },
{ L"OpenTelemetry.Instrumentation.Wcf", {1, 0, 0, 10} },
{ L"OpenTelemetry.Instrumentation.Wcf", {1, 0, 0, 11} },
{ L"OpenTelemetry.ResourceDetectors.Azure", {1, 0, 0, 2} },
{ L"OpenTelemetry.ResourceDetectors.Container", {1, 0, 0, 4} },
{ L"OpenTelemetry.Shims.OpenTracing", {1, 0, 0, 0} },

View File

@ -20,7 +20,6 @@ OpenTelemetry.AutoInstrumentation.Instrumentations.AspNet.HttpModuleIntegration
OpenTelemetry.AutoInstrumentation.Instrumentations.MongoDB.MongoClientIntegration
OpenTelemetry.AutoInstrumentation.Instrumentations.NServiceBus.EndpointConfigurationIntegration
OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Service.ServiceHostIntegration
OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client.InvokeServiceChannelProxyIntegration
OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client.WcfClientIntegration
override OpenTelemetry.AutoInstrumentation.CallTarget.CallTargetReturn<T>.ToString() -> string!
override OpenTelemetry.AutoInstrumentation.CallTarget.CallTargetState.ToString() -> string!

View File

@ -21,6 +21,7 @@ OpenTelemetry.AutoInstrumentation.Instrumentations.MongoDB.MongoClientIntegratio
OpenTelemetry.AutoInstrumentation.Instrumentations.NServiceBus.EndpointConfigurationIntegration
OpenTelemetry.AutoInstrumentation.Instrumentations.StackExchangeRedis.StackExchangeRedisIntegration
OpenTelemetry.AutoInstrumentation.Instrumentations.StackExchangeRedis.StackExchangeRedisIntegrationAsync
OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client.WcfClientIntegration
override OpenTelemetry.AutoInstrumentation.CallTarget.CallTargetReturn<T>.ToString() -> string!
override OpenTelemetry.AutoInstrumentation.CallTarget.CallTargetState.ToString() -> string!
static OpenTelemetry.AutoInstrumentation.CallTarget.CallTargetInvoker.BeginMethod<TIntegration, TTarget, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8>(TTarget instance, ref TArg1 arg1, ref TArg2 arg2, ref TArg3 arg3, ref TArg4 arg4, ref TArg5 arg5, ref TArg6 arg6, ref TArg7 arg7, ref TArg8 arg8) -> OpenTelemetry.AutoInstrumentation.CallTarget.CallTargetState

View File

@ -74,13 +74,11 @@ internal static class DelayedInitialization
}
#endif
#if NETFRAMEWORK
[MethodImpl(MethodImplOptions.NoInlining)]
public static void AddWcf(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager)
{
lazyInstrumentationLoader.Add(new WcfInitializer(pluginManager));
}
#endif
[MethodImpl(MethodImplOptions.NoInlining)]
public static void AddQuartz(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager)

View File

@ -29,13 +29,17 @@ internal static class EnvironmentConfigurationTracerHelper
TracerSettings settings,
PluginManager pluginManager)
{
// ensure WcfInitializer is added only once,
// it is needed when either WcfClient or WcfService instrumentations are enabled
// to initialize WcfInstrumentationOptions
var wcfInstrumentationAdded = false;
foreach (var enabledInstrumentation in settings.EnabledInstrumentations)
{
_ = enabledInstrumentation switch
{
#if NETFRAMEWORK
TracerInstrumentation.AspNet => Wrappers.AddAspNetInstrumentation(builder, pluginManager, lazyInstrumentationLoader),
TracerInstrumentation.WcfService => Wrappers.AddWcfInstrumentation(builder, pluginManager, lazyInstrumentationLoader),
TracerInstrumentation.WcfService => AddWcfIfNeeded(builder, pluginManager, lazyInstrumentationLoader, ref wcfInstrumentationAdded),
#endif
TracerInstrumentation.GrpcNetClient => Wrappers.AddGrpcClientInstrumentation(builder, pluginManager, lazyInstrumentationLoader),
TracerInstrumentation.HttpClient => Wrappers.AddHttpClientInstrumentation(builder, pluginManager, lazyInstrumentationLoader),
@ -47,6 +51,7 @@ internal static class EnvironmentConfigurationTracerHelper
TracerInstrumentation.MongoDB => builder.AddSource("MongoDB.Driver.Core.Extensions.DiagnosticSources"),
TracerInstrumentation.MySqlConnector => builder.AddSource("MySqlConnector"),
TracerInstrumentation.Azure => Wrappers.AddAzureInstrumentation(builder),
TracerInstrumentation.WcfClient => AddWcfIfNeeded(builder, pluginManager, lazyInstrumentationLoader, ref wcfInstrumentationAdded),
#if NET6_0_OR_GREATER
TracerInstrumentation.AspNetCore => Wrappers.AddAspNetCoreInstrumentation(builder, pluginManager, lazyInstrumentationLoader),
TracerInstrumentation.MassTransit => builder.AddSource("MassTransit"),
@ -74,6 +79,23 @@ internal static class EnvironmentConfigurationTracerHelper
return builder;
}
private static TracerProviderBuilder AddWcfIfNeeded(
TracerProviderBuilder tracerProviderBuilder,
PluginManager pluginManager,
LazyInstrumentationLoader lazyInstrumentationLoader,
ref bool wcfInstrumentationAdded)
{
if (wcfInstrumentationAdded)
{
return tracerProviderBuilder;
}
Wrappers.AddWcfInstrumentation(tracerProviderBuilder, pluginManager, lazyInstrumentationLoader);
wcfInstrumentationAdded = true;
return tracerProviderBuilder;
}
private static TracerProviderBuilder SetSampler(this TracerProviderBuilder builder, TracerSettings settings)
{
if (settings.TracesSampler == null)
@ -115,7 +137,6 @@ internal static class EnvironmentConfigurationTracerHelper
{
// Instrumentations
#if NETFRAMEWORK
[MethodImpl(MethodImplOptions.NoInlining)]
public static TracerProviderBuilder AddWcfInstrumentation(TracerProviderBuilder builder, PluginManager pluginManager, LazyInstrumentationLoader lazyInstrumentationLoader)
{
@ -123,7 +144,6 @@ internal static class EnvironmentConfigurationTracerHelper
return builder.AddSource("OpenTelemetry.Instrumentation.Wcf");
}
#endif
[MethodImpl(MethodImplOptions.NoInlining)]
public static TracerProviderBuilder AddHttpClientInstrumentation(TracerProviderBuilder builder, PluginManager pluginManager, LazyInstrumentationLoader lazyInstrumentationLoader)

View File

@ -111,12 +111,11 @@ internal enum TracerInstrumentation
/// </summary>
AspNetCore = 15,
#endif
#if NETFRAMEWORK
/// <summary>
/// WcfClient instrumentation.
/// </summary>
WcfClient = 16,
#endif
/// <summary>
/// MySqlConnector instrumentation.

View File

@ -19,7 +19,7 @@ internal static partial class InstrumentationDefinitions
private static NativeCallTargetDefinition[] GetDefinitionsArray()
{
var nativeCallTargetDefinitions = new List<NativeCallTargetDefinition>(10);
var nativeCallTargetDefinitions = new List<NativeCallTargetDefinition>(9);
// Traces
var tracerSettings = Instrumentation.TracerSettings.Value;
if (tracerSettings.TracesEnabled)
@ -45,11 +45,10 @@ internal static partial class InstrumentationDefinitions
// WcfClient
if (tracerSettings.EnabledInstrumentations.Contains(TracerInstrumentation.WcfClient))
{
nativeCallTargetDefinitions.Add(new("System.ServiceModel", "System.ServiceModel.Channels.ServiceChannelProxy", "Invoke", new[] {"System.Runtime.Remoting.Messaging.IMessage", "System.Runtime.Remoting.Messaging.IMessage"}, 4, 0, 0, 4, 65535, 65535, AssemblyFullName, "OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client.InvokeServiceChannelProxyIntegration"));
nativeCallTargetDefinitions.Add(new("System.ServiceModel", "System.ServiceModel.ChannelFactory", "InitializeEndpoint", new[] {"System.Void", "System.String", "System.ServiceModel.EndpointAddress"}, 4, 0, 0, 4, 65535, 65535, AssemblyFullName, "OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client.WcfClientIntegration"));
nativeCallTargetDefinitions.Add(new("System.ServiceModel", "System.ServiceModel.ChannelFactory", "InitializeEndpoint", new[] {"System.Void", "System.String", "System.ServiceModel.EndpointAddress", "System.Configuration.Configuration"}, 4, 0, 0, 4, 65535, 65535, AssemblyFullName, "OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client.WcfClientIntegration"));
nativeCallTargetDefinitions.Add(new("System.ServiceModel", "System.ServiceModel.ChannelFactory", "InitializeEndpoint", new[] {"System.Void", "System.ServiceModel.Description.ServiceEndpoint"}, 4, 0, 0, 4, 65535, 65535, AssemblyFullName, "OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client.WcfClientIntegration"));
nativeCallTargetDefinitions.Add(new("System.ServiceModel", "System.ServiceModel.ChannelFactory", "InitializeEndpoint", new[] {"System.Void", "System.ServiceModel.Channels.Binding", "System.ServiceModel.EndpointAddress"}, 4, 0, 0, 4, 65535, 65535, AssemblyFullName, "OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client.WcfClientIntegration"));
nativeCallTargetDefinitions.Add(new("System.ServiceModel", "System.ServiceModel.ChannelFactory", "InitializeEndpoint", new[] {"System.Void", "System.String", "System.ServiceModel.EndpointAddress", "System.Configuration.Configuration"}, 4, 0, 0, 4, 65535, 65535, AssemblyFullName, "OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client.WcfClientIntegration"));
}
// WcfService

View File

@ -19,7 +19,7 @@ internal static partial class InstrumentationDefinitions
private static NativeCallTargetDefinition[] GetDefinitionsArray()
{
var nativeCallTargetDefinitions = new List<NativeCallTargetDefinition>(11);
var nativeCallTargetDefinitions = new List<NativeCallTargetDefinition>(17);
// Traces
var tracerSettings = Instrumentation.TracerSettings.Value;
if (tracerSettings.TracesEnabled)
@ -47,6 +47,17 @@ internal static partial class InstrumentationDefinitions
nativeCallTargetDefinitions.Add(new("StackExchange.Redis", "StackExchange.Redis.ConnectionMultiplexer", "ConnectImplAsync", new[] {"System.Threading.Tasks.Task`1[StackExchange.Redis.ConnectionMultiplexer]", "StackExchange.Redis.ConfigurationOptions", "System.IO.TextWriter"}, 2, 0, 0, 2, 65535, 65535, AssemblyFullName, "OpenTelemetry.AutoInstrumentation.Instrumentations.StackExchangeRedis.StackExchangeRedisIntegrationAsync"));
nativeCallTargetDefinitions.Add(new("StackExchange.Redis", "StackExchange.Redis.ConnectionMultiplexer", "ConnectImplAsync", new[] {"System.Threading.Tasks.Task`1[StackExchange.Redis.ConnectionMultiplexer]", "StackExchange.Redis.ConfigurationOptions", "System.IO.TextWriter", "System.Nullable`1[StackExchange.Redis.ServerType]"}, 2, 0, 0, 2, 65535, 65535, AssemblyFullName, "OpenTelemetry.AutoInstrumentation.Instrumentations.StackExchangeRedis.StackExchangeRedisIntegrationAsync"));
}
// WcfClient
if (tracerSettings.EnabledInstrumentations.Contains(TracerInstrumentation.WcfClient))
{
nativeCallTargetDefinitions.Add(new("System.Private.ServiceModel", "System.ServiceModel.ChannelFactory", "InitializeEndpoint", new[] {"System.Void", "System.String", "System.ServiceModel.EndpointAddress"}, 4, 0, 0, 4, 65535, 65535, AssemblyFullName, "OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client.WcfClientIntegration"));
nativeCallTargetDefinitions.Add(new("System.Private.ServiceModel", "System.ServiceModel.ChannelFactory", "InitializeEndpoint", new[] {"System.Void", "System.ServiceModel.Description.ServiceEndpoint"}, 4, 0, 0, 4, 65535, 65535, AssemblyFullName, "OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client.WcfClientIntegration"));
nativeCallTargetDefinitions.Add(new("System.Private.ServiceModel", "System.ServiceModel.ChannelFactory", "InitializeEndpoint", new[] {"System.Void", "System.ServiceModel.Channels.Binding", "System.ServiceModel.EndpointAddress"}, 4, 0, 0, 4, 65535, 65535, AssemblyFullName, "OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client.WcfClientIntegration"));
nativeCallTargetDefinitions.Add(new("System.ServiceModel.Primitives", "System.ServiceModel.ChannelFactory", "InitializeEndpoint", new[] {"System.Void", "System.String", "System.ServiceModel.EndpointAddress"}, 6, 0, 0, 6, 65535, 65535, AssemblyFullName, "OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client.WcfClientIntegration"));
nativeCallTargetDefinitions.Add(new("System.ServiceModel.Primitives", "System.ServiceModel.ChannelFactory", "InitializeEndpoint", new[] {"System.Void", "System.ServiceModel.Description.ServiceEndpoint"}, 6, 0, 0, 6, 65535, 65535, AssemblyFullName, "OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client.WcfClientIntegration"));
nativeCallTargetDefinitions.Add(new("System.ServiceModel.Primitives", "System.ServiceModel.ChannelFactory", "InitializeEndpoint", new[] {"System.Void", "System.ServiceModel.Channels.Binding", "System.ServiceModel.EndpointAddress"}, 6, 0, 0, 6, 65535, 65535, AssemblyFullName, "OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client.WcfClientIntegration"));
}
}
// Logs

View File

@ -262,6 +262,10 @@ internal static class Instrumentation
private static void AddLazilyLoadedTraceInstrumentations(LazyInstrumentationLoader lazyInstrumentationLoader, PluginManager pluginManager, TracerSettings tracerSettings)
{
// ensure WcfInitializer is added only once,
// it is needed when either WcfClient or WcfService instrumentations are enabled
// to initialize WcfInstrumentationOptions
var wcfInstrumentationAdded = false;
foreach (var instrumentation in tracerSettings.EnabledInstrumentations)
{
switch (instrumentation)
@ -271,7 +275,7 @@ internal static class Instrumentation
DelayedInitialization.Traces.AddAspNet(lazyInstrumentationLoader, pluginManager);
break;
case TracerInstrumentation.WcfService:
DelayedInitialization.Traces.AddWcf(lazyInstrumentationLoader, pluginManager);
AddWcfIfNeeded(lazyInstrumentationLoader, pluginManager, ref wcfInstrumentationAdded);
break;
#endif
case TracerInstrumentation.HttpClient:
@ -286,6 +290,9 @@ internal static class Instrumentation
case TracerInstrumentation.Quartz:
DelayedInitialization.Traces.AddQuartz(lazyInstrumentationLoader, pluginManager);
break;
case TracerInstrumentation.WcfClient:
AddWcfIfNeeded(lazyInstrumentationLoader, pluginManager, ref wcfInstrumentationAdded);
break;
#if NET6_0_OR_GREATER
case TracerInstrumentation.AspNetCore:
DelayedInitialization.Traces.AddAspNetCore(lazyInstrumentationLoader, pluginManager);
@ -327,6 +334,20 @@ internal static class Instrumentation
}
}
private static void AddWcfIfNeeded(
LazyInstrumentationLoader lazyInstrumentationLoader,
PluginManager pluginManager,
ref bool wcfInstrumentationAdded)
{
if (wcfInstrumentationAdded)
{
return;
}
DelayedInitialization.Traces.AddWcf(lazyInstrumentationLoader, pluginManager);
wcfInstrumentationAdded = true;
}
private static void OnExit(object? sender, EventArgs e)
{
if (Interlocked.Exchange(ref _isExiting, value: 1) != 0)

View File

@ -1,237 +0,0 @@
// <copyright file="InvokeServiceChannelProxyIntegration.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>
#if NETFRAMEWORK
using System.Diagnostics;
using OpenTelemetry.AutoInstrumentation.CallTarget;
using OpenTelemetry.AutoInstrumentation.DuckTyping;
using OpenTelemetry.AutoInstrumentation.Util;
namespace OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client;
/// <summary>
/// InvokeService integration.
/// </summary>
[InstrumentMethod(
assemblyName: WcfCommonConstants.ServiceModelAssemblyName,
typeName: WcfClientConstants.ServiceChannelProxyTypeName,
methodName: WcfClientConstants.InvokeMethodName,
returnTypeName: WcfClientConstants.MessageTypeName,
parameterTypeNames: new[] { WcfClientConstants.MessageTypeName },
minimumVersion: WcfCommonConstants.MinVersion,
maximumVersion: WcfCommonConstants.MaxVersion,
integrationName: WcfClientConstants.IntegrationName,
type: InstrumentationType.Trace)]
public static class InvokeServiceChannelProxyIntegration
{
private static object? _serviceEnum;
private static object? _beginServiceEnum;
private static object? _taskServiceEnum;
internal interface IServiceProxyChannel
{
IMethodData GetMethodData(object message);
}
internal interface IMethodData
{
object MethodType { get; }
}
private interface IExceptionReturnMessage
{
Exception? Exception { get; }
}
private interface IReturnMessage
{
object ReturnValue { get; }
}
private interface ISendAsyncResult
{
[DuckField(Name = "exception")]
Exception? Exception { get; }
[DuckField(Name = "callback")]
AsyncCallback? Callback { get; set; }
}
/// <summary>
/// OnMethodBegin callback
/// </summary>
/// <typeparam name="TTarget">Type of the target</typeparam>
/// <typeparam name="TMessage">Type of the message</typeparam>
/// <param name="instance">Instance value, aka `this` of the instrumented method.</param>
/// <param name="message">Message instance</param>
/// <returns>CallTarget state value</returns>
internal static CallTargetState OnMethodBegin<TTarget, TMessage>(TTarget instance, TMessage message)
where TTarget : IServiceProxyChannel
{
var methodData = instance.GetMethodData(message!);
var methodType = methodData.MethodType;
if (_serviceEnum == null || _beginServiceEnum == null || _taskServiceEnum == null)
{
InitializeServiceEnums(methodType);
}
if (IsOfSupportedType(methodType))
{
var activity = WcfClientCommon.StartActivity();
return new CallTargetState(activity: activity, methodType);
}
return CallTargetState.GetDefault();
}
/// <summary>
/// OnMethodEnd callback
/// </summary>
/// <param name="returnValue">Return value</param>
/// <param name="exception">Exception value</param>
/// <param name="callTargetState">CallTarget state</param>
/// <typeparam name="TTarget">Type of the target</typeparam>
/// <typeparam name="TReturn">Return type</typeparam>
/// <returns>A response value, in an async scenario will be T of Task of T</returns>
internal static CallTargetReturn<TReturn> OnMethodEnd<TTarget, TReturn>(TReturn returnValue, Exception? exception, in CallTargetState callTargetState)
{
var activity = callTargetState.Activity;
if (activity is null)
{
return new CallTargetReturn<TReturn>(returnValue);
}
if (exception is not null)
{
StopWithException(exception, activity);
return new CallTargetReturn<TReturn>(returnValue);
}
var methodType = callTargetState.State;
if (methodType is null)
{
return new CallTargetReturn<TReturn>(returnValue);
}
if (methodType.Equals(_serviceEnum))
{
if (returnValue.TryDuckCast<IExceptionReturnMessage>(out var returnMessage))
{
CompleteSync(returnMessage.Exception, activity);
}
}
else
{
try
{
if (!returnValue.TryDuckCast<IReturnMessage>(out var returnMessage))
{
return new CallTargetReturn<TReturn>(returnValue);
}
if (methodType.Equals(_taskServiceEnum))
{
AttachActivityCompletionContinuation(returnMessage, activity);
}
else if (methodType.Equals(_beginServiceEnum))
{
OverrideCallback(returnMessage.ReturnValue, activity);
}
}
finally
{
Activity.Current = activity.Parent;
}
}
return new CallTargetReturn<TReturn>(returnValue);
}
private static void OverrideCallback(object sendAsyncResult, Activity activity)
{
var duckCastedAsyncResult = DuckCast(sendAsyncResult);
var initialCallback = duckCastedAsyncResult.Callback;
duckCastedAsyncResult.Callback = NewCallback;
void NewCallback(IAsyncResult asyncResult)
{
var e = duckCastedAsyncResult.Exception;
if (e != null)
{
activity.SetException(e);
activity.Stop();
}
initialCallback?.Invoke(asyncResult);
}
}
private static ISendAsyncResult DuckCast(object sendAsyncResult)
{
// we are interested in private fields of grandparent type
var targetType = sendAsyncResult.GetType().BaseType.BaseType;
var createTypeResult = DuckType.CreateCache<ISendAsyncResult>.GetProxy(targetType);
return createTypeResult.CreateInstance<ISendAsyncResult>(sendAsyncResult);
}
private static void StopWithException(Exception exception, Activity activity)
{
activity.SetException(exception);
activity.Stop();
}
private static bool IsOfSupportedType(object methodType)
{
return methodType.Equals(_serviceEnum) || methodType.Equals(_beginServiceEnum) || methodType.Equals(_taskServiceEnum);
}
private static void InitializeServiceEnums(object methodType)
{
var type = methodType.GetType();
_serviceEnum = Enum.Parse(type, "Service");
_beginServiceEnum = Enum.Parse(type, "BeginService");
_taskServiceEnum = Enum.Parse(type, "TaskService");
}
private static void AttachActivityCompletionContinuation(IReturnMessage returnValue, Activity activity)
{
var task = returnValue.ReturnValue as Task;
task?.ContinueWith(
(t, a) =>
{
var localActivity = a as Activity;
if (t.Exception is not null)
{
localActivity.SetException(t.Exception.InnerException);
localActivity?.Stop();
}
},
activity,
continuationOptions: TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.HideScheduler);
}
private static void CompleteSync(Exception? ex, Activity activity)
{
if (ex != null)
{
activity.SetException(ex);
activity.Stop();
}
}
}
#endif

View File

@ -13,7 +13,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
#if NETFRAMEWORK
using OpenTelemetry.AutoInstrumentation.Configurations;
namespace OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client;
@ -21,28 +20,10 @@ namespace OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client;
internal static class WcfClientConstants
{
public const string ChannelFactoryTypeName = "System.ServiceModel.ChannelFactory";
public const string ServiceChannelProxyTypeName = "System.ServiceModel.Channels.ServiceChannelProxy";
public const string MessageTypeName = "System.Runtime.Remoting.Messaging.IMessage";
public const string InitializeEndpointMethodName = "InitializeEndpoint";
public const string InvokeMethodName = "Invoke";
public const string IntegrationName = nameof(TracerInstrumentation.WcfClient);
public const string EndpointAddressTypeName = "System.ServiceModel.EndpointAddress";
public const string ServiceEndpointTypeName = "System.ServiceModel.Description.ServiceEndpoint";
public const string BindingTypeName = "System.ServiceModel.Channels.Binding";
public const string ConfigurationTypeName = "System.Configuration.Configuration";
// source originated from: https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/06b9a286a6ab2af5257ce26b5dcb6fac56112f96/src/OpenTelemetry.Instrumentation.Wcf
public const string RpcSystemTag = "rpc.system";
public const string RpcServiceTag = "rpc.service";
public const string RpcMethodTag = "rpc.method";
public const string NetPeerNameTag = "net.peer.name";
public const string NetPeerPortTag = "net.peer.port";
public const string SoapMessageVersionTag = "soap.message_version";
public const string SoapReplyActionTag = "soap.reply_action";
public const string SoapViaTag = "soap.via";
public const string WcfChannelSchemeTag = "wcf.channel.scheme";
public const string WcfChannelPathTag = "wcf.channel.path";
public const string WcfSystemValue = "dotnet_wcf";
}
#endif

View File

@ -1,58 +0,0 @@
// <copyright file="WcfClientEndpointBehavior.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>
#if NETFRAMEWORK
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
// source originated from: https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/06b9a286a6ab2af5257ce26b5dcb6fac56112f96/src/OpenTelemetry.Instrumentation.Wcf
namespace OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client;
/// <inheritdoc />
internal class WcfClientEndpointBehavior : IEndpointBehavior
{
/// <inheritdoc />
public void Validate(ServiceEndpoint endpoint)
{
}
/// <inheritdoc />
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
/// <inheritdoc />
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
}
/// <inheritdoc />
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
var actionMappings = new Dictionary<string, ActionMetadata>(StringComparer.OrdinalIgnoreCase);
foreach (var clientOperation in clientRuntime.ClientOperations)
{
actionMappings[clientOperation.Action] = new ActionMetadata($"{clientRuntime.ContractNamespace}{clientRuntime.ContractName}", clientOperation.Name);
}
clientRuntime.ClientMessageInspectors.Add(new WcfClientMessageInspector(actionMappings));
}
}
#endif

View File

@ -14,7 +14,7 @@
// limitations under the License.
// </copyright>
#if NETFRAMEWORK
using OpenTelemetry.Instrumentation.Wcf;
namespace OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client;
@ -33,10 +33,9 @@ internal static class WcfClientInitializer
public static void Initialize(IChannelFactory channelFactory)
{
var behaviors = channelFactory.Endpoint.Behaviors;
if (!behaviors.Contains(typeof(WcfClientEndpointBehavior)))
if (!behaviors.Contains(typeof(TelemetryEndpointBehavior)))
{
behaviors.Add(new WcfClientEndpointBehavior());
behaviors.Add(new TelemetryEndpointBehavior());
}
}
}
#endif

View File

@ -13,7 +13,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
#if NETFRAMEWORK
using OpenTelemetry.AutoInstrumentation.CallTarget;
@ -28,18 +27,8 @@ namespace OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client;
methodName: WcfClientConstants.InitializeEndpointMethodName,
returnTypeName: ClrNames.Void,
parameterTypeNames: new[] { ClrNames.String, WcfClientConstants.EndpointAddressTypeName },
minimumVersion: WcfCommonConstants.MinVersion,
maximumVersion: WcfCommonConstants.MaxVersion,
integrationName: WcfClientConstants.IntegrationName,
type: InstrumentationType.Trace)]
[InstrumentMethod(
assemblyName: WcfCommonConstants.ServiceModelAssemblyName,
typeName: WcfClientConstants.ChannelFactoryTypeName,
methodName: WcfClientConstants.InitializeEndpointMethodName,
returnTypeName: ClrNames.Void,
parameterTypeNames: new[] { ClrNames.String, WcfClientConstants.EndpointAddressTypeName, WcfClientConstants.ConfigurationTypeName },
minimumVersion: WcfCommonConstants.MinVersion,
maximumVersion: WcfCommonConstants.MaxVersion,
minimumVersion: WcfCommonConstants.Min4Version,
maximumVersion: WcfCommonConstants.Max4Version,
integrationName: WcfClientConstants.IntegrationName,
type: InstrumentationType.Trace)]
[InstrumentMethod(
@ -48,8 +37,8 @@ namespace OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client;
methodName: WcfClientConstants.InitializeEndpointMethodName,
returnTypeName: ClrNames.Void,
parameterTypeNames: new[] { WcfClientConstants.ServiceEndpointTypeName },
minimumVersion: WcfCommonConstants.MinVersion,
maximumVersion: WcfCommonConstants.MaxVersion,
minimumVersion: WcfCommonConstants.Min4Version,
maximumVersion: WcfCommonConstants.Max4Version,
integrationName: WcfClientConstants.IntegrationName,
type: InstrumentationType.Trace)]
[InstrumentMethod(
@ -58,10 +47,54 @@ namespace OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client;
methodName: WcfClientConstants.InitializeEndpointMethodName,
returnTypeName: ClrNames.Void,
parameterTypeNames: new[] { WcfClientConstants.BindingTypeName, WcfClientConstants.EndpointAddressTypeName },
minimumVersion: WcfCommonConstants.MinVersion,
maximumVersion: WcfCommonConstants.MaxVersion,
minimumVersion: WcfCommonConstants.Min4Version,
maximumVersion: WcfCommonConstants.Max4Version,
integrationName: WcfClientConstants.IntegrationName,
type: InstrumentationType.Trace)]
#if NET6_0_OR_GREATER
[InstrumentMethod(
assemblyName: WcfCommonConstants.ServiceModelPrimitivesAssemblyName,
typeName: WcfClientConstants.ChannelFactoryTypeName,
methodName: WcfClientConstants.InitializeEndpointMethodName,
returnTypeName: ClrNames.Void,
parameterTypeNames: new[] { ClrNames.String, WcfClientConstants.EndpointAddressTypeName },
minimumVersion: WcfCommonConstants.Min6Version,
maximumVersion: WcfCommonConstants.Max6Version,
integrationName: WcfClientConstants.IntegrationName,
type: InstrumentationType.Trace)]
[InstrumentMethod(
assemblyName: WcfCommonConstants.ServiceModelPrimitivesAssemblyName,
typeName: WcfClientConstants.ChannelFactoryTypeName,
methodName: WcfClientConstants.InitializeEndpointMethodName,
returnTypeName: ClrNames.Void,
parameterTypeNames: new[] { WcfClientConstants.ServiceEndpointTypeName },
minimumVersion: WcfCommonConstants.Min6Version,
maximumVersion: WcfCommonConstants.Max6Version,
integrationName: WcfClientConstants.IntegrationName,
type: InstrumentationType.Trace)]
[InstrumentMethod(
assemblyName: WcfCommonConstants.ServiceModelPrimitivesAssemblyName,
typeName: WcfClientConstants.ChannelFactoryTypeName,
methodName: WcfClientConstants.InitializeEndpointMethodName,
returnTypeName: ClrNames.Void,
parameterTypeNames: new[] { WcfClientConstants.BindingTypeName, WcfClientConstants.EndpointAddressTypeName },
minimumVersion: WcfCommonConstants.Min6Version,
maximumVersion: WcfCommonConstants.Max6Version,
integrationName: WcfClientConstants.IntegrationName,
type: InstrumentationType.Trace)]
#endif
#if NETFRAMEWORK
[InstrumentMethod(
assemblyName: WcfCommonConstants.ServiceModelAssemblyName,
typeName: WcfClientConstants.ChannelFactoryTypeName,
methodName: WcfClientConstants.InitializeEndpointMethodName,
returnTypeName: ClrNames.Void,
parameterTypeNames: new[] { ClrNames.String, WcfClientConstants.EndpointAddressTypeName, WcfClientConstants.ConfigurationTypeName },
minimumVersion: WcfCommonConstants.Min4Version,
maximumVersion: WcfCommonConstants.Max4Version,
integrationName: WcfClientConstants.IntegrationName,
type: InstrumentationType.Trace)]
#endif
public static class WcfClientIntegration
{
/// <summary>
@ -80,4 +113,3 @@ public static class WcfClientIntegration
return CallTargetReturn.GetDefault();
}
}
#endif

View File

@ -1,136 +0,0 @@
// <copyright file="WcfClientMessageInspector.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>
#if NETFRAMEWORK
using System.Diagnostics;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using OpenTelemetry.Context.Propagation;
using OpenTelemetry.Trace;
using Status = OpenTelemetry.Trace.Status;
// source originated from: https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/06b9a286a6ab2af5257ce26b5dcb6fac56112f96/src/OpenTelemetry.Instrumentation.Wcf
namespace OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client;
/// <summary>
/// Telemetry client message inspector.
/// </summary>
internal class WcfClientMessageInspector : IClientMessageInspector
{
private readonly Dictionary<string, ActionMetadata> _actionMappings;
public WcfClientMessageInspector(Dictionary<string, ActionMetadata> actionMappings)
{
_actionMappings = actionMappings;
}
private static Action<Message, string, string> MessageHeaderValueSetter { get; }
= (request, name, value) => request.Headers.Add(MessageHeader.CreateHeader(name, "https://www.w3.org/TR/trace-context/", value, false));
/// <inheritdoc/>
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
var activity = Activity.Current;
if (activity is { Source.Name: "OpenTelemetry.AutoInstrumentation.Wcf" })
{
string action;
if (!string.IsNullOrEmpty(request.Headers.Action))
{
action = request.Headers.Action;
activity.DisplayName = action;
}
else
{
action = string.Empty;
}
Propagators.DefaultTextMapPropagator.Inject(
new PropagationContext(activity.Context, Baggage.Current),
request,
MessageHeaderValueSetter);
if (activity.IsAllDataRequested)
{
activity.SetTag(WcfClientConstants.RpcSystemTag, WcfClientConstants.WcfSystemValue);
if (!_actionMappings.TryGetValue(action, out ActionMetadata actionMetadata))
{
actionMetadata = new ActionMetadata(null, action);
}
activity.SetTag(WcfClientConstants.RpcServiceTag, actionMetadata.ContractName);
activity.SetTag(WcfClientConstants.RpcMethodTag, actionMetadata.OperationName);
activity.SetTag(WcfClientConstants.SoapMessageVersionTag, request.Version.ToString());
var remoteAddressUri = request.Headers.To ?? channel.RemoteAddress?.Uri;
if (remoteAddressUri != null)
{
activity.SetTag(WcfClientConstants.NetPeerNameTag, remoteAddressUri.Host);
activity.SetTag(WcfClientConstants.NetPeerPortTag, remoteAddressUri.Port);
activity.SetTag(WcfClientConstants.WcfChannelSchemeTag, remoteAddressUri.Scheme);
activity.SetTag(WcfClientConstants.WcfChannelPathTag, remoteAddressUri.LocalPath);
}
if (request.Properties.Via != null)
{
activity.SetTag(WcfClientConstants.SoapViaTag, request.Properties.Via.ToString());
}
}
return new CorrelationState(activity);
}
return new CorrelationState(null);
}
/// <inheritdoc />
public void AfterReceiveReply(ref Message reply, object correlationState)
{
var state = correlationState as CorrelationState;
if (state?.Activity is { IsStopped: false } activity)
{
if (activity.IsAllDataRequested)
{
if (reply.IsFault)
{
activity.SetStatus(Status.Error);
}
activity.SetTag(WcfClientConstants.SoapReplyActionTag, reply.Headers.Action);
}
activity.Stop();
}
}
// TODO: inline
private class CorrelationState
{
public CorrelationState(Activity? activity)
{
Activity = activity;
}
public Activity? Activity { get; }
}
}
#endif

View File

@ -13,7 +13,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
#if NETFRAMEWORK
namespace OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf;
internal interface IKeyedByTypeCollection
@ -22,4 +21,3 @@ internal interface IKeyedByTypeCollection
bool Contains(Type t);
}
#endif

View File

@ -27,8 +27,8 @@ namespace OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Service;
methodName: WcfServiceConstants.InitializeDescriptionMethodName,
returnTypeName: ClrNames.Void,
parameterTypeNames: new[] { WcfServiceConstants.UriSchemeKeyedCollectionTypeName },
minimumVersion: WcfCommonConstants.MinVersion,
maximumVersion: WcfCommonConstants.MaxVersion,
minimumVersion: WcfCommonConstants.Min4Version,
maximumVersion: WcfCommonConstants.Max4Version,
integrationName: WcfServiceConstants.IntegrationName,
type: InstrumentationType.Trace)]
public static class ServiceHostIntegration

View File

@ -13,13 +13,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
#if NETFRAMEWORK
namespace OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf;
internal static class WcfCommonConstants
{
#if NETFRAMEWORK
public const string ServiceModelAssemblyName = "System.ServiceModel";
public const string MinVersion = "4.0.0";
public const string MaxVersion = "4.*.*";
}
#else
public const string ServiceModelAssemblyName = "System.Private.ServiceModel";
public const string ServiceModelPrimitivesAssemblyName = "System.ServiceModel.Primitives";
public const string Min6Version = "6.0.0";
public const string Max6Version = "6.*.*";
#endif
public const string Min4Version = "4.0.0";
public const string Max4Version = "4.*.*";
}

View File

@ -14,8 +14,6 @@
// limitations under the License.
// </copyright>
#if NETFRAMEWORK
using OpenTelemetry.AutoInstrumentation.Plugins;
using OpenTelemetry.Instrumentation.Wcf;
@ -46,4 +44,3 @@ internal class WcfInitializer : InstrumentationInitializer
instrumentationType?.GetProperty("Options")?.SetValue(null, options);
}
}
#endif

View File

@ -40,6 +40,7 @@
<PackageReference Include="OpenTelemetry.ResourceDetectors.Azure" />
<PackageReference Include="OpenTelemetry.ResourceDetectors.Container" />
<PackageReference Include="OpenTelemetry.Shims.OpenTracing" />
<PackageReference Include="OpenTelemetry.Instrumentation.Wcf" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net462' ">
@ -47,7 +48,6 @@
<Reference Include="System.Configuration" />
<Reference Include="System.Web" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNet" />
<PackageReference Include="OpenTelemetry.Instrumentation.Wcf" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net6.0' ">

View File

@ -46,6 +46,8 @@
<PackageVersion Include="System.Reactive" Version="6.0.0" />
<PackageVersion Include="System.Runtime.InteropServices" Version="4.3.0" />
<PackageVersion Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.3.0" />
<PackageVersion Include="System.ServiceModel.Http" Version="6.0.0" />
<PackageVersion Include="System.ServiceModel.NetTcp" Version="6.0.0" />
<PackageVersion Include="System.ValueTuple" Version="4.5.0" />
<PackageVersion Include="Testcontainers" Version="3.4.0" />
<PackageVersion Include="Verify.Xunit" Version="20.8.2" />

View File

@ -34,13 +34,17 @@
/net/OpenTelemetry.Instrumentation.Runtime.dll,
/net/OpenTelemetry.Instrumentation.SqlClient.dll,
/net/OpenTelemetry.Instrumentation.StackExchangeRedis.dll,
/net/OpenTelemetry.Instrumentation.Wcf.dll,
/net/OpenTelemetry.ResourceDetectors.Azure.dll,
/net/OpenTelemetry.ResourceDetectors.Container.dll,
/net/OpenTelemetry.Shims.OpenTracing.dll,
/net/OpenTelemetry.dll,
/net/OpenTracing.dll,
/net/System.Diagnostics.DiagnosticSource.dll,
/net/System.Private.ServiceModel.dll,
/net/System.Security.Permissions.dll,
/net/System.ServiceModel.Primitives.dll,
/net/System.ServiceModel.dll,
/net/ruleEngine.json,
/store/x64/net6.0/microsoft.extensions.configuration.abstractions/7.0.0/lib/net7.0/Microsoft.Extensions.Configuration.Abstractions.dll,
/store/x64/net6.0/microsoft.extensions.configuration.binder/7.0.0/lib/net7.0/Microsoft.Extensions.Configuration.Binder.dll,

View File

@ -34,13 +34,17 @@
/net/OpenTelemetry.Instrumentation.Runtime.dll,
/net/OpenTelemetry.Instrumentation.SqlClient.dll,
/net/OpenTelemetry.Instrumentation.StackExchangeRedis.dll,
/net/OpenTelemetry.Instrumentation.Wcf.dll,
/net/OpenTelemetry.ResourceDetectors.Azure.dll,
/net/OpenTelemetry.ResourceDetectors.Container.dll,
/net/OpenTelemetry.Shims.OpenTracing.dll,
/net/OpenTelemetry.dll,
/net/OpenTracing.dll,
/net/System.Diagnostics.DiagnosticSource.dll,
/net/System.Private.ServiceModel.dll,
/net/System.Security.Permissions.dll,
/net/System.ServiceModel.Primitives.dll,
/net/System.ServiceModel.dll,
/net/ruleEngine.json,
/store/x64/net6.0/microsoft.extensions.configuration.abstractions/7.0.0/lib/net7.0/Microsoft.Extensions.Configuration.Abstractions.dll,
/store/x64/net6.0/microsoft.extensions.configuration.binder/7.0.0/lib/net7.0/Microsoft.Extensions.Configuration.Binder.dll,

View File

@ -33,13 +33,17 @@
/net/OpenTelemetry.Instrumentation.Runtime.dll,
/net/OpenTelemetry.Instrumentation.SqlClient.dll,
/net/OpenTelemetry.Instrumentation.StackExchangeRedis.dll,
/net/OpenTelemetry.Instrumentation.Wcf.dll,
/net/OpenTelemetry.ResourceDetectors.Azure.dll,
/net/OpenTelemetry.ResourceDetectors.Container.dll,
/net/OpenTelemetry.Shims.OpenTracing.dll,
/net/OpenTelemetry.dll,
/net/OpenTracing.dll,
/net/System.Diagnostics.DiagnosticSource.dll,
/net/System.Private.ServiceModel.dll,
/net/System.Security.Permissions.dll,
/net/System.ServiceModel.Primitives.dll,
/net/System.ServiceModel.dll,
/net/ruleEngine.json,
/osx-x64/OpenTelemetry.AutoInstrumentation.Native.dylib,
/store/x64/net6.0/microsoft.extensions.configuration.abstractions/7.0.0/lib/net7.0/Microsoft.Extensions.Configuration.Abstractions.dll,

View File

@ -33,13 +33,17 @@
\net\OpenTelemetry.Instrumentation.Runtime.dll,
\net\OpenTelemetry.Instrumentation.SqlClient.dll,
\net\OpenTelemetry.Instrumentation.StackExchangeRedis.dll,
\net\OpenTelemetry.Instrumentation.Wcf.dll,
\net\OpenTelemetry.ResourceDetectors.Azure.dll,
\net\OpenTelemetry.ResourceDetectors.Container.dll,
\net\OpenTelemetry.Shims.OpenTracing.dll,
\net\OpenTelemetry.dll,
\net\OpenTracing.dll,
\net\System.Diagnostics.DiagnosticSource.dll,
\net\System.Private.ServiceModel.dll,
\net\System.Security.Permissions.dll,
\net\System.ServiceModel.Primitives.dll,
\net\System.ServiceModel.dll,
\net\ruleEngine.json,
\netfx\Google.Protobuf.dll,
\netfx\Grpc.Core.Api.dll,

View File

@ -152,6 +152,15 @@ public static class LibraryVersion
new object[] { "2.5.61" },
new object[] { "2.6.66" },
new object[] { "2.6.122" },
#endif
};
public static readonly IReadOnlyCollection<object[]> WCFCoreClient = new List<object[]>
{
#if DEFAULT_TEST_PACKAGE_VERSIONS
new object[] { string.Empty }
#else
new object[] { "4.10.2" },
new object[] { "6.0.0" },
#endif
};
}

View File

@ -13,72 +13,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
#if NETFRAMEWORK
using IntegrationTests.Helpers;
using OpenTelemetry.Proto.Common.V1;
using OpenTelemetry.Proto.Trace.V1;
namespace IntegrationTests;
internal static class WcfClientInstrumentation
{
public const string NetTcpBindingMessageVersion = "Soap12 (http://www.w3.org/2003/05/soap-envelope) Addressing10 (http://www.w3.org/2005/08/addressing)";
public const string HttpBindingMessageVersion = "Soap11 (http://schemas.xmlsoap.org/soap/envelope/) AddressingNone (http://schemas.microsoft.com/ws/2005/05/addressing/none)";
public const string NetTcpChannelScheme = "net.tcp";
public const string HttpChannelScheme = "http";
public static bool ValidateBasicSpanExpectations(
Span span,
string expectedChannelScheme,
string expectedChannelPath,
string expectedPeerName,
int expectedPeerPort,
string expectedMessageVersion)
{
var attributes = span.Attributes;
var rpcSystem = ExtractAttribute(attributes, "rpc.system");
var rpcService = ExtractAttribute(attributes, "rpc.service");
var rpcMethod = ExtractAttribute(attributes, "rpc.method");
var soapMessageVersion = ExtractAttribute(attributes, "soap.message_version");
var netPeerPort = ExtractAttribute(attributes, "net.peer.port");
var netPeerName = ExtractAttribute(attributes, "net.peer.name");
var channelSchemeTag = ExtractAttribute(attributes, "wcf.channel.scheme");
var channelPath = ExtractAttribute(attributes, "wcf.channel.path");
return span.Kind == Span.Types.SpanKind.Client &&
rpcSystem.Value.StringValue == "dotnet_wcf" &&
rpcService.Value.StringValue == "http://opentelemetry.io/StatusService" &&
rpcMethod.Value.StringValue == "Ping" &&
netPeerName.Value.StringValue == expectedPeerName &&
netPeerPort.Value.IntValue == expectedPeerPort &&
channelSchemeTag.Value.StringValue == expectedChannelScheme &&
soapMessageVersion.Value.StringValue == expectedMessageVersion &&
channelPath.Value.StringValue == expectedChannelPath;
}
public static bool ValidateSpanSuccessStatus(Span span)
{
return span.Status == null;
}
public static bool ValidateExpectedSpanHierarchy(ICollection<MockSpansCollector.Collected> assertedSpans)
{
var customParent = assertedSpans.Single(collected =>
collected.InstrumentationScopeName == "TestApplication.Wcf.Client.NetFramework" &&
collected.InstrumentationScopeName.StartsWith("TestApplication.Wcf.Client") &&
collected.Span.Name == "Parent");
var customSibling = assertedSpans.Single(collected =>
collected.InstrumentationScopeName == "TestApplication.Wcf.Client.NetFramework" &&
collected.InstrumentationScopeName.StartsWith("TestApplication.Wcf.Client") &&
collected.Span.Name == "Sibling");
var wcfClientSpans = assertedSpans.Where(collected =>
collected.InstrumentationScopeName == "OpenTelemetry.AutoInstrumentation.Wcf");
collected.Span.Kind == Span.Types.SpanKind.Client &&
collected.InstrumentationScopeName == "OpenTelemetry.Instrumentation.Wcf");
return wcfClientSpans.All(span => span.Span.ParentSpanId == customParent.Span.SpanId) &&
customSibling.Span.ParentSpanId == customParent.Span.SpanId;
}
private static KeyValue ExtractAttribute(IEnumerable<KeyValue> attributes, string key)
{
return attributes.Single(kv => kv.Key == key);
}
}
#endif

View File

@ -0,0 +1,40 @@
// <copyright file="WcfDotNetTests.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>
// This test won't work outside of windows as it need the server side which is .NET Framework only.
#if NET6_0_OR_GREATER && _WINDOWS
using Xunit.Abstractions;
namespace IntegrationTests;
public class WcfDotNetTests : WcfTestsBase
{
public WcfDotNetTests(ITestOutputHelper output)
: base("Wcf.Client.DotNet", output)
{
}
[Trait("Category", "EndToEnd")]
[Theory]
[MemberData(nameof(LibraryVersion.WCFCoreClient), MemberType = typeof(LibraryVersion))]
public async Task SubmitTraces(string clientPackageVersion)
{
EnableBytecodeInstrumentation();
await SubmitsTracesInternal(clientPackageVersion);
}
}
#endif

View File

@ -18,7 +18,6 @@
using DotNet.Testcontainers.Builders;
using DotNet.Testcontainers.Containers;
using FluentAssertions;
using Google.Protobuf;
using IntegrationTests.Helpers;
using OpenTelemetry.Proto.Trace.V1;
using Xunit.Abstractions;
@ -27,8 +26,6 @@ namespace IntegrationTests;
public class WcfIISTests : TestHelper
{
private const string ExpectedChannelPath = "/StatusService.svc";
private const string ExpectedPeerName = "localhost";
private readonly Dictionary<string, string> _environmentVariables = new();
public WcfIISTests(ITestOutputHelper output)
@ -54,18 +51,11 @@ public class WcfIISTests : TestHelper
var netTcpPort = TcpPortProvider.GetOpenPort();
var httpPort = TcpPortProvider.GetOpenPort();
collector.Expect("OpenTelemetry.Instrumentation.Wcf", span => span.Kind == Span.Types.SpanKind.Server && span.ParentSpanId != ByteString.Empty, "Server 1");
collector.Expect("OpenTelemetry.AutoInstrumentation.Wcf", span => WcfClientInstrumentation.ValidateBasicSpanExpectations(span, WcfClientInstrumentation.NetTcpChannelScheme, ExpectedChannelPath, ExpectedPeerName, netTcpPort, WcfClientInstrumentation.NetTcpBindingMessageVersion) && WcfClientInstrumentation.ValidateSpanSuccessStatus(span), "Client 1");
collector.Expect("OpenTelemetry.Instrumentation.Wcf", span => span.Kind == Span.Types.SpanKind.Server && span.ParentSpanId != ByteString.Empty, "Server 2");
collector.Expect("OpenTelemetry.AutoInstrumentation.Wcf", span => WcfClientInstrumentation.ValidateBasicSpanExpectations(span, WcfClientInstrumentation.NetTcpChannelScheme, ExpectedChannelPath, ExpectedPeerName, netTcpPort, WcfClientInstrumentation.NetTcpBindingMessageVersion) && WcfClientInstrumentation.ValidateSpanSuccessStatus(span), "Client 2");
collector.Expect("OpenTelemetry.Instrumentation.Wcf", span => span.Kind == Span.Types.SpanKind.Server && span.ParentSpanId != ByteString.Empty, "Server 3");
collector.Expect("OpenTelemetry.AutoInstrumentation.Wcf", span => WcfClientInstrumentation.ValidateBasicSpanExpectations(span, WcfClientInstrumentation.NetTcpChannelScheme, ExpectedChannelPath, ExpectedPeerName, netTcpPort, WcfClientInstrumentation.NetTcpBindingMessageVersion) && WcfClientInstrumentation.ValidateSpanSuccessStatus(span), "Client 3");
collector.Expect("OpenTelemetry.Instrumentation.Wcf", span => span.Kind == Span.Types.SpanKind.Server && span.ParentSpanId != ByteString.Empty, "Server 4");
collector.Expect("OpenTelemetry.AutoInstrumentation.Wcf", span => WcfClientInstrumentation.ValidateBasicSpanExpectations(span, WcfClientInstrumentation.HttpChannelScheme, ExpectedChannelPath, ExpectedPeerName, httpPort, WcfClientInstrumentation.HttpBindingMessageVersion) && WcfClientInstrumentation.ValidateSpanSuccessStatus(span), "Client 4");
collector.Expect("OpenTelemetry.Instrumentation.Wcf", span => span.Kind == Span.Types.SpanKind.Server && span.ParentSpanId != ByteString.Empty, "Server 5");
collector.Expect("OpenTelemetry.AutoInstrumentation.Wcf", span => WcfClientInstrumentation.ValidateBasicSpanExpectations(span, WcfClientInstrumentation.HttpChannelScheme, ExpectedChannelPath, ExpectedPeerName, httpPort, WcfClientInstrumentation.HttpBindingMessageVersion) && WcfClientInstrumentation.ValidateSpanSuccessStatus(span), "Client 5");
collector.Expect("OpenTelemetry.Instrumentation.Wcf", span => span.Kind == Span.Types.SpanKind.Server && span.ParentSpanId != ByteString.Empty, "Server 6");
collector.Expect("OpenTelemetry.AutoInstrumentation.Wcf", span => WcfClientInstrumentation.ValidateBasicSpanExpectations(span, WcfClientInstrumentation.HttpChannelScheme, ExpectedChannelPath, ExpectedPeerName, httpPort, WcfClientInstrumentation.HttpBindingMessageVersion) && WcfClientInstrumentation.ValidateSpanSuccessStatus(span), "Client 6");
collector.Expect("OpenTelemetry.Instrumentation.Wcf", span => span.Kind == Span.Types.SpanKind.Server, "Server 1");
// filtering by name required, because client instrumentation creates some additional spans, e.g for connection registration
collector.Expect("OpenTelemetry.Instrumentation.Wcf", span => span.Kind == Span.Types.SpanKind.Client && span.Name == "http://opentelemetry.io/StatusService/Ping", "Client 1");
collector.Expect("OpenTelemetry.Instrumentation.Wcf", span => span.Kind == Span.Types.SpanKind.Server, "Server 2");
collector.Expect("OpenTelemetry.Instrumentation.Wcf", span => span.Kind == Span.Types.SpanKind.Client && span.Name == "http://opentelemetry.io/StatusService/Ping", "Client 2");
collector.Expect("TestApplication.Wcf.Client.NetFramework", span => span.Kind == Span.Types.SpanKind.Internal, "Custom parent");
collector.Expect("TestApplication.Wcf.Client.NetFramework", span => span.Kind == Span.Types.SpanKind.Internal, "Custom sibling");

View File

@ -15,10 +15,7 @@
// </copyright>
#if NETFRAMEWORK
using Google.Protobuf;
using Google.Protobuf.Collections;
using IntegrationTests.Helpers;
using OpenTelemetry.Proto.Common.V1;
using OpenTelemetry.Proto.Trace.V1;
using Xunit.Abstractions;
@ -26,11 +23,6 @@ namespace IntegrationTests;
public class WcfNetFrameworkTests : WcfTestsBase
{
private const int NetTcpPort = 9090;
private const int HttpPort = 9009;
private const string ExpectedChannelPath = "/Telemetry";
private const string ExpectedPeerName = "127.0.0.1";
public WcfNetFrameworkTests(ITestOutputHelper output)
: base("Wcf.Client.NetFramework", output)
{
@ -40,26 +32,7 @@ public class WcfNetFrameworkTests : WcfTestsBase
[Trait("Category", "EndToEnd")]
public async Task SubmitsTraces()
{
using var collector = await SubmitsTracesInternal(string.Empty);
collector.Expect("OpenTelemetry.Instrumentation.Wcf", span => span.Kind == Span.Types.SpanKind.Server && span.ParentSpanId != ByteString.Empty, "Server 1");
collector.Expect("OpenTelemetry.AutoInstrumentation.Wcf", span => WcfClientInstrumentation.ValidateBasicSpanExpectations(span, WcfClientInstrumentation.NetTcpChannelScheme, ExpectedChannelPath, ExpectedPeerName, NetTcpPort, WcfClientInstrumentation.NetTcpBindingMessageVersion) && WcfClientInstrumentation.ValidateSpanSuccessStatus(span), "Client 1");
collector.Expect("OpenTelemetry.Instrumentation.Wcf", span => span.Kind == Span.Types.SpanKind.Server && span.ParentSpanId != ByteString.Empty, "Server 2");
collector.Expect("OpenTelemetry.AutoInstrumentation.Wcf", span => WcfClientInstrumentation.ValidateBasicSpanExpectations(span, WcfClientInstrumentation.NetTcpChannelScheme, ExpectedChannelPath, ExpectedPeerName, NetTcpPort, WcfClientInstrumentation.NetTcpBindingMessageVersion) && WcfClientInstrumentation.ValidateSpanSuccessStatus(span), "Client 2");
collector.Expect("OpenTelemetry.Instrumentation.Wcf", span => span.Kind == Span.Types.SpanKind.Server && span.ParentSpanId != ByteString.Empty, "Server 3");
collector.Expect("OpenTelemetry.AutoInstrumentation.Wcf", span => WcfClientInstrumentation.ValidateBasicSpanExpectations(span, WcfClientInstrumentation.NetTcpChannelScheme, ExpectedChannelPath, ExpectedPeerName, NetTcpPort, WcfClientInstrumentation.NetTcpBindingMessageVersion) && WcfClientInstrumentation.ValidateSpanSuccessStatus(span), "Client 3");
collector.Expect("OpenTelemetry.Instrumentation.Wcf", span => span.Kind == Span.Types.SpanKind.Server && span.ParentSpanId != ByteString.Empty, "Server 4");
collector.Expect("OpenTelemetry.AutoInstrumentation.Wcf", span => WcfClientInstrumentation.ValidateBasicSpanExpectations(span, WcfClientInstrumentation.HttpChannelScheme, ExpectedChannelPath, ExpectedPeerName, HttpPort, WcfClientInstrumentation.HttpBindingMessageVersion) && WcfClientInstrumentation.ValidateSpanSuccessStatus(span), "Client 4");
collector.Expect("OpenTelemetry.Instrumentation.Wcf", span => span.Kind == Span.Types.SpanKind.Server && span.ParentSpanId != ByteString.Empty, "Server 5");
collector.Expect("OpenTelemetry.AutoInstrumentation.Wcf", span => WcfClientInstrumentation.ValidateBasicSpanExpectations(span, WcfClientInstrumentation.HttpChannelScheme, ExpectedChannelPath, ExpectedPeerName, HttpPort, WcfClientInstrumentation.HttpBindingMessageVersion) && WcfClientInstrumentation.ValidateSpanSuccessStatus(span), "Client 5");
collector.Expect("OpenTelemetry.Instrumentation.Wcf", span => span.Kind == Span.Types.SpanKind.Server && span.ParentSpanId != ByteString.Empty, "Server 6");
collector.Expect("OpenTelemetry.AutoInstrumentation.Wcf", span => WcfClientInstrumentation.ValidateBasicSpanExpectations(span, WcfClientInstrumentation.HttpChannelScheme, ExpectedChannelPath, ExpectedPeerName, HttpPort, WcfClientInstrumentation.HttpBindingMessageVersion) && WcfClientInstrumentation.ValidateSpanSuccessStatus(span), "Client 6");
collector.Expect("TestApplication.Wcf.Client.NetFramework", span => span.Kind == Span.Types.SpanKind.Internal, "Custom parent");
collector.Expect("TestApplication.Wcf.Client.NetFramework", span => span.Kind == Span.Types.SpanKind.Internal, "Custom sibling");
collector.ExpectCollected(WcfClientInstrumentation.ValidateExpectedSpanHierarchy);
collector.AssertExpectations();
await SubmitsTracesInternal(string.Empty);
}
[Fact]
@ -69,9 +42,7 @@ public class WcfNetFrameworkTests : WcfTestsBase
using var collector = new MockSpansCollector(Output);
SetExporter(collector);
collector.Expect("OpenTelemetry.AutoInstrumentation.Wcf", span => ValidateErrorSpanExpectations(span), "Client 1");
collector.Expect("OpenTelemetry.AutoInstrumentation.Wcf", span => ValidateErrorSpanExpectations(span), "Client 2");
collector.Expect("OpenTelemetry.AutoInstrumentation.Wcf", span => ValidateErrorSpanExpectations(span), "Client 3");
collector.Expect("OpenTelemetry.Instrumentation.Wcf", span => span.Status.Code == Status.Types.StatusCode.Error, "Client 1");
collector.Expect("TestApplication.Wcf.Client.NetFramework", span => span.Kind == Span.Types.SpanKind.Internal, "Custom parent");
collector.Expect("TestApplication.Wcf.Client.NetFramework", span => span.Kind == Span.Types.SpanKind.Internal, "Custom sibling");
@ -85,11 +56,6 @@ public class WcfNetFrameworkTests : WcfTestsBase
collector.AssertExpectations();
}
private static bool ValidateErrorSpanExpectations(Span span)
{
return WcfClientInstrumentation.ValidateBasicSpanExpectations(span, WcfClientInstrumentation.HttpChannelScheme, ExpectedChannelPath, ExpectedPeerName, HttpPort, WcfClientInstrumentation.HttpBindingMessageVersion) && span.Status.Code == Status.Types.StatusCode.Error;
}
}
#endif

View File

@ -17,18 +17,19 @@
using System.Net.Sockets;
using FluentAssertions;
using IntegrationTests.Helpers;
using OpenTelemetry.Proto.Trace.V1;
using Xunit.Abstractions;
using static OpenTelemetry.Proto.Trace.V1.Span.Types;
namespace IntegrationTests;
public abstract class WcfTestsBase : TestHelper, IDisposable
{
private readonly string _testAppName;
private ProcessHelper? _serverProcess;
protected WcfTestsBase(string testAppName, ITestOutputHelper output)
: base(testAppName, output)
{
_testAppName = testAppName;
}
public void Dispose()
@ -52,7 +53,7 @@ public abstract class WcfTestsBase : TestHelper, IDisposable
Output.WriteResult(_serverProcess);
}
protected async Task<MockSpansCollector> SubmitsTracesInternal(string clientPackageVersion)
protected async Task SubmitsTracesInternal(string clientPackageVersion)
{
EnvironmentTools.IsWindowsAdministrator().Should().BeTrue(); // WCF Server needs admin
@ -68,7 +69,17 @@ public abstract class WcfTestsBase : TestHelper, IDisposable
PackageVersion = clientPackageVersion
});
return collector;
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");
collector.Expect($"TestApplication.{_testAppName}", span => span.Kind == SpanKind.Internal, "Custom parent");
collector.Expect($"TestApplication.{_testAppName}", span => span.Kind == SpanKind.Internal, "Custom sibling");
collector.ExpectCollected(WcfClientInstrumentation.ValidateExpectedSpanHierarchy);
collector.AssertExpectations();
}
private async Task WaitForServer()

View File

@ -246,9 +246,7 @@ public class SettingsTests : IDisposable
[InlineData("ENTITYFRAMEWORKCORE", TracerInstrumentation.EntityFrameworkCore)]
[InlineData("ASPNETCORE", TracerInstrumentation.AspNetCore)]
#endif
#if NETFRAMEWORK
[InlineData("WCFCLIENT", TracerInstrumentation.WcfClient)]
#endif
[InlineData("MYSQLCONNECTOR", TracerInstrumentation.MySqlConnector)]
[InlineData("AZURE", TracerInstrumentation.Azure)]
internal void TracerSettings_Instrumentations_SupportedValues(string tracerInstrumentation, TracerInstrumentation expectedTracerInstrumentation)

View File

@ -1,4 +1,4 @@
// <copyright file="WcfClientCommon.cs" company="OpenTelemetry Authors">
// <copyright file="IStatusServiceContract.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
@ -14,20 +14,14 @@
// limitations under the License.
// </copyright>
#if NETFRAMEWORK
using System.Diagnostics;
using System.ServiceModel;
using System.Threading.Tasks;
namespace OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client;
namespace TestApplication.Wcf.Client.DotNet;
internal static class WcfClientCommon
[ServiceContract(Namespace = "http://opentelemetry.io/", Name = "StatusService", SessionMode = SessionMode.Allowed)]
public interface IStatusServiceContract
{
private static readonly ActivitySource Source = new("OpenTelemetry.AutoInstrumentation.Wcf", AutoInstrumentationVersion.Version);
private static readonly string OutgoingActivityName = $"{Source.Name}.OutgoingActivity";
internal static Activity? StartActivity()
{
return Source.StartActivity(OutgoingActivityName, ActivityKind.Client);
}
[OperationContract]
Task<StatusResponse> PingAsync(StatusRequest request);
}
#endif

View File

@ -0,0 +1,91 @@
// <copyright file="Program.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.Diagnostics;
using System.Reflection;
using System.ServiceModel;
using System.ServiceModel.Channels;
namespace TestApplication.Wcf.Client.DotNet;
internal static class Program
{
private static readonly ActivitySource Source = new(Assembly.GetExecutingAssembly().GetName().Name!, "1.0.0.0");
public static async Task Main(string[] args)
{
var netTcpAddress = "net.tcp://127.0.0.1:9090/Telemetry";
var httpAddress = "http://127.0.0.1:9009/Telemetry";
using var parent = Source.StartActivity("Parent");
try
{
Console.WriteLine("=============NetTcp===============");
await CallService(netTcpAddress, new NetTcpBinding(SecurityMode.None)).ConfigureAwait(false);
}
catch (Exception)
{
// netTcp fails on open when there is no endpoint
}
Console.WriteLine("=============Http===============");
await CallService(httpAddress, new BasicHttpBinding()).ConfigureAwait(false);
using var sibling = Source.StartActivity("Sibling");
}
private static async Task CallService(string address, Binding binding)
{
// Note: Best practice is to re-use your client/channel instances.
// This code is not meant to illustrate best practices, only the
// instrumentation.
var client = new StatusServiceClient(binding, new EndpointAddress(new Uri(address)));
await client.OpenAsync().ConfigureAwait(false);
try
{
try
{
Console.WriteLine("Task-based Asynchronous Pattern call");
var rq = new StatusRequest { Status = "1" };
var response = await client.PingAsync(rq).ConfigureAwait(false);
Console.WriteLine(
$"[{DateTimeOffset.UtcNow:o}] Request with status {rq.Status}. Server returned: {response?.ServerTime:o}");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
finally
{
try
{
if (client.State == CommunicationState.Faulted)
{
client.Abort();
}
else
{
await client.CloseAsync().ConfigureAwait(false);
}
}
catch
{
}
}
}
}

View File

@ -0,0 +1,26 @@
// <copyright file="StatusRequest.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.Runtime.Serialization;
namespace TestApplication.Wcf.Client.DotNet;
[DataContract(Namespace = "http://opentelemetry.io/")]
public class StatusRequest
{
[DataMember]
public string? Status { get; set; }
}

View File

@ -1,4 +1,4 @@
// <copyright file="ActionMetadata.cs" company="OpenTelemetry Authors">
// <copyright file="StatusResponse.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
@ -14,19 +14,13 @@
// limitations under the License.
// </copyright>
// source originated from: https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/06b9a286a6ab2af5257ce26b5dcb6fac56112f96/src/OpenTelemetry.Instrumentation.Wcf
#if NETFRAMEWORK
namespace OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.Client;
internal sealed class ActionMetadata
using System.Runtime.Serialization;
namespace TestApplication.Wcf.Client.DotNet;
[DataContract(Namespace = "http://opentelemetry.io/")]
public class StatusResponse
{
public ActionMetadata(string? contractName, string operationName)
{
ContractName = contractName;
OperationName = operationName;
}
public string? ContractName { get; }
public string OperationName { get; }
[DataMember]
public DateTimeOffset ServerTime { get; set; }
}
#endif

View File

@ -0,0 +1,40 @@
// <copyright file="StatusServiceClient.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.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading.Tasks;
namespace TestApplication.Wcf.Client.DotNet;
public class StatusServiceClient : ClientBase<IStatusServiceContract>, IStatusServiceContract
{
public StatusServiceClient(Binding binding, EndpointAddress remoteAddress)
: base(binding, remoteAddress)
{
}
public Task<StatusResponse> PingAsync(StatusRequest request)
{
return this.Channel.PingAsync(request);
}
public Task OpenAsync()
{
ICommunicationObject communicationObject = this;
return Task.Factory.FromAsync(communicationObject.BeginOpen, communicationObject.EndOpen, null);
}
}

View File

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net7.0;net6.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.ServiceModel.Http" VersionOverride="$(LibraryVersion)" />
<PackageReference Include="System.ServiceModel.NetTcp" VersionOverride="$(LibraryVersion)" />
</ItemGroup>
</Project>

View File

@ -22,14 +22,6 @@ namespace TestApplication.Wcf.Client.NetFramework;
[ServiceContract(Namespace = "http://opentelemetry.io/", Name = "StatusService", SessionMode = SessionMode.Allowed)]
public interface IStatusServiceContract
{
[OperationContract(Name = "Ping")]
[OperationContract]
Task<StatusResponse> PingAsync(StatusRequest request);
[OperationContract(Name = "Ping")]
StatusResponse PingSync(StatusRequest request);
[OperationContract(Name = "Ping", AsyncPattern = true)]
IAsyncResult BeginPing(StatusRequest request, AsyncCallback callback, object asyncState);
StatusResponse EndPing(IAsyncResult asyncResult);
}

View File

@ -91,35 +91,6 @@ internal static class Program
{
Console.WriteLine(e.Message);
}
try
{
Console.WriteLine("Asynchronous Programming Model pattern call");
var rq = new StatusRequest { Status = "2" };
var asyncResult = client.BeginPing(rq, null!, null!);
var statusResponse = client.EndPing(asyncResult);
Console.WriteLine(
$"[{DateTimeOffset.UtcNow:o}] Request with status {rq.Status}. Server returned: {statusResponse?.ServerTime:o}");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
try
{
Console.WriteLine("Synchronous call");
var rq = new StatusRequest { Status = "3" };
var response = client.PingSync(rq);
Console.WriteLine(
$"[{DateTimeOffset.UtcNow:o}] Request with status {rq.Status}. Server returned: {response?.ServerTime:o}");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
finally
{

View File

@ -32,21 +32,6 @@ public class StatusServiceClient : ClientBase<IStatusServiceContract>, IStatusSe
return this.Channel.PingAsync(request);
}
public StatusResponse PingSync(StatusRequest request)
{
return Channel.PingSync(request);
}
public IAsyncResult BeginPing(StatusRequest request, AsyncCallback callback, object asyncState)
{
return Channel.BeginPing(request, callback, asyncState);
}
public StatusResponse EndPing(IAsyncResult asyncResult)
{
return Channel.EndPing(asyncResult);
}
public Task OpenAsync()
{
ICommunicationObject communicationObject = this;

View File

@ -192,6 +192,17 @@ internal static class PackageVersionDefinitions
new("2.6.66"),
new("*")
}
},
new()
{
IntegrationName = "WCFCoreClient",
NugetPackageName = "System.ServiceModel.Http",
TestApplicationName = "TestApplication.Wcf.Client.DotNet",
Versions = new List<PackageVersion>
{
new("4.10.2"),
new("*")
}
}
};