From 914feda35fbfcadfa0ced42d552260c626511b92 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Fri, 25 Sep 2020 21:58:16 -0700 Subject: [PATCH 01/34] WCF instrumentation + example apps. --- examples/Directory.Build.props | 3 + examples/wcf/client/App.config | 33 +++++++ .../wcf/client/Examples.Wcf.Client.csproj | 17 ++++ examples/wcf/client/Program.cs | 72 ++++++++++++++ examples/wcf/client/StatusServiceClient.cs | 32 ++++++ examples/wcf/server/App.config | 41 ++++++++ .../wcf/server/Examples.Wcf.Server.csproj | 17 ++++ examples/wcf/server/Program.cs | 45 +++++++++ examples/wcf/server/StatusService.cs | 40 ++++++++ .../wcf/shared/Examples.Wcf.Shared.csproj | 21 ++++ examples/wcf/shared/IStatusServiceContract.cs | 28 ++++++ examples/wcf/shared/StatusRequest.cs | 27 ++++++ examples/wcf/shared/StatusResponse.cs | 28 ++++++ opentelemetry-dotnet-contrib.sln | 33 +++++++ ...lemetry.Contrib.Instrumentation.Wcf.csproj | 14 +++ .../README.md | 3 + .../TelemetryBehaviourExtensionElement.cs | 36 +++++++ .../TelemetryClientMessageInspector.cs | 95 ++++++++++++++++++ .../TelemetryDispatchMessageInspector.cs | 97 +++++++++++++++++++ .../TelemetryEndpointBehavior.cs | 52 ++++++++++ .../TracerProviderBuilderExtensions.cs | 53 ++++++++++ .../WcfInstrumentationActivitySource.cs | 51 ++++++++++ .../WcfInstrumentationOptions.cs | 58 +++++++++++ 23 files changed, 896 insertions(+) create mode 100644 examples/Directory.Build.props create mode 100644 examples/wcf/client/App.config create mode 100644 examples/wcf/client/Examples.Wcf.Client.csproj create mode 100644 examples/wcf/client/Program.cs create mode 100644 examples/wcf/client/StatusServiceClient.cs create mode 100644 examples/wcf/server/App.config create mode 100644 examples/wcf/server/Examples.Wcf.Server.csproj create mode 100644 examples/wcf/server/Program.cs create mode 100644 examples/wcf/server/StatusService.cs create mode 100644 examples/wcf/shared/Examples.Wcf.Shared.csproj create mode 100644 examples/wcf/shared/IStatusServiceContract.cs create mode 100644 examples/wcf/shared/StatusRequest.cs create mode 100644 examples/wcf/shared/StatusResponse.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Wcf/OpenTelemetry.Contrib.Instrumentation.Wcf.csproj create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryBehaviourExtensionElement.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryEndpointBehavior.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Wcf/TracerProviderBuilderExtensions.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationActivitySource.cs create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationOptions.cs diff --git a/examples/Directory.Build.props b/examples/Directory.Build.props new file mode 100644 index 0000000000..1927158094 --- /dev/null +++ b/examples/Directory.Build.props @@ -0,0 +1,3 @@ + + + diff --git a/examples/wcf/client/App.config b/examples/wcf/client/App.config new file mode 100644 index 0000000000..03b0248a4b --- /dev/null +++ b/examples/wcf/client/App.config @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/wcf/client/Examples.Wcf.Client.csproj b/examples/wcf/client/Examples.Wcf.Client.csproj new file mode 100644 index 0000000000..14fd9819e9 --- /dev/null +++ b/examples/wcf/client/Examples.Wcf.Client.csproj @@ -0,0 +1,17 @@ + + + + Exe + net46 + false + + + + + + + + + + + diff --git a/examples/wcf/client/Program.cs b/examples/wcf/client/Program.cs new file mode 100644 index 0000000000..a60a65b5c3 --- /dev/null +++ b/examples/wcf/client/Program.cs @@ -0,0 +1,72 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.ServiceModel; +using System.Threading.Tasks; +using OpenTelemetry; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; + +namespace Examples.Wcf.Client +{ + internal static class Program + { + public static async Task Main() + { + using var openTelemetry = Sdk.CreateTracerProviderBuilder() + .AddWcfInstrumentation() + .AddHttpClientInstrumentation() // <- Added to test suppression of http spans. + .AddJaegerExporter() + .SetResource(Resources.CreateServiceResource("Wcf-Client")) + .Build(); + + await CallService("StatusService_Http").ConfigureAwait(false); + await CallService("StatusService_Tcp").ConfigureAwait(false); + + Console.WriteLine("Press enter to exit."); + Console.ReadLine(); + } + + private static async Task CallService(string name) + { + StatusServiceClient client = new StatusServiceClient(name); + try + { + client.Open(); + + var response = await client.Ping( + new StatusRequest + { + Status = Guid.NewGuid().ToString("N"), + }).ConfigureAwait(false); + + Console.WriteLine($"Server returned: {response?.ServerTime}"); + } + finally + { + if (client.State == CommunicationState.Faulted) + { + client.Abort(); + } + else + { + client.Close(); + } + } + } + } +} diff --git a/examples/wcf/client/StatusServiceClient.cs b/examples/wcf/client/StatusServiceClient.cs new file mode 100644 index 0000000000..03af25a42c --- /dev/null +++ b/examples/wcf/client/StatusServiceClient.cs @@ -0,0 +1,32 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.ServiceModel; +using System.Threading.Tasks; + +namespace Examples.Wcf.Client +{ + public class StatusServiceClient : ClientBase, IStatusServiceContract + { + public StatusServiceClient(string name) + : base(name) + { + } + + public Task Ping(StatusRequest request) + => this.Channel.Ping(request); + } +} diff --git a/examples/wcf/server/App.config b/examples/wcf/server/App.config new file mode 100644 index 0000000000..cbfe519905 --- /dev/null +++ b/examples/wcf/server/App.config @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/wcf/server/Examples.Wcf.Server.csproj b/examples/wcf/server/Examples.Wcf.Server.csproj new file mode 100644 index 0000000000..14fd9819e9 --- /dev/null +++ b/examples/wcf/server/Examples.Wcf.Server.csproj @@ -0,0 +1,17 @@ + + + + Exe + net46 + false + + + + + + + + + + + diff --git a/examples/wcf/server/Program.cs b/examples/wcf/server/Program.cs new file mode 100644 index 0000000000..f969f2271c --- /dev/null +++ b/examples/wcf/server/Program.cs @@ -0,0 +1,45 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.ServiceModel; +using OpenTelemetry; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; + +namespace Examples.Wcf.Server +{ + internal static class Program + { + public static void Main() + { + using var openTelemetry = Sdk.CreateTracerProviderBuilder() + .AddWcfInstrumentation() + .AddAspNetInstrumentation() // <- Added to test suppression of http spans. + .AddJaegerExporter() + .SetResource(Resources.CreateServiceResource("Wcf-Server")) + .Build(); + + ServiceHost serviceHost = new ServiceHost(typeof(StatusService)); + serviceHost.Open(); + + Console.WriteLine("Service listening. Press enter to exit."); + Console.ReadLine(); + + serviceHost.Close(); + } + } +} diff --git a/examples/wcf/server/StatusService.cs b/examples/wcf/server/StatusService.cs new file mode 100644 index 0000000000..1ba417e3f1 --- /dev/null +++ b/examples/wcf/server/StatusService.cs @@ -0,0 +1,40 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.ServiceModel; +using System.Threading.Tasks; + +namespace Examples.Wcf.Server +{ + [ServiceBehavior( + Namespace = "http://opentelemetry.io/", + ConcurrencyMode = ConcurrencyMode.Multiple, + InstanceContextMode = InstanceContextMode.Single, + UseSynchronizationContext = false, + Name = "StatusService")] + public class StatusService : IStatusServiceContract + { + public Task Ping(StatusRequest request) + { + return Task.FromResult( + new StatusResponse + { + ServerTime = DateTimeOffset.UtcNow, + }); + } + } +} diff --git a/examples/wcf/shared/Examples.Wcf.Shared.csproj b/examples/wcf/shared/Examples.Wcf.Shared.csproj new file mode 100644 index 0000000000..368ba43946 --- /dev/null +++ b/examples/wcf/shared/Examples.Wcf.Shared.csproj @@ -0,0 +1,21 @@ + + + + net46 + + + + + + + + + + + + + + + + + diff --git a/examples/wcf/shared/IStatusServiceContract.cs b/examples/wcf/shared/IStatusServiceContract.cs new file mode 100644 index 0000000000..5e829864f5 --- /dev/null +++ b/examples/wcf/shared/IStatusServiceContract.cs @@ -0,0 +1,28 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.ServiceModel; +using System.Threading.Tasks; + +namespace Examples.Wcf +{ + [ServiceContract(Namespace = "http://opentelemetry.io/", Name = "StatusService", SessionMode = SessionMode.Allowed)] + public interface IStatusServiceContract + { + [OperationContract] + Task Ping(StatusRequest request); + } +} diff --git a/examples/wcf/shared/StatusRequest.cs b/examples/wcf/shared/StatusRequest.cs new file mode 100644 index 0000000000..450480946c --- /dev/null +++ b/examples/wcf/shared/StatusRequest.cs @@ -0,0 +1,27 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.Runtime.Serialization; + +namespace Examples.Wcf +{ + [DataContract] + public class StatusRequest + { + [DataMember] + public string Status { get; set; } + } +} diff --git a/examples/wcf/shared/StatusResponse.cs b/examples/wcf/shared/StatusResponse.cs new file mode 100644 index 0000000000..cdde1af310 --- /dev/null +++ b/examples/wcf/shared/StatusResponse.cs @@ -0,0 +1,28 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Runtime.Serialization; + +namespace Examples.Wcf +{ + [DataContract] + public class StatusResponse + { + [DataMember] + public DateTimeOffset ServerTime { get; set; } + } +} diff --git a/opentelemetry-dotnet-contrib.sln b/opentelemetry-dotnet-contrib.sln index 784ae93ff8..f9bf137907 100644 --- a/opentelemetry-dotnet-contrib.sln +++ b/opentelemetry-dotnet-contrib.sln @@ -77,6 +77,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.Instr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.Instrumentation.EntityFrameworkCore.Tests", "test\OpenTelemetry.Contrib.Instrumentation.EntityFrameworkCoreTests\OpenTelemetry.Contrib.Instrumentation.EntityFrameworkCore.Tests.csproj", "{899E506C-8525-4471-8C6D-5A9FA6B8517B}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.Instrumentation.Wcf", "src\OpenTelemetry.Contrib.Instrumentation.Wcf\OpenTelemetry.Contrib.Instrumentation.Wcf.csproj", "{CAD5C27A-D359-4086-9C4F-02204C084A8E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{B75EE478-97F7-4E9F-9A5A-DB3D0988EDEA}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "wcf", "wcf", "{73474960-8F91-4EE5-8E3E-F7E7ADA99238}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.Wcf.Client", "examples\wcf\client\Examples.Wcf.Client.csproj", "{351C01F2-9664-48AC-9E1B-69AB42907392}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.Wcf.Server", "examples\wcf\server\Examples.Wcf.Server.csproj", "{DAFE7886-DD52-4C96-BFF0-9744E7F9B45F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.Wcf.Shared", "examples\wcf\shared\Examples.Wcf.Shared.csproj", "{21716C26-3B2A-4208-BDFB-8E58E2AF49EA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -119,6 +131,22 @@ Global {899E506C-8525-4471-8C6D-5A9FA6B8517B}.Debug|Any CPU.Build.0 = Debug|Any CPU {899E506C-8525-4471-8C6D-5A9FA6B8517B}.Release|Any CPU.ActiveCfg = Release|Any CPU {899E506C-8525-4471-8C6D-5A9FA6B8517B}.Release|Any CPU.Build.0 = Release|Any CPU + {CAD5C27A-D359-4086-9C4F-02204C084A8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CAD5C27A-D359-4086-9C4F-02204C084A8E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CAD5C27A-D359-4086-9C4F-02204C084A8E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CAD5C27A-D359-4086-9C4F-02204C084A8E}.Release|Any CPU.Build.0 = Release|Any CPU + {351C01F2-9664-48AC-9E1B-69AB42907392}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {351C01F2-9664-48AC-9E1B-69AB42907392}.Debug|Any CPU.Build.0 = Debug|Any CPU + {351C01F2-9664-48AC-9E1B-69AB42907392}.Release|Any CPU.ActiveCfg = Release|Any CPU + {351C01F2-9664-48AC-9E1B-69AB42907392}.Release|Any CPU.Build.0 = Release|Any CPU + {DAFE7886-DD52-4C96-BFF0-9744E7F9B45F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DAFE7886-DD52-4C96-BFF0-9744E7F9B45F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DAFE7886-DD52-4C96-BFF0-9744E7F9B45F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DAFE7886-DD52-4C96-BFF0-9744E7F9B45F}.Release|Any CPU.Build.0 = Release|Any CPU + {21716C26-3B2A-4208-BDFB-8E58E2AF49EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {21716C26-3B2A-4208-BDFB-8E58E2AF49EA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {21716C26-3B2A-4208-BDFB-8E58E2AF49EA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {21716C26-3B2A-4208-BDFB-8E58E2AF49EA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -136,6 +164,11 @@ Global {5F10395B-DF38-438A-B5DB-5F6449184F67} = {22DF5DC0-1290-4E83-A9D8-6BB7DE3B3E63} {BC839E07-108A-4184-B1F9-EF28A1E81088} = {2097345F-4DD3-477D-BC54-A922F9B2B402} {899E506C-8525-4471-8C6D-5A9FA6B8517B} = {2097345F-4DD3-477D-BC54-A922F9B2B402} + {CAD5C27A-D359-4086-9C4F-02204C084A8E} = {22DF5DC0-1290-4E83-A9D8-6BB7DE3B3E63} + {73474960-8F91-4EE5-8E3E-F7E7ADA99238} = {B75EE478-97F7-4E9F-9A5A-DB3D0988EDEA} + {351C01F2-9664-48AC-9E1B-69AB42907392} = {73474960-8F91-4EE5-8E3E-F7E7ADA99238} + {DAFE7886-DD52-4C96-BFF0-9744E7F9B45F} = {73474960-8F91-4EE5-8E3E-F7E7ADA99238} + {21716C26-3B2A-4208-BDFB-8E58E2AF49EA} = {73474960-8F91-4EE5-8E3E-F7E7ADA99238} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B0816796-CDB3-47D7-8C3C-946434DE3B66} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/OpenTelemetry.Contrib.Instrumentation.Wcf.csproj b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/OpenTelemetry.Contrib.Instrumentation.Wcf.csproj new file mode 100644 index 0000000000..08a96b5d7f --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/OpenTelemetry.Contrib.Instrumentation.Wcf.csproj @@ -0,0 +1,14 @@ + + + + net452 + OpenTelemetry instrumentation for WCF + $(PackageTags);distributed-tracing;WCF + + + + + + + + diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md new file mode 100644 index 0000000000..a08e852f5e --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md @@ -0,0 +1,3 @@ +# WCF Instrumentation for OpenTelemetry.Contrib .NET + +TODO \ No newline at end of file diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryBehaviourExtensionElement.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryBehaviourExtensionElement.cs new file mode 100644 index 0000000000..ca187406ff --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryBehaviourExtensionElement.cs @@ -0,0 +1,36 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.ServiceModel.Configuration; + +namespace OpenTelemetry.Contrib.Instrumentation.Wcf +{ + /// + /// A for registering on service through configuation. + /// + public class TelemetryBehaviourExtensionElement : BehaviorExtensionElement + { + /// + public override Type BehaviorType => typeof(TelemetryEndpointBehavior); + + /// + protected override object CreateBehavior() + { + return new TelemetryEndpointBehavior(); + } + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs new file mode 100644 index 0000000000..4b64abd9c9 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs @@ -0,0 +1,95 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.Diagnostics; +using System.ServiceModel; +using System.ServiceModel.Channels; +using System.ServiceModel.Dispatcher; +using OpenTelemetry.Context.Propagation; +using OpenTelemetry.Trace; + +namespace OpenTelemetry.Contrib.Instrumentation.Wcf +{ + /// + /// An implementation which adds telemetry to outgoing requests. + /// + public class TelemetryClientMessageInspector : IClientMessageInspector + { + /// + public object BeforeSendRequest(ref Message request, IClientChannel channel) + { + try + { + if (WcfInstrumentationActivitySource.Options == null || WcfInstrumentationActivitySource.Options.OutgoingRequestFilter?.Invoke(request) == false) + { + // AspNetInstrumentationEventSource.Log.RequestIsFilteredOut(activity.OperationName); + return null; + } + } + catch + { + /*(Exception ex)*/ + // AspNetInstrumentationEventSource.Log.RequestFilterException(ex); + return null; + } + + Activity activity = WcfInstrumentationActivitySource.ActivitySource.StartActivity( + WcfInstrumentationActivitySource.OutgoingRequestActivityName, + ActivityKind.Client); + + if (activity != null) + { + if (WcfInstrumentationActivitySource.Options.SuppressDownstreamInstrumentation) + { + SuppressInstrumentationScope.Enter(); + } + + activity.DisplayName = request.Headers.Action; + + WcfInstrumentationActivitySource.Options.Propagator.Inject( + new PropagationContext(activity.Context, Baggage.Current), + request, + WcfInstrumentationActivitySource.MessageHeaderValueSetter); + + if (activity.IsAllDataRequested) + { + activity.SetTag("soap.version", request.Version.ToString()); + activity.SetTag("wcf.channel.scheme", $"{channel.RemoteAddress.Uri.Scheme}"); + activity.SetTag("wcf.channel.host", $"{channel.RemoteAddress.Uri.Host}:{channel.RemoteAddress.Uri.Port}"); + activity.SetTag("wcf.channel.path", channel.RemoteAddress.Uri.LocalPath); + } + } + + return activity; + } + + /// + public void AfterReceiveReply(ref Message reply, object correlationState) + { + Activity activity = (Activity)correlationState; + if (activity != null) + { + if (activity.IsAllDataRequested) + { + activity.SetStatus(!reply.IsFault ? Status.Ok : Status.Unknown); + activity.SetTag("soap.reply_action", reply.Headers.Action); + } + + activity.Stop(); + } + } + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs new file mode 100644 index 0000000000..85a7b8a81a --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs @@ -0,0 +1,97 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.Diagnostics; +using System.ServiceModel; +using System.ServiceModel.Channels; +using System.ServiceModel.Dispatcher; +using OpenTelemetry.Trace; + +namespace OpenTelemetry.Contrib.Instrumentation.Wcf +{ + /// + /// An implementation which adds telemetry to incoming requests. + /// + public class TelemetryDispatchMessageInspector : IDispatchMessageInspector + { + /// + public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) + { + try + { + if (WcfInstrumentationActivitySource.Options == null || WcfInstrumentationActivitySource.Options.IncomingRequestFilter?.Invoke(request) == false) + { + // AspNetInstrumentationEventSource.Log.RequestIsFilteredOut(activity.OperationName); + return null; + } + } + catch + { + /*(Exception ex)*/ + // AspNetInstrumentationEventSource.Log.RequestFilterException(ex); + return null; + } + + var ctx = WcfInstrumentationActivitySource.Options.Propagator.Extract(default, request, WcfInstrumentationActivitySource.MessageHeaderValuesGetter); + + Activity activity = WcfInstrumentationActivitySource.ActivitySource.StartActivity( + WcfInstrumentationActivitySource.IncomingRequestActivityName, + ActivityKind.Server, + ctx.ActivityContext); + + if (activity != null) + { + if (WcfInstrumentationActivitySource.Options.SuppressDownstreamInstrumentation) + { + SuppressInstrumentationScope.Enter(); + } + + activity.DisplayName = request.Headers.Action; + + if (activity.IsAllDataRequested) + { + activity.SetTag("soap.version", request.Version.ToString()); + activity.SetTag("wcf.channel.scheme", $"{channel.LocalAddress.Uri.Scheme}"); + activity.SetTag("wcf.channel.host", $"{channel.LocalAddress.Uri.Host}:{channel.LocalAddress.Uri.Port}"); + activity.SetTag("wcf.channel.path", channel.LocalAddress.Uri.LocalPath); + } + } + + if (ctx.Baggage != default) + { + Baggage.Current = ctx.Baggage; + } + + return activity; + } + + /// + public void BeforeSendReply(ref Message reply, object correlationState) + { + Activity activity = (Activity)correlationState; + if (activity != null) + { + if (activity.IsAllDataRequested) + { + activity.SetStatus(!reply.IsFault ? Status.Ok : Status.Unknown); + activity.SetTag("soap.reply_action", reply.Headers.Action); + } + + activity.Stop(); + } + } + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryEndpointBehavior.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryEndpointBehavior.cs new file mode 100644 index 0000000000..90367f6671 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryEndpointBehavior.cs @@ -0,0 +1,52 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.ServiceModel.Channels; +using System.ServiceModel.Description; +using System.ServiceModel.Dispatcher; + +namespace OpenTelemetry.Contrib.Instrumentation.Wcf +{ + /// + /// An implementation whichs add the to client endpoints and the + /// to service endpoints. + /// + public class TelemetryEndpointBehavior : IEndpointBehavior + { + /// + public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) + { + } + + /// + public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) + { + clientRuntime.MessageInspectors.Add(new TelemetryClientMessageInspector()); + } + + /// + public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) + { + endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new TelemetryDispatchMessageInspector()); + } + + /// + public void Validate(ServiceEndpoint endpoint) + { + } + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TracerProviderBuilderExtensions.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TracerProviderBuilderExtensions.cs new file mode 100644 index 0000000000..3a83f5b43c --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TracerProviderBuilderExtensions.cs @@ -0,0 +1,53 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using OpenTelemetry.Contrib.Instrumentation.Wcf; + +namespace OpenTelemetry.Trace +{ + /// + /// Extension methods to simplify registering of dependency instrumentation. + /// + public static class TracerProviderBuilderExtensions + { + /// + /// Enables the outgoing requests automatic data collection for WCF. + /// + /// being configured. + /// Wcf configuration options. + /// The instance of to chain the calls. + public static TracerProviderBuilder AddWcfInstrumentation(this TracerProviderBuilder builder, Action configure = null) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (WcfInstrumentationActivitySource.Options != null) + { + throw new NotSupportedException("WCF instrumentation has already been registered and doesn't support multiple registrations."); + } + + var options = new WcfInstrumentationOptions(); + configure?.Invoke(options); + + WcfInstrumentationActivitySource.Options = options; + + return builder.AddSource(WcfInstrumentationActivitySource.ActivitySourceName); + } + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationActivitySource.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationActivitySource.cs new file mode 100644 index 0000000000..6effced40b --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationActivitySource.cs @@ -0,0 +1,51 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.ServiceModel.Channels; + +namespace OpenTelemetry.Contrib.Instrumentation.Wcf +{ + /// + /// WCF instrumentation. + /// + internal static class WcfInstrumentationActivitySource + { + public const string ActivitySourceName = "OpenTelemetry.WCF"; + public const string IncomingRequestActivityName = ActivitySourceName + ".IncomingRequest"; + public const string OutgoingRequestActivityName = ActivitySourceName + ".OutgoingRequest"; + + private static readonly Version Version = typeof(TelemetryDispatchMessageInspector).Assembly.GetName().Version; + + public static ActivitySource ActivitySource { get; } = new ActivitySource(ActivitySourceName, Version.ToString()); + + public static Func> MessageHeaderValuesGetter { get; } + = (request, name) => + { + var headerIndex = request.Headers.FindHeader(name, "https://www.w3.org/TR/trace-context/"); + return headerIndex < 0 + ? null + : new[] { request.Headers.GetHeader(headerIndex) }; + }; + + public static Action MessageHeaderValueSetter { get; } + = (request, name, value) => request.Headers.Add(MessageHeader.CreateHeader(name, "https://www.w3.org/TR/trace-context/", value, false)); + + public static WcfInstrumentationOptions Options { get; set; } + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationOptions.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationOptions.cs new file mode 100644 index 0000000000..3eebe49486 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationOptions.cs @@ -0,0 +1,58 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.ServiceModel.Channels; +using OpenTelemetry.Context.Propagation; + +namespace OpenTelemetry.Contrib.Instrumentation.Wcf +{ + /// + /// Options for WCF instrumentation. + /// + public class WcfInstrumentationOptions + { + /// + /// Gets or sets for context propagation. Default value: with & . + /// + public IPropagator Propagator { get; set; } = new CompositePropagator(new IPropagator[] + { + new TextMapPropagator(), + new BaggagePropagator(), + }); + + /// + /// Gets or sets a Filter function to filter instrumentation for requests on a per request basis. + /// The Filter gets the Message, and should return a boolean. + /// If Filter returns true, the request is collected. + /// If Filter returns false or throw exception, the request is filtered out. + /// + public Func IncomingRequestFilter { get; set; } + + /// + /// Gets or sets a Filter function to filter instrumentation for requests on a per request basis. + /// The Filter gets the Message, and should return a boolean. + /// If Filter returns true, the request is collected. + /// If Filter returns false or throw exception, the request is filtered out. + /// + public Func OutgoingRequestFilter { get; set; } + + /// + /// Gets or sets a value indicating whether down stream instrumentation (HttpClient) is suppressed (disabled). + /// + public bool SuppressDownstreamInstrumentation { get; set; } = true; + } +} From e25beefeab4d1c7a787f6036d76293ac9a93da77 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 28 Sep 2020 20:03:26 -0700 Subject: [PATCH 02/34] Added an event source to log errors in Wcf instrumentation. --- .../WcfInstrumentationEventSource.cs | 69 +++++++++++++++++++ .../TelemetryClientMessageInspector.cs | 9 +-- .../TelemetryDispatchMessageInspector.cs | 9 +-- 3 files changed, 79 insertions(+), 8 deletions(-) create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationEventSource.cs diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationEventSource.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationEventSource.cs new file mode 100644 index 0000000000..bd137863d6 --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationEventSource.cs @@ -0,0 +1,69 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Diagnostics.Tracing; +using System.Globalization; +using System.Threading; + +namespace OpenTelemetry.Contrib.Instrumentation.Wcf.Implementation +{ + [EventSource(Name = "OpenTelemetry-Instrumentation-Wcf")] + internal class WcfInstrumentationEventSource : EventSource + { + public static readonly WcfInstrumentationEventSource Log = new WcfInstrumentationEventSource(); + + [NonEvent] + public void RequestFilterException(Exception ex) + { + if (this.IsEnabled(EventLevel.Error, (EventKeywords)(-1))) + { + this.RequestFilterException(ToInvariantString(ex)); + } + } + + [Event(1, Message = "Request is filtered out.", Level = EventLevel.Verbose)] + public void RequestIsFilteredOut(string eventName) + { + this.WriteEvent(1, eventName); + } + + [Event(2, Message = "InstrumentationFilter threw exception. Request will not be collected. Exception {0}.", Level = EventLevel.Error)] + public void RequestFilterException(string exception) + { + this.WriteEvent(2, exception); + } + + /// + /// Returns a culture-independent string representation of the given object, + /// appropriate for diagnostics tracing. + /// + private static string ToInvariantString(Exception exception) + { + var originalUICulture = Thread.CurrentThread.CurrentUICulture; + + try + { + Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; + return exception.ToString(); + } + finally + { + Thread.CurrentThread.CurrentUICulture = originalUICulture; + } + } + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs index 4b64abd9c9..59ab4a77a5 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs @@ -14,11 +14,13 @@ // limitations under the License. // +using System; using System.Diagnostics; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Dispatcher; using OpenTelemetry.Context.Propagation; +using OpenTelemetry.Contrib.Instrumentation.Wcf.Implementation; using OpenTelemetry.Trace; namespace OpenTelemetry.Contrib.Instrumentation.Wcf @@ -35,14 +37,13 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel) { if (WcfInstrumentationActivitySource.Options == null || WcfInstrumentationActivitySource.Options.OutgoingRequestFilter?.Invoke(request) == false) { - // AspNetInstrumentationEventSource.Log.RequestIsFilteredOut(activity.OperationName); + WcfInstrumentationEventSource.Log.RequestIsFilteredOut(request.Headers.Action); return null; } } - catch + catch (Exception ex) { - /*(Exception ex)*/ - // AspNetInstrumentationEventSource.Log.RequestFilterException(ex); + WcfInstrumentationEventSource.Log.RequestFilterException(ex); return null; } diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs index 85a7b8a81a..4329c90916 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs @@ -14,10 +14,12 @@ // limitations under the License. // +using System; using System.Diagnostics; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Dispatcher; +using OpenTelemetry.Contrib.Instrumentation.Wcf.Implementation; using OpenTelemetry.Trace; namespace OpenTelemetry.Contrib.Instrumentation.Wcf @@ -34,14 +36,13 @@ public object AfterReceiveRequest(ref Message request, IClientChannel channel, I { if (WcfInstrumentationActivitySource.Options == null || WcfInstrumentationActivitySource.Options.IncomingRequestFilter?.Invoke(request) == false) { - // AspNetInstrumentationEventSource.Log.RequestIsFilteredOut(activity.OperationName); + WcfInstrumentationEventSource.Log.RequestIsFilteredOut(request.Headers.Action); return null; } } - catch + catch (Exception ex) { - /*(Exception ex)*/ - // AspNetInstrumentationEventSource.Log.RequestFilterException(ex); + WcfInstrumentationEventSource.Log.RequestFilterException(ex); return null; } From 74d3c4c81549f1c8c5c11924254a7e0ecaf16a40 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 5 Oct 2020 17:25:54 -0700 Subject: [PATCH 03/34] Added content to the WCF instrumentation's README. --- .../README.md | 2 +- .../README.md | 2 +- .../README.md | 105 +++++++++++++++++- 3 files changed, 105 insertions(+), 4 deletions(-) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.EntityFrameworkCore/README.md b/src/OpenTelemetry.Contrib.Instrumentation.EntityFrameworkCore/README.md index d473bdb8d4..bab115a539 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.EntityFrameworkCore/README.md +++ b/src/OpenTelemetry.Contrib.Instrumentation.EntityFrameworkCore/README.md @@ -1,4 +1,4 @@ -# EntityFrameworkCore Instrumentation for OpenTelemetry.Contrib .NET +# EntityFrameworkCore Instrumentation for OpenTelemetry .NET Automatically instruments the outgoing database requests from [Microsoft.EntityFrameworkCore](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore). diff --git a/src/OpenTelemetry.Contrib.Instrumentation.MassTransit/README.md b/src/OpenTelemetry.Contrib.Instrumentation.MassTransit/README.md index 2ed324f9ee..8790167c38 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.MassTransit/README.md +++ b/src/OpenTelemetry.Contrib.Instrumentation.MassTransit/README.md @@ -1,4 +1,4 @@ -# MassTransit Instrumentation for OpenTelemetry.Contrib .NET +# MassTransit Instrumentation for OpenTelemetry .NET Automatically instruments [DiagnosticSource](https://masstransit-project.com/advanced/monitoring/diagnostic-source.html) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md index a08e852f5e..6c0b0c088e 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md @@ -1,3 +1,104 @@ -# WCF Instrumentation for OpenTelemetry.Contrib .NET +# WCF Instrumentation for OpenTelemetry .NET -TODO \ No newline at end of file +Instruments WCF clients and/or services using implementations of +`IClientMessageInspector` and `IDispatchMessageInspector` respectively. + +## Installation + +Add the OpenTelemetry.Contrib.Instrumentation.Wcf package via NuGet. + +## OpenTelemetry Configuration + +Use the `AddWcfInstrumentation` extension method to add WCF instrumentation to +an OpenTelemetry TracerProvider: + +```csharp +using var openTelemetry = Sdk.CreateTracerProviderBuilder() + .AddWcfInstrumentation() + .Build(); +``` + +## WCF Client Configuration + +Add the `IClientMessageInspector` instrumentation as a behavior extension on the +clients you want to instrument: + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +Example project available in [examples/wcf/server](../../examples/wcf/client/Program.cs) folder. + +## WCF Server Configuration + +Add the `IDispatchMessageInspector` instrumentation as a behavior extension on +the services you want to instrument: + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +Example project available in [examples/wcf/server](../../examples/wcf/server/Program.cs) folder. + +## References + +* [OpenTelemetry Project](https://opentelemetry.io/) \ No newline at end of file From 3c67689165d829b3c50b540e8731a158de9c1380 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 26 Oct 2020 10:56:21 -0700 Subject: [PATCH 04/34] Added support for the .NET Core version of WCF clients. --- .../Examples.Wcf.Client.Core.csproj | 33 ++++++++ examples/wcf/client-core/Program.cs | 84 +++++++++++++++++++ .../wcf/client-core/StatusServiceClient.cs | 38 +++++++++ examples/wcf/client-core/appsettings.json | 6 ++ .../App.config | 0 .../Examples.Wcf.Client.NetFramework.csproj | 27 ++++++ .../Program.cs | 0 .../StatusServiceClient.cs | 0 .../wcf/client/Examples.Wcf.Client.csproj | 17 ---- .../App.config | 0 .../Examples.Wcf.Server.NetFramework.csproj | 27 ++++++ .../Program.cs | 0 .../StatusService.cs | 0 .../wcf/server/Examples.Wcf.Server.csproj | 17 ---- .../wcf/shared/Examples.Wcf.Shared.csproj | 16 ++-- opentelemetry-dotnet-contrib.sln | 33 +++++--- ...lemetry.Contrib.Instrumentation.Wcf.csproj | 8 +- .../README.md | 31 +++++-- .../TelemetryBehaviourExtensionElement.cs | 2 + .../TelemetryDispatchMessageInspector.cs | 2 + .../TelemetryEndpointBehavior.cs | 33 +++++++- .../WcfInstrumentationActivitySource.cs | 2 +- 22 files changed, 306 insertions(+), 70 deletions(-) create mode 100644 examples/wcf/client-core/Examples.Wcf.Client.Core.csproj create mode 100644 examples/wcf/client-core/Program.cs create mode 100644 examples/wcf/client-core/StatusServiceClient.cs create mode 100644 examples/wcf/client-core/appsettings.json rename examples/wcf/{client => client-netframework}/App.config (100%) create mode 100644 examples/wcf/client-netframework/Examples.Wcf.Client.NetFramework.csproj rename examples/wcf/{client => client-netframework}/Program.cs (100%) rename examples/wcf/{client => client-netframework}/StatusServiceClient.cs (100%) delete mode 100644 examples/wcf/client/Examples.Wcf.Client.csproj rename examples/wcf/{server => server-netframework}/App.config (100%) create mode 100644 examples/wcf/server-netframework/Examples.Wcf.Server.NetFramework.csproj rename examples/wcf/{server => server-netframework}/Program.cs (100%) rename examples/wcf/{server => server-netframework}/StatusService.cs (100%) delete mode 100644 examples/wcf/server/Examples.Wcf.Server.csproj diff --git a/examples/wcf/client-core/Examples.Wcf.Client.Core.csproj b/examples/wcf/client-core/Examples.Wcf.Client.Core.csproj new file mode 100644 index 0000000000..55089e5b03 --- /dev/null +++ b/examples/wcf/client-core/Examples.Wcf.Client.Core.csproj @@ -0,0 +1,33 @@ + + + + Exe + netcoreapp3.1 + false + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + + diff --git a/examples/wcf/client-core/Program.cs b/examples/wcf/client-core/Program.cs new file mode 100644 index 0000000000..d7f2b8cf90 --- /dev/null +++ b/examples/wcf/client-core/Program.cs @@ -0,0 +1,84 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.IO; +using System.ServiceModel; +using System.ServiceModel.Channels; +using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; +using OpenTelemetry; +using OpenTelemetry.Contrib.Instrumentation.Wcf; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; + +namespace Examples.Wcf.Client +{ + internal static class Program + { + public static async Task Main() + { + IConfigurationRoot config = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json") + .Build(); + + using var openTelemetry = Sdk.CreateTracerProviderBuilder() + .AddWcfInstrumentation() + .AddHttpClientInstrumentation() // <- Added to test suppression of http spans. + .AddJaegerExporter() + .SetResource(Resources.CreateServiceResource("Wcf-Client-Core")) + .Build(); + + await CallService( + new BasicHttpBinding(BasicHttpSecurityMode.None), + new EndpointAddress(config.GetSection("Service").GetValue("HttpAddress"))).ConfigureAwait(false); + await CallService( + new NetTcpBinding(SecurityMode.None), + new EndpointAddress(config.GetSection("Service").GetValue("TcpAddress"))).ConfigureAwait(false); + + Console.WriteLine("Press enter to exit."); + Console.ReadLine(); + } + + private static async Task CallService(Binding binding, EndpointAddress remoteAddress) + { + StatusServiceClient client = new StatusServiceClient(binding, remoteAddress); + client.Endpoint.EndpointBehaviors.Add(new TelemetryEndpointBehavior()); + try + { + var response = await client.Ping( + new StatusRequest + { + Status = Guid.NewGuid().ToString("N"), + }).ConfigureAwait(false); + + Console.WriteLine($"Server returned: {response?.ServerTime}"); + } + finally + { + if (client.State == CommunicationState.Faulted) + { + client.Abort(); + } + else + { + client.Close(); + } + } + } + } +} diff --git a/examples/wcf/client-core/StatusServiceClient.cs b/examples/wcf/client-core/StatusServiceClient.cs new file mode 100644 index 0000000000..7926cf2ab4 --- /dev/null +++ b/examples/wcf/client-core/StatusServiceClient.cs @@ -0,0 +1,38 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.ServiceModel; +using System.ServiceModel.Channels; +using System.Threading.Tasks; + +namespace Examples.Wcf.Client +{ + public class StatusServiceClient : ClientBase, IStatusServiceContract + { + public StatusServiceClient(string name) + : base(name) + { + } + + public StatusServiceClient(Binding binding, EndpointAddress remoteAddress) + : base(binding, remoteAddress) + { + } + + public Task Ping(StatusRequest request) + => this.Channel.Ping(request); + } +} diff --git a/examples/wcf/client-core/appsettings.json b/examples/wcf/client-core/appsettings.json new file mode 100644 index 0000000000..a468e204fb --- /dev/null +++ b/examples/wcf/client-core/appsettings.json @@ -0,0 +1,6 @@ +{ + "Service": { + "HttpAddress": "http://localhost:9009/Telemetry", + "TcpAddress": "net.tcp://localhost:9090/Telemetry" + } +} \ No newline at end of file diff --git a/examples/wcf/client/App.config b/examples/wcf/client-netframework/App.config similarity index 100% rename from examples/wcf/client/App.config rename to examples/wcf/client-netframework/App.config diff --git a/examples/wcf/client-netframework/Examples.Wcf.Client.NetFramework.csproj b/examples/wcf/client-netframework/Examples.Wcf.Client.NetFramework.csproj new file mode 100644 index 0000000000..6734d69467 --- /dev/null +++ b/examples/wcf/client-netframework/Examples.Wcf.Client.NetFramework.csproj @@ -0,0 +1,27 @@ + + + + Exe + net46 + false + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/wcf/client/Program.cs b/examples/wcf/client-netframework/Program.cs similarity index 100% rename from examples/wcf/client/Program.cs rename to examples/wcf/client-netframework/Program.cs diff --git a/examples/wcf/client/StatusServiceClient.cs b/examples/wcf/client-netframework/StatusServiceClient.cs similarity index 100% rename from examples/wcf/client/StatusServiceClient.cs rename to examples/wcf/client-netframework/StatusServiceClient.cs diff --git a/examples/wcf/client/Examples.Wcf.Client.csproj b/examples/wcf/client/Examples.Wcf.Client.csproj deleted file mode 100644 index 14fd9819e9..0000000000 --- a/examples/wcf/client/Examples.Wcf.Client.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - Exe - net46 - false - - - - - - - - - - - diff --git a/examples/wcf/server/App.config b/examples/wcf/server-netframework/App.config similarity index 100% rename from examples/wcf/server/App.config rename to examples/wcf/server-netframework/App.config diff --git a/examples/wcf/server-netframework/Examples.Wcf.Server.NetFramework.csproj b/examples/wcf/server-netframework/Examples.Wcf.Server.NetFramework.csproj new file mode 100644 index 0000000000..6734d69467 --- /dev/null +++ b/examples/wcf/server-netframework/Examples.Wcf.Server.NetFramework.csproj @@ -0,0 +1,27 @@ + + + + Exe + net46 + false + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/wcf/server/Program.cs b/examples/wcf/server-netframework/Program.cs similarity index 100% rename from examples/wcf/server/Program.cs rename to examples/wcf/server-netframework/Program.cs diff --git a/examples/wcf/server/StatusService.cs b/examples/wcf/server-netframework/StatusService.cs similarity index 100% rename from examples/wcf/server/StatusService.cs rename to examples/wcf/server-netframework/StatusService.cs diff --git a/examples/wcf/server/Examples.Wcf.Server.csproj b/examples/wcf/server/Examples.Wcf.Server.csproj deleted file mode 100644 index 14fd9819e9..0000000000 --- a/examples/wcf/server/Examples.Wcf.Server.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - Exe - net46 - false - - - - - - - - - - - diff --git a/examples/wcf/shared/Examples.Wcf.Shared.csproj b/examples/wcf/shared/Examples.Wcf.Shared.csproj index 368ba43946..7eba215421 100644 --- a/examples/wcf/shared/Examples.Wcf.Shared.csproj +++ b/examples/wcf/shared/Examples.Wcf.Shared.csproj @@ -1,21 +1,15 @@  - net46 + net452;netstandard2.0 - - - - - - - - + + - - + + diff --git a/opentelemetry-dotnet-contrib.sln b/opentelemetry-dotnet-contrib.sln index f9bf137907..4d9d9065f7 100644 --- a/opentelemetry-dotnet-contrib.sln +++ b/opentelemetry-dotnet-contrib.sln @@ -83,11 +83,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{B7 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "wcf", "wcf", "{73474960-8F91-4EE5-8E3E-F7E7ADA99238}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.Wcf.Client", "examples\wcf\client\Examples.Wcf.Client.csproj", "{351C01F2-9664-48AC-9E1B-69AB42907392}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.Wcf.Shared", "examples\wcf\shared\Examples.Wcf.Shared.csproj", "{21716C26-3B2A-4208-BDFB-8E58E2AF49EA}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.Wcf.Server", "examples\wcf\server\Examples.Wcf.Server.csproj", "{DAFE7886-DD52-4C96-BFF0-9744E7F9B45F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.Wcf.Client.Core", "examples\wcf\client-core\Examples.Wcf.Client.Core.csproj", "{3AF5D7E4-CA7D-401B-9729-A6D8F63B023C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.Wcf.Shared", "examples\wcf\shared\Examples.Wcf.Shared.csproj", "{21716C26-3B2A-4208-BDFB-8E58E2AF49EA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.Wcf.Client.NetFramework", "examples\wcf\client-netframework\Examples.Wcf.Client.NetFramework.csproj", "{2A7867E5-0FD6-42F8-B594-19E897EDA54C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.Wcf.Server.NetFramework", "examples\wcf\server-netframework\Examples.Wcf.Server.NetFramework.csproj", "{E205AA70-36BD-461D-8B87-909ED1BCA721}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -135,18 +137,22 @@ Global {CAD5C27A-D359-4086-9C4F-02204C084A8E}.Debug|Any CPU.Build.0 = Debug|Any CPU {CAD5C27A-D359-4086-9C4F-02204C084A8E}.Release|Any CPU.ActiveCfg = Release|Any CPU {CAD5C27A-D359-4086-9C4F-02204C084A8E}.Release|Any CPU.Build.0 = Release|Any CPU - {351C01F2-9664-48AC-9E1B-69AB42907392}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {351C01F2-9664-48AC-9E1B-69AB42907392}.Debug|Any CPU.Build.0 = Debug|Any CPU - {351C01F2-9664-48AC-9E1B-69AB42907392}.Release|Any CPU.ActiveCfg = Release|Any CPU - {351C01F2-9664-48AC-9E1B-69AB42907392}.Release|Any CPU.Build.0 = Release|Any CPU - {DAFE7886-DD52-4C96-BFF0-9744E7F9B45F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DAFE7886-DD52-4C96-BFF0-9744E7F9B45F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DAFE7886-DD52-4C96-BFF0-9744E7F9B45F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DAFE7886-DD52-4C96-BFF0-9744E7F9B45F}.Release|Any CPU.Build.0 = Release|Any CPU {21716C26-3B2A-4208-BDFB-8E58E2AF49EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {21716C26-3B2A-4208-BDFB-8E58E2AF49EA}.Debug|Any CPU.Build.0 = Debug|Any CPU {21716C26-3B2A-4208-BDFB-8E58E2AF49EA}.Release|Any CPU.ActiveCfg = Release|Any CPU {21716C26-3B2A-4208-BDFB-8E58E2AF49EA}.Release|Any CPU.Build.0 = Release|Any CPU + {3AF5D7E4-CA7D-401B-9729-A6D8F63B023C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3AF5D7E4-CA7D-401B-9729-A6D8F63B023C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3AF5D7E4-CA7D-401B-9729-A6D8F63B023C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3AF5D7E4-CA7D-401B-9729-A6D8F63B023C}.Release|Any CPU.Build.0 = Release|Any CPU + {2A7867E5-0FD6-42F8-B594-19E897EDA54C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2A7867E5-0FD6-42F8-B594-19E897EDA54C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2A7867E5-0FD6-42F8-B594-19E897EDA54C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2A7867E5-0FD6-42F8-B594-19E897EDA54C}.Release|Any CPU.Build.0 = Release|Any CPU + {E205AA70-36BD-461D-8B87-909ED1BCA721}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E205AA70-36BD-461D-8B87-909ED1BCA721}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E205AA70-36BD-461D-8B87-909ED1BCA721}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E205AA70-36BD-461D-8B87-909ED1BCA721}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -166,9 +172,10 @@ Global {899E506C-8525-4471-8C6D-5A9FA6B8517B} = {2097345F-4DD3-477D-BC54-A922F9B2B402} {CAD5C27A-D359-4086-9C4F-02204C084A8E} = {22DF5DC0-1290-4E83-A9D8-6BB7DE3B3E63} {73474960-8F91-4EE5-8E3E-F7E7ADA99238} = {B75EE478-97F7-4E9F-9A5A-DB3D0988EDEA} - {351C01F2-9664-48AC-9E1B-69AB42907392} = {73474960-8F91-4EE5-8E3E-F7E7ADA99238} - {DAFE7886-DD52-4C96-BFF0-9744E7F9B45F} = {73474960-8F91-4EE5-8E3E-F7E7ADA99238} {21716C26-3B2A-4208-BDFB-8E58E2AF49EA} = {73474960-8F91-4EE5-8E3E-F7E7ADA99238} + {3AF5D7E4-CA7D-401B-9729-A6D8F63B023C} = {73474960-8F91-4EE5-8E3E-F7E7ADA99238} + {2A7867E5-0FD6-42F8-B594-19E897EDA54C} = {73474960-8F91-4EE5-8E3E-F7E7ADA99238} + {E205AA70-36BD-461D-8B87-909ED1BCA721} = {73474960-8F91-4EE5-8E3E-F7E7ADA99238} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B0816796-CDB3-47D7-8C3C-946434DE3B66} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/OpenTelemetry.Contrib.Instrumentation.Wcf.csproj b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/OpenTelemetry.Contrib.Instrumentation.Wcf.csproj index 08a96b5d7f..d85137e35c 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/OpenTelemetry.Contrib.Instrumentation.Wcf.csproj +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/OpenTelemetry.Contrib.Instrumentation.Wcf.csproj @@ -1,14 +1,18 @@  - net452 + net452;netstandard2.0 OpenTelemetry instrumentation for WCF $(PackageTags);distributed-tracing;WCF - + + + + + diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md index 6c0b0c088e..2eb98f3f4f 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md @@ -18,10 +18,10 @@ using var openTelemetry = Sdk.CreateTracerProviderBuilder() .Build(); ``` -## WCF Client Configuration +## WCF Client Configuration (.NET Framework) -Add the `IClientMessageInspector` instrumentation as a behavior extension on the -clients you want to instrument: +Add the `IClientMessageInspector` instrumentation via a behavior extension on +the clients you want to instrument: ```xml @@ -53,11 +53,26 @@ clients you want to instrument: ``` -Example project available in [examples/wcf/server](../../examples/wcf/client/Program.cs) folder. +Example project available in +[examples/wcf/client-netframework](../../examples/wcf/client-netframework/Program.cs) +folder. + +## WCF Client Configuration (.NET Core) + +Add the `IClientMessageInspector` instrumentation as an endpoint behavior on the +clients you want to instrument: + +```csharp +StatusServiceClient client = new StatusServiceClient(binding, remoteAddress); +client.Endpoint.EndpointBehaviors.Add(new TelemetryEndpointBehavior()); +``` + +Example project available in +[examples/wcf/client-core](../../examples/wcf/client-core/Program.cs) folder. -## WCF Server Configuration +## WCF Server Configuration (.NET Framework) -Add the `IDispatchMessageInspector` instrumentation as a behavior extension on +Add the `IDispatchMessageInspector` instrumentation via a behavior extension on the services you want to instrument: ```xml @@ -97,7 +112,9 @@ the services you want to instrument: ``` -Example project available in [examples/wcf/server](../../examples/wcf/server/Program.cs) folder. +Example project available in +[examples/wcf/server-netframework](../../examples/wcf/server-netframework/Program.cs) +folder. ## References diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryBehaviourExtensionElement.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryBehaviourExtensionElement.cs index ca187406ff..380dde1a21 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryBehaviourExtensionElement.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryBehaviourExtensionElement.cs @@ -14,6 +14,7 @@ // limitations under the License. // +#if NETFRAMEWORK using System; using System.ServiceModel.Configuration; @@ -34,3 +35,4 @@ protected override object CreateBehavior() } } } +#endif diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs index 4329c90916..e604fa2ae7 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs @@ -14,6 +14,7 @@ // limitations under the License. // +#if NETFRAMEWORK using System; using System.Diagnostics; using System.ServiceModel; @@ -96,3 +97,4 @@ public void BeforeSendReply(ref Message reply, object correlationState) } } } +#endif diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryEndpointBehavior.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryEndpointBehavior.cs index 90367f6671..ece41a33e9 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryEndpointBehavior.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryEndpointBehavior.cs @@ -20,13 +20,36 @@ namespace OpenTelemetry.Contrib.Instrumentation.Wcf { +#if NETFRAMEWORK /// /// An implementation whichs add the to client endpoints and the /// to service endpoints. /// +#else + /// + /// An implementation whichs add the to client endpoints. + /// +#endif public class TelemetryEndpointBehavior : IEndpointBehavior { + private readonly TelemetryClientMessageInspector telemetryClientMessageInspector; +#if NETFRAMEWORK + private readonly TelemetryDispatchMessageInspector telemetryDispatchMessageInspector; +#endif + + /// + /// Initializes a new instance of the class. + /// + public TelemetryEndpointBehavior() + { + this.telemetryClientMessageInspector = new TelemetryClientMessageInspector(); +#if NETFRAMEWORK + this.telemetryDispatchMessageInspector = new TelemetryDispatchMessageInspector(); +#endif + } + /// public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { @@ -35,13 +58,19 @@ public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterColle /// public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { - clientRuntime.MessageInspectors.Add(new TelemetryClientMessageInspector()); +#if NETFRAMEWORK + clientRuntime.MessageInspectors.Add(this.telemetryClientMessageInspector); +#else + clientRuntime.ClientMessageInspectors.Add(this.telemetryClientMessageInspector); +#endif } /// public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { - endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new TelemetryDispatchMessageInspector()); +#if NETFRAMEWORK + endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this.telemetryDispatchMessageInspector); +#endif } /// diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationActivitySource.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationActivitySource.cs index 6effced40b..82b7e09713 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationActivitySource.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationActivitySource.cs @@ -30,7 +30,7 @@ internal static class WcfInstrumentationActivitySource public const string IncomingRequestActivityName = ActivitySourceName + ".IncomingRequest"; public const string OutgoingRequestActivityName = ActivitySourceName + ".OutgoingRequest"; - private static readonly Version Version = typeof(TelemetryDispatchMessageInspector).Assembly.GetName().Version; + private static readonly Version Version = typeof(WcfInstrumentationActivitySource).Assembly.GetName().Version; public static ActivitySource ActivitySource { get; } = new ActivitySource(ActivitySourceName, Version.ToString()); From ffb5f080905029d37d441ccb1fcc86822d6eec98 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 26 Oct 2020 21:01:06 -0700 Subject: [PATCH 05/34] Code review feedback. --- .../client-core/Examples.Wcf.Client.Core.csproj | 2 +- examples/wcf/client-core/Program.cs | 4 +--- .../Examples.Wcf.Client.NetFramework.csproj | 2 +- examples/wcf/client-netframework/Program.cs | 4 +--- .../Examples.Wcf.Server.NetFramework.csproj | 2 +- examples/wcf/server-netframework/Program.cs | 4 +--- .../TelemetryClientMessageInspector.cs | 14 +++++++++++--- .../TelemetryDispatchMessageInspector.cs | 14 +++++++++++--- 8 files changed, 28 insertions(+), 18 deletions(-) diff --git a/examples/wcf/client-core/Examples.Wcf.Client.Core.csproj b/examples/wcf/client-core/Examples.Wcf.Client.Core.csproj index 55089e5b03..e457a7db5d 100644 --- a/examples/wcf/client-core/Examples.Wcf.Client.Core.csproj +++ b/examples/wcf/client-core/Examples.Wcf.Client.Core.csproj @@ -11,7 +11,7 @@ - + diff --git a/examples/wcf/client-core/Program.cs b/examples/wcf/client-core/Program.cs index d7f2b8cf90..325a04fa7c 100644 --- a/examples/wcf/client-core/Program.cs +++ b/examples/wcf/client-core/Program.cs @@ -22,7 +22,6 @@ using Microsoft.Extensions.Configuration; using OpenTelemetry; using OpenTelemetry.Contrib.Instrumentation.Wcf; -using OpenTelemetry.Resources; using OpenTelemetry.Trace; namespace Examples.Wcf.Client @@ -39,8 +38,7 @@ public static async Task Main() using var openTelemetry = Sdk.CreateTracerProviderBuilder() .AddWcfInstrumentation() .AddHttpClientInstrumentation() // <- Added to test suppression of http spans. - .AddJaegerExporter() - .SetResource(Resources.CreateServiceResource("Wcf-Client-Core")) + .AddZipkinExporter(o => o.ServiceName = "Wcf-Client-Core") .Build(); await CallService( diff --git a/examples/wcf/client-netframework/Examples.Wcf.Client.NetFramework.csproj b/examples/wcf/client-netframework/Examples.Wcf.Client.NetFramework.csproj index 6734d69467..2e073c4f54 100644 --- a/examples/wcf/client-netframework/Examples.Wcf.Client.NetFramework.csproj +++ b/examples/wcf/client-netframework/Examples.Wcf.Client.NetFramework.csproj @@ -7,7 +7,7 @@ - + diff --git a/examples/wcf/client-netframework/Program.cs b/examples/wcf/client-netframework/Program.cs index a60a65b5c3..2a3d8a0af1 100644 --- a/examples/wcf/client-netframework/Program.cs +++ b/examples/wcf/client-netframework/Program.cs @@ -18,7 +18,6 @@ using System.ServiceModel; using System.Threading.Tasks; using OpenTelemetry; -using OpenTelemetry.Resources; using OpenTelemetry.Trace; namespace Examples.Wcf.Client @@ -30,8 +29,7 @@ public static async Task Main() using var openTelemetry = Sdk.CreateTracerProviderBuilder() .AddWcfInstrumentation() .AddHttpClientInstrumentation() // <- Added to test suppression of http spans. - .AddJaegerExporter() - .SetResource(Resources.CreateServiceResource("Wcf-Client")) + .AddZipkinExporter(o => o.ServiceName = "Wcf-Client") .Build(); await CallService("StatusService_Http").ConfigureAwait(false); diff --git a/examples/wcf/server-netframework/Examples.Wcf.Server.NetFramework.csproj b/examples/wcf/server-netframework/Examples.Wcf.Server.NetFramework.csproj index 6734d69467..2e073c4f54 100644 --- a/examples/wcf/server-netframework/Examples.Wcf.Server.NetFramework.csproj +++ b/examples/wcf/server-netframework/Examples.Wcf.Server.NetFramework.csproj @@ -7,7 +7,7 @@ - + diff --git a/examples/wcf/server-netframework/Program.cs b/examples/wcf/server-netframework/Program.cs index f969f2271c..e058158c42 100644 --- a/examples/wcf/server-netframework/Program.cs +++ b/examples/wcf/server-netframework/Program.cs @@ -17,7 +17,6 @@ using System; using System.ServiceModel; using OpenTelemetry; -using OpenTelemetry.Resources; using OpenTelemetry.Trace; namespace Examples.Wcf.Server @@ -29,8 +28,7 @@ public static void Main() using var openTelemetry = Sdk.CreateTracerProviderBuilder() .AddWcfInstrumentation() .AddAspNetInstrumentation() // <- Added to test suppression of http spans. - .AddJaegerExporter() - .SetResource(Resources.CreateServiceResource("Wcf-Server")) + .AddZipkinExporter(o => o.ServiceName = "Wcf-Server") .Build(); ServiceHost serviceHost = new ServiceHost(typeof(StatusService)); diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs index 59ab4a77a5..c045886781 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs @@ -58,7 +58,8 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel) SuppressInstrumentationScope.Enter(); } - activity.DisplayName = request.Headers.Action; + string action = request.Headers.Action; + activity.DisplayName = action; WcfInstrumentationActivitySource.Options.Propagator.Inject( new PropagationContext(activity.Context, Baggage.Current), @@ -67,9 +68,16 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel) if (activity.IsAllDataRequested) { + int lastIndex = action.LastIndexOf('/'); + + activity.SetTag("rpc.system", "wcf"); + activity.SetTag("rpc.service", action.Substring(0, lastIndex)); + activity.SetTag("rpc.method", action.Substring(lastIndex + 1)); + activity.SetTag("net.peer.name", channel.RemoteAddress.Uri.Host); + activity.SetTag("net.peer.port", channel.RemoteAddress.Uri.Port); + activity.SetTag("soap.version", request.Version.ToString()); - activity.SetTag("wcf.channel.scheme", $"{channel.RemoteAddress.Uri.Scheme}"); - activity.SetTag("wcf.channel.host", $"{channel.RemoteAddress.Uri.Host}:{channel.RemoteAddress.Uri.Port}"); + activity.SetTag("wcf.channel.scheme", channel.RemoteAddress.Uri.Scheme); activity.SetTag("wcf.channel.path", channel.RemoteAddress.Uri.LocalPath); } } diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs index e604fa2ae7..f0b203a907 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs @@ -61,13 +61,21 @@ public object AfterReceiveRequest(ref Message request, IClientChannel channel, I SuppressInstrumentationScope.Enter(); } - activity.DisplayName = request.Headers.Action; + string action = request.Headers.Action; + activity.DisplayName = action; if (activity.IsAllDataRequested) { + int lastIndex = action.LastIndexOf('/'); + + activity.SetTag("rpc.system", "wcf"); + activity.SetTag("rpc.service", action.Substring(0, lastIndex)); + activity.SetTag("rpc.method", action.Substring(lastIndex + 1)); + activity.SetTag("net.host.name", channel.LocalAddress.Uri.Host); + activity.SetTag("net.host.port", channel.LocalAddress.Uri.Port); + activity.SetTag("soap.version", request.Version.ToString()); - activity.SetTag("wcf.channel.scheme", $"{channel.LocalAddress.Uri.Scheme}"); - activity.SetTag("wcf.channel.host", $"{channel.LocalAddress.Uri.Host}:{channel.LocalAddress.Uri.Port}"); + activity.SetTag("wcf.channel.scheme", channel.LocalAddress.Uri.Scheme); activity.SetTag("wcf.channel.path", channel.LocalAddress.Uri.LocalPath); } } From 4dbc5b15b4ef8b82e49346cf83ae735d6bc37dcc Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Thu, 11 Feb 2021 07:57:52 -0800 Subject: [PATCH 06/34] Updated code for 1.0 and changes done in main. --- examples/wcf/client-core/Examples.Wcf.Client.Core.csproj | 6 +++--- examples/wcf/client-core/Program.cs | 4 +++- .../Examples.Wcf.Client.NetFramework.csproj | 6 +++--- examples/wcf/client-netframework/Program.cs | 4 +++- .../Examples.Wcf.Server.NetFramework.csproj | 6 +++--- examples/wcf/server-netframework/Program.cs | 4 +++- .../OpenTelemetry.Contrib.Instrumentation.Wcf.csproj | 4 ++++ .../TelemetryClientMessageInspector.cs | 6 +++++- .../TelemetryDispatchMessageInspector.cs | 6 +++++- .../WcfInstrumentationOptions.cs | 6 +++--- 10 files changed, 35 insertions(+), 17 deletions(-) diff --git a/examples/wcf/client-core/Examples.Wcf.Client.Core.csproj b/examples/wcf/client-core/Examples.Wcf.Client.Core.csproj index e457a7db5d..cbf13f2385 100644 --- a/examples/wcf/client-core/Examples.Wcf.Client.Core.csproj +++ b/examples/wcf/client-core/Examples.Wcf.Client.Core.csproj @@ -11,9 +11,9 @@ - - - + + + diff --git a/examples/wcf/client-core/Program.cs b/examples/wcf/client-core/Program.cs index 325a04fa7c..503f273d62 100644 --- a/examples/wcf/client-core/Program.cs +++ b/examples/wcf/client-core/Program.cs @@ -22,6 +22,7 @@ using Microsoft.Extensions.Configuration; using OpenTelemetry; using OpenTelemetry.Contrib.Instrumentation.Wcf; +using OpenTelemetry.Resources; using OpenTelemetry.Trace; namespace Examples.Wcf.Client @@ -36,9 +37,10 @@ public static async Task Main() .Build(); using var openTelemetry = Sdk.CreateTracerProviderBuilder() + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("Wcf-Client-Core")) .AddWcfInstrumentation() .AddHttpClientInstrumentation() // <- Added to test suppression of http spans. - .AddZipkinExporter(o => o.ServiceName = "Wcf-Client-Core") + .AddZipkinExporter() .Build(); await CallService( diff --git a/examples/wcf/client-netframework/Examples.Wcf.Client.NetFramework.csproj b/examples/wcf/client-netframework/Examples.Wcf.Client.NetFramework.csproj index 2e073c4f54..7e79c160c0 100644 --- a/examples/wcf/client-netframework/Examples.Wcf.Client.NetFramework.csproj +++ b/examples/wcf/client-netframework/Examples.Wcf.Client.NetFramework.csproj @@ -7,9 +7,9 @@ - - - + + + diff --git a/examples/wcf/client-netframework/Program.cs b/examples/wcf/client-netframework/Program.cs index 2a3d8a0af1..836e8a8f63 100644 --- a/examples/wcf/client-netframework/Program.cs +++ b/examples/wcf/client-netframework/Program.cs @@ -18,6 +18,7 @@ using System.ServiceModel; using System.Threading.Tasks; using OpenTelemetry; +using OpenTelemetry.Resources; using OpenTelemetry.Trace; namespace Examples.Wcf.Client @@ -27,9 +28,10 @@ internal static class Program public static async Task Main() { using var openTelemetry = Sdk.CreateTracerProviderBuilder() + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("Wcf-Client")) .AddWcfInstrumentation() .AddHttpClientInstrumentation() // <- Added to test suppression of http spans. - .AddZipkinExporter(o => o.ServiceName = "Wcf-Client") + .AddZipkinExporter() .Build(); await CallService("StatusService_Http").ConfigureAwait(false); diff --git a/examples/wcf/server-netframework/Examples.Wcf.Server.NetFramework.csproj b/examples/wcf/server-netframework/Examples.Wcf.Server.NetFramework.csproj index 2e073c4f54..7e79c160c0 100644 --- a/examples/wcf/server-netframework/Examples.Wcf.Server.NetFramework.csproj +++ b/examples/wcf/server-netframework/Examples.Wcf.Server.NetFramework.csproj @@ -7,9 +7,9 @@ - - - + + + diff --git a/examples/wcf/server-netframework/Program.cs b/examples/wcf/server-netframework/Program.cs index e058158c42..e39786c127 100644 --- a/examples/wcf/server-netframework/Program.cs +++ b/examples/wcf/server-netframework/Program.cs @@ -17,6 +17,7 @@ using System; using System.ServiceModel; using OpenTelemetry; +using OpenTelemetry.Resources; using OpenTelemetry.Trace; namespace Examples.Wcf.Server @@ -26,9 +27,10 @@ internal static class Program public static void Main() { using var openTelemetry = Sdk.CreateTracerProviderBuilder() + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("Wcf-Server")) .AddWcfInstrumentation() .AddAspNetInstrumentation() // <- Added to test suppression of http spans. - .AddZipkinExporter(o => o.ServiceName = "Wcf-Server") + .AddZipkinExporter() .Build(); ServiceHost serviceHost = new ServiceHost(typeof(StatusService)); diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/OpenTelemetry.Contrib.Instrumentation.Wcf.csproj b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/OpenTelemetry.Contrib.Instrumentation.Wcf.csproj index d85137e35c..7581c6c914 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/OpenTelemetry.Contrib.Instrumentation.Wcf.csproj +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/OpenTelemetry.Contrib.Instrumentation.Wcf.csproj @@ -6,6 +6,10 @@ $(PackageTags);distributed-tracing;WCF + + + + diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs index c045886781..09cadfd50b 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs @@ -93,7 +93,11 @@ public void AfterReceiveReply(ref Message reply, object correlationState) { if (activity.IsAllDataRequested) { - activity.SetStatus(!reply.IsFault ? Status.Ok : Status.Unknown); + if (reply.IsFault) + { + activity.SetStatus(Status.Error); + } + activity.SetTag("soap.reply_action", reply.Headers.Action); } diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs index f0b203a907..70120a5300 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs @@ -96,7 +96,11 @@ public void BeforeSendReply(ref Message reply, object correlationState) { if (activity.IsAllDataRequested) { - activity.SetStatus(!reply.IsFault ? Status.Ok : Status.Unknown); + if (reply.IsFault) + { + activity.SetStatus(Status.Error); + } + activity.SetTag("soap.reply_action", reply.Headers.Action); } diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationOptions.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationOptions.cs index 3eebe49486..aba0be8169 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationOptions.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationOptions.cs @@ -26,11 +26,11 @@ namespace OpenTelemetry.Contrib.Instrumentation.Wcf public class WcfInstrumentationOptions { /// - /// Gets or sets for context propagation. Default value: with & . + /// Gets or sets for context propagation. Default value: with & . /// - public IPropagator Propagator { get; set; } = new CompositePropagator(new IPropagator[] + public TextMapPropagator Propagator { get; set; } = new CompositeTextMapPropagator(new TextMapPropagator[] { - new TextMapPropagator(), + new TraceContextPropagator(), new BaggagePropagator(), }); From fe0f5df136d7fe1fd5e5e66dc8a1b4e9278356ff Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Thu, 11 Feb 2021 08:09:47 -0800 Subject: [PATCH 07/34] Lint errors. --- examples/wcf/client-netframework/App.config | 2 +- examples/wcf/server-netframework/App.config | 2 +- .../README.md | 20 +++++++++++++++---- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/examples/wcf/client-netframework/App.config b/examples/wcf/client-netframework/App.config index 03b0248a4b..4716577152 100644 --- a/examples/wcf/client-netframework/App.config +++ b/examples/wcf/client-netframework/App.config @@ -30,4 +30,4 @@ - \ No newline at end of file + diff --git a/examples/wcf/server-netframework/App.config b/examples/wcf/server-netframework/App.config index cbfe519905..0046bbfe76 100644 --- a/examples/wcf/server-netframework/App.config +++ b/examples/wcf/server-netframework/App.config @@ -38,4 +38,4 @@ - \ No newline at end of file + diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md index 2eb98f3f4f..a3e77fb39e 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md @@ -47,7 +47,13 @@ the clients you want to instrument: - + @@ -81,7 +87,9 @@ the services you want to instrument: - + @@ -100,7 +108,11 @@ the services you want to instrument: - + @@ -118,4 +130,4 @@ folder. ## References -* [OpenTelemetry Project](https://opentelemetry.io/) \ No newline at end of file +* [OpenTelemetry Project](https://opentelemetry.io/) From 4bf55a1295deeff0ac4d8811572628cc5caffc4a Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Thu, 11 Feb 2021 12:49:23 -0800 Subject: [PATCH 08/34] Server tests and cleanup. --- .../Examples.Wcf.Client.Core.csproj | 1 - .../Examples.Wcf.Client.NetFramework.csproj | 1 - .../Examples.Wcf.Server.NetFramework.csproj | 2 - examples/wcf/server-netframework/Program.cs | 1 - opentelemetry-dotnet-contrib.sln | 7 + .../AssemblyInfo.cs | 22 +++ .../TelemetryDispatchMessageInspector.cs | 5 - ...y.Contrib.Instrumentation.Wcf.Tests.csproj | 31 ++++ ...etryDispatchMessageInspectorTests.netfx.cs | 152 ++++++++++++++++++ .../WCF/IServiceContract.cs | 28 ++++ .../WCF/Service.netfx.cs | 40 +++++ .../WCF/ServiceClient.cs | 33 ++++ .../WCF/ServiceRequest.cs | 27 ++++ .../WCF/ServiceResponse.cs | 27 ++++ 14 files changed, 367 insertions(+), 10 deletions(-) create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Wcf/AssemblyInfo.cs create mode 100644 test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests.csproj create mode 100644 test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryDispatchMessageInspectorTests.netfx.cs create mode 100644 test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/WCF/IServiceContract.cs create mode 100644 test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/WCF/Service.netfx.cs create mode 100644 test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/WCF/ServiceClient.cs create mode 100644 test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/WCF/ServiceRequest.cs create mode 100644 test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/WCF/ServiceResponse.cs diff --git a/examples/wcf/client-core/Examples.Wcf.Client.Core.csproj b/examples/wcf/client-core/Examples.Wcf.Client.Core.csproj index cbf13f2385..06377393ef 100644 --- a/examples/wcf/client-core/Examples.Wcf.Client.Core.csproj +++ b/examples/wcf/client-core/Examples.Wcf.Client.Core.csproj @@ -12,7 +12,6 @@ - diff --git a/examples/wcf/client-netframework/Examples.Wcf.Client.NetFramework.csproj b/examples/wcf/client-netframework/Examples.Wcf.Client.NetFramework.csproj index 7e79c160c0..4066923d8e 100644 --- a/examples/wcf/client-netframework/Examples.Wcf.Client.NetFramework.csproj +++ b/examples/wcf/client-netframework/Examples.Wcf.Client.NetFramework.csproj @@ -8,7 +8,6 @@ - diff --git a/examples/wcf/server-netframework/Examples.Wcf.Server.NetFramework.csproj b/examples/wcf/server-netframework/Examples.Wcf.Server.NetFramework.csproj index 7e79c160c0..20050361d9 100644 --- a/examples/wcf/server-netframework/Examples.Wcf.Server.NetFramework.csproj +++ b/examples/wcf/server-netframework/Examples.Wcf.Server.NetFramework.csproj @@ -8,8 +8,6 @@ - - diff --git a/examples/wcf/server-netframework/Program.cs b/examples/wcf/server-netframework/Program.cs index e39786c127..dcaa43185c 100644 --- a/examples/wcf/server-netframework/Program.cs +++ b/examples/wcf/server-netframework/Program.cs @@ -29,7 +29,6 @@ public static void Main() using var openTelemetry = Sdk.CreateTracerProviderBuilder() .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("Wcf-Server")) .AddWcfInstrumentation() - .AddAspNetInstrumentation() // <- Added to test suppression of http spans. .AddZipkinExporter() .Build(); diff --git a/opentelemetry-dotnet-contrib.sln b/opentelemetry-dotnet-contrib.sln index 6f72fed8a5..e9927565be 100644 --- a/opentelemetry-dotnet-contrib.sln +++ b/opentelemetry-dotnet-contrib.sln @@ -93,6 +93,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.Exten EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.Extensions.AWSXRay.Tests", "test\OpenTelemetry.Contrib.Extensions.AWSXRay.Tests\OpenTelemetry.Contrib.Extensions.AWSXRay.Tests.csproj", "{9CE513AC-CFC5-4DD1-9F16-8719EDCE9BF9}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.Instrumentation.Wcf.Tests", "test\OpenTelemetry.Contrib.Instrumentation.Wcf.Tests\OpenTelemetry.Contrib.Instrumentation.Wcf.Tests.csproj", "{76BAB24F-85DB-4FCE-89D0-EFB4185004C9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -159,6 +161,10 @@ Global {9CE513AC-CFC5-4DD1-9F16-8719EDCE9BF9}.Debug|Any CPU.Build.0 = Debug|Any CPU {9CE513AC-CFC5-4DD1-9F16-8719EDCE9BF9}.Release|Any CPU.ActiveCfg = Release|Any CPU {9CE513AC-CFC5-4DD1-9F16-8719EDCE9BF9}.Release|Any CPU.Build.0 = Release|Any CPU + {76BAB24F-85DB-4FCE-89D0-EFB4185004C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {76BAB24F-85DB-4FCE-89D0-EFB4185004C9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {76BAB24F-85DB-4FCE-89D0-EFB4185004C9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {76BAB24F-85DB-4FCE-89D0-EFB4185004C9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -183,6 +189,7 @@ Global {E205AA70-36BD-461D-8B87-909ED1BCA721} = {73474960-8F91-4EE5-8E3E-F7E7ADA99238} {D8C9AD2A-5C6A-46F5-A216-3D67E6C0FA94} = {22DF5DC0-1290-4E83-A9D8-6BB7DE3B3E63} {9CE513AC-CFC5-4DD1-9F16-8719EDCE9BF9} = {2097345F-4DD3-477D-BC54-A922F9B2B402} + {76BAB24F-85DB-4FCE-89D0-EFB4185004C9} = {2097345F-4DD3-477D-BC54-A922F9B2B402} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B0816796-CDB3-47D7-8C3C-946434DE3B66} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/AssemblyInfo.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/AssemblyInfo.cs new file mode 100644 index 0000000000..6160622a5f --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/AssemblyInfo.cs @@ -0,0 +1,22 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +using System.Runtime.CompilerServices; + +#if SIGNED +[assembly: InternalsVisibleTo("OpenTelemetry.Contrib.Instrumentation.Wcf.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")] +#else +[assembly: InternalsVisibleTo("OpenTelemetry.Contrib.Instrumentation.Wcf.Tests")] +#endif diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs index 70120a5300..9576898499 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs @@ -56,11 +56,6 @@ public object AfterReceiveRequest(ref Message request, IClientChannel channel, I if (activity != null) { - if (WcfInstrumentationActivitySource.Options.SuppressDownstreamInstrumentation) - { - SuppressInstrumentationScope.Enter(); - } - string action = request.Headers.Action; activity.DisplayName = action; diff --git a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests.csproj b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests.csproj new file mode 100644 index 0000000000..a4b35433f5 --- /dev/null +++ b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests.csproj @@ -0,0 +1,31 @@ + + + + Unit test project for OpenTelemetry WCF instrumentation + netcoreapp2.1;netcoreapp3.1;net5.0 + $(TargetFrameworks);net452 + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + + + + + + + + diff --git a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryDispatchMessageInspectorTests.netfx.cs b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryDispatchMessageInspectorTests.netfx.cs new file mode 100644 index 0000000000..f844cc9ffe --- /dev/null +++ b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryDispatchMessageInspectorTests.netfx.cs @@ -0,0 +1,152 @@ +// +// 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. +// +#if NETFRAMEWORK +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.ServiceModel; +using System.ServiceModel.Channels; +using System.Threading.Tasks; +using OpenTelemetry.Trace; +using Xunit; + +namespace OpenTelemetry.Contrib.Instrumentation.Wcf.Tests +{ + public class TelemetryDispatchMessageInspectorTests : IDisposable + { + private readonly Uri serviceBaseUri; + private readonly ServiceHost serviceHost; + + public TelemetryDispatchMessageInspectorTests() + { + Random random = new Random(); + var retryCount = 5; + while (retryCount > 0) + { + try + { + this.serviceBaseUri = new Uri($"http://localhost:{random.Next(2000, 5000)}/"); + this.serviceHost = new ServiceHost(new Service(), this.serviceBaseUri); + var endpoint = this.serviceHost.AddServiceEndpoint( + typeof(IServiceContract), + new BasicHttpBinding(BasicHttpSecurityMode.None), + "/Service"); + endpoint.Behaviors.Add(new TelemetryEndpointBehavior()); + this.serviceHost.Open(); + break; + } + catch + { + this.serviceHost.Close(); + this.serviceHost = null; + retryCount--; + } + } + + if (this.serviceHost == null) + { + throw new InvalidOperationException("ServiceHost could not be started."); + } + } + + public void Dispose() + { + this.serviceHost.Close(); + } + + [Theory] + [InlineData(true, false)] + [InlineData(true, true)] + [InlineData(false)] + public async Task IncomingRequestInstrumentationTest(bool instrument, bool filter = false) + { + List stoppedActivities = new List(); + + using ActivityListener activityListener = new ActivityListener + { + ShouldListenTo = activitySource => true, + ActivityStopped = activity => stoppedActivities.Add(activity), + }; + + ActivitySource.AddActivityListener(activityListener); + + TracerProvider tracerProvider = null; + if (instrument) + { + tracerProvider = Sdk.CreateTracerProviderBuilder() + .AddWcfInstrumentation(options => + { + options.IncomingRequestFilter = (Message m) => + { + return !filter; + }; + }) + .Build(); + } + + ServiceClient client = new ServiceClient( + new BasicHttpBinding(BasicHttpSecurityMode.None), + new EndpointAddress(new Uri(this.serviceBaseUri, "/Service"))); + try + { + var response = await client.ExecuteAsync( + new ServiceRequest + { + Payload = "Hello Open Telemetry!", + }); + } + finally + { + if (client.State == CommunicationState.Faulted) + { + client.Abort(); + } + else + { + client.Close(); + } + + tracerProvider?.Dispose(); + + WcfInstrumentationActivitySource.Options = null; + } + + if (instrument && !filter) + { + Assert.NotEmpty(stoppedActivities); + Assert.Single(stoppedActivities); + + Activity activity = stoppedActivities[0]; + Assert.Equal(WcfInstrumentationActivitySource.IncomingRequestActivityName, activity.OperationName); + Assert.Equal("wcf", activity.TagObjects.FirstOrDefault(t => t.Key == "rpc.system").Value); + Assert.Equal("http://opentelemetry.io/Service", activity.TagObjects.FirstOrDefault(t => t.Key == "rpc.service").Value); + Assert.Equal("Execute", activity.TagObjects.FirstOrDefault(t => t.Key == "rpc.method").Value); + Assert.Equal(this.serviceBaseUri.Host, activity.TagObjects.FirstOrDefault(t => t.Key == "net.host.name").Value); + Assert.Equal(this.serviceBaseUri.Port, activity.TagObjects.FirstOrDefault(t => t.Key == "net.host.port").Value); + Assert.Equal("Soap11 (http://schemas.xmlsoap.org/soap/envelope/) AddressingNone (http://schemas.microsoft.com/ws/2005/05/addressing/none)", activity.TagObjects.FirstOrDefault(t => t.Key == "soap.version").Value); + Assert.Equal("http", activity.TagObjects.FirstOrDefault(t => t.Key == "wcf.channel.scheme").Value); + Assert.Equal("/Service", activity.TagObjects.FirstOrDefault(t => t.Key == "wcf.channel.path").Value); + Assert.Equal("http://opentelemetry.io/Service/ExecuteResponse", activity.TagObjects.FirstOrDefault(t => t.Key == "soap.reply_action").Value); + } + else + { + Assert.Empty(stoppedActivities); + } + } + } +} +#endif diff --git a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/WCF/IServiceContract.cs b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/WCF/IServiceContract.cs new file mode 100644 index 0000000000..1901582b40 --- /dev/null +++ b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/WCF/IServiceContract.cs @@ -0,0 +1,28 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.ServiceModel; +using System.Threading.Tasks; + +namespace OpenTelemetry.Contrib.Instrumentation.Wcf.Tests +{ + [ServiceContract(Namespace = "http://opentelemetry.io/", Name = "Service", SessionMode = SessionMode.Allowed)] + public interface IServiceContract + { + [OperationContract] + Task ExecuteAsync(ServiceRequest request); + } +} diff --git a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/WCF/Service.netfx.cs b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/WCF/Service.netfx.cs new file mode 100644 index 0000000000..05ec64df59 --- /dev/null +++ b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/WCF/Service.netfx.cs @@ -0,0 +1,40 @@ +// +// 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. +// +#if NETFRAMEWORK +using System.ServiceModel; +using System.Threading.Tasks; + +namespace OpenTelemetry.Contrib.Instrumentation.Wcf.Tests +{ + [ServiceBehavior( + Namespace = "http://opentelemetry.io/", + ConcurrencyMode = ConcurrencyMode.Multiple, + InstanceContextMode = InstanceContextMode.Single, + UseSynchronizationContext = false, + Name = "Service")] + public class Service : IServiceContract + { + public Task ExecuteAsync(ServiceRequest request) + { + return Task.FromResult( + new ServiceResponse + { + Payload = $"RSP: {request.Payload}", + }); + } + } +} +#endif diff --git a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/WCF/ServiceClient.cs b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/WCF/ServiceClient.cs new file mode 100644 index 0000000000..adf92082fc --- /dev/null +++ b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/WCF/ServiceClient.cs @@ -0,0 +1,33 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.ServiceModel; +using System.ServiceModel.Channels; +using System.Threading.Tasks; + +namespace OpenTelemetry.Contrib.Instrumentation.Wcf.Tests +{ + public class ServiceClient : ClientBase, IServiceContract + { + public ServiceClient(Binding binding, EndpointAddress remoteAddress) + : base(binding, remoteAddress) + { + } + + public Task ExecuteAsync(ServiceRequest request) + => this.Channel.ExecuteAsync(request); + } +} diff --git a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/WCF/ServiceRequest.cs b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/WCF/ServiceRequest.cs new file mode 100644 index 0000000000..80259ee9b1 --- /dev/null +++ b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/WCF/ServiceRequest.cs @@ -0,0 +1,27 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.Runtime.Serialization; + +namespace OpenTelemetry.Contrib.Instrumentation.Wcf.Tests +{ + [DataContract] + public class ServiceRequest + { + [DataMember] + public string Payload { get; set; } + } +} diff --git a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/WCF/ServiceResponse.cs b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/WCF/ServiceResponse.cs new file mode 100644 index 0000000000..fafe9bfba6 --- /dev/null +++ b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/WCF/ServiceResponse.cs @@ -0,0 +1,27 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.Runtime.Serialization; + +namespace OpenTelemetry.Contrib.Instrumentation.Wcf.Tests +{ + [DataContract] + public class ServiceResponse + { + [DataMember] + public string Payload { get; set; } + } +} From 47974fca7cc80ef7044bb6bfc7f985f4cc8cb769 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Thu, 11 Feb 2021 13:46:06 -0800 Subject: [PATCH 09/34] Added client tests. --- ...y.Contrib.Instrumentation.Wcf.Tests.csproj | 4 + .../TelemetryClientMessageInspectorTests.cs | 226 ++++++++++++++++++ ...etryDispatchMessageInspectorTests.netfx.cs | 3 +- 3 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryClientMessageInspectorTests.cs diff --git a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests.csproj b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests.csproj index a4b35433f5..191303eb54 100644 --- a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests.csproj +++ b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests.csproj @@ -21,6 +21,10 @@ + + + + diff --git a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryClientMessageInspectorTests.cs b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryClientMessageInspectorTests.cs new file mode 100644 index 0000000000..4cb7f12955 --- /dev/null +++ b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryClientMessageInspectorTests.cs @@ -0,0 +1,226 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net; +using System.ServiceModel; +using System.ServiceModel.Channels; +using System.Threading; +using System.Threading.Tasks; +using OpenTelemetry.Trace; +using Xunit; + +namespace OpenTelemetry.Contrib.Instrumentation.Wcf.Tests +{ + [Collection("WCF")] + public class TelemetryClientMessageInspectorTests : IDisposable + { + private readonly Uri serviceBaseUri; + private readonly HttpListener listener; + private readonly EventWaitHandle initializationHandle; + + public TelemetryClientMessageInspectorTests() + { + Random random = new Random(); + var retryCount = 5; + while (retryCount > 0) + { + try + { + this.serviceBaseUri = new Uri($"http://localhost:{random.Next(2000, 5000)}/"); + + this.listener = new HttpListener(); + this.listener.Prefixes.Add(this.serviceBaseUri.OriginalString); + this.listener.Start(); + break; + } + catch + { + this.listener.Stop(); + this.listener = null; + retryCount--; + } + } + + if (this.listener == null) + { + throw new InvalidOperationException("HttpListener could not be started."); + } + + this.initializationHandle = new EventWaitHandle(false, EventResetMode.ManualReset); + try + { + Listener(); + + this.initializationHandle.WaitOne(); + } + finally + { + this.initializationHandle.Dispose(); + this.initializationHandle = null; + } + + async void Listener() + { + while (true) + { + try + { + var ctxTask = this.listener.GetContextAsync(); + + this.initializationHandle?.Set(); + + var ctx = await ctxTask.ConfigureAwait(false); + + ctx.Response.StatusCode = 200; + ctx.Response.ContentType = "text/xml; charset=utf-8"; + + using (StreamWriter writer = new StreamWriter(ctx.Response.OutputStream)) + { + writer.Write(@"RSP: Hello Open Telemetry!"); + } + + ctx.Response.Close(); + } + catch (Exception ex) + { + if (ex is ObjectDisposedException + || (ex is HttpListenerException httpEx && httpEx.ErrorCode == 995)) + { + // Listener was closed before we got into GetContextAsync or + // Listener was closed while we were in GetContextAsync. + break; + } + + throw; + } + } + } + } + + public void Dispose() + { + this.listener?.Stop(); + } + + [Theory] + [InlineData(true, false)] + [InlineData(true, true)] + [InlineData(true, false, false)] + [InlineData(false)] + public async Task OutgoingRequestInstrumentationTest(bool instrument, bool filter = false, bool suppressDownstreamInstrumentation = true) + { + List stoppedActivities = new List(); + + using ActivityListener activityListener = new ActivityListener + { + ShouldListenTo = activitySource => true, + ActivityStopped = activity => stoppedActivities.Add(activity), + }; + + ActivitySource.AddActivityListener(activityListener); + + TracerProvider tracerProvider = null; + if (instrument) + { + tracerProvider = Sdk.CreateTracerProviderBuilder() + .AddWcfInstrumentation(options => + { + options.OutgoingRequestFilter = (Message m) => + { + return !filter; + }; + options.SuppressDownstreamInstrumentation = suppressDownstreamInstrumentation; + }) + .AddHttpClientInstrumentation() // <- Added to test SuppressDownstreamInstrumentation. + .Build(); + } + + ServiceClient client = new ServiceClient( + new BasicHttpBinding(BasicHttpSecurityMode.None), + new EndpointAddress(new Uri(this.serviceBaseUri, "/Service"))); + try + { + client.Endpoint.EndpointBehaviors.Add(new TelemetryEndpointBehavior()); + + var response = await client.ExecuteAsync( + new ServiceRequest + { + Payload = "Hello Open Telemetry!", + }); + } + finally + { + if (client.State == CommunicationState.Faulted) + { + client.Abort(); + } + else + { + client.Close(); + } + + tracerProvider?.Dispose(); + + WcfInstrumentationActivitySource.Options = null; + } + + if (instrument) + { + if (!suppressDownstreamInstrumentation) + { + Assert.NotEmpty(stoppedActivities); + Assert.Equal(2, stoppedActivities.Count); + + Assert.Equal("OpenTelemetry.HttpWebRequest.HttpRequestOut", stoppedActivities[0].OperationName); + Assert.Equal(WcfInstrumentationActivitySource.OutgoingRequestActivityName, stoppedActivities[1].OperationName); + } + else + { + Assert.NotEmpty(stoppedActivities); + Assert.Single(stoppedActivities); + + Activity activity = stoppedActivities[0]; + if (!filter) + { + Assert.Equal(WcfInstrumentationActivitySource.OutgoingRequestActivityName, activity.OperationName); + Assert.Equal("wcf", activity.TagObjects.FirstOrDefault(t => t.Key == "rpc.system").Value); + Assert.Equal("http://opentelemetry.io/Service", activity.TagObjects.FirstOrDefault(t => t.Key == "rpc.service").Value); + Assert.Equal("Execute", activity.TagObjects.FirstOrDefault(t => t.Key == "rpc.method").Value); + Assert.Equal(this.serviceBaseUri.Host, activity.TagObjects.FirstOrDefault(t => t.Key == "net.peer.name").Value); + Assert.Equal(this.serviceBaseUri.Port, activity.TagObjects.FirstOrDefault(t => t.Key == "net.peer.port").Value); + Assert.Equal("Soap11 (http://schemas.xmlsoap.org/soap/envelope/) AddressingNone (http://schemas.microsoft.com/ws/2005/05/addressing/none)", activity.TagObjects.FirstOrDefault(t => t.Key == "soap.version").Value); + Assert.Equal("http", activity.TagObjects.FirstOrDefault(t => t.Key == "wcf.channel.scheme").Value); + Assert.Equal("/Service", activity.TagObjects.FirstOrDefault(t => t.Key == "wcf.channel.path").Value); + } + else + { + // Note: If WCF outgoing request is filtered out, the outgoing Http span is created. + Assert.Equal("OpenTelemetry.HttpWebRequest.HttpRequestOut", activity.OperationName); + } + } + } + else + { + Assert.Empty(stoppedActivities); + } + } + } +} diff --git a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryDispatchMessageInspectorTests.netfx.cs b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryDispatchMessageInspectorTests.netfx.cs index f844cc9ffe..82a7d87a7e 100644 --- a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryDispatchMessageInspectorTests.netfx.cs +++ b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryDispatchMessageInspectorTests.netfx.cs @@ -26,6 +26,7 @@ namespace OpenTelemetry.Contrib.Instrumentation.Wcf.Tests { + [Collection("WCF")] public class TelemetryDispatchMessageInspectorTests : IDisposable { private readonly Uri serviceBaseUri; @@ -65,7 +66,7 @@ public TelemetryDispatchMessageInspectorTests() public void Dispose() { - this.serviceHost.Close(); + this.serviceHost?.Close(); } [Theory] From ec2d91adc773ee20439c7bd91819a6b5ab531c1e Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Thu, 11 Feb 2021 13:56:29 -0800 Subject: [PATCH 10/34] Fixed some weirdness with SuppressDownstreamInstrumentation. --- .../TelemetryClientMessageInspector.cs | 42 +++++++++++++++---- .../TelemetryClientMessageInspectorTests.cs | 26 +++++++----- 2 files changed, 49 insertions(+), 19 deletions(-) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs index 09cadfd50b..14cd9c8de0 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs @@ -38,26 +38,29 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel) if (WcfInstrumentationActivitySource.Options == null || WcfInstrumentationActivitySource.Options.OutgoingRequestFilter?.Invoke(request) == false) { WcfInstrumentationEventSource.Log.RequestIsFilteredOut(request.Headers.Action); - return null; + return new State + { + SuppressionScope = this.SuppressDownstreamInstrumentation(), + }; } } catch (Exception ex) { WcfInstrumentationEventSource.Log.RequestFilterException(ex); - return null; + return new State + { + SuppressionScope = this.SuppressDownstreamInstrumentation(), + }; } Activity activity = WcfInstrumentationActivitySource.ActivitySource.StartActivity( WcfInstrumentationActivitySource.OutgoingRequestActivityName, ActivityKind.Client); + IDisposable suppressionScope = this.SuppressDownstreamInstrumentation(); + if (activity != null) { - if (WcfInstrumentationActivitySource.Options.SuppressDownstreamInstrumentation) - { - SuppressInstrumentationScope.Enter(); - } - string action = request.Headers.Action; activity.DisplayName = action; @@ -82,13 +85,19 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel) } } - return activity; + return new State + { + SuppressionScope = suppressionScope, + Activity = activity, + }; } /// public void AfterReceiveReply(ref Message reply, object correlationState) { - Activity activity = (Activity)correlationState; + State state = (State)correlationState; + + Activity activity = state.Activity; if (activity != null) { if (activity.IsAllDataRequested) @@ -103,6 +112,21 @@ public void AfterReceiveReply(ref Message reply, object correlationState) activity.Stop(); } + + state.SuppressionScope?.Dispose(); + } + + private IDisposable SuppressDownstreamInstrumentation() + { + return WcfInstrumentationActivitySource.Options?.SuppressDownstreamInstrumentation ?? false + ? SuppressInstrumentationScope.Begin() + : null; + } + + private class State + { + public IDisposable SuppressionScope; + public Activity Activity; } } } diff --git a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryClientMessageInspectorTests.cs b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryClientMessageInspectorTests.cs index 4cb7f12955..74a2a32f6b 100644 --- a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryClientMessageInspectorTests.cs +++ b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryClientMessageInspectorTests.cs @@ -187,19 +187,26 @@ public async Task OutgoingRequestInstrumentationTest(bool instrument, bool filte if (!suppressDownstreamInstrumentation) { Assert.NotEmpty(stoppedActivities); - Assert.Equal(2, stoppedActivities.Count); - - Assert.Equal("OpenTelemetry.HttpWebRequest.HttpRequestOut", stoppedActivities[0].OperationName); - Assert.Equal(WcfInstrumentationActivitySource.OutgoingRequestActivityName, stoppedActivities[1].OperationName); + if (filter) + { + Assert.Single(stoppedActivities); + Assert.Equal("OpenTelemetry.HttpWebRequest.HttpRequestOut", stoppedActivities[0].OperationName); + } + else + { + Assert.Equal(2, stoppedActivities.Count); + Assert.Equal("OpenTelemetry.HttpWebRequest.HttpRequestOut", stoppedActivities[0].OperationName); + Assert.Equal(WcfInstrumentationActivitySource.OutgoingRequestActivityName, stoppedActivities[1].OperationName); + } } else { - Assert.NotEmpty(stoppedActivities); - Assert.Single(stoppedActivities); - - Activity activity = stoppedActivities[0]; if (!filter) { + Assert.NotEmpty(stoppedActivities); + Assert.Single(stoppedActivities); + + Activity activity = stoppedActivities[0]; Assert.Equal(WcfInstrumentationActivitySource.OutgoingRequestActivityName, activity.OperationName); Assert.Equal("wcf", activity.TagObjects.FirstOrDefault(t => t.Key == "rpc.system").Value); Assert.Equal("http://opentelemetry.io/Service", activity.TagObjects.FirstOrDefault(t => t.Key == "rpc.service").Value); @@ -212,8 +219,7 @@ public async Task OutgoingRequestInstrumentationTest(bool instrument, bool filte } else { - // Note: If WCF outgoing request is filtered out, the outgoing Http span is created. - Assert.Equal("OpenTelemetry.HttpWebRequest.HttpRequestOut", activity.OperationName); + Assert.Empty(stoppedActivities); } } } From ab34a6528e7693a35d4d907ef13f0ded120c15fb Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Thu, 11 Feb 2021 14:38:23 -0800 Subject: [PATCH 11/34] Bug fixes. --- .../TelemetryClientMessageInspector.cs | 4 +-- ...y.Contrib.Instrumentation.Wcf.Tests.csproj | 1 + .../TelemetryClientMessageInspectorTests.cs | 31 ++++++++++--------- ...etryDispatchMessageInspectorTests.netfx.cs | 3 +- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs index 14cd9c8de0..847e0deee5 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs @@ -97,6 +97,8 @@ public void AfterReceiveReply(ref Message reply, object correlationState) { State state = (State)correlationState; + state.SuppressionScope?.Dispose(); + Activity activity = state.Activity; if (activity != null) { @@ -112,8 +114,6 @@ public void AfterReceiveReply(ref Message reply, object correlationState) activity.Stop(); } - - state.SuppressionScope?.Dispose(); } private IDisposable SuppressDownstreamInstrumentation() diff --git a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests.csproj b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests.csproj index 191303eb54..32baa6ec5e 100644 --- a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests.csproj +++ b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests.csproj @@ -27,6 +27,7 @@ + diff --git a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryClientMessageInspectorTests.cs b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryClientMessageInspectorTests.cs index 74a2a32f6b..bab962fa41 100644 --- a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryClientMessageInspectorTests.cs +++ b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryClientMessageInspectorTests.cs @@ -127,20 +127,19 @@ public void Dispose() [InlineData(false)] public async Task OutgoingRequestInstrumentationTest(bool instrument, bool filter = false, bool suppressDownstreamInstrumentation = true) { +#if NETFRAMEWORK + const string OutgoingHttpOperationName = "OpenTelemetry.HttpWebRequest.HttpRequestOut"; +#else + const string OutgoingHttpOperationName = "System.Net.Http.HttpRequestOut"; +#endif List stoppedActivities = new List(); - using ActivityListener activityListener = new ActivityListener - { - ShouldListenTo = activitySource => true, - ActivityStopped = activity => stoppedActivities.Add(activity), - }; - - ActivitySource.AddActivityListener(activityListener); + var builder = Sdk.CreateTracerProviderBuilder() + .AddInMemoryExporter(stoppedActivities); - TracerProvider tracerProvider = null; if (instrument) { - tracerProvider = Sdk.CreateTracerProviderBuilder() + builder .AddWcfInstrumentation(options => { options.OutgoingRequestFilter = (Message m) => @@ -149,10 +148,11 @@ public async Task OutgoingRequestInstrumentationTest(bool instrument, bool filte }; options.SuppressDownstreamInstrumentation = suppressDownstreamInstrumentation; }) - .AddHttpClientInstrumentation() // <- Added to test SuppressDownstreamInstrumentation. - .Build(); + .AddHttpClientInstrumentation(); // <- Added to test SuppressDownstreamInstrumentation. } + TracerProvider tracerProvider = builder.Build(); + ServiceClient client = new ServiceClient( new BasicHttpBinding(BasicHttpSecurityMode.None), new EndpointAddress(new Uri(this.serviceBaseUri, "/Service"))); @@ -164,7 +164,7 @@ public async Task OutgoingRequestInstrumentationTest(bool instrument, bool filte new ServiceRequest { Payload = "Hello Open Telemetry!", - }); + }).ConfigureAwait(false); } finally { @@ -177,7 +177,8 @@ public async Task OutgoingRequestInstrumentationTest(bool instrument, bool filte client.Close(); } - tracerProvider?.Dispose(); + tracerProvider.Shutdown(); + tracerProvider.Dispose(); WcfInstrumentationActivitySource.Options = null; } @@ -190,12 +191,12 @@ public async Task OutgoingRequestInstrumentationTest(bool instrument, bool filte if (filter) { Assert.Single(stoppedActivities); - Assert.Equal("OpenTelemetry.HttpWebRequest.HttpRequestOut", stoppedActivities[0].OperationName); + Assert.Equal(OutgoingHttpOperationName, stoppedActivities[0].OperationName); } else { Assert.Equal(2, stoppedActivities.Count); - Assert.Equal("OpenTelemetry.HttpWebRequest.HttpRequestOut", stoppedActivities[0].OperationName); + Assert.Equal(OutgoingHttpOperationName, stoppedActivities[0].OperationName); Assert.Equal(WcfInstrumentationActivitySource.OutgoingRequestActivityName, stoppedActivities[1].OperationName); } } diff --git a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryDispatchMessageInspectorTests.netfx.cs b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryDispatchMessageInspectorTests.netfx.cs index 82a7d87a7e..ac4d34dc43 100644 --- a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryDispatchMessageInspectorTests.netfx.cs +++ b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryDispatchMessageInspectorTests.netfx.cs @@ -108,7 +108,7 @@ public async Task IncomingRequestInstrumentationTest(bool instrument, bool filte new ServiceRequest { Payload = "Hello Open Telemetry!", - }); + }).ConfigureAwait(false); } finally { @@ -121,6 +121,7 @@ public async Task IncomingRequestInstrumentationTest(bool instrument, bool filte client.Close(); } + tracerProvider?.Shutdown(); tracerProvider?.Dispose(); WcfInstrumentationActivitySource.Options = null; From d07c9c30641aa4745448f17b8b8b6f76ac15aa66 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Thu, 11 Feb 2021 14:46:12 -0800 Subject: [PATCH 12/34] Attempting to fix lint errors. --- .../README.md | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md index a3e77fb39e..16cd70bd63 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md @@ -23,6 +23,7 @@ using var openTelemetry = Sdk.CreateTracerProviderBuilder() Add the `IClientMessageInspector` instrumentation via a behavior extension on the clients you want to instrument: + ```xml @@ -47,17 +48,12 @@ the clients you want to instrument: - + ``` + Example project available in [examples/wcf/client-netframework](../../examples/wcf/client-netframework/Program.cs) @@ -81,15 +77,14 @@ Example project available in Add the `IDispatchMessageInspector` instrumentation via a behavior extension on the services you want to instrument: + ```xml - + @@ -108,11 +103,7 @@ the services you want to instrument: - + @@ -123,6 +114,7 @@ the services you want to instrument: ``` + Example project available in [examples/wcf/server-netframework](../../examples/wcf/server-netframework/Program.cs) From 5e01cac4885586539869689795c3b1e577219195 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sat, 13 Feb 2021 22:29:43 -0800 Subject: [PATCH 13/34] Code review and clean up. --- .../WcfInstrumentationActivitySource.cs | 0 .../WcfInstrumentationConstants.cs | 35 ++++++++++++ .../TelemetryClientMessageInspector.cs | 24 +++++---- .../TelemetryDispatchMessageInspector.cs | 22 ++++---- .../WcfInstrumentationOptions.cs | 5 ++ .../TelemetryClientMessageInspectorTests.cs | 27 ++++++---- ...etryDispatchMessageInspectorTests.netfx.cs | 53 +++++++++++++------ 7 files changed, 122 insertions(+), 44 deletions(-) rename src/OpenTelemetry.Contrib.Instrumentation.Wcf/{ => Implementation}/WcfInstrumentationActivitySource.cs (100%) create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationConstants.cs diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationActivitySource.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationActivitySource.cs similarity index 100% rename from src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationActivitySource.cs rename to src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationActivitySource.cs diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationConstants.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationConstants.cs new file mode 100644 index 0000000000..6294cfb9ac --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationConstants.cs @@ -0,0 +1,35 @@ +// +// 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. +// + +namespace OpenTelemetry.Contrib.Instrumentation.Wcf +{ + internal static class WcfInstrumentationConstants + { + public const string RpcSystemTag = "rpc.system"; + public const string RpcServiceTag = "rpc.service"; + public const string RpcMethodTag = "rpc.method"; + public const string NetHostNameTag = "net.host.name"; + public const string NetHostPortTag = "net.host.port"; + public const string NetPeerNameTag = "net.peer.name"; + public const string NetPeerPortTag = "net.peer.port"; + public const string SoapVersionTag = "soap.version"; + public const string SoapReplyActionTag = "soap.reply_action"; + public const string WcfChannelSchemeTag = "wcf.channel.scheme"; + public const string WcfChannelPathTag = "wcf.channel.path"; + + public const string WcfSystemValue = "wcf"; + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs index 847e0deee5..54ba5c13d4 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs @@ -73,15 +73,19 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel) { int lastIndex = action.LastIndexOf('/'); - activity.SetTag("rpc.system", "wcf"); - activity.SetTag("rpc.service", action.Substring(0, lastIndex)); - activity.SetTag("rpc.method", action.Substring(lastIndex + 1)); - activity.SetTag("net.peer.name", channel.RemoteAddress.Uri.Host); - activity.SetTag("net.peer.port", channel.RemoteAddress.Uri.Port); - - activity.SetTag("soap.version", request.Version.ToString()); - activity.SetTag("wcf.channel.scheme", channel.RemoteAddress.Uri.Scheme); - activity.SetTag("wcf.channel.path", channel.RemoteAddress.Uri.LocalPath); + activity.SetTag(WcfInstrumentationConstants.RpcSystemTag, WcfInstrumentationConstants.WcfSystemValue); + activity.SetTag(WcfInstrumentationConstants.RpcServiceTag, action.Substring(0, lastIndex)); + activity.SetTag(WcfInstrumentationConstants.RpcMethodTag, action.Substring(lastIndex + 1)); + activity.SetTag(WcfInstrumentationConstants.NetPeerNameTag, channel.RemoteAddress.Uri.Host); + activity.SetTag(WcfInstrumentationConstants.NetPeerPortTag, channel.RemoteAddress.Uri.Port); + + if (WcfInstrumentationActivitySource.Options.SetSoapVersion) + { + activity.SetTag(WcfInstrumentationConstants.SoapVersionTag, request.Version.ToString()); + } + + activity.SetTag(WcfInstrumentationConstants.WcfChannelSchemeTag, channel.RemoteAddress.Uri.Scheme); + activity.SetTag(WcfInstrumentationConstants.WcfChannelPathTag, channel.RemoteAddress.Uri.LocalPath); } } @@ -109,7 +113,7 @@ public void AfterReceiveReply(ref Message reply, object correlationState) activity.SetStatus(Status.Error); } - activity.SetTag("soap.reply_action", reply.Headers.Action); + activity.SetTag(WcfInstrumentationConstants.SoapReplyActionTag, reply.Headers.Action); } activity.Stop(); diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs index 9576898499..84204c25f5 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs @@ -63,15 +63,19 @@ public object AfterReceiveRequest(ref Message request, IClientChannel channel, I { int lastIndex = action.LastIndexOf('/'); - activity.SetTag("rpc.system", "wcf"); - activity.SetTag("rpc.service", action.Substring(0, lastIndex)); - activity.SetTag("rpc.method", action.Substring(lastIndex + 1)); - activity.SetTag("net.host.name", channel.LocalAddress.Uri.Host); - activity.SetTag("net.host.port", channel.LocalAddress.Uri.Port); + activity.SetTag(WcfInstrumentationConstants.RpcSystemTag, WcfInstrumentationConstants.WcfSystemValue); + activity.SetTag(WcfInstrumentationConstants.RpcServiceTag, action.Substring(0, lastIndex)); + activity.SetTag(WcfInstrumentationConstants.RpcMethodTag, action.Substring(lastIndex + 1)); + activity.SetTag(WcfInstrumentationConstants.NetHostNameTag, channel.LocalAddress.Uri.Host); + activity.SetTag(WcfInstrumentationConstants.NetHostPortTag, channel.LocalAddress.Uri.Port); - activity.SetTag("soap.version", request.Version.ToString()); - activity.SetTag("wcf.channel.scheme", channel.LocalAddress.Uri.Scheme); - activity.SetTag("wcf.channel.path", channel.LocalAddress.Uri.LocalPath); + if (WcfInstrumentationActivitySource.Options.SetSoapVersion) + { + activity.SetTag(WcfInstrumentationConstants.SoapVersionTag, request.Version.ToString()); + } + + activity.SetTag(WcfInstrumentationConstants.WcfChannelSchemeTag, channel.LocalAddress.Uri.Scheme); + activity.SetTag(WcfInstrumentationConstants.WcfChannelPathTag, channel.LocalAddress.Uri.LocalPath); } } @@ -96,7 +100,7 @@ public void BeforeSendReply(ref Message reply, object correlationState) activity.SetStatus(Status.Error); } - activity.SetTag("soap.reply_action", reply.Headers.Action); + activity.SetTag(WcfInstrumentationConstants.SoapReplyActionTag, reply.Headers.Action); } activity.Stop(); diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationOptions.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationOptions.cs index aba0be8169..ac33158304 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationOptions.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationOptions.cs @@ -54,5 +54,10 @@ public class WcfInstrumentationOptions /// Gets or sets a value indicating whether down stream instrumentation (HttpClient) is suppressed (disabled). /// public bool SuppressDownstreamInstrumentation { get; set; } = true; + + /// + /// Gets or sets a value indicating whether or not the SOAP version should be added as the tag. Default value: False. + /// + public bool SetSoapVersion { get; set; } } } diff --git a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryClientMessageInspectorTests.cs b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryClientMessageInspectorTests.cs index bab962fa41..4581ac8819 100644 --- a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryClientMessageInspectorTests.cs +++ b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryClientMessageInspectorTests.cs @@ -125,7 +125,12 @@ public void Dispose() [InlineData(true, true)] [InlineData(true, false, false)] [InlineData(false)] - public async Task OutgoingRequestInstrumentationTest(bool instrument, bool filter = false, bool suppressDownstreamInstrumentation = true) + [InlineData(true, false, true, true)] + public async Task OutgoingRequestInstrumentationTest( + bool instrument, + bool filter = false, + bool suppressDownstreamInstrumentation = true, + bool includeVersion = false) { #if NETFRAMEWORK const string OutgoingHttpOperationName = "OpenTelemetry.HttpWebRequest.HttpRequestOut"; @@ -147,6 +152,7 @@ public async Task OutgoingRequestInstrumentationTest(bool instrument, bool filte return !filter; }; options.SuppressDownstreamInstrumentation = suppressDownstreamInstrumentation; + options.SetSoapVersion = includeVersion; }) .AddHttpClientInstrumentation(); // <- Added to test SuppressDownstreamInstrumentation. } @@ -209,14 +215,17 @@ public async Task OutgoingRequestInstrumentationTest(bool instrument, bool filte Activity activity = stoppedActivities[0]; Assert.Equal(WcfInstrumentationActivitySource.OutgoingRequestActivityName, activity.OperationName); - Assert.Equal("wcf", activity.TagObjects.FirstOrDefault(t => t.Key == "rpc.system").Value); - Assert.Equal("http://opentelemetry.io/Service", activity.TagObjects.FirstOrDefault(t => t.Key == "rpc.service").Value); - Assert.Equal("Execute", activity.TagObjects.FirstOrDefault(t => t.Key == "rpc.method").Value); - Assert.Equal(this.serviceBaseUri.Host, activity.TagObjects.FirstOrDefault(t => t.Key == "net.peer.name").Value); - Assert.Equal(this.serviceBaseUri.Port, activity.TagObjects.FirstOrDefault(t => t.Key == "net.peer.port").Value); - Assert.Equal("Soap11 (http://schemas.xmlsoap.org/soap/envelope/) AddressingNone (http://schemas.microsoft.com/ws/2005/05/addressing/none)", activity.TagObjects.FirstOrDefault(t => t.Key == "soap.version").Value); - Assert.Equal("http", activity.TagObjects.FirstOrDefault(t => t.Key == "wcf.channel.scheme").Value); - Assert.Equal("/Service", activity.TagObjects.FirstOrDefault(t => t.Key == "wcf.channel.path").Value); + Assert.Equal(WcfInstrumentationConstants.WcfSystemValue, activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.RpcSystemTag).Value); + Assert.Equal("http://opentelemetry.io/Service", activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.RpcServiceTag).Value); + Assert.Equal("Execute", activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.RpcMethodTag).Value); + Assert.Equal(this.serviceBaseUri.Host, activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.NetPeerNameTag).Value); + Assert.Equal(this.serviceBaseUri.Port, activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.NetPeerPortTag).Value); + Assert.Equal("http", activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.WcfChannelSchemeTag).Value); + Assert.Equal("/Service", activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.WcfChannelPathTag).Value); + if (includeVersion) + { + Assert.Equal("Soap11 (http://schemas.xmlsoap.org/soap/envelope/) AddressingNone (http://schemas.microsoft.com/ws/2005/05/addressing/none)", activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.SoapVersionTag).Value); + } } else { diff --git a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryDispatchMessageInspectorTests.netfx.cs b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryDispatchMessageInspectorTests.netfx.cs index ac4d34dc43..250d8f10e8 100644 --- a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryDispatchMessageInspectorTests.netfx.cs +++ b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryDispatchMessageInspectorTests.netfx.cs @@ -23,36 +23,49 @@ using System.Threading.Tasks; using OpenTelemetry.Trace; using Xunit; +using Xunit.Abstractions; namespace OpenTelemetry.Contrib.Instrumentation.Wcf.Tests { [Collection("WCF")] public class TelemetryDispatchMessageInspectorTests : IDisposable { + private readonly ITestOutputHelper output; private readonly Uri serviceBaseUri; private readonly ServiceHost serviceHost; - public TelemetryDispatchMessageInspectorTests() + public TelemetryDispatchMessageInspectorTests(ITestOutputHelper outputHelper) { + this.output = outputHelper; + Random random = new Random(); var retryCount = 5; while (retryCount > 0) { try { - this.serviceBaseUri = new Uri($"http://localhost:{random.Next(2000, 5000)}/"); + this.serviceBaseUri = new Uri($"net.tcp://localhost:{random.Next(2000, 5000)}/"); this.serviceHost = new ServiceHost(new Service(), this.serviceBaseUri); var endpoint = this.serviceHost.AddServiceEndpoint( typeof(IServiceContract), - new BasicHttpBinding(BasicHttpSecurityMode.None), + new NetTcpBinding(), "/Service"); endpoint.Behaviors.Add(new TelemetryEndpointBehavior()); this.serviceHost.Open(); break; } - catch + catch (Exception ex) { - this.serviceHost.Close(); + this.output.WriteLine(ex.ToString()); + if (this.serviceHost.State == CommunicationState.Faulted) + { + this.serviceHost.Abort(); + } + else + { + this.serviceHost.Close(); + } + this.serviceHost = null; retryCount--; } @@ -73,7 +86,11 @@ public void Dispose() [InlineData(true, false)] [InlineData(true, true)] [InlineData(false)] - public async Task IncomingRequestInstrumentationTest(bool instrument, bool filter = false) + [InlineData(true, false, true)] + public async Task IncomingRequestInstrumentationTest( + bool instrument, + bool filter = false, + bool includeVersion = false) { List stoppedActivities = new List(); @@ -95,12 +112,13 @@ public async Task IncomingRequestInstrumentationTest(bool instrument, bool filte { return !filter; }; + options.SetSoapVersion = includeVersion; }) .Build(); } ServiceClient client = new ServiceClient( - new BasicHttpBinding(BasicHttpSecurityMode.None), + new NetTcpBinding(), new EndpointAddress(new Uri(this.serviceBaseUri, "/Service"))); try { @@ -134,15 +152,18 @@ public async Task IncomingRequestInstrumentationTest(bool instrument, bool filte Activity activity = stoppedActivities[0]; Assert.Equal(WcfInstrumentationActivitySource.IncomingRequestActivityName, activity.OperationName); - Assert.Equal("wcf", activity.TagObjects.FirstOrDefault(t => t.Key == "rpc.system").Value); - Assert.Equal("http://opentelemetry.io/Service", activity.TagObjects.FirstOrDefault(t => t.Key == "rpc.service").Value); - Assert.Equal("Execute", activity.TagObjects.FirstOrDefault(t => t.Key == "rpc.method").Value); - Assert.Equal(this.serviceBaseUri.Host, activity.TagObjects.FirstOrDefault(t => t.Key == "net.host.name").Value); - Assert.Equal(this.serviceBaseUri.Port, activity.TagObjects.FirstOrDefault(t => t.Key == "net.host.port").Value); - Assert.Equal("Soap11 (http://schemas.xmlsoap.org/soap/envelope/) AddressingNone (http://schemas.microsoft.com/ws/2005/05/addressing/none)", activity.TagObjects.FirstOrDefault(t => t.Key == "soap.version").Value); - Assert.Equal("http", activity.TagObjects.FirstOrDefault(t => t.Key == "wcf.channel.scheme").Value); - Assert.Equal("/Service", activity.TagObjects.FirstOrDefault(t => t.Key == "wcf.channel.path").Value); - Assert.Equal("http://opentelemetry.io/Service/ExecuteResponse", activity.TagObjects.FirstOrDefault(t => t.Key == "soap.reply_action").Value); + Assert.Equal(WcfInstrumentationConstants.WcfSystemValue, activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.RpcSystemTag).Value); + Assert.Equal("http://opentelemetry.io/Service", activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.RpcServiceTag).Value); + Assert.Equal("Execute", activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.RpcMethodTag).Value); + Assert.Equal(this.serviceBaseUri.Host, activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.NetHostNameTag).Value); + Assert.Equal(this.serviceBaseUri.Port, activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.NetHostPortTag).Value); + Assert.Equal("net.tcp", activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.WcfChannelSchemeTag).Value); + Assert.Equal("/Service", activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.WcfChannelPathTag).Value); + Assert.Equal("http://opentelemetry.io/Service/ExecuteResponse", activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.SoapReplyActionTag).Value); + if (includeVersion) + { + Assert.Equal("Soap12 (http://www.w3.org/2003/05/soap-envelope) Addressing10 (http://www.w3.org/2005/08/addressing)", activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.SoapVersionTag).Value); + } } else { From 405c01073c2203ec5a1648bdea97ef9e2ae7d7b6 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sun, 14 Feb 2021 19:31:06 -0800 Subject: [PATCH 14/34] Added "wcf-" MinVerTagPrefix in csproj. --- .../OpenTelemetry.Contrib.Instrumentation.Wcf.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/OpenTelemetry.Contrib.Instrumentation.Wcf.csproj b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/OpenTelemetry.Contrib.Instrumentation.Wcf.csproj index 7581c6c914..e25118c452 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/OpenTelemetry.Contrib.Instrumentation.Wcf.csproj +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/OpenTelemetry.Contrib.Instrumentation.Wcf.csproj @@ -4,6 +4,7 @@ net452;netstandard2.0 OpenTelemetry instrumentation for WCF $(PackageTags);distributed-tracing;WCF + wcf- From 9c3fb4207d05a23368819e3bf6f646db03a903fd Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sat, 20 Feb 2021 21:37:28 -0800 Subject: [PATCH 15/34] Fun with copy-paste. --- .../package-Instrumentation.Wcf.yaml | 50 +++++++++++++++++++ opentelemetry-dotnet-contrib.sln | 1 + 2 files changed, 51 insertions(+) create mode 100644 .github/workflows/package-Instrumentation.Wcf.yaml diff --git a/.github/workflows/package-Instrumentation.Wcf.yaml b/.github/workflows/package-Instrumentation.Wcf.yaml new file mode 100644 index 0000000000..c124babb21 --- /dev/null +++ b/.github/workflows/package-Instrumentation.Wcf.yaml @@ -0,0 +1,50 @@ +name: Pack OpenTelemetry.Contrib.Instrumentation.Wcf + +on: + workflow_dispatch: + inputs: + logLevel: + description: 'Log level' + required: true + default: 'warning' + push: + tags: + - 'Instrumentation.Wcf-*' + +jobs: + build-test-pack: + runs-on: ${{ matrix.os }} + env: + PROJECT: OpenTelemetry.Contrib.Instrumentation.Wcf + + strategy: + matrix: + os: [windows-latest] + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 # fetching all + + - name: Install dependencies + run: dotnet restore + + - name: dotnet build ${{env.PROJECT}} + run: dotnet build src/${{env.PROJECT}} --configuration Release --no-restore -p:Deterministic=true + + - name: dotnet test ${{env.PROJECT}} + run: dotnet test test/${{env.PROJECT}} + + - name: dotnet pack ${{env.PROJECT}} + run: dotnet pack src/${{env.PROJECT}} --configuration Release --no-build + + - name: Publish Artifacts + uses: actions/upload-artifact@v2 + with: + name: ${{env.PROJECT}}-packages + path: '**/${{env.PROJECT}}/bin/**/*.*nupkg' + + - name: Publish MyGet + run: | + nuget setApiKey ${{ secrets.MYGET_TOKEN }} -Source https://www.myget.org/F/opentelemetry-contrib/api/v2/package + nuget push **/${{env.PROJECT}}/bin/**/*.nupkg -Source https://www.myget.org/F/opentelemetry-contrib/api/v2/package diff --git a/opentelemetry-dotnet-contrib.sln b/opentelemetry-dotnet-contrib.sln index e9927565be..60f03fc47a 100644 --- a/opentelemetry-dotnet-contrib.sln +++ b/opentelemetry-dotnet-contrib.sln @@ -30,6 +30,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{ .github\workflows\dotnet-core-win.yml = .github\workflows\dotnet-core-win.yml .github\workflows\dotnet-core.yml = .github\workflows\dotnet-core.yml .github\workflows\markdownlint.yml = .github\workflows\markdownlint.yml + .github\workflows\package-Instrumentation.Wcf.yaml = .github\workflows\package-Instrumentation.Wcf.yaml .github\workflows\sanitycheck.yml = .github\workflows\sanitycheck.yml EndProjectSection EndProject From 7eb6622ae33d10d6f983c2ccf6ed050d49095e3a Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sat, 20 Feb 2021 21:40:42 -0800 Subject: [PATCH 16/34] Removed OpenTelemetry.Instrumentation.Http from wcf client examples. --- examples/wcf/client-core/Examples.Wcf.Client.Core.csproj | 1 - examples/wcf/client-core/Program.cs | 1 - .../client-netframework/Examples.Wcf.Client.NetFramework.csproj | 1 - examples/wcf/client-netframework/Program.cs | 1 - 4 files changed, 4 deletions(-) diff --git a/examples/wcf/client-core/Examples.Wcf.Client.Core.csproj b/examples/wcf/client-core/Examples.Wcf.Client.Core.csproj index 06377393ef..fedd23c29b 100644 --- a/examples/wcf/client-core/Examples.Wcf.Client.Core.csproj +++ b/examples/wcf/client-core/Examples.Wcf.Client.Core.csproj @@ -12,7 +12,6 @@ - diff --git a/examples/wcf/client-core/Program.cs b/examples/wcf/client-core/Program.cs index 503f273d62..1c631ce968 100644 --- a/examples/wcf/client-core/Program.cs +++ b/examples/wcf/client-core/Program.cs @@ -39,7 +39,6 @@ public static async Task Main() using var openTelemetry = Sdk.CreateTracerProviderBuilder() .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("Wcf-Client-Core")) .AddWcfInstrumentation() - .AddHttpClientInstrumentation() // <- Added to test suppression of http spans. .AddZipkinExporter() .Build(); diff --git a/examples/wcf/client-netframework/Examples.Wcf.Client.NetFramework.csproj b/examples/wcf/client-netframework/Examples.Wcf.Client.NetFramework.csproj index 4066923d8e..20050361d9 100644 --- a/examples/wcf/client-netframework/Examples.Wcf.Client.NetFramework.csproj +++ b/examples/wcf/client-netframework/Examples.Wcf.Client.NetFramework.csproj @@ -8,7 +8,6 @@ - diff --git a/examples/wcf/client-netframework/Program.cs b/examples/wcf/client-netframework/Program.cs index 836e8a8f63..dff3b4de4b 100644 --- a/examples/wcf/client-netframework/Program.cs +++ b/examples/wcf/client-netframework/Program.cs @@ -30,7 +30,6 @@ public static async Task Main() using var openTelemetry = Sdk.CreateTracerProviderBuilder() .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("Wcf-Client")) .AddWcfInstrumentation() - .AddHttpClientInstrumentation() // <- Added to test suppression of http spans. .AddZipkinExporter() .Build(); From 63aeb50dd79a6fe0c5ea9abfc63c3e7d083bc830 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sat, 20 Feb 2021 21:55:24 -0800 Subject: [PATCH 17/34] Updated link paths. --- src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md index 16cd70bd63..c93ee261bd 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/README.md @@ -56,7 +56,7 @@ the clients you want to instrument: Example project available in -[examples/wcf/client-netframework](../../examples/wcf/client-netframework/Program.cs) +[examples/wcf/client-netframework](../../examples/wcf/client-netframework/) folder. ## WCF Client Configuration (.NET Core) @@ -70,7 +70,7 @@ client.Endpoint.EndpointBehaviors.Add(new TelemetryEndpointBehavior()); ``` Example project available in -[examples/wcf/client-core](../../examples/wcf/client-core/Program.cs) folder. +[examples/wcf/client-core](../../examples/wcf/client-core/) folder. ## WCF Server Configuration (.NET Framework) @@ -117,7 +117,7 @@ the services you want to instrument: Example project available in -[examples/wcf/server-netframework](../../examples/wcf/server-netframework/Program.cs) +[examples/wcf/server-netframework](../../examples/wcf/server-netframework/) folder. ## References From 6f6a671ddb4b00e6fd99bc9338f762dccc87947a Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sat, 20 Feb 2021 21:57:26 -0800 Subject: [PATCH 18/34] Code review. --- .../WcfInstrumentationOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationOptions.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationOptions.cs index ac33158304..a3d5a5915c 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationOptions.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationOptions.cs @@ -51,7 +51,7 @@ public class WcfInstrumentationOptions public Func OutgoingRequestFilter { get; set; } /// - /// Gets or sets a value indicating whether down stream instrumentation (HttpClient) is suppressed (disabled). + /// Gets or sets a value indicating whether down stream instrumentation (HttpClient) is suppressed (disabled). Default value: True. /// public bool SuppressDownstreamInstrumentation { get; set; } = true; From da1bfc154b1d4a836e5de55bae2c1cb7d5e97d67 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sat, 20 Feb 2021 22:03:38 -0800 Subject: [PATCH 19/34] Added wcf examples readme. --- examples/wcf/README.md | 25 +++++++++++++++++++++++++ opentelemetry-dotnet-contrib.sln | 3 +++ 2 files changed, 28 insertions(+) create mode 100644 examples/wcf/README.md diff --git a/examples/wcf/README.md b/examples/wcf/README.md new file mode 100644 index 0000000000..555eeeb05b --- /dev/null +++ b/examples/wcf/README.md @@ -0,0 +1,25 @@ +# WCF Instrumentation for OpenTelemetry .NET - Examples + +Project structure: + +* Examples.Wcf.Client.Core + + An example of how to call a WCF service with OpenTelemetry instrumentation on + .NET Core. + +* Examples.Wcf.Client.NetFramework + + An example of how to call a WCF service with OpenTelemetry instrumentation on + .NET Framework. + +* Examples.Wcf.Server.NetFramework + + An example of how to implement a WCF service with OpenTelemetry + instrumentation on .NET Framework. + + Note: There is no .NET Core server example because only the client libraries + for WCF are available on .NET Core / .NET 5. + +* Examples.Wcf.Shared + + Contains the shared contract and request & response objects. \ No newline at end of file diff --git a/opentelemetry-dotnet-contrib.sln b/opentelemetry-dotnet-contrib.sln index 60f03fc47a..3d70e15978 100644 --- a/opentelemetry-dotnet-contrib.sln +++ b/opentelemetry-dotnet-contrib.sln @@ -81,6 +81,9 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{B75EE478-97F7-4E9F-9A5A-DB3D0988EDEA}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "wcf", "wcf", "{73474960-8F91-4EE5-8E3E-F7E7ADA99238}" + ProjectSection(SolutionItems) = preProject + examples\wcf\README.md = examples\wcf\README.md + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.Wcf.Shared", "examples\wcf\shared\Examples.Wcf.Shared.csproj", "{21716C26-3B2A-4208-BDFB-8E58E2AF49EA}" EndProject From f34b0f6d34d97dd59f5a3febd76a8ddb83a24b17 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sat, 20 Feb 2021 22:07:31 -0800 Subject: [PATCH 20/34] Code review. --- examples/wcf/client-core/Program.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/wcf/client-core/Program.cs b/examples/wcf/client-core/Program.cs index 1c631ce968..684012b5ee 100644 --- a/examples/wcf/client-core/Program.cs +++ b/examples/wcf/client-core/Program.cs @@ -55,6 +55,9 @@ await CallService( private static async Task CallService(Binding binding, EndpointAddress remoteAddress) { + // Note: Best practice is to re-use your client/channel instances. + // This code is not meant to illustrate best practics, only the + // instrumentation. StatusServiceClient client = new StatusServiceClient(binding, remoteAddress); client.Endpoint.EndpointBehaviors.Add(new TelemetryEndpointBehavior()); try From 0c42c6f9290e295c23b122d826778717144098f2 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sat, 20 Feb 2021 22:10:21 -0800 Subject: [PATCH 21/34] Typo. --- examples/wcf/client-core/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/wcf/client-core/Program.cs b/examples/wcf/client-core/Program.cs index 684012b5ee..2ac45e8af3 100644 --- a/examples/wcf/client-core/Program.cs +++ b/examples/wcf/client-core/Program.cs @@ -56,7 +56,7 @@ await CallService( private static async Task CallService(Binding binding, EndpointAddress remoteAddress) { // Note: Best practice is to re-use your client/channel instances. - // This code is not meant to illustrate best practics, only the + // This code is not meant to illustrate best practices, only the // instrumentation. StatusServiceClient client = new StatusServiceClient(binding, remoteAddress); client.Endpoint.EndpointBehaviors.Add(new TelemetryEndpointBehavior()); From c53598eed3d647668e12634d2dcced9c398f8975 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sat, 20 Feb 2021 22:35:01 -0800 Subject: [PATCH 22/34] Markdown lint. --- examples/wcf/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/wcf/README.md b/examples/wcf/README.md index 555eeeb05b..d45ea29a35 100644 --- a/examples/wcf/README.md +++ b/examples/wcf/README.md @@ -22,4 +22,4 @@ Project structure: * Examples.Wcf.Shared - Contains the shared contract and request & response objects. \ No newline at end of file + Contains the shared contract and request & response objects. From 6814bd1603490a195501f6671e4ed30f4a6a31db Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sun, 21 Feb 2021 10:43:04 -0800 Subject: [PATCH 23/34] Code review. --- .../TelemetryClientMessageInspector.cs | 12 ++++++++---- .../TelemetryDispatchMessageInspector.cs | 12 ++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs index 54ba5c13d4..252f483df1 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs @@ -76,16 +76,20 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel) activity.SetTag(WcfInstrumentationConstants.RpcSystemTag, WcfInstrumentationConstants.WcfSystemValue); activity.SetTag(WcfInstrumentationConstants.RpcServiceTag, action.Substring(0, lastIndex)); activity.SetTag(WcfInstrumentationConstants.RpcMethodTag, action.Substring(lastIndex + 1)); - activity.SetTag(WcfInstrumentationConstants.NetPeerNameTag, channel.RemoteAddress.Uri.Host); - activity.SetTag(WcfInstrumentationConstants.NetPeerPortTag, channel.RemoteAddress.Uri.Port); if (WcfInstrumentationActivitySource.Options.SetSoapVersion) { activity.SetTag(WcfInstrumentationConstants.SoapVersionTag, request.Version.ToString()); } - activity.SetTag(WcfInstrumentationConstants.WcfChannelSchemeTag, channel.RemoteAddress.Uri.Scheme); - activity.SetTag(WcfInstrumentationConstants.WcfChannelPathTag, channel.RemoteAddress.Uri.LocalPath); + var remoteAddressUri = channel.RemoteAddress?.Uri; + if (remoteAddressUri != null) + { + activity.SetTag(WcfInstrumentationConstants.NetPeerNameTag, remoteAddressUri.Host); + activity.SetTag(WcfInstrumentationConstants.NetPeerPortTag, remoteAddressUri.Port); + activity.SetTag(WcfInstrumentationConstants.WcfChannelSchemeTag, remoteAddressUri.Scheme); + activity.SetTag(WcfInstrumentationConstants.WcfChannelPathTag, remoteAddressUri.LocalPath); + } } } diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs index 84204c25f5..96775263ba 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs @@ -66,16 +66,20 @@ public object AfterReceiveRequest(ref Message request, IClientChannel channel, I activity.SetTag(WcfInstrumentationConstants.RpcSystemTag, WcfInstrumentationConstants.WcfSystemValue); activity.SetTag(WcfInstrumentationConstants.RpcServiceTag, action.Substring(0, lastIndex)); activity.SetTag(WcfInstrumentationConstants.RpcMethodTag, action.Substring(lastIndex + 1)); - activity.SetTag(WcfInstrumentationConstants.NetHostNameTag, channel.LocalAddress.Uri.Host); - activity.SetTag(WcfInstrumentationConstants.NetHostPortTag, channel.LocalAddress.Uri.Port); if (WcfInstrumentationActivitySource.Options.SetSoapVersion) { activity.SetTag(WcfInstrumentationConstants.SoapVersionTag, request.Version.ToString()); } - activity.SetTag(WcfInstrumentationConstants.WcfChannelSchemeTag, channel.LocalAddress.Uri.Scheme); - activity.SetTag(WcfInstrumentationConstants.WcfChannelPathTag, channel.LocalAddress.Uri.LocalPath); + var localAddressUri = channel.LocalAddress?.Uri; + if (localAddressUri != null) + { + activity.SetTag(WcfInstrumentationConstants.NetHostNameTag, localAddressUri.Host); + activity.SetTag(WcfInstrumentationConstants.NetHostPortTag, localAddressUri.Port); + activity.SetTag(WcfInstrumentationConstants.WcfChannelSchemeTag, localAddressUri.Scheme); + activity.SetTag(WcfInstrumentationConstants.WcfChannelPathTag, localAddressUri.LocalPath); + } } } From abee3cce6db638200ab14070fe39d779e779857e Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sun, 21 Feb 2021 11:01:18 -0800 Subject: [PATCH 24/34] Code review. --- .../TelemetryClientMessageInspector.cs | 39 +++++++++++++++++-- .../TelemetryDispatchMessageInspector.cs | 39 +++++++++++++++++-- 2 files changed, 70 insertions(+), 8 deletions(-) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs index 252f483df1..b101f464a3 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs @@ -15,6 +15,7 @@ // using System; +using System.Collections.Concurrent; using System.Diagnostics; using System.ServiceModel; using System.ServiceModel.Channels; @@ -30,6 +31,8 @@ namespace OpenTelemetry.Contrib.Instrumentation.Wcf /// public class TelemetryClientMessageInspector : IClientMessageInspector { + private static readonly ConcurrentDictionary ActionMetadataCache = new ConcurrentDictionary(); + /// public object BeforeSendRequest(ref Message request, IClientChannel channel) { @@ -71,11 +74,33 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel) if (activity.IsAllDataRequested) { - int lastIndex = action.LastIndexOf('/'); - activity.SetTag(WcfInstrumentationConstants.RpcSystemTag, WcfInstrumentationConstants.WcfSystemValue); - activity.SetTag(WcfInstrumentationConstants.RpcServiceTag, action.Substring(0, lastIndex)); - activity.SetTag(WcfInstrumentationConstants.RpcMethodTag, action.Substring(lastIndex + 1)); + + if (!ActionMetadataCache.TryGetValue(action, out var actionMetadata)) + { + int lastIndex = action.LastIndexOf('/'); + if (lastIndex >= 0) + { + actionMetadata = new ActionMetadata + { + Service = action.Substring(0, lastIndex), + Method = action.Substring(lastIndex + 1), + }; + } + else + { + actionMetadata = new ActionMetadata + { + Service = null, + Method = action, + }; + } + + ActionMetadataCache.TryAdd(action, actionMetadata); + } + + activity.SetTag(WcfInstrumentationConstants.RpcServiceTag, actionMetadata.Service); + activity.SetTag(WcfInstrumentationConstants.RpcMethodTag, actionMetadata.Method); if (WcfInstrumentationActivitySource.Options.SetSoapVersion) { @@ -131,6 +156,12 @@ private IDisposable SuppressDownstreamInstrumentation() : null; } + private class ActionMetadata + { + public string Service; + public string Method; + } + private class State { public IDisposable SuppressionScope; diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs index 96775263ba..82884373a1 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs @@ -16,6 +16,7 @@ #if NETFRAMEWORK using System; +using System.Collections.Concurrent; using System.Diagnostics; using System.ServiceModel; using System.ServiceModel.Channels; @@ -30,6 +31,8 @@ namespace OpenTelemetry.Contrib.Instrumentation.Wcf /// public class TelemetryDispatchMessageInspector : IDispatchMessageInspector { + private static readonly ConcurrentDictionary ActionMetadataCache = new ConcurrentDictionary(); + /// public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) { @@ -61,11 +64,33 @@ public object AfterReceiveRequest(ref Message request, IClientChannel channel, I if (activity.IsAllDataRequested) { - int lastIndex = action.LastIndexOf('/'); - activity.SetTag(WcfInstrumentationConstants.RpcSystemTag, WcfInstrumentationConstants.WcfSystemValue); - activity.SetTag(WcfInstrumentationConstants.RpcServiceTag, action.Substring(0, lastIndex)); - activity.SetTag(WcfInstrumentationConstants.RpcMethodTag, action.Substring(lastIndex + 1)); + + if (!ActionMetadataCache.TryGetValue(action, out var actionMetadata)) + { + int lastIndex = action.LastIndexOf('/'); + if (lastIndex >= 0) + { + actionMetadata = new ActionMetadata + { + Service = action.Substring(0, lastIndex), + Method = action.Substring(lastIndex + 1), + }; + } + else + { + actionMetadata = new ActionMetadata + { + Service = null, + Method = action, + }; + } + + ActionMetadataCache.TryAdd(action, actionMetadata); + } + + activity.SetTag(WcfInstrumentationConstants.RpcServiceTag, actionMetadata.Service); + activity.SetTag(WcfInstrumentationConstants.RpcMethodTag, actionMetadata.Method); if (WcfInstrumentationActivitySource.Options.SetSoapVersion) { @@ -110,6 +135,12 @@ public void BeforeSendReply(ref Message reply, object correlationState) activity.Stop(); } } + + private class ActionMetadata + { + public string Service; + public string Method; + } } } #endif From fa2c7bd4cd911dbed584c60df973af8394f8c497 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sun, 21 Feb 2021 12:12:34 -0800 Subject: [PATCH 25/34] Code review. --- examples/wcf/client-core/Program.cs | 18 +++++++++++----- .../wcf/client-core/StatusServiceClient.cs | 16 ++++++++++++-- examples/wcf/client-netframework/Program.cs | 21 +++++++++++++------ .../StatusServiceClient.cs | 16 ++++++++++++-- .../wcf/server-netframework/StatusService.cs | 2 +- examples/wcf/shared/IStatusServiceContract.cs | 2 +- 6 files changed, 58 insertions(+), 17 deletions(-) diff --git a/examples/wcf/client-core/Program.cs b/examples/wcf/client-core/Program.cs index 2ac45e8af3..433ac6090c 100644 --- a/examples/wcf/client-core/Program.cs +++ b/examples/wcf/client-core/Program.cs @@ -62,7 +62,9 @@ private static async Task CallService(Binding binding, EndpointAddress remoteAdd client.Endpoint.EndpointBehaviors.Add(new TelemetryEndpointBehavior()); try { - var response = await client.Ping( + await client.OpenAsync().ConfigureAwait(false); + + var response = await client.PingAsync( new StatusRequest { Status = Guid.NewGuid().ToString("N"), @@ -72,13 +74,19 @@ private static async Task CallService(Binding binding, EndpointAddress remoteAdd } finally { - if (client.State == CommunicationState.Faulted) + try { - client.Abort(); + if (client.State == CommunicationState.Faulted) + { + client.Abort(); + } + else + { + await client.CloseAsync().ConfigureAwait(false); + } } - else + catch { - client.Close(); } } } diff --git a/examples/wcf/client-core/StatusServiceClient.cs b/examples/wcf/client-core/StatusServiceClient.cs index 7926cf2ab4..6616063312 100644 --- a/examples/wcf/client-core/StatusServiceClient.cs +++ b/examples/wcf/client-core/StatusServiceClient.cs @@ -32,7 +32,19 @@ public StatusServiceClient(Binding binding, EndpointAddress remoteAddress) { } - public Task Ping(StatusRequest request) - => this.Channel.Ping(request); + public Task PingAsync(StatusRequest request) + => this.Channel.PingAsync(request); + + public Task OpenAsync() + { + ICommunicationObject communicationObject = this; + return Task.Factory.FromAsync(communicationObject.BeginOpen, communicationObject.EndOpen, null); + } + + public Task CloseAsync() + { + ICommunicationObject communicationObject = this; + return Task.Factory.FromAsync(communicationObject.BeginClose, communicationObject.EndClose, null); + } } } diff --git a/examples/wcf/client-netframework/Program.cs b/examples/wcf/client-netframework/Program.cs index dff3b4de4b..73f66bc503 100644 --- a/examples/wcf/client-netframework/Program.cs +++ b/examples/wcf/client-netframework/Program.cs @@ -42,12 +42,15 @@ public static async Task Main() private static async Task CallService(string name) { + // Note: Best practice is to re-use your client/channel instances. + // This code is not meant to illustrate best practices, only the + // instrumentation. StatusServiceClient client = new StatusServiceClient(name); try { - client.Open(); + await client.OpenAsync().ConfigureAwait(false); - var response = await client.Ping( + var response = await client.PingAsync( new StatusRequest { Status = Guid.NewGuid().ToString("N"), @@ -57,13 +60,19 @@ private static async Task CallService(string name) } finally { - if (client.State == CommunicationState.Faulted) + try { - client.Abort(); + if (client.State == CommunicationState.Faulted) + { + client.Abort(); + } + else + { + await client.CloseAsync().ConfigureAwait(false); + } } - else + catch { - client.Close(); } } } diff --git a/examples/wcf/client-netframework/StatusServiceClient.cs b/examples/wcf/client-netframework/StatusServiceClient.cs index 03af25a42c..a0fb5eeaed 100644 --- a/examples/wcf/client-netframework/StatusServiceClient.cs +++ b/examples/wcf/client-netframework/StatusServiceClient.cs @@ -26,7 +26,19 @@ public StatusServiceClient(string name) { } - public Task Ping(StatusRequest request) - => this.Channel.Ping(request); + public Task PingAsync(StatusRequest request) + => this.Channel.PingAsync(request); + + public Task OpenAsync() + { + ICommunicationObject communicationObject = this; + return Task.Factory.FromAsync(communicationObject.BeginOpen, communicationObject.EndOpen, null); + } + + public Task CloseAsync() + { + ICommunicationObject communicationObject = this; + return Task.Factory.FromAsync(communicationObject.BeginClose, communicationObject.EndClose, null); + } } } diff --git a/examples/wcf/server-netframework/StatusService.cs b/examples/wcf/server-netframework/StatusService.cs index 1ba417e3f1..d2fab77ffe 100644 --- a/examples/wcf/server-netframework/StatusService.cs +++ b/examples/wcf/server-netframework/StatusService.cs @@ -28,7 +28,7 @@ namespace Examples.Wcf.Server Name = "StatusService")] public class StatusService : IStatusServiceContract { - public Task Ping(StatusRequest request) + public Task PingAsync(StatusRequest request) { return Task.FromResult( new StatusResponse diff --git a/examples/wcf/shared/IStatusServiceContract.cs b/examples/wcf/shared/IStatusServiceContract.cs index 5e829864f5..72e6a783cc 100644 --- a/examples/wcf/shared/IStatusServiceContract.cs +++ b/examples/wcf/shared/IStatusServiceContract.cs @@ -23,6 +23,6 @@ namespace Examples.Wcf public interface IStatusServiceContract { [OperationContract] - Task Ping(StatusRequest request); + Task PingAsync(StatusRequest request); } } From c97f97bcacdc6480eae91a851cea354c242fef70 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sun, 21 Feb 2021 22:42:01 -0800 Subject: [PATCH 26/34] Code review. --- .../WcfInstrumentationEventSource.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationEventSource.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationEventSource.cs index bd137863d6..4b38de172b 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationEventSource.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationEventSource.cs @@ -35,16 +35,16 @@ public void RequestFilterException(Exception ex) } } - [Event(1, Message = "Request is filtered out.", Level = EventLevel.Verbose)] + [Event(Constants.RequestIsFilteredOutEventId, Message = "Request is filtered out.", Level = EventLevel.Verbose)] public void RequestIsFilteredOut(string eventName) { - this.WriteEvent(1, eventName); + this.WriteEvent(Constants.RequestIsFilteredOutEventId, eventName); } - [Event(2, Message = "InstrumentationFilter threw exception. Request will not be collected. Exception {0}.", Level = EventLevel.Error)] + [Event(Constants.RequestFilterExceptionEventId, Message = "InstrumentationFilter threw exception. Request will not be collected. Exception {0}.", Level = EventLevel.Error)] public void RequestFilterException(string exception) { - this.WriteEvent(2, exception); + this.WriteEvent(Constants.RequestFilterExceptionEventId, exception); } /// @@ -65,5 +65,11 @@ private static string ToInvariantString(Exception exception) Thread.CurrentThread.CurrentUICulture = originalUICulture; } } + + private class Constants + { + public const int RequestIsFilteredOutEventId = 1; + public const int RequestFilterExceptionEventId = 2; + } } } From aca82846737418a58f609f3ebb6e78190a8e6e54 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sun, 21 Feb 2021 23:11:52 -0800 Subject: [PATCH 27/34] Add support for To & Via properties. --- .../Implementation/WcfInstrumentationConstants.cs | 1 + .../TelemetryClientMessageInspector.cs | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationConstants.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationConstants.cs index 6294cfb9ac..9539e17050 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationConstants.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationConstants.cs @@ -27,6 +27,7 @@ internal static class WcfInstrumentationConstants public const string NetPeerPortTag = "net.peer.port"; public const string SoapVersionTag = "soap.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"; diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs index b101f464a3..f3ef11aa20 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs @@ -107,7 +107,7 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel) activity.SetTag(WcfInstrumentationConstants.SoapVersionTag, request.Version.ToString()); } - var remoteAddressUri = channel.RemoteAddress?.Uri; + var remoteAddressUri = request.Headers.To ?? channel.RemoteAddress?.Uri; if (remoteAddressUri != null) { activity.SetTag(WcfInstrumentationConstants.NetPeerNameTag, remoteAddressUri.Host); @@ -115,6 +115,11 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel) activity.SetTag(WcfInstrumentationConstants.WcfChannelSchemeTag, remoteAddressUri.Scheme); activity.SetTag(WcfInstrumentationConstants.WcfChannelPathTag, remoteAddressUri.LocalPath); } + + if (request.Properties.Via != null) + { + activity.SetTag(WcfInstrumentationConstants.SoapViaTag, request.Properties.Via.ToString()); + } } } From 2460ad877c2e3d13e930439bf746cf6fb2d0019c Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sun, 21 Feb 2021 23:16:20 -0800 Subject: [PATCH 28/34] Code review. --- .../TelemetryEndpointBehavior.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryEndpointBehavior.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryEndpointBehavior.cs index ece41a33e9..94b4159d6e 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryEndpointBehavior.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryEndpointBehavior.cs @@ -58,11 +58,7 @@ public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterColle /// public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { -#if NETFRAMEWORK - clientRuntime.MessageInspectors.Add(this.telemetryClientMessageInspector); -#else clientRuntime.ClientMessageInspectors.Add(this.telemetryClientMessageInspector); -#endif } /// From c81888d38ef62d6193baf790f897027797c0acf6 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sun, 21 Feb 2021 23:25:22 -0800 Subject: [PATCH 29/34] Code review. --- .../Implementation/WcfInstrumentationConstants.cs | 2 +- .../TelemetryClientMessageInspector.cs | 4 ++-- .../TelemetryDispatchMessageInspector.cs | 4 ++-- .../WcfInstrumentationOptions.cs | 4 ++-- .../TelemetryClientMessageInspectorTests.cs | 4 ++-- .../TelemetryDispatchMessageInspectorTests.netfx.cs | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationConstants.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationConstants.cs index 9539e17050..093e49adb8 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationConstants.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationConstants.cs @@ -25,7 +25,7 @@ internal static class WcfInstrumentationConstants public const string NetHostPortTag = "net.host.port"; public const string NetPeerNameTag = "net.peer.name"; public const string NetPeerPortTag = "net.peer.port"; - public const string SoapVersionTag = "soap.version"; + 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"; diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs index f3ef11aa20..78c99c8d2e 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs @@ -102,9 +102,9 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel) activity.SetTag(WcfInstrumentationConstants.RpcServiceTag, actionMetadata.Service); activity.SetTag(WcfInstrumentationConstants.RpcMethodTag, actionMetadata.Method); - if (WcfInstrumentationActivitySource.Options.SetSoapVersion) + if (WcfInstrumentationActivitySource.Options.SetSoapMessageVersion) { - activity.SetTag(WcfInstrumentationConstants.SoapVersionTag, request.Version.ToString()); + activity.SetTag(WcfInstrumentationConstants.SoapMessageVersionTag, request.Version.ToString()); } var remoteAddressUri = request.Headers.To ?? channel.RemoteAddress?.Uri; diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs index 82884373a1..c9dc2dda5a 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs @@ -92,9 +92,9 @@ public object AfterReceiveRequest(ref Message request, IClientChannel channel, I activity.SetTag(WcfInstrumentationConstants.RpcServiceTag, actionMetadata.Service); activity.SetTag(WcfInstrumentationConstants.RpcMethodTag, actionMetadata.Method); - if (WcfInstrumentationActivitySource.Options.SetSoapVersion) + if (WcfInstrumentationActivitySource.Options.SetSoapMessageVersion) { - activity.SetTag(WcfInstrumentationConstants.SoapVersionTag, request.Version.ToString()); + activity.SetTag(WcfInstrumentationConstants.SoapMessageVersionTag, request.Version.ToString()); } var localAddressUri = channel.LocalAddress?.Uri; diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationOptions.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationOptions.cs index a3d5a5915c..782ca0fa8d 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationOptions.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/WcfInstrumentationOptions.cs @@ -56,8 +56,8 @@ public class WcfInstrumentationOptions public bool SuppressDownstreamInstrumentation { get; set; } = true; /// - /// Gets or sets a value indicating whether or not the SOAP version should be added as the tag. Default value: False. + /// Gets or sets a value indicating whether or not the SOAP message version should be added as the tag. Default value: False. /// - public bool SetSoapVersion { get; set; } + public bool SetSoapMessageVersion { get; set; } } } diff --git a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryClientMessageInspectorTests.cs b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryClientMessageInspectorTests.cs index 4581ac8819..bc4a446063 100644 --- a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryClientMessageInspectorTests.cs +++ b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryClientMessageInspectorTests.cs @@ -152,7 +152,7 @@ public async Task OutgoingRequestInstrumentationTest( return !filter; }; options.SuppressDownstreamInstrumentation = suppressDownstreamInstrumentation; - options.SetSoapVersion = includeVersion; + options.SetSoapMessageVersion = includeVersion; }) .AddHttpClientInstrumentation(); // <- Added to test SuppressDownstreamInstrumentation. } @@ -224,7 +224,7 @@ public async Task OutgoingRequestInstrumentationTest( Assert.Equal("/Service", activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.WcfChannelPathTag).Value); if (includeVersion) { - Assert.Equal("Soap11 (http://schemas.xmlsoap.org/soap/envelope/) AddressingNone (http://schemas.microsoft.com/ws/2005/05/addressing/none)", activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.SoapVersionTag).Value); + Assert.Equal("Soap11 (http://schemas.xmlsoap.org/soap/envelope/) AddressingNone (http://schemas.microsoft.com/ws/2005/05/addressing/none)", activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.SoapMessageVersionTag).Value); } } else diff --git a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryDispatchMessageInspectorTests.netfx.cs b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryDispatchMessageInspectorTests.netfx.cs index 250d8f10e8..2c214018bf 100644 --- a/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryDispatchMessageInspectorTests.netfx.cs +++ b/test/OpenTelemetry.Contrib.Instrumentation.Wcf.Tests/TelemetryDispatchMessageInspectorTests.netfx.cs @@ -112,7 +112,7 @@ public async Task IncomingRequestInstrumentationTest( { return !filter; }; - options.SetSoapVersion = includeVersion; + options.SetSoapMessageVersion = includeVersion; }) .Build(); } @@ -162,7 +162,7 @@ public async Task IncomingRequestInstrumentationTest( Assert.Equal("http://opentelemetry.io/Service/ExecuteResponse", activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.SoapReplyActionTag).Value); if (includeVersion) { - Assert.Equal("Soap12 (http://www.w3.org/2003/05/soap-envelope) Addressing10 (http://www.w3.org/2005/08/addressing)", activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.SoapVersionTag).Value); + Assert.Equal("Soap12 (http://www.w3.org/2003/05/soap-envelope) Addressing10 (http://www.w3.org/2005/08/addressing)", activity.TagObjects.FirstOrDefault(t => t.Key == WcfInstrumentationConstants.SoapMessageVersionTag).Value); } } else From 7034b5842e6e3cabc8ca17a7e54a61d16362875d Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 22 Feb 2021 10:37:46 -0800 Subject: [PATCH 30/34] Example readme update. --- examples/wcf/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/wcf/README.md b/examples/wcf/README.md index d45ea29a35..c97490f97c 100644 --- a/examples/wcf/README.md +++ b/examples/wcf/README.md @@ -15,7 +15,10 @@ Project structure: * Examples.Wcf.Server.NetFramework An example of how to implement a WCF service with OpenTelemetry - instrumentation on .NET Framework. + instrumentation on .NET Framework. The service will listen on http which + requires special permission on Windows. The easiest thing to do is run VS with + admin privileges. For details see: [Configuring HTTP and + HTTPS](https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/configuring-http-and-https). Note: There is no .NET Core server example because only the client libraries for WCF are available on .NET Core / .NET 5. From 1cafa4a49dad20d32fbdcdd516eecdaed92f5c4d Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Thu, 25 Feb 2021 10:42:56 -0800 Subject: [PATCH 31/34] Mirror fix from PR #73. --- .github/workflows/package-Instrumentation.Wcf.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/package-Instrumentation.Wcf.yaml b/.github/workflows/package-Instrumentation.Wcf.yaml index c124babb21..6af7c03105 100644 --- a/.github/workflows/package-Instrumentation.Wcf.yaml +++ b/.github/workflows/package-Instrumentation.Wcf.yaml @@ -33,7 +33,7 @@ jobs: run: dotnet build src/${{env.PROJECT}} --configuration Release --no-restore -p:Deterministic=true - name: dotnet test ${{env.PROJECT}} - run: dotnet test test/${{env.PROJECT}} + run: dotnet test test/${{env.PROJECT}}.Tests - name: dotnet pack ${{env.PROJECT}} run: dotnet pack src/${{env.PROJECT}} --configuration Release --no-build From 7b28226e93656e201d112cdbc74c95b92a1cda06 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sat, 27 Feb 2021 11:17:14 -0800 Subject: [PATCH 32/34] Nits. --- .../WcfInstrumentationEventSource.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationEventSource.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationEventSource.cs index 4b38de172b..698ead1460 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationEventSource.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/WcfInstrumentationEventSource.cs @@ -35,16 +35,16 @@ public void RequestFilterException(Exception ex) } } - [Event(Constants.RequestIsFilteredOutEventId, Message = "Request is filtered out.", Level = EventLevel.Verbose)] + [Event(EventIds.RequestIsFilteredOut, Message = "Request is filtered out.", Level = EventLevel.Verbose)] public void RequestIsFilteredOut(string eventName) { - this.WriteEvent(Constants.RequestIsFilteredOutEventId, eventName); + this.WriteEvent(EventIds.RequestIsFilteredOut, eventName); } - [Event(Constants.RequestFilterExceptionEventId, Message = "InstrumentationFilter threw exception. Request will not be collected. Exception {0}.", Level = EventLevel.Error)] + [Event(EventIds.RequestFilterException, Message = "InstrumentationFilter threw exception. Request will not be collected. Exception {0}.", Level = EventLevel.Error)] public void RequestFilterException(string exception) { - this.WriteEvent(Constants.RequestFilterExceptionEventId, exception); + this.WriteEvent(EventIds.RequestFilterException, exception); } /// @@ -66,10 +66,10 @@ private static string ToInvariantString(Exception exception) } } - private class Constants + private class EventIds { - public const int RequestIsFilteredOutEventId = 1; - public const int RequestFilterExceptionEventId = 2; + public const int RequestIsFilteredOut = 1; + public const int RequestFilterException = 2; } } } From 0d7afa360b26d35b7a280f535e1333e3a5fd48bf Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sat, 27 Feb 2021 19:39:22 -0800 Subject: [PATCH 33/34] Hashtable for cache. --- .../TelemetryClientMessageInspector.cs | 8 ++++---- .../TelemetryDispatchMessageInspector.cs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs index 78c99c8d2e..6ac646b6a1 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs @@ -15,7 +15,7 @@ // using System; -using System.Collections.Concurrent; +using System.Collections; using System.Diagnostics; using System.ServiceModel; using System.ServiceModel.Channels; @@ -31,7 +31,7 @@ namespace OpenTelemetry.Contrib.Instrumentation.Wcf /// public class TelemetryClientMessageInspector : IClientMessageInspector { - private static readonly ConcurrentDictionary ActionMetadataCache = new ConcurrentDictionary(); + private static readonly Hashtable ActionMetadataCache = Hashtable.Synchronized(new Hashtable()); /// public object BeforeSendRequest(ref Message request, IClientChannel channel) @@ -76,7 +76,7 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel) { activity.SetTag(WcfInstrumentationConstants.RpcSystemTag, WcfInstrumentationConstants.WcfSystemValue); - if (!ActionMetadataCache.TryGetValue(action, out var actionMetadata)) + if (!(ActionMetadataCache[action] is ActionMetadata actionMetadata)) { int lastIndex = action.LastIndexOf('/'); if (lastIndex >= 0) @@ -96,7 +96,7 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel) }; } - ActionMetadataCache.TryAdd(action, actionMetadata); + ActionMetadataCache[action] = actionMetadata; } activity.SetTag(WcfInstrumentationConstants.RpcServiceTag, actionMetadata.Service); diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs index c9dc2dda5a..0a4492ad25 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs @@ -16,7 +16,7 @@ #if NETFRAMEWORK using System; -using System.Collections.Concurrent; +using System.Collections; using System.Diagnostics; using System.ServiceModel; using System.ServiceModel.Channels; @@ -31,7 +31,7 @@ namespace OpenTelemetry.Contrib.Instrumentation.Wcf /// public class TelemetryDispatchMessageInspector : IDispatchMessageInspector { - private static readonly ConcurrentDictionary ActionMetadataCache = new ConcurrentDictionary(); + private static readonly Hashtable ActionMetadataCache = Hashtable.Synchronized(new Hashtable()); /// public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) @@ -66,7 +66,7 @@ public object AfterReceiveRequest(ref Message request, IClientChannel channel, I { activity.SetTag(WcfInstrumentationConstants.RpcSystemTag, WcfInstrumentationConstants.WcfSystemValue); - if (!ActionMetadataCache.TryGetValue(action, out var actionMetadata)) + if (!(ActionMetadataCache[action] is ActionMetadata actionMetadata)) { int lastIndex = action.LastIndexOf('/'); if (lastIndex >= 0) @@ -86,7 +86,7 @@ public object AfterReceiveRequest(ref Message request, IClientChannel channel, I }; } - ActionMetadataCache.TryAdd(action, actionMetadata); + ActionMetadataCache[action] = actionMetadata; } activity.SetTag(WcfInstrumentationConstants.RpcServiceTag, actionMetadata.Service); From 0d1ee707dfdb323522e541821ffb7e39db637df5 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Sat, 27 Feb 2021 21:28:59 -0800 Subject: [PATCH 34/34] Code review. --- .../Implementation/ActionMetadata.cs | 25 +++++++++++ .../TelemetryClientMessageInspector.cs | 43 ++++++------------ .../TelemetryDispatchMessageInspector.cs | 43 ++++++------------ .../TelemetryEndpointBehavior.cs | 44 +++++++++++-------- 4 files changed, 79 insertions(+), 76 deletions(-) create mode 100644 src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/ActionMetadata.cs diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/ActionMetadata.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/ActionMetadata.cs new file mode 100644 index 0000000000..463e26008f --- /dev/null +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/Implementation/ActionMetadata.cs @@ -0,0 +1,25 @@ +// +// 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. +// + +namespace OpenTelemetry.Contrib.Instrumentation.Wcf +{ + internal class ActionMetadata + { + public string ContractName { get; set; } + + public string OperationName { get; set; } + } +} diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs index 6ac646b6a1..c2113adc39 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryClientMessageInspector.cs @@ -15,7 +15,7 @@ // using System; -using System.Collections; +using System.Collections.Generic; using System.Diagnostics; using System.ServiceModel; using System.ServiceModel.Channels; @@ -31,7 +31,12 @@ namespace OpenTelemetry.Contrib.Instrumentation.Wcf /// public class TelemetryClientMessageInspector : IClientMessageInspector { - private static readonly Hashtable ActionMetadataCache = Hashtable.Synchronized(new Hashtable()); + private readonly IDictionary actionMappings; + + internal TelemetryClientMessageInspector(IDictionary actionMappings) + { + this.actionMappings = actionMappings ?? throw new ArgumentNullException(nameof(actionMappings)); + } /// public object BeforeSendRequest(ref Message request, IClientChannel channel) @@ -76,31 +81,17 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel) { activity.SetTag(WcfInstrumentationConstants.RpcSystemTag, WcfInstrumentationConstants.WcfSystemValue); - if (!(ActionMetadataCache[action] is ActionMetadata actionMetadata)) + if (!this.actionMappings.TryGetValue(action, out ActionMetadata actionMetadata)) { - int lastIndex = action.LastIndexOf('/'); - if (lastIndex >= 0) - { - actionMetadata = new ActionMetadata - { - Service = action.Substring(0, lastIndex), - Method = action.Substring(lastIndex + 1), - }; - } - else + actionMetadata = new ActionMetadata { - actionMetadata = new ActionMetadata - { - Service = null, - Method = action, - }; - } - - ActionMetadataCache[action] = actionMetadata; + ContractName = null, + OperationName = action, + }; } - activity.SetTag(WcfInstrumentationConstants.RpcServiceTag, actionMetadata.Service); - activity.SetTag(WcfInstrumentationConstants.RpcMethodTag, actionMetadata.Method); + activity.SetTag(WcfInstrumentationConstants.RpcServiceTag, actionMetadata.ContractName); + activity.SetTag(WcfInstrumentationConstants.RpcMethodTag, actionMetadata.OperationName); if (WcfInstrumentationActivitySource.Options.SetSoapMessageVersion) { @@ -161,12 +152,6 @@ private IDisposable SuppressDownstreamInstrumentation() : null; } - private class ActionMetadata - { - public string Service; - public string Method; - } - private class State { public IDisposable SuppressionScope; diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs index 0a4492ad25..312931d1ab 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryDispatchMessageInspector.cs @@ -16,7 +16,7 @@ #if NETFRAMEWORK using System; -using System.Collections; +using System.Collections.Generic; using System.Diagnostics; using System.ServiceModel; using System.ServiceModel.Channels; @@ -31,7 +31,12 @@ namespace OpenTelemetry.Contrib.Instrumentation.Wcf /// public class TelemetryDispatchMessageInspector : IDispatchMessageInspector { - private static readonly Hashtable ActionMetadataCache = Hashtable.Synchronized(new Hashtable()); + private readonly IDictionary actionMappings; + + internal TelemetryDispatchMessageInspector(IDictionary actionMappings) + { + this.actionMappings = actionMappings ?? throw new ArgumentNullException(nameof(actionMappings)); + } /// public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) @@ -66,31 +71,17 @@ public object AfterReceiveRequest(ref Message request, IClientChannel channel, I { activity.SetTag(WcfInstrumentationConstants.RpcSystemTag, WcfInstrumentationConstants.WcfSystemValue); - if (!(ActionMetadataCache[action] is ActionMetadata actionMetadata)) + if (!this.actionMappings.TryGetValue(action, out ActionMetadata actionMetadata)) { - int lastIndex = action.LastIndexOf('/'); - if (lastIndex >= 0) - { - actionMetadata = new ActionMetadata - { - Service = action.Substring(0, lastIndex), - Method = action.Substring(lastIndex + 1), - }; - } - else + actionMetadata = new ActionMetadata { - actionMetadata = new ActionMetadata - { - Service = null, - Method = action, - }; - } - - ActionMetadataCache[action] = actionMetadata; + ContractName = null, + OperationName = action, + }; } - activity.SetTag(WcfInstrumentationConstants.RpcServiceTag, actionMetadata.Service); - activity.SetTag(WcfInstrumentationConstants.RpcMethodTag, actionMetadata.Method); + activity.SetTag(WcfInstrumentationConstants.RpcServiceTag, actionMetadata.ContractName); + activity.SetTag(WcfInstrumentationConstants.RpcMethodTag, actionMetadata.OperationName); if (WcfInstrumentationActivitySource.Options.SetSoapMessageVersion) { @@ -135,12 +126,6 @@ public void BeforeSendReply(ref Message reply, object correlationState) activity.Stop(); } } - - private class ActionMetadata - { - public string Service; - public string Method; - } } } #endif diff --git a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryEndpointBehavior.cs b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryEndpointBehavior.cs index 94b4159d6e..8c3cfa6698 100644 --- a/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryEndpointBehavior.cs +++ b/src/OpenTelemetry.Contrib.Instrumentation.Wcf/TelemetryEndpointBehavior.cs @@ -14,6 +14,8 @@ // limitations under the License. // +using System; +using System.Collections.Generic; using System.ServiceModel.Channels; using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; @@ -34,22 +36,6 @@ namespace OpenTelemetry.Contrib.Instrumentation.Wcf #endif public class TelemetryEndpointBehavior : IEndpointBehavior { - private readonly TelemetryClientMessageInspector telemetryClientMessageInspector; -#if NETFRAMEWORK - private readonly TelemetryDispatchMessageInspector telemetryDispatchMessageInspector; -#endif - - /// - /// Initializes a new instance of the class. - /// - public TelemetryEndpointBehavior() - { - this.telemetryClientMessageInspector = new TelemetryClientMessageInspector(); -#if NETFRAMEWORK - this.telemetryDispatchMessageInspector = new TelemetryDispatchMessageInspector(); -#endif - } - /// public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { @@ -58,14 +44,36 @@ public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterColle /// public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { - clientRuntime.ClientMessageInspectors.Add(this.telemetryClientMessageInspector); + var actionMappings = new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (var clientOperation in clientRuntime.ClientOperations) + { + actionMappings[clientOperation.Action] = new ActionMetadata + { + ContractName = $"{clientRuntime.ContractNamespace}{clientRuntime.ContractName}", + OperationName = clientOperation.Name, + }; + } + + clientRuntime.ClientMessageInspectors.Add(new TelemetryClientMessageInspector(actionMappings)); } /// public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { #if NETFRAMEWORK - endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this.telemetryDispatchMessageInspector); + var actionMappings = new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (var dispatchOperation in endpointDispatcher.DispatchRuntime.Operations) + { + actionMappings[dispatchOperation.Action] = new ActionMetadata + { + ContractName = $"{endpointDispatcher.ContractNamespace}{endpointDispatcher.ContractName}", + OperationName = dispatchOperation.Name, + }; + } + + endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new TelemetryDispatchMessageInspector(actionMappings)); #endif }