diff --git a/BaseIntermediateOutputPath.props b/BaseIntermediateOutputPath.props new file mode 100644 index 0000000..48e7654 --- /dev/null +++ b/BaseIntermediateOutputPath.props @@ -0,0 +1,12 @@ + + + + + $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), 'EnlistmentRoot.marker')) + $(EnlistmentRoot)\..\obj\$(MSBuildProjectName) + $([System.IO.Path]::GetFullPath( $(BaseIntermediateOutputPath) )) + + \ No newline at end of file diff --git a/Global.props b/Global.props index 8bf307a..be82fc5 100644 --- a/Global.props +++ b/Global.props @@ -1,6 +1,8 @@  + + @@ -60,16 +62,11 @@ - $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), 'EnlistmentRoot.marker')) - $(EnlistmentRoot)\..\bin $([System.IO.Path]::GetFullPath( $(BinRoot) )) $(MSBuildProjectDirectory.Substring($(EnlistmentRoot.Length))) - $(EnlistmentRoot)\..\obj - $([System.IO.Path]::GetFullPath( $(BaseIntermediateOutputPath) )) - Debug $(BinRoot)\$(Configuration)\$(RelativeOutputPathBase) @@ -117,4 +114,4 @@ <_Parameter1>$(SemanticVersionMajor).$(SemanticVersionMinor).$(SemanticVersionPatch).$(PreReleaseVersion) - \ No newline at end of file + diff --git a/WCF/.editorconfig b/WCF/.editorconfig new file mode 100644 index 0000000..9284239 --- /dev/null +++ b/WCF/.editorconfig @@ -0,0 +1,78 @@ +# EditorConfig is awesome:http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Don't use tabs for indentation. +[*] +indent_style = space +# (Please don't specify an indent_size here; that has too many unintended consequences.) + +# Code files +[*.{cs,csx,vb,vbx}] +indent_size = 4 + +# Xml project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +indent_size = 2 + +# Xml config files +[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] +indent_size = 2 + +# JSON files +[*.json] +indent_size = 2 + +# Dotnet code style settings: +[*.{cs,vb}] +# Sort using and Import directives with System.* appearing first +dotnet_sort_system_directives_first = true +# Avoid "this." and "Me." if not necessary +dotnet_style_qualification_for_field = true:suggestion +dotnet_style_qualification_for_property = true:suggestion +dotnet_style_qualification_for_method = true:suggestion +dotnet_style_qualification_for_event = true:suggestion + +# Use language keywords instead of framework type names for type references +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# Suggest more modern language features when available +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion + +# CSharp code style settings: +[*.cs] +# Prefer "var" everywhere +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_style_var_elsewhere = true:suggestion + +# Prefer method-like constructs to have a block body +csharp_style_expression_bodied_methods = false:none +csharp_style_expression_bodied_constructors = false:none +csharp_style_expression_bodied_operators = false:none + +# Prefer property-like constructs to have an expression-body +csharp_style_expression_bodied_properties = true:none +csharp_style_expression_bodied_indexers = true:none +csharp_style_expression_bodied_accessors = true:none + +# Suggest more modern language features when available +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# Newline settings +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true \ No newline at end of file diff --git a/WCF/Settings.StyleCop b/WCF/Settings.StyleCop new file mode 100644 index 0000000..9d40616 --- /dev/null +++ b/WCF/Settings.StyleCop @@ -0,0 +1,128 @@ + + + NoMerge + + + + + False + + \.g\.cs$ + \.generated\.cs$ + \.g\.i\.cs$ + TemporaryGeneratedFile_.*\.cs$ + + + + + + + + + + True + + + + + True + + + + + True + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + Microsoft + Copyright © Microsoft. All Rights Reserved. + + + + + + + False + + + + + + + + + if + is + on + to + + + + + \ No newline at end of file diff --git a/WCF/Shared.Tests/ChannelTestBase.cs b/WCF/Shared.Tests/ChannelTestBase.cs index 008f619..080376d 100644 --- a/WCF/Shared.Tests/ChannelTestBase.cs +++ b/WCF/Shared.Tests/ChannelTestBase.cs @@ -1,20 +1,20 @@ -using Microsoft.ApplicationInsights.DataContracts; -using Microsoft.ApplicationInsights.Wcf.Implementation; -using Microsoft.ApplicationInsights.Wcf.Tests.Channels; -using Microsoft.ApplicationInsights.Wcf.Tests.Integration; -using Microsoft.ApplicationInsights.Wcf.Tests.Service; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Linq; -using System.Reflection; -using System.ServiceModel.Channels; - -namespace Microsoft.ApplicationInsights.Wcf.Tests +namespace Microsoft.ApplicationInsights.Wcf.Tests { + using System; + using System.Linq; + using System.Reflection; + using System.ServiceModel.Channels; + using Microsoft.ApplicationInsights.DataContracts; + using Microsoft.ApplicationInsights.Wcf.Implementation; + using Microsoft.ApplicationInsights.Wcf.Tests.Channels; + using Microsoft.ApplicationInsights.Wcf.Tests.Integration; + using Microsoft.ApplicationInsights.Wcf.Tests.Service; + using Microsoft.VisualStudio.TestTools.UnitTesting; + public abstract class ChannelTestBase where TChannel : IChannel { - public const String SvcUrl = "http://localhost/MyService.svc"; - public const String HostName = "localhost"; + public const string SvcUrl = "http://localhost/MyService.svc"; + public const string HostName = "localhost"; [TestMethod] [TestCategory("Client")] @@ -24,11 +24,13 @@ public void WhenChannelManagerIsNull_ConstructorThrowsException() bool failed = false; try { - var channel = GetChannel(null, innerChannel); - } catch ( ArgumentNullException ) + var channel = this.GetChannel(null, innerChannel); + } + catch (ArgumentNullException) { failed = true; } + Assert.IsTrue(failed, "Constructor did not throw ArgumentNullException"); } @@ -36,44 +38,43 @@ public void WhenChannelManagerIsNull_ConstructorThrowsException() [TestCategory("Client")] public void WhenInnerChannelIsNull_ConstructorThrowsException() { - var manager = new ClientChannelManager(new TelemetryClient(), typeof(ISimpleService)); bool failed = false; try { - var channel = GetChannel(manager, null); - } catch ( ArgumentNullException ) + var channel = this.GetChannel(manager, null); + } + catch (ArgumentNullException) { failed = true; } + Assert.IsTrue(failed, "Constructor did not throw ArgumentNullException"); } - [TestMethod] [TestCategory("Client")] public void WhenChannelIsCreated_InnerChannelRemoteAddressIsReturned() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); - // IChannel does not define RemoteAddress, so cheat + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); + // IChannel does not define RemoteAddress, so cheat var prop = channel.GetType().GetProperty("RemoteAddress", BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty); Assert.AreEqual(innerChannel.RemoteAddress, prop.GetValue(channel, null)); } - // + // ------------------------------- // Event Handling - // - + // ------------------------------- [TestMethod] [TestCategory("Client")] public void WhenChannelIsOpened_InnerChannelEventsAreHooked() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); channel.Open(); Assert.IsTrue(innerChannel.OpeningIsHooked(), "Opening event is not hooked"); @@ -89,7 +90,7 @@ public void WhenChannelIsOpenedWithTimeout_InnerChannelEventsAreHooked() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); channel.Open(TimeSpan.FromSeconds(10)); Assert.IsTrue(innerChannel.OpeningIsHooked(), "Opening event is not hooked"); @@ -99,14 +100,13 @@ public void WhenChannelIsOpenedWithTimeout_InnerChannelEventsAreHooked() Assert.IsTrue(innerChannel.FaultedIsHooked(), "Faulted event is not hooked"); } - [TestMethod] [TestCategory("Client")] public void WhenChannelIsOpenedAsync_InnerChannelEventsAreHooked() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); var result = channel.BeginOpen(null, null); channel.EndOpen(result); @@ -123,7 +123,7 @@ public void WhenChannelIsOpenedAsyncWithTimeout_InnerChannelEventsAreHooked() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); var result = channel.BeginOpen(TimeSpan.FromSeconds(10), null, null); channel.EndOpen(result); @@ -139,7 +139,7 @@ public void WhenChannelIsClosed_InnerChannelEventsAreUnhooked() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); channel.Open(); channel.Close(); @@ -156,7 +156,7 @@ public void WhenChannelIsClosedWithTimeout_InnerChannelEventsAreUnhooked() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); channel.Open(); channel.Close(TimeSpan.FromSeconds(1)); @@ -173,7 +173,7 @@ public void WhenChannelIsClosedAsync_InnerChannelEventsAreUnhooked() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); channel.Open(); var result = channel.BeginClose(null, null); channel.EndClose(result); @@ -191,7 +191,7 @@ public void WhenChannelIsClosedAsyncWithTimeout_InnerChannelEventsAreUnhooked() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); channel.Open(); var result = channel.BeginClose(TimeSpan.FromSeconds(10), null, null); channel.EndClose(result); @@ -209,7 +209,7 @@ public void WhenChannelIsAborted_InnerChannelEventsAreUnhooked() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); channel.Open(); channel.Abort(); @@ -220,17 +220,16 @@ public void WhenChannelIsAborted_InnerChannelEventsAreUnhooked() Assert.IsFalse(innerChannel.FaultedIsHooked(), "Faulted event is hooked"); } - // + // --------------------------------------- // Channel.Open Telemetry - // - + // --------------------------------------- [TestMethod] [TestCategory("Client")] public void WhenChannelIsOpened_TelemetryIsWritten() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); channel.Open(); CheckOpenDependencyWritten(typeof(ISimpleService), true); @@ -242,7 +241,7 @@ public void WhenChannelIsOpenedWithTimeout_TelemetryIsWritten() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); channel.Open(TimeSpan.FromSeconds(10)); CheckOpenDependencyWritten(typeof(ISimpleService), true); @@ -254,7 +253,7 @@ public void WhenChannelIsOpenedAsync_TelemetryIsWritten() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); var result = channel.BeginOpen(null, null); channel.EndOpen(result); @@ -267,7 +266,7 @@ public void WhenChannelIsOpenedAsyncWithTimeout_TelemetryIsWritten() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); var result = channel.BeginOpen(TimeSpan.FromSeconds(10), null, null); channel.EndOpen(result); @@ -282,15 +281,17 @@ public void WhenChannelIsOpened_TelemetryIsWritten_Failure() innerChannel.FailOpen = true; TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); bool failed = false; try { channel.Open(); - } catch ( Exception ) + } + catch (Exception) { failed = true; } + Assert.IsTrue(failed, "Open() did not throw exception"); CheckOpenDependencyWritten(typeof(ISimpleService), false); @@ -304,15 +305,17 @@ public void WhenChannelIsOpenedWithTimeout_TelemetryIsWritten_Failure() innerChannel.FailOpen = true; TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); bool failed = false; try { channel.Open(TimeSpan.FromSeconds(10)); - } catch ( Exception ) + } + catch (Exception) { failed = true; } + Assert.IsTrue(failed, "Open() did not throw exception"); CheckOpenDependencyWritten(typeof(ISimpleService), false); @@ -326,15 +329,17 @@ public void WhenChannelIsOpenedAsync_TelemetryIsWritten_FailureInBegin() innerChannel.FailBeginOpen = true; TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); bool failed = false; try { channel.BeginOpen(null, null); - } catch ( Exception ) + } + catch (Exception) { failed = true; } + Assert.IsTrue(failed, "BeginOpen() did not throw exception"); CheckOpenDependencyWritten(typeof(ISimpleService), false); @@ -348,26 +353,27 @@ public void WhenChannelIsOpenedAsyncWithTimeout_TelemetryIsWritten_FailureInBegi innerChannel.FailBeginOpen = true; TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); bool failed = false; try { channel.BeginOpen(TimeSpan.FromSeconds(10), null, null); - } catch ( Exception ) + } + catch (Exception) { failed = true; } + Assert.IsTrue(failed, "BeginOpen() did not throw exception"); CheckOpenDependencyWritten(typeof(ISimpleService), false); } - - internal abstract TChannel GetChannel(IChannel channel, Type contract); + internal abstract TChannel GetChannel(IChannelManager manager, IChannel channel); - private void CheckOpenDependencyWritten(Type contract, bool success) + private static void CheckOpenDependencyWritten(Type contract, bool success) { var dependency = TestTelemetryChannel.CollectedData().OfType().FirstOrDefault(); Assert.IsNotNull(dependency, "Did not write dependency event"); diff --git a/WCF/Shared.Tests/Channels/TestTelemetryChannel.cs b/WCF/Shared.Tests/Channels/TestTelemetryChannel.cs index 78aa9b7..e35f0d1 100644 --- a/WCF/Shared.Tests/Channels/TestTelemetryChannel.cs +++ b/WCF/Shared.Tests/Channels/TestTelemetryChannel.cs @@ -1,13 +1,9 @@ -using Microsoft.ApplicationInsights.Channel; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Microsoft.ApplicationInsights.Wcf.Tests.Channels +namespace Microsoft.ApplicationInsights.Wcf.Tests.Channels { + using System; + using System.Collections.Generic; + using Microsoft.ApplicationInsights.Channel; + public sealed class TestTelemetryChannel : ITelemetryChannel { private static List items = new List(); @@ -17,36 +13,37 @@ public sealed class TestTelemetryChannel : ITelemetryChannel public string EndpointAddress { get; set; } - public void Flush() + public static void Clear() { + lock (lockobj) + { + items.Clear(); + } } - public void Send(ITelemetry item) + public static IList CollectedData() { - lock ( lockobj ) + lock (lockobj) { - items.Add(item); + List list = new List(items); + return list; } } - public void Dispose() + public void Flush() { } - public static void Clear() + public void Send(ITelemetry item) { - lock ( lockobj ) + lock (lockobj) { - items.Clear(); + items.Add(item); } } - public static IList CollectedData() + + public void Dispose() { - lock ( lockobj ) - { - List list = new List(items); - return list; - } } } } diff --git a/WCF/Shared.Tests/ClientChannelManager.cs b/WCF/Shared.Tests/ClientChannelManager.cs index f5e2a9d..04fb654 100644 --- a/WCF/Shared.Tests/ClientChannelManager.cs +++ b/WCF/Shared.Tests/ClientChannelManager.cs @@ -1,11 +1,18 @@ -using Microsoft.ApplicationInsights.Wcf.Implementation; -using System; -using System.ServiceModel.Description; - -namespace Microsoft.ApplicationInsights.Wcf.Tests +namespace Microsoft.ApplicationInsights.Wcf.Tests { + using System; + using System.ServiceModel.Description; + using Microsoft.ApplicationInsights.Wcf.Implementation; + internal class ClientChannelManager : IChannelManager { + public ClientChannelManager(TelemetryClient client, Type contractType) + { + this.TelemetryClient = client; + this.OperationMap = new ClientContract(ContractDescription.GetContract(contractType)); + this.CloseTimeout = this.OpenTimeout = this.ReceiveTimeout = this.SendTimeout = TimeSpan.FromSeconds(5); + } + public TimeSpan CloseTimeout { get; private set; } public TimeSpan OpenTimeout { get; private set; } @@ -17,17 +24,15 @@ internal class ClientChannelManager : IChannelManager public TimeSpan SendTimeout { get; private set; } public TelemetryClient TelemetryClient { get; private set; } - public String RootOperationIdHeaderName { get; set; } - public String ParentOperationIdHeaderName { get; set; } - public String SoapRootOperationIdHeaderName { get; set; } - public String SoapParentOperationIdHeaderName { get; set; } - public String SoapHeaderNamespace { get; set; } - public ClientChannelManager(TelemetryClient client, Type contractType) - { - this.TelemetryClient = client; - this.OperationMap = new ClientContract(ContractDescription.GetContract(contractType)); - this.CloseTimeout = this.OpenTimeout = this.ReceiveTimeout = this.SendTimeout = TimeSpan.FromSeconds(5); - } + public string RootOperationIdHeaderName { get; set; } + + public string ParentOperationIdHeaderName { get; set; } + + public string SoapRootOperationIdHeaderName { get; set; } + + public string SoapParentOperationIdHeaderName { get; set; } + + public string SoapHeaderNamespace { get; set; } } } diff --git a/WCF/Shared.Tests/ClientContractTests.cs b/WCF/Shared.Tests/ClientContractTests.cs index ddd81e8..038e597 100644 --- a/WCF/Shared.Tests/ClientContractTests.cs +++ b/WCF/Shared.Tests/ClientContractTests.cs @@ -1,11 +1,11 @@ -using Microsoft.ApplicationInsights.Wcf.Implementation; -using Microsoft.ApplicationInsights.Wcf.Tests.Service; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.ServiceModel.Description; - -namespace Microsoft.ApplicationInsights.Wcf.Tests +namespace Microsoft.ApplicationInsights.Wcf.Tests { + using System; + using System.ServiceModel.Description; + using Microsoft.ApplicationInsights.Wcf.Implementation; + using Microsoft.ApplicationInsights.Wcf.Tests.Service; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] public class ClientContractTests { diff --git a/WCF/Shared.Tests/ClientExceptionExtensionsTests.cs b/WCF/Shared.Tests/ClientExceptionExtensionsTests.cs index 7238423..f3a2ecc 100644 --- a/WCF/Shared.Tests/ClientExceptionExtensionsTests.cs +++ b/WCF/Shared.Tests/ClientExceptionExtensionsTests.cs @@ -1,10 +1,10 @@ -using Microsoft.ApplicationInsights.Wcf.Implementation; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.ServiceModel; - -namespace Microsoft.ApplicationInsights.Wcf.Tests +namespace Microsoft.ApplicationInsights.Wcf.Tests { + using System; + using System.ServiceModel; + using Microsoft.ApplicationInsights.Wcf.Implementation; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] public class ClientExceptionExtensionsTests { @@ -15,10 +15,12 @@ public void When_ExceptionIsNull_ExceptionIsThrown() try { ClientExceptionExtensions.ToResultCode(null); - } catch ( ArgumentNullException ) + } + catch (ArgumentNullException) { failed = true; } + Assert.IsTrue(failed, "ToResultCode() did not throw ArgumentNullException"); } @@ -52,8 +54,7 @@ public void When_OtherException() TestException("ChannelTerminatedException"); } - - private void TestException(string expected) where TException : Exception, new() + private static void TestException(string expected) where TException : Exception, new() { var ex = new TException(); Assert.AreEqual(expected, ex.ToResultCode()); diff --git a/WCF/Shared.Tests/ClientIpTelemetryInitializerTests.cs b/WCF/Shared.Tests/ClientIpTelemetryInitializerTests.cs index 6a6e842..5d5d403 100644 --- a/WCF/Shared.Tests/ClientIpTelemetryInitializerTests.cs +++ b/WCF/Shared.Tests/ClientIpTelemetryInitializerTests.cs @@ -1,41 +1,39 @@ -using Microsoft.ApplicationInsights.DataContracts; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Collections.Generic; -using System.ServiceModel.Channels; -using System.Text; - -namespace Microsoft.ApplicationInsights.Wcf.Tests +namespace Microsoft.ApplicationInsights.Wcf.Tests { + using System; + using System.ServiceModel.Channels; + using Microsoft.ApplicationInsights.DataContracts; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] public class ClientIpTelemetryInitializerTests { [TestMethod] public void SetsClientIpFromWcfContextOnRequest() { - const String clientIp = "10.12.32.12"; + const string ClientIp = "10.12.32.12"; var context = new MockOperationContext(); context.IncomingProperties.Add( RemoteEndpointMessageProperty.Name, - new RemoteEndpointMessageProperty(clientIp, 7656)); + new RemoteEndpointMessageProperty(ClientIp, 7656)); var initializer = new ClientIpTelemetryInitializer(); var telemetry = context.Request; initializer.Initialize(telemetry, context); - Assert.AreEqual(clientIp, telemetry.Context.Location.Ip); + Assert.AreEqual(ClientIp, telemetry.Context.Location.Ip); } [TestMethod] public void SetsClientIpFromWcfContextOnOtherEvent() { - const String originalIp = "10.12.32.12"; - const String newIp = "172.34.12.45"; + const string OriginalIp = "10.12.32.12"; + const string NewIp = "172.34.12.45"; var context = new MockOperationContext(); context.IncomingProperties.Add( RemoteEndpointMessageProperty.Name, - new RemoteEndpointMessageProperty(originalIp, 7656)); + new RemoteEndpointMessageProperty(OriginalIp, 7656)); var initializer = new ClientIpTelemetryInitializer(); @@ -48,26 +46,26 @@ public void SetsClientIpFromWcfContextOnOtherEvent() context.IncomingProperties.Clear(); context.IncomingProperties.Add( RemoteEndpointMessageProperty.Name, - new RemoteEndpointMessageProperty(newIp, 7656)); + new RemoteEndpointMessageProperty(NewIp, 7656)); var telemetry = new EventTelemetry("myevent"); initializer.Initialize(telemetry, context); - Assert.AreEqual(originalIp, telemetry.Context.Location.Ip); + Assert.AreEqual(OriginalIp, telemetry.Context.Location.Ip); } [TestMethod] public void ClientIpIsCopiedFromRequestIfPresent() { - const String clientIp = "10.12.32.12"; + const string ClientIp = "10.12.32.12"; var context = new MockOperationContext(); - context.Request.Context.Location.Ip = clientIp; + context.Request.Context.Location.Ip = ClientIp; var initializer = new ClientIpTelemetryInitializer(); var telemetry = new EventTelemetry(); initializer.Initialize(telemetry, context); - Assert.AreEqual(clientIp, telemetry.Context.Location.Ip); + Assert.AreEqual(ClientIp, telemetry.Context.Location.Ip); } } } diff --git a/WCF/Shared.Tests/ClientTelemetryBindingElementTests.cs b/WCF/Shared.Tests/ClientTelemetryBindingElementTests.cs index 43f5368..3b7c461 100644 --- a/WCF/Shared.Tests/ClientTelemetryBindingElementTests.cs +++ b/WCF/Shared.Tests/ClientTelemetryBindingElementTests.cs @@ -1,17 +1,17 @@ -using Microsoft.ApplicationInsights.Wcf.Implementation; -using Microsoft.ApplicationInsights.Wcf.Tests.Service; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.ServiceModel; -using System.ServiceModel.Channels; - -namespace Microsoft.ApplicationInsights.Wcf.Tests +namespace Microsoft.ApplicationInsights.Wcf.Tests { + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + using Microsoft.ApplicationInsights.Wcf.Implementation; + using Microsoft.ApplicationInsights.Wcf.Tests.Service; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] public class ClientTelemetryBindingElementTests { - const String TwoWayOp1 = "http://tempuri.org/ISimpleService/GetSimpleData"; - const String TwoWayOp2 = "http://tempuri.org/ISimpleService/CallFailsWithFault"; + private const string TwoWayOp1 = "http://tempuri.org/ISimpleService/GetSimpleData"; + private const string TwoWayOp2 = "http://tempuri.org/ISimpleService/CallFailsWithFault"; [TestMethod] [TestCategory("Client")] @@ -23,10 +23,12 @@ public void WhenClientIsNull_ConstructorThrowsException() try { var element = new ClientTelemetryBindingElement(client, map); - } catch ( ArgumentNullException ) + } + catch (ArgumentNullException) { failed = true; } + Assert.IsTrue(failed, "Constructor did not throw ArgumentNullException"); } @@ -41,10 +43,12 @@ public void WhenOperationMapIsNull_ConstructorThrowsException() try { var element = new ClientTelemetryBindingElement(client, map); - } catch ( ArgumentNullException ) + } + catch (ArgumentNullException) { failed = true; } + Assert.IsTrue(failed, "Constructor did not throw ArgumentNullException"); } @@ -59,10 +63,12 @@ public void WhenContextIsNull_CanBuildChannelFactoryThrowsException() { var element = new ClientTelemetryBindingElement(client, map); element.CanBuildChannelFactory(null); - } catch ( ArgumentNullException ) + } + catch (ArgumentNullException) { failed = true; } + Assert.IsTrue(failed, "CanBuildChannelFactory did not throw ArgumentNullException"); } @@ -77,10 +83,12 @@ public void WhenContextIsNull_BuildChannelFactoryThrowsException() { var element = new ClientTelemetryBindingElement(client, map); element.BuildChannelFactory(null); - } catch ( ArgumentNullException ) + } + catch (ArgumentNullException) { failed = true; } + Assert.IsTrue(failed, "BuildChannelFactory did not throw ArgumentNullException"); } @@ -132,7 +140,6 @@ public void WithIInputChannel_CanBuildChannelFactoryReturnsFalse() Assert.IsFalse(element.CanBuildChannelFactory(context)); } - public void TestChannelShape(Binding binding, bool tryCreate = true) { TelemetryClient client = new TelemetryClient(); @@ -143,7 +150,7 @@ public void TestChannelShape(Binding binding, bool tryCreate = true) BindingContext context = new BindingContext(custom, new BindingParameterCollection()); Assert.IsTrue(element.CanBuildChannelFactory(context)); - if ( tryCreate ) + if (tryCreate) { var factory = element.BuildChannelFactory(context); Assert.IsNotNull(factory, "BuildChannelFactory() returned null"); diff --git a/WCF/Shared.Tests/ClientTelemetryDuplexChannelTests.cs b/WCF/Shared.Tests/ClientTelemetryDuplexChannelTests.cs index 19f4ec5..cf044c2 100644 --- a/WCF/Shared.Tests/ClientTelemetryDuplexChannelTests.cs +++ b/WCF/Shared.Tests/ClientTelemetryDuplexChannelTests.cs @@ -1,35 +1,23 @@ -using Microsoft.ApplicationInsights.DataContracts; -using Microsoft.ApplicationInsights.Wcf.Implementation; -using Microsoft.ApplicationInsights.Wcf.Tests.Channels; -using Microsoft.ApplicationInsights.Wcf.Tests.Integration; -using Microsoft.ApplicationInsights.Wcf.Tests.Service; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Linq; -using System.ServiceModel.Channels; -using System.Threading; -using System.Xml; - -namespace Microsoft.ApplicationInsights.Wcf.Tests +namespace Microsoft.ApplicationInsights.Wcf.Tests { + using System; + using System.Linq; + using System.ServiceModel.Channels; + using System.Threading; + using System.Xml; + using Microsoft.ApplicationInsights.DataContracts; + using Microsoft.ApplicationInsights.Wcf.Implementation; + using Microsoft.ApplicationInsights.Wcf.Tests.Channels; + using Microsoft.ApplicationInsights.Wcf.Tests.Integration; + using Microsoft.ApplicationInsights.Wcf.Tests.Service; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] public class ClientTelemetryDuplexChannelTests : ChannelTestBase { - const String TwoWayOp1 = "http://tempuri.org/ISimpleService/GetSimpleData"; - const String TwoWayOp2 = "http://tempuri.org/ISimpleService/CallFailsWithFault"; - const String OneWayOp1 = "http://tempuri.org/IOneWayService/SuccessfullOneWayCall"; - - internal override IDuplexChannel GetChannel(IChannel innerChannel, Type contract) - { - return GetChannel( - new ClientChannelManager(new TelemetryClient(), contract), - innerChannel - ); - } - internal override IDuplexChannel GetChannel(IChannelManager manager, IChannel innerChannel) - { - return new ClientTelemetryDuplexChannel(manager, innerChannel); - } + private const string TwoWayOp1 = "http://tempuri.org/ISimpleService/GetSimpleData"; + private const string TwoWayOp2 = "http://tempuri.org/ISimpleService/CallFailsWithFault"; + private const string OneWayOp1 = "http://tempuri.org/IOneWayService/SuccessfullOneWayCall"; [TestMethod] [TestCategory("Client")] @@ -37,34 +25,39 @@ public void Send_WhenMessageIsNull_ThrowsException() { TestTelemetryChannel.Clear(); var innerChannel = new MockClientChannel(SvcUrl); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); bool failed = false; try { channel.Send(null); - } catch ( ArgumentNullException ) + } + catch (ArgumentNullException) { failed = true; } + Assert.IsTrue(failed, "Send did not throw an exception"); } + [TestMethod] [TestCategory("Client")] public void BeginSend_WhenMessageIsNull_ThrowsException() { TestTelemetryChannel.Clear(); var innerChannel = new MockClientChannel(SvcUrl); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); bool failed = false; try { channel.BeginSend(null, null, null); - } catch ( ArgumentNullException ) + } + catch (ArgumentNullException) { failed = true; } + Assert.IsTrue(failed, "BeginSend did not throw an exception"); } @@ -74,7 +67,7 @@ public void Send_OneWay_WritesTelemetry() { TestTelemetryChannel.Clear(); var innerChannel = new MockClientChannel(SvcUrl); - var channel = GetChannel(innerChannel, typeof(IOneWayService)); + var channel = this.GetChannel(innerChannel, typeof(IOneWayService)); var request = BuildMessage(OneWayOp1); request.Headers.MessageId = new UniqueId(); @@ -92,7 +85,7 @@ public void SendFollowedByMatchingReceive_WritesTelemetry() { TestTelemetryChannel.Clear(); var innerChannel = new MockClientChannel(SvcUrl); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); var request = BuildMessage(TwoWayOp1); request.Headers.MessageId = new UniqueId(); @@ -115,7 +108,7 @@ public void AsyncSendAndReceive_WritesTelemetry() { TestTelemetryChannel.Clear(); var innerChannel = new MockClientChannel(SvcUrl); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); var request = BuildMessage(TwoWayOp1); request.Headers.MessageId = new UniqueId(); @@ -140,7 +133,7 @@ public void AsyncSendAndTryReceive_WritesTelemetry() { TestTelemetryChannel.Clear(); var innerChannel = new MockClientChannel(SvcUrl); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); var request = BuildMessage(TwoWayOp1); request.Headers.MessageId = new UniqueId(); @@ -166,7 +159,7 @@ public void SendFollowedWithReceiveTimeout_WritesTelemetry() { TestTelemetryChannel.Clear(); var innerChannel = new MockClientChannel(SvcUrl); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); var request = BuildMessage(TwoWayOp1); request.Headers.MessageId = new UniqueId(); @@ -177,12 +170,14 @@ public void SendFollowedWithReceiveTimeout_WritesTelemetry() try { channel.Receive(); - } catch ( TimeoutException ) + } + catch (TimeoutException) { failed = true; } Assert.IsTrue(failed, "Receive did not fail with TimeoutException"); + // there's potentially some additional delay between our timeout firing internally, // and the callback firing so that telemetry can be written Thread.Sleep(200); @@ -194,7 +189,19 @@ public void SendFollowedWithReceiveTimeout_WritesTelemetry() Assert.IsFalse(telemetry.Success.Value, "Dependency call succeeded"); } - private Message BuildMessage(String action) + internal override IDuplexChannel GetChannel(IChannel innerChannel, Type contract) + { + return this.GetChannel( + new ClientChannelManager(new TelemetryClient(), contract), + innerChannel); + } + + internal override IDuplexChannel GetChannel(IChannelManager manager, IChannel innerChannel) + { + return new ClientTelemetryDuplexChannel(manager, innerChannel); + } + + private static Message BuildMessage(string action) { return Message.CreateMessage(MessageVersion.Default, action, ""); } diff --git a/WCF/Shared.Tests/ClientTelemetryEndpointBehaviorTests.cs b/WCF/Shared.Tests/ClientTelemetryEndpointBehaviorTests.cs index 9df1f60..11ca185 100644 --- a/WCF/Shared.Tests/ClientTelemetryEndpointBehaviorTests.cs +++ b/WCF/Shared.Tests/ClientTelemetryEndpointBehaviorTests.cs @@ -1,16 +1,16 @@ -using Microsoft.ApplicationInsights.Extensibility; -using Microsoft.ApplicationInsights.Wcf.Implementation; -using Microsoft.ApplicationInsights.Wcf.Tests.Channels; -using Microsoft.ApplicationInsights.Wcf.Tests.Integration; -using Microsoft.ApplicationInsights.Wcf.Tests.Service; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Reflection; -using System.Runtime.Remoting; -using System.ServiceModel; - -namespace Microsoft.ApplicationInsights.Wcf.Tests +namespace Microsoft.ApplicationInsights.Wcf.Tests { + using System; + using System.Reflection; + using System.Runtime.Remoting; + using System.ServiceModel; + using Microsoft.ApplicationInsights.Extensibility; + using Microsoft.ApplicationInsights.Wcf.Implementation; + using Microsoft.ApplicationInsights.Wcf.Tests.Channels; + using Microsoft.ApplicationInsights.Wcf.Tests.Integration; + using Microsoft.ApplicationInsights.Wcf.Tests.Service; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] public class ClientTelemetryEndpointBehaviorTests { @@ -18,10 +18,9 @@ public class ClientTelemetryEndpointBehaviorTests [TestCategory("Client")] public void BehaviorAddsCustomBinding() { - using ( var host = new HostingContext() ) + using (var host = new HostingContext()) { var binding = new NetTcpBinding(); - //binding.TransferMode = TransferMode.Streamed; var configuration = new TelemetryConfiguration(); var factory = new ChannelFactory(binding, host.GetServiceAddress()); ISimpleService channel = null; @@ -40,30 +39,30 @@ public void BehaviorAddsCustomBinding() factory.Close(); Assert.IsInstanceOfType(innerChannel, typeof(ClientTelemetryChannelBase), "Telemetry channel is missing"); - } catch + } + catch { factory.Abort(); - if ( channel != null ) + if (channel != null) { ((IClientChannel)channel).Abort(); } + throw; } } } - [TestMethod] [TestCategory("Integration"), TestCategory("Client")] public void RequestReply_TelemetryIsWritten() { TestTelemetryChannel.Clear(); - using ( var host = new HostingContext() ) + using (var host = new HostingContext()) { host.Open(); var binding = new NetTcpBinding(); - //binding.TransferMode = TransferMode.Streamed; var configuration = new TelemetryConfiguration(); var factory = new ChannelFactory(binding, host.GetServiceAddress()); ISimpleService channel = null; @@ -82,18 +81,21 @@ public void RequestReply_TelemetryIsWritten() factory.Close(); Assert.IsTrue(TestTelemetryChannel.CollectedData().Count > 0, "No telemetry events written"); - } catch + } + catch { - if ( channel != null ) + if (channel != null) { ((IClientChannel)channel).Abort(); } + factory.Abort(); throw; } } } - private System.ServiceModel.Channels.IChannel GetInnerChannel(object proxy) + + private static System.ServiceModel.Channels.IChannel GetInnerChannel(object proxy) { // TransparentProxy -> ServiceChannelProxy -> ServiceChannel var realProxy = RemotingServices.GetRealProxy(proxy); diff --git a/WCF/Shared.Tests/ClientTelemetryExtensionElementTestscs.cs b/WCF/Shared.Tests/ClientTelemetryExtensionElementTestscs.cs index 9493656..d62126c 100644 --- a/WCF/Shared.Tests/ClientTelemetryExtensionElementTestscs.cs +++ b/WCF/Shared.Tests/ClientTelemetryExtensionElementTestscs.cs @@ -1,11 +1,11 @@ -using Microsoft.ApplicationInsights.Wcf.Implementation; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Configuration; -using System.Linq; - -namespace Microsoft.ApplicationInsights.Wcf.Tests +namespace Microsoft.ApplicationInsights.Wcf.Tests { + using System; + using System.Configuration; + using System.Linq; + using Microsoft.ApplicationInsights.Wcf.Implementation; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] public class ClientTelemetryExtensionElementTestscs { diff --git a/WCF/Shared.Tests/ClientTelemetryOutputChannelTests.cs b/WCF/Shared.Tests/ClientTelemetryOutputChannelTests.cs index b355e12..525d102 100644 --- a/WCF/Shared.Tests/ClientTelemetryOutputChannelTests.cs +++ b/WCF/Shared.Tests/ClientTelemetryOutputChannelTests.cs @@ -1,36 +1,19 @@ -using Microsoft.ApplicationInsights.DataContracts; -using Microsoft.ApplicationInsights.Wcf.Implementation; -using Microsoft.ApplicationInsights.Wcf.Tests.Channels; -using Microsoft.ApplicationInsights.Wcf.Tests.Integration; -using Microsoft.ApplicationInsights.Wcf.Tests.Service; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Linq; -using System.ServiceModel; -using System.ServiceModel.Channels; - -namespace Microsoft.ApplicationInsights.Wcf.Tests +namespace Microsoft.ApplicationInsights.Wcf.Tests { + using System; + using System.Linq; + using System.ServiceModel.Channels; + using Microsoft.ApplicationInsights.DataContracts; + using Microsoft.ApplicationInsights.Wcf.Implementation; + using Microsoft.ApplicationInsights.Wcf.Tests.Channels; + using Microsoft.ApplicationInsights.Wcf.Tests.Integration; + using Microsoft.ApplicationInsights.Wcf.Tests.Service; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] public class ClientTelemetryOutputChannelTests : ChannelTestBase { - const String OneWayOp1 = "http://tempuri.org/IOneWayService/SuccessfullOneWayCall"; - - private IOutputChannel GetChannel(IChannel innerChannel, Type contract, TelemetryClient client) - { - return GetChannel( - new ClientChannelManager(client ?? new TelemetryClient(), contract), - innerChannel - ); - } - internal override IOutputChannel GetChannel(IChannel innerChannel, Type contract) - { - return GetChannel(innerChannel, contract, null); - } - internal override IOutputChannel GetChannel(IChannelManager manager, IChannel innerChannel) - { - return new ClientTelemetryOutputChannel(manager, innerChannel); - } + private const string OneWayOp1 = "http://tempuri.org/IOneWayService/SuccessfullOneWayCall"; [TestMethod] [TestCategory("Client")] @@ -38,7 +21,7 @@ public void WhenMessageIsSent_TelemetryIsWritten() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(IOneWayService)); + var channel = this.GetChannel(innerChannel, typeof(IOneWayService)); channel.Send(BuildMessage(OneWayOp1)); @@ -51,7 +34,7 @@ public void WhenMessageIsSentWithTimeout_TelemetryIsWritten() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(IOneWayService)); + var channel = this.GetChannel(innerChannel, typeof(IOneWayService)); channel.Send(BuildMessage(OneWayOp1), TimeSpan.FromSeconds(10)); @@ -64,7 +47,7 @@ public void WhenMessageIsSent_TelemetryIsWritten_Exception() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(IOneWayService)); + var channel = this.GetChannel(innerChannel, typeof(IOneWayService)); innerChannel.FailRequest = true; @@ -72,10 +55,12 @@ public void WhenMessageIsSent_TelemetryIsWritten_Exception() try { channel.Send(BuildMessage(OneWayOp1)); - } catch + } + catch { failed = true; } + Assert.IsTrue(failed, "Send did not throw an exception"); CheckOpDependencyWritten(DependencyConstants.WcfClientCall, typeof(IOneWayService), OneWayOp1, "SuccessfullOneWayCall", false); @@ -87,7 +72,7 @@ public void WhenMessageIsSent_WithTimeoutException_TelemetryHasResultCode() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(IOneWayService)); + var channel = this.GetChannel(innerChannel, typeof(IOneWayService)); innerChannel.FailRequest = true; innerChannel.ExceptionToThrowOnSend = new TimeoutException(); @@ -96,10 +81,12 @@ public void WhenMessageIsSent_WithTimeoutException_TelemetryHasResultCode() try { channel.Send(BuildMessage(OneWayOp1)); - } catch ( TimeoutException ) + } + catch (TimeoutException) { failed = true; } + Assert.IsTrue(failed, "Send did not throw TimeoutException"); var telemetry = TestTelemetryChannel.CollectedData().OfType().First(); @@ -112,7 +99,7 @@ public void WhenMessageIsSent_Async_TelemetryIsWritten() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(IOneWayService)); + var channel = this.GetChannel(innerChannel, typeof(IOneWayService)); var result = channel.BeginSend(BuildMessage(OneWayOp1), null, null); channel.EndSend(result); @@ -126,7 +113,7 @@ public void WhenMessageIsSentWithTimeout_Async_TelemetryIsWritten() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(IOneWayService)); + var channel = this.GetChannel(innerChannel, typeof(IOneWayService)); var result = channel.BeginSend(BuildMessage(OneWayOp1), TimeSpan.FromSeconds(10), null, null); channel.EndSend(result); @@ -140,7 +127,7 @@ public void WhenMessageIsSent_Async_TelemetryIsWritten_ExceptionOnEnd() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(IOneWayService)); + var channel = this.GetChannel(innerChannel, typeof(IOneWayService)); innerChannel.FailEndRequest = true; @@ -149,19 +136,20 @@ public void WhenMessageIsSent_Async_TelemetryIsWritten_ExceptionOnEnd() try { channel.EndSend(result); - } catch + } + catch { failed = true; } + Assert.IsTrue(failed, "EndSend did not throw an exception"); CheckOpDependencyWritten(DependencyConstants.WcfClientCall, typeof(IOneWayService), OneWayOp1, "SuccessfullOneWayCall", false); } - - // + // ----------------------- // Other tests - // + // ----------------------- [TestMethod] [TestCategory("Client")] public void WhenMessageIsSent_CorrelationHeadersAreSet() @@ -171,7 +159,7 @@ public void WhenMessageIsSent_CorrelationHeadersAreSet() var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(IOneWayService), client); + var channel = this.GetChannel(innerChannel, typeof(IOneWayService), client); channel.Send(BuildMessage(OneWayOp1)); @@ -183,27 +171,37 @@ public void WhenMessageIsSent_CorrelationHeadersAreSet() Assert.IsNotNull(GetSoapHeader(message, CorrelationHeaders.SoapStandardNamespace, CorrelationHeaders.SoapStandardParentIdHeader)); } - private String GetSoapHeader(Message message, String ns, String headerName) + internal override IOutputChannel GetChannel(IChannel innerChannel, Type contract) { - return message.Headers.GetHeader(headerName, ns); + return this.GetChannel(innerChannel, contract, null); + } + + internal override IOutputChannel GetChannel(IChannelManager manager, IChannel innerChannel) + { + return new ClientTelemetryOutputChannel(manager, innerChannel); } - private String GetHttpHeader(Message message, String headerName) + private static string GetSoapHeader(Message message, string ns, string headerName) + { + return message.Headers.GetHeader(headerName, ns); + } + + private static string GetHttpHeader(Message message, string headerName) { var httpHeaders = message.GetHttpRequestHeaders(); return httpHeaders.Headers[headerName]; } - private Message BuildMessage(String action) + private static Message BuildMessage(string action) { return Message.CreateMessage(MessageVersion.Default, action, ""); } - private void CheckOpDependencyWritten(String type, Type contract, String action, String method, bool success) + private static void CheckOpDependencyWritten(string type, Type contract, string action, string method, bool success) { var dependency = TestTelemetryChannel.CollectedData().OfType().FirstOrDefault(); Assert.IsNotNull(dependency, "Did not write dependency event"); - Assert.AreEqual(SvcUrl, dependency.Data); + Assert.AreEqual(ClientTelemetryOutputChannelTests.SvcUrl, dependency.Data); Assert.AreEqual("localhost", dependency.Target); Assert.AreEqual(type, dependency.Type); Assert.AreEqual(contract.Name + "." + method, dependency.Name); @@ -211,5 +209,12 @@ private void CheckOpDependencyWritten(String type, Type contract, String action, Assert.AreEqual("True", dependency.Properties["isOneWay"]); Assert.AreEqual(success, dependency.Success.Value); } + + private IOutputChannel GetChannel(IChannel innerChannel, Type contract, TelemetryClient client) + { + return this.GetChannel( + new ClientChannelManager(client ?? new TelemetryClient(), contract), + innerChannel); + } } } diff --git a/WCF/Shared.Tests/ClientTelemetryRequestChannelTests.cs b/WCF/Shared.Tests/ClientTelemetryRequestChannelTests.cs index 088dfc1..ce4f58e 100644 --- a/WCF/Shared.Tests/ClientTelemetryRequestChannelTests.cs +++ b/WCF/Shared.Tests/ClientTelemetryRequestChannelTests.cs @@ -1,45 +1,32 @@ -using Microsoft.ApplicationInsights.DataContracts; -using Microsoft.ApplicationInsights.Wcf.Implementation; -using Microsoft.ApplicationInsights.Wcf.Tests.Channels; -using Microsoft.ApplicationInsights.Wcf.Tests.Integration; -using Microsoft.ApplicationInsights.Wcf.Tests.Service; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Linq; -using System.ServiceModel.Channels; - -namespace Microsoft.ApplicationInsights.Wcf.Tests +namespace Microsoft.ApplicationInsights.Wcf.Tests { + using System; + using System.Linq; + using System.ServiceModel.Channels; + using Microsoft.ApplicationInsights.DataContracts; + using Microsoft.ApplicationInsights.Wcf.Implementation; + using Microsoft.ApplicationInsights.Wcf.Tests.Channels; + using Microsoft.ApplicationInsights.Wcf.Tests.Integration; + using Microsoft.ApplicationInsights.Wcf.Tests.Service; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] - public class ClientTelemetryRequestChannelTests : ChannelTestBase + public class ClientTelemetryRequestChannelTests : ChannelTestBase { - const String TwoWayOp1 = "http://tempuri.org/ISimpleService/GetSimpleData"; - const String TwoWayOp2 = "http://tempuri.org/ISimpleService/CallFailsWithFault"; - const String OneWayOp1 = "http://tempuri.org/IOneWayService/SuccessfullOneWayCall"; - - - internal override IRequestChannel GetChannel(IChannel innerChannel, Type contract) - { - return new ClientTelemetryRequestChannel( - new ClientChannelManager(new TelemetryClient(), contract), - innerChannel - ); - } - internal override IRequestChannel GetChannel(IChannelManager manager, IChannel innerChannel) - { - return new ClientTelemetryRequestChannel(manager, innerChannel); - } + private const string TwoWayOp1 = "http://tempuri.org/ISimpleService/GetSimpleData"; + private const string TwoWayOp2 = "http://tempuri.org/ISimpleService/CallFailsWithFault"; + private const string OneWayOp1 = "http://tempuri.org/IOneWayService/SuccessfullOneWayCall"; - // + // ---------------------------------- // Request Telemetry - // + // ---------------------------------- [TestMethod] [TestCategory("Client")] public void WhenRequestIsSent_TelemetryIsWritten() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); var response = channel.Request(BuildMessage(TwoWayOp1)); @@ -52,7 +39,7 @@ public void WhenRequestIsSentWithTimeout_TelemetryIsWritten() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); var response = channel.Request(BuildMessage(TwoWayOp1), TimeSpan.FromSeconds(10)); @@ -65,7 +52,7 @@ public void WhenRequestIsSent_TelemetryIsWritten_SoapFault() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); innerChannel.ReturnSoapFault = true; @@ -81,7 +68,7 @@ public void WhenRequestIsSent_TelemetryIsWritten_Exception() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); innerChannel.FailRequest = true; @@ -89,9 +76,12 @@ public void WhenRequestIsSent_TelemetryIsWritten_Exception() try { var response = channel.Request(BuildMessage(TwoWayOp1)); - } catch { + } + catch + { failed = true; } + Assert.IsTrue(failed, "Request did not throw an exception"); CheckOpDependencyWritten(DependencyConstants.WcfClientCall, typeof(ISimpleService), TwoWayOp1, "GetSimpleData", false); } @@ -102,7 +92,7 @@ public void WhenRequestIsSentWithTimeout_TelemetryIsWritten_Exception() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); innerChannel.FailRequest = true; @@ -110,9 +100,12 @@ public void WhenRequestIsSentWithTimeout_TelemetryIsWritten_Exception() try { var response = channel.Request(BuildMessage(TwoWayOp1), TimeSpan.FromSeconds(10)); - } catch { + } + catch + { failed = true; } + Assert.IsTrue(failed, "Request did not throw an exception"); CheckOpDependencyWritten(DependencyConstants.WcfClientCall, typeof(ISimpleService), TwoWayOp1, "GetSimpleData", false); } @@ -123,7 +116,7 @@ public void WhenRequestIsSent_Async_TelemetryIsWritten() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); var result = channel.BeginRequest(BuildMessage(TwoWayOp1), null, null); var response = channel.EndRequest(result); @@ -137,7 +130,7 @@ public void WhenRequestIsSent_AsyncWithTimeout_TelemetryIsWritten() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); var result = channel.BeginRequest(BuildMessage(TwoWayOp1), TimeSpan.FromSeconds(10), null, null); var response = channel.EndRequest(result); @@ -151,7 +144,7 @@ public void WhenRequestIsSent_Async_TelemetryIsWritten_Exception() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); innerChannel.FailRequest = true; @@ -159,11 +152,12 @@ public void WhenRequestIsSent_Async_TelemetryIsWritten_Exception() try { var result = channel.BeginRequest(BuildMessage(TwoWayOp1), null, null); - - } catch + } + catch { failed = true; } + Assert.IsTrue(failed, "BeginRequest did not throw an exception"); CheckOpDependencyWritten(DependencyConstants.WcfClientCall, typeof(ISimpleService), TwoWayOp1, "GetSimpleData", false); @@ -175,7 +169,7 @@ public void WhenRequestIsSent_AsyncWithTimeout_TelemetryIsWritten_Exception() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); innerChannel.FailRequest = true; @@ -183,11 +177,12 @@ public void WhenRequestIsSent_AsyncWithTimeout_TelemetryIsWritten_Exception() try { var result = channel.BeginRequest(BuildMessage(TwoWayOp1), TimeSpan.FromSeconds(10), null, null); - - } catch + } + catch { failed = true; } + Assert.IsTrue(failed, "BeginRequest did not throw an exception"); CheckOpDependencyWritten(DependencyConstants.WcfClientCall, typeof(ISimpleService), TwoWayOp1, "GetSimpleData", false); @@ -199,7 +194,7 @@ public void WhenRequestIsSent_Async_TelemetryIsWritten_ExceptionOnEnd() { var innerChannel = new MockClientChannel(SvcUrl); TestTelemetryChannel.Clear(); - var channel = GetChannel(innerChannel, typeof(ISimpleService)); + var channel = this.GetChannel(innerChannel, typeof(ISimpleService)); innerChannel.FailEndRequest = true; @@ -208,27 +203,39 @@ public void WhenRequestIsSent_Async_TelemetryIsWritten_ExceptionOnEnd() try { var response = channel.EndRequest(result); - } catch + } + catch { failed = true; } + Assert.IsTrue(failed, "EndRequest did not throw an exception"); CheckOpDependencyWritten(DependencyConstants.WcfClientCall, typeof(ISimpleService), TwoWayOp1, "GetSimpleData", false); } + internal override IRequestChannel GetChannel(IChannel innerChannel, Type contract) + { + return new ClientTelemetryRequestChannel( + new ClientChannelManager(new TelemetryClient(), contract), + innerChannel); + } + internal override IRequestChannel GetChannel(IChannelManager manager, IChannel innerChannel) + { + return new ClientTelemetryRequestChannel(manager, innerChannel); + } - private Message BuildMessage(String action) + private static Message BuildMessage(string action) { return Message.CreateMessage(MessageVersion.Default, action, ""); } - private DependencyTelemetry CheckOpDependencyWritten(String type, Type contract, String action, String method, bool success) + private static DependencyTelemetry CheckOpDependencyWritten(string type, Type contract, string action, string method, bool success) { var dependency = TestTelemetryChannel.CollectedData().OfType().FirstOrDefault(); Assert.IsNotNull(dependency, "Did not write dependency event"); - Assert.AreEqual(SvcUrl, dependency.Data); + Assert.AreEqual(ClientTelemetryRequestChannelTests.SvcUrl, dependency.Data); Assert.AreEqual("localhost", dependency.Target); Assert.AreEqual(type, dependency.Type); Assert.AreEqual(contract.Name + "." + method, dependency.Name); diff --git a/WCF/Shared.Tests/ContractDescriptionBuilder.cs b/WCF/Shared.Tests/ContractDescriptionBuilder.cs index 687cef8..46dd788 100644 --- a/WCF/Shared.Tests/ContractDescriptionBuilder.cs +++ b/WCF/Shared.Tests/ContractDescriptionBuilder.cs @@ -1,11 +1,11 @@ -using System; -using System.Globalization; -using System.Reflection; -using System.ServiceModel.Description; - -namespace Microsoft.ApplicationInsights.Wcf.Tests +namespace Microsoft.ApplicationInsights.Wcf.Tests { - class ContractBuilder + using System; + using System.Globalization; + using System.Reflection; + using System.ServiceModel.Description; + + internal class ContractBuilder { public static ContractDescription CreateDescription(Type contractType, Type serviceType) { @@ -16,10 +16,10 @@ public static ContractDescription CreateDescription(Type contractType, Type serv return (ContractDescription)typeLoaderType.InvokeMember( "LoadContractDescription", BindingFlags.InvokeMethod, - null, typeLoader, + null, + typeLoader, new object[] { contractType, serviceType }, - CultureInfo.InvariantCulture - ); + CultureInfo.InvariantCulture); } } } diff --git a/WCF/Shared.Tests/ContractFilterTests.cs b/WCF/Shared.Tests/ContractFilterTests.cs index 9d1cc3d..2851a3f 100644 --- a/WCF/Shared.Tests/ContractFilterTests.cs +++ b/WCF/Shared.Tests/ContractFilterTests.cs @@ -1,11 +1,11 @@ -using Microsoft.ApplicationInsights.Wcf.Implementation; -using Microsoft.ApplicationInsights.Wcf.Tests.Service; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.ServiceModel.Description; - -namespace Microsoft.ApplicationInsights.Wcf.Tests +namespace Microsoft.ApplicationInsights.Wcf.Tests { + using System; + using System.ServiceModel.Description; + using Microsoft.ApplicationInsights.Wcf.Implementation; + using Microsoft.ApplicationInsights.Wcf.Tests.Service; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] public class ContractFilterTests { @@ -13,26 +13,25 @@ public class ContractFilterTests public void WhenNoOpsInAnyContractAreInstrumentedShouldProcessReturnsTrue() { ContractDescription contract1 = ContractBuilder.CreateDescription( - typeof(ISimpleService), typeof(SimpleService) - ); + typeof(ISimpleService), typeof(SimpleService)); ContractDescription contract2 = ContractBuilder.CreateDescription( - typeof(ISimpleService2), typeof(SimpleService) - ); + typeof(ISimpleService2), typeof(SimpleService)); ContractFilter filter = new ContractFilter(new[] { contract1, contract2 }); - foreach ( var operation in contract1.Operations ) + foreach (var operation in contract1.Operations) { Assert.IsTrue( filter.ShouldProcess(contract1.Name, contract1.Namespace, operation.Name), - "Operation {0} not processed", operation.Name - ); + "Operation {0} not processed", + operation.Name); } - foreach ( var operation in contract2.Operations ) + + foreach (var operation in contract2.Operations) { Assert.IsTrue( filter.ShouldProcess(contract2.Name, contract2.Namespace, operation.Name), - "Operation {0} not processed", operation.Name - ); + "Operation {0} not processed", + operation.Name); } } @@ -40,44 +39,40 @@ public void WhenNoOpsInAnyContractAreInstrumentedShouldProcessReturnsTrue() public void WhenOpsInOneContractAreInstrumentedShouldProcessReturnsTrueForAllInstrumented() { ContractDescription contract1 = ContractBuilder.CreateDescription( - typeof(ISimpleService), typeof(SimpleService) - ); + typeof(ISimpleService), typeof(SimpleService)); ContractDescription contract2 = ContractBuilder.CreateDescription( - typeof(ISelectiveTelemetryService), typeof(SelectiveTelemetryService) - ); + typeof(ISelectiveTelemetryService), typeof(SelectiveTelemetryService)); // Only check contract 1 operations ContractFilter filter = new ContractFilter(new[] { contract1, contract2 }); - foreach ( var operation in contract1.Operations ) + foreach (var operation in contract1.Operations) { Assert.IsTrue( filter.ShouldProcess(contract1.Name, contract1.Namespace, operation.Name), - "Operation {0} not processed", operation.Name - ); + "Operation {0} not processed", + operation.Name); } // Check instrumented operation in second contract Assert.IsTrue( filter.ShouldProcess(contract2.Name, contract2.Namespace, "OperationWithTelemetry"), - "Operation {0} not processed", "OperationWithTelemetry" - ); - + "Operation {0} not processed", + "OperationWithTelemetry"); } [TestMethod] public void WhenOpsInOneContractAreInstrumentedShouldProcessReturnsFalseForAllInstrumented() { ContractDescription contract1 = ContractBuilder.CreateDescription( - typeof(ISimpleService), typeof(SimpleService) - ); + typeof(ISimpleService), typeof(SimpleService)); ContractDescription contract2 = ContractBuilder.CreateDescription( typeof(ISelectiveTelemetryService), typeof(SelectiveTelemetryService)); ContractFilter filter = new ContractFilter(new[] { contract1, contract2 }); Assert.IsFalse( filter.ShouldProcess(contract2.Name, contract2.Namespace, "OperationWithoutTelemetry"), - "Operation {0} is processed", "OperationWithoutTelemetry" - ); + "Operation {0} is processed", + "OperationWithoutTelemetry"); } } } diff --git a/WCF/Shared.Tests/Integration/AsyncStackTests.cs b/WCF/Shared.Tests/Integration/AsyncStackTests.cs index 2095fa1..9da97f4 100644 --- a/WCF/Shared.Tests/Integration/AsyncStackTests.cs +++ b/WCF/Shared.Tests/Integration/AsyncStackTests.cs @@ -1,12 +1,12 @@ -using Microsoft.ApplicationInsights.DataContracts; -using Microsoft.ApplicationInsights.Wcf.Tests.Channels; -using Microsoft.ApplicationInsights.Wcf.Tests.Service; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Linq; - -namespace Microsoft.ApplicationInsights.Wcf.Tests.Integration +namespace Microsoft.ApplicationInsights.Wcf.Tests.Integration { + using System; + using System.Linq; + using Microsoft.ApplicationInsights.DataContracts; + using Microsoft.ApplicationInsights.Wcf.Tests.Channels; + using Microsoft.ApplicationInsights.Wcf.Tests.Service; + using Microsoft.VisualStudio.TestTools.UnitTesting; + #if NET45 [TestClass] public class AsyncStackTests @@ -16,7 +16,7 @@ public class AsyncStackTests public void TelemetryEventsAreGeneratedOnAsyncCall() { TestTelemetryChannel.Clear(); - using ( var host = new HostingContext() ) + using (var host = new HostingContext()) { host.Open(); IAsyncService client = host.GetChannel(); @@ -32,17 +32,19 @@ public void ErrorTelemetryEventsAreGeneratedOnAsyncFault() TestTelemetryChannel.Clear(); var host = new HostingContext() .ShouldWaitForCompletion(); - using ( host ) + using (host) { host.Open(); IAsyncService client = host.GetChannel(); try { client.FailWithFaultAsync().Wait(); - } catch + } + catch { } } + var errors = from item in TestTelemetryChannel.CollectedData() where item is ExceptionTelemetry select item; @@ -56,17 +58,19 @@ public void ErrorTelemetryEventsContainAllDataOnAsyncCall() TestTelemetryChannel.Clear(); var host = new HostingContext() .ShouldWaitForCompletion(); - using ( host ) + using (host) { host.Open(); IAsyncService client = host.GetChannel(); try { client.FailWithFaultAsync().Wait(); - } catch + } + catch { } } + var error = (from item in TestTelemetryChannel.CollectedData() where item is ExceptionTelemetry select item).Cast().First(); @@ -82,17 +86,19 @@ public void ErrorTelemetryEventsAreGeneratedOnAsyncExceptionAndIEDIF_False() TestTelemetryChannel.Clear(); var host = new HostingContext() .ShouldWaitForCompletion(); - using ( host ) + using (host) { host.Open(); IAsyncService client = host.GetChannel(); try { client.FailWithExceptionAsync().Wait(); - } catch + } + catch { } } + var errors = from item in TestTelemetryChannel.CollectedData() where item is ExceptionTelemetry select item; @@ -107,17 +113,19 @@ public void ErrorTelemetryEventsAreGeneratedOnAsyncExceptionAndIEDIF_True() var host = new HostingContext() .ShouldWaitForCompletion() .IncludeDetailsInFaults(); - using ( host ) + using (host) { host.Open(); IAsyncService client = host.GetChannel(); try { client.FailWithExceptionAsync().Wait(); - } catch + } + catch { } } + var errors = from item in TestTelemetryChannel.CollectedData() where item is ExceptionTelemetry select item; @@ -129,12 +137,13 @@ public void ErrorTelemetryEventsAreGeneratedOnAsyncExceptionAndIEDIF_True() public void TelemetryContextIsFlowedAccrossAsyncCalls() { TestTelemetryChannel.Clear(); - using ( var host = new HostingContext() ) + using (var host = new HostingContext()) { host.Open(); IAsyncService client = host.GetChannel(); client.WriteDependencyEventAsync().Wait(); } + var data = TestTelemetryChannel.CollectedData(); var request = data .OfType() @@ -144,7 +153,6 @@ public void TelemetryContextIsFlowedAccrossAsyncCalls() .FirstOrDefault(); Assert.AreEqual(request.Context.Operation.Id, dependency.Context.Operation.Id); Assert.AreEqual(request.Context.Operation.Name, dependency.Context.Operation.Name); - } } #endif // NET45 diff --git a/WCF/Shared.Tests/Integration/HostingContext.cs b/WCF/Shared.Tests/Integration/HostingContext.cs index 97674fd..f37b692 100644 --- a/WCF/Shared.Tests/Integration/HostingContext.cs +++ b/WCF/Shared.Tests/Integration/HostingContext.cs @@ -1,13 +1,13 @@ -using System; -using System.Globalization; -using System.ServiceModel; -using System.ServiceModel.Channels; -using System.ServiceModel.Description; -using System.ServiceModel.Dispatcher; -using System.Threading; - -namespace Microsoft.ApplicationInsights.Wcf.Tests.Integration +namespace Microsoft.ApplicationInsights.Wcf.Tests.Integration { + using System; + using System.Globalization; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.ServiceModel.Description; + using System.ServiceModel.Dispatcher; + using System.Threading; + public sealed class HostingContext : IDisposable where TServiceImpl : TServiceIFace { @@ -22,34 +22,35 @@ public sealed class HostingContext : IDisposable public HostingContext() { - binding = new NetTcpBinding(); - baseAddress = new Uri(String.Format(CultureInfo.InvariantCulture, "net.tcp://localhost:57669/{0}/", Guid.NewGuid())); + this.binding = new NetTcpBinding(); + this.baseAddress = new Uri(string.Format(CultureInfo.InvariantCulture, "net.tcp://localhost:57669/{0}/", Guid.NewGuid())); - host = new ServiceHost(typeof(TServiceImpl), baseAddress); - endpoint = host.AddServiceEndpoint(typeof(TServiceIFace), binding, "svc"); + this.host = new ServiceHost(typeof(TServiceImpl), this.baseAddress); + this.endpoint = this.host.AddServiceEndpoint(typeof(TServiceIFace), this.binding, "svc"); } - public String GetServiceAddress() + public string GetServiceAddress() { - return new Uri(baseAddress, new Uri("svc", UriKind.Relative)).ToString(); // endpoint.Address.Uri.ToString(); + return new Uri(this.baseAddress, new Uri("svc", UriKind.Relative)).ToString(); // endpoint.Address.Uri.ToString(); } public HostingContext IncludeDetailsInFaults() { - ServiceDebugBehavior sdb = host.Description.Behaviors.Find(); - if ( sdb == null ) + var sdb = this.host.Description.Behaviors.Find(); + if (sdb == null) { sdb = new ServiceDebugBehavior(); - host.Description.Behaviors.Add(sdb); + this.host.Description.Behaviors.Add(sdb); } + sdb.IncludeExceptionDetailInFaults = true; return this; } public HostingContext ShouldWaitForCompletion() { - completionWait = new EventWaitHandle(false, EventResetMode.ManualReset); - endpoint.Behaviors.Add(new CompletionBehavior(completionWait)); + this.completionWait = new EventWaitHandle(false, EventResetMode.ManualReset); + this.endpoint.Behaviors.Add(new CompletionBehavior(this.completionWait)); return this; } @@ -61,55 +62,61 @@ public String GetServiceAddress() public void Open() { - host.Open(); + this.host.Open(); - channelFactory = new ChannelFactory(binding, GetServiceAddress()); - channelFactory.Open(); + this.channelFactory = new ChannelFactory(this.binding, this.GetServiceAddress()); + this.channelFactory.Open(); } public TServiceIFace GetChannel() { - if ( channel == null ) + if (this.channel == null) { - channel = channelFactory.CreateChannel(); + this.channel = this.channelFactory.CreateChannel(); } - return channel; + + return this.channel; } public void Dispose() { - if ( channel != null ) + if (this.channel != null) { - Dispose((ICommunicationObject)channel); - channel = default(TServiceIFace); + this.Dispose((ICommunicationObject)this.channel); + this.channel = default(TServiceIFace); } - if ( channelFactory != null ) + + if (this.channelFactory != null) { - Dispose(channelFactory); - channelFactory = null; + this.Dispose(this.channelFactory); + this.channelFactory = null; } - if ( host != null ) + + if (this.host != null) { - Dispose(host); - host = null; + this.Dispose(this.host); + this.host = null; } + // WCF calls IErrorHandler on a background there // by the time we've processed the reply and the // ServiceHost has closed, it might not have gotten // around to calling IErrorHander.HandleError() - - if ( completionWait != null ) - completionWait.WaitOne(); + if (this.completionWait != null) + { + this.completionWait.WaitOne(); + } } private void Dispose(ICommunicationObject obj) { - if ( expectFailure ) + if (this.expectFailure) { obj.Abort(); return; } - switch ( obj.State ) + + switch (obj.State) { case CommunicationState.Created: case CommunicationState.Opened: @@ -127,12 +134,12 @@ private class CompletionBehavior : IErrorHandler, IEndpointBehavior public CompletionBehavior(EventWaitHandle handle) { - completion = handle; + this.completion = handle; } public bool HandleError(Exception error) { - completion.Set(); + this.completion.Set(); return false; } @@ -157,6 +164,5 @@ public void Validate(ServiceEndpoint endpoint) { } } - } } diff --git a/WCF/Shared.Tests/Integration/MockClientChannel.cs b/WCF/Shared.Tests/Integration/MockClientChannel.cs index 7e5a50a..3281b66 100644 --- a/WCF/Shared.Tests/Integration/MockClientChannel.cs +++ b/WCF/Shared.Tests/Integration/MockClientChannel.cs @@ -1,15 +1,34 @@ -using System; -using System.Collections.Generic; -using System.ServiceModel; -using System.ServiceModel.Channels; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.ApplicationInsights.Wcf.Tests.Integration +namespace Microsoft.ApplicationInsights.Wcf.Tests.Integration { - class MockClientChannel : IRequestChannel, IOutputChannel, IDuplexChannel + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.Threading; + + internal class MockClientChannel : IRequestChannel, IOutputChannel, IDuplexChannel { + public MockClientChannel(string remoteUrl) + { + this.RemoteAddress = new EndpointAddress(remoteUrl); + this.ChangeState(CommunicationState.Created); + } + + public event EventHandler Closed; + + public event EventHandler Closing; + + public event EventHandler Faulted; + + public event EventHandler Opened; + + public event EventHandler Opening; + + public event EventHandler UnknownMessageReceived + { + add { } + remove { } + } + public EndpointAddress LocalAddress { get; private set; } public TimeSpan OperationTimeout { get; set; } @@ -22,141 +41,109 @@ class MockClientChannel : IRequestChannel, IOutputChannel, IDuplexChannel public Uri Via { get; private set; } - public event EventHandler Closed; - public event EventHandler Closing; - public event EventHandler Faulted; - public event EventHandler Opened; - public event EventHandler Opening; - public event EventHandler UnknownMessageReceived { add { } remove { } } - public bool FailOpen { get; set; } + public bool FailBeginOpen { get; set; } + public bool ReturnSoapFault { get; set; } + public bool FailRequest { get; set; } + public bool FailEndRequest { get; set; } + public Message LastMessageSent { get; private set; } - public Message MessageToReceive { get; set; } - public Exception ExceptionToThrowOnSend { get; set; } + public Message MessageToReceive { get; set; } - public MockClientChannel(String remoteUrl) - { - this.RemoteAddress = new EndpointAddress(remoteUrl); - ChangeState(CommunicationState.Created); - } + public Exception ExceptionToThrowOnSend { get; set; } public void Open() { - SimulateOpen(TimeSpan.MaxValue, FailOpen); - if ( FailOpen ) - { - throw new EndpointNotFoundException(); - } + this.Open(TimeSpan.MaxValue); } + public void Open(TimeSpan timeout) { - SimulateOpen(TimeSpan.Zero, FailOpen); - if ( FailOpen ) + this.SimulateOpen(TimeSpan.Zero, this.FailOpen); + if (this.FailOpen) { throw new EndpointNotFoundException(); } } + public void SimulateOpen(TimeSpan duration, bool fail) { - ChangeState(CommunicationState.Opening); - if ( !fail ) + this.ChangeState(CommunicationState.Opening); + if (!fail) { - ChangeState(CommunicationState.Opened); - } else + this.ChangeState(CommunicationState.Opened); + } + else { - ChangeState(CommunicationState.Faulted); + this.ChangeState(CommunicationState.Faulted); } } public void Close() { - Close(TimeSpan.MaxValue); + this.Close(TimeSpan.MaxValue); } public void Close(TimeSpan timeout) { - ChangeState(CommunicationState.Closing); - ChangeState(CommunicationState.Closed); + this.ChangeState(CommunicationState.Closing); + this.ChangeState(CommunicationState.Closed); } public void Abort() { - Close(); + this.Close(); } public void Dispose() { - Close(); + this.Close(); } public bool OpeningIsHooked() { - return Opening != null && Opening.GetInvocationList().Length > 0; + return this.Opening != null && this.Opening.GetInvocationList().Length > 0; } + public bool OpenedIsHooked() { - return Opened != null && Opened.GetInvocationList().Length > 0; + return this.Opened != null && this.Opened.GetInvocationList().Length > 0; } + public bool ClosingIsHooked() { - return Closing != null && Closing.GetInvocationList().Length > 0; + return this.Closing != null && this.Closing.GetInvocationList().Length > 0; } + public bool ClosedIsHooked() { - return Closed != null && Closed.GetInvocationList().Length > 0; - } - public bool FaultedIsHooked() - { - return Faulted != null && Faulted.GetInvocationList().Length > 0; + return this.Closed != null && this.Closed.GetInvocationList().Length > 0; } - private void ChangeState(CommunicationState state) + public bool FaultedIsHooked() { - this.State = state; - switch ( state ) - { - case CommunicationState.Opening: - Opening?.Invoke(this, EventArgs.Empty); - break; - case CommunicationState.Opened: - Opened?.Invoke(this, EventArgs.Empty); - break; - case CommunicationState.Closing: - Closing?.Invoke(this, EventArgs.Empty); - break; - case CommunicationState.Closed: - Closed?.Invoke(this, EventArgs.Empty); - break; - case CommunicationState.Faulted: - Faulted?.Invoke(this, EventArgs.Empty); - break; - } + return this.Faulted != null && this.Faulted.GetInvocationList().Length > 0; } public IAsyncResult BeginOpen(AsyncCallback callback, object state) { - ChangeState(CommunicationState.Opening); - if ( FailBeginOpen ) - { - ChangeState(CommunicationState.Faulted); - throw new EndpointNotFoundException(); - } - return new SyncAsyncResult(callback, state); + return this.BeginOpen(TimeSpan.MaxValue, callback, state); } public IAsyncResult BeginOpen(TimeSpan timeout, AsyncCallback callback, object state) { - ChangeState(CommunicationState.Opening); - if ( FailBeginOpen ) + this.ChangeState(CommunicationState.Opening); + if (this.FailBeginOpen) { - ChangeState(CommunicationState.Faulted); + this.ChangeState(CommunicationState.Faulted); throw new EndpointNotFoundException(); } + return new SyncAsyncResult(callback, state); } @@ -188,132 +175,119 @@ public void EndClose(IAsyncResult result) // request channel methods public Message Request(Message message) { - return Request(message, TimeSpan.FromSeconds(10)); + return this.Request(message, TimeSpan.FromSeconds(10)); } public Message Request(Message message, TimeSpan timeout) { - if ( FailRequest) + if (this.FailRequest) { throw new TimeoutException(); } - return BuildMessage(message.Headers.Action); + + return this.BuildMessage(message.Headers.Action); } public IAsyncResult BeginRequest(Message message, AsyncCallback callback, object state) { - return BeginRequest(message, TimeSpan.FromSeconds(10), callback, state); + return this.BeginRequest(message, TimeSpan.FromSeconds(10), callback, state); } public IAsyncResult BeginRequest(Message message, TimeSpan timeout, AsyncCallback callback, object state) { - LastMessageSent = message; - if ( FailRequest ) + this.LastMessageSent = message; + if (this.FailRequest) { - if ( ExceptionToThrowOnSend != null ) + if (this.ExceptionToThrowOnSend != null) { - throw ExceptionToThrowOnSend; + throw this.ExceptionToThrowOnSend; } + throw new TimeoutException(); } + return new SyncAsyncResult(callback, state); } public Message EndRequest(IAsyncResult result) { ((SyncAsyncResult)result).End(); - if ( FailEndRequest ) + if (this.FailEndRequest) { throw new TimeoutException(); } - return BuildMessage("*"); - } + return this.BuildMessage("*"); + } - // + // ----------------------------------- // Output Channel Impl - // + // ----------------------------------- public void Send(Message message) { - Send(message, TimeSpan.FromSeconds(10)); + this.Send(message, TimeSpan.FromSeconds(10)); } public void Send(Message message, TimeSpan timeout) { - LastMessageSent = message; - if ( FailRequest ) + this.LastMessageSent = message; + if (this.FailRequest) { - if ( ExceptionToThrowOnSend != null ) + if (this.ExceptionToThrowOnSend != null) { - throw ExceptionToThrowOnSend; + throw this.ExceptionToThrowOnSend; } + throw new TimeoutException(); } } public IAsyncResult BeginSend(Message message, AsyncCallback callback, object state) { - return BeginSend(message, TimeSpan.FromSeconds(10), callback, state); + return this.BeginSend(message, TimeSpan.FromSeconds(10), callback, state); } public IAsyncResult BeginSend(Message message, TimeSpan timeout, AsyncCallback callback, object state) { - LastMessageSent = message; - if ( FailRequest ) + this.LastMessageSent = message; + if (this.FailRequest) { throw new TimeoutException(); } + return new SyncAsyncResult(callback, state); } public void EndSend(IAsyncResult result) { - if ( FailEndRequest ) + if (this.FailEndRequest) { throw new TimeoutException(); } - ((SyncAsyncResult)result).End(); - } - - private Message BuildMessage(String action) - { - if ( ReturnSoapFault ) - { - return BuildFaultMessage(action); - } - return Message.CreateMessage(MessageVersion.Default, action, ""); - } - private Message BuildFaultMessage(String action) - { - return Message.CreateMessage( - MessageVersion.Default, - MessageFault.CreateFault( - FaultCode.CreateReceiverFaultCode("e1", "http://tempuri.org"), - "There was an error processing the message" - ), - action); + ((SyncAsyncResult)result).End(); } - // - // + // ------------------------------------------- // IDuplexChannel fields - // + // ------------------------------------------- public Message Receive() { - if ( MessageToReceive != null ) + if (this.MessageToReceive != null) { - return MessageToReceive; + return this.MessageToReceive; } + throw new TimeoutException(); } public Message Receive(TimeSpan timeout) { - if ( MessageToReceive != null ) + if (this.MessageToReceive != null) { - return MessageToReceive; + return this.MessageToReceive; } + throw new TimeoutException(); } @@ -330,16 +304,17 @@ public IAsyncResult BeginReceive(TimeSpan timeout, AsyncCallback callback, objec public Message EndReceive(IAsyncResult result) { ((SyncAsyncResult)result).End(); - if ( MessageToReceive != null ) + if (this.MessageToReceive != null) { - return MessageToReceive; + return this.MessageToReceive; } + throw new TimeoutException(); } public bool TryReceive(TimeSpan timeout, out Message message) { - message = MessageToReceive; + message = this.MessageToReceive; return message != null; } @@ -351,13 +326,13 @@ public IAsyncResult BeginTryReceive(TimeSpan timeout, AsyncCallback callback, ob public bool EndTryReceive(IAsyncResult result, out Message message) { ((SyncAsyncResult)result).End(); - message = MessageToReceive; + message = this.MessageToReceive; return message != null; } public bool WaitForMessage(TimeSpan timeout) { - return MessageToReceive != null; + return this.MessageToReceive != null; } public IAsyncResult BeginWaitForMessage(TimeSpan timeout, AsyncCallback callback, object state) @@ -368,54 +343,100 @@ public IAsyncResult BeginWaitForMessage(TimeSpan timeout, AsyncCallback callback public bool EndWaitForMessage(IAsyncResult result) { ((SyncAsyncResult)result).End(); - return MessageToReceive != null; + return this.MessageToReceive != null; } - class SyncAsyncResult : IAsyncResult, IDisposable + private void ChangeState(CommunicationState state) { - private EventWaitHandle waitHandle; - private AsyncCallback callback; - private Timer timer; - public object AsyncState { get; private set; } + this.State = state; + switch (state) + { + case CommunicationState.Opening: + this.Opening?.Invoke(this, EventArgs.Empty); + break; + case CommunicationState.Opened: + this.Opened?.Invoke(this, EventArgs.Empty); + break; + case CommunicationState.Closing: + this.Closing?.Invoke(this, EventArgs.Empty); + break; + case CommunicationState.Closed: + this.Closed?.Invoke(this, EventArgs.Empty); + break; + case CommunicationState.Faulted: + this.Faulted?.Invoke(this, EventArgs.Empty); + break; + } + } - public WaitHandle AsyncWaitHandle { get { return waitHandle; } } + private Message BuildMessage(string action) + { + if (this.ReturnSoapFault) + { + return this.BuildFaultMessage(action); + } - public bool CompletedSynchronously { get; private set; } + return Message.CreateMessage(MessageVersion.Default, action, ""); + } - public bool IsCompleted { get; private set; } + private Message BuildFaultMessage(string action) + { + var fault = MessageFault.CreateFault( + FaultCode.CreateReceiverFaultCode("e1", "http://tempuri.org"), + "There was an error processing the message"); + return Message.CreateMessage(MessageVersion.Default, fault, action); + } + + private class SyncAsyncResult : IAsyncResult, IDisposable + { + private EventWaitHandle waitHandle; + private AsyncCallback callback; + private Timer timer; public SyncAsyncResult(AsyncCallback callback, object state) { this.AsyncState = state; this.callback = callback; - waitHandle = new ManualResetEvent(false); - CompletedSynchronously = false; - IsCompleted = false; - timer = new Timer(this.OnDone, null, TimeSpan.FromMilliseconds(10), TimeSpan.FromMilliseconds(-1)); + this.waitHandle = new ManualResetEvent(false); + this.CompletedSynchronously = false; + this.IsCompleted = false; + this.timer = new Timer(this.OnDone, null, TimeSpan.FromMilliseconds(10), TimeSpan.FromMilliseconds(-1)); + } + + public object AsyncState { get; private set; } + + public WaitHandle AsyncWaitHandle + { + get { return this.waitHandle; } } + public bool CompletedSynchronously { get; private set; } + + public bool IsCompleted { get; private set; } + public void End() { this.waitHandle.WaitOne(); this.waitHandle.Close(); this.timer.Dispose(); } + + void IDisposable.Dispose() + { + this.End(); + } + private void Complete() { this.IsCompleted = true; this.waitHandle.Set(); - callback?.Invoke(this); + this.callback?.Invoke(this); } private void OnDone(object state) { this.Complete(); } - - void IDisposable.Dispose() - { - this.End(); - } } } } diff --git a/WCF/Shared.Tests/Integration/MultipleServiceCallsTests.cs b/WCF/Shared.Tests/Integration/MultipleServiceCallsTests.cs index 270e997..e9052ae 100644 --- a/WCF/Shared.Tests/Integration/MultipleServiceCallsTests.cs +++ b/WCF/Shared.Tests/Integration/MultipleServiceCallsTests.cs @@ -1,10 +1,10 @@ -using Microsoft.ApplicationInsights.Wcf.Tests.Channels; -using Microsoft.ApplicationInsights.Wcf.Tests.Service; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; - -namespace Microsoft.ApplicationInsights.Wcf.Tests.Integration +namespace Microsoft.ApplicationInsights.Wcf.Tests.Integration { + using System; + using Microsoft.ApplicationInsights.Wcf.Tests.Channels; + using Microsoft.ApplicationInsights.Wcf.Tests.Service; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] public class MultipleServiceCallsTests { @@ -13,8 +13,8 @@ public class MultipleServiceCallsTests public void OperationMethodThatCallsAnotherServiceDoesNotLoseOperationContext() { TestTelemetryChannel.Clear(); - using ( var host = new HostingContext() ) - using ( var hostSecond = new HostingContext() ) + using (var host = new HostingContext()) + using (var hostSecond = new HostingContext()) { host.IncludeDetailsInFaults(); host.Open(); @@ -24,6 +24,5 @@ public void OperationMethodThatCallsAnotherServiceDoesNotLoseOperationContext() Assert.IsTrue(TestTelemetryChannel.CollectedData().Count > 0); } } - } } diff --git a/WCF/Shared.Tests/Integration/OneWayTests.cs b/WCF/Shared.Tests/Integration/OneWayTests.cs index 02a2a20..3964f34 100644 --- a/WCF/Shared.Tests/Integration/OneWayTests.cs +++ b/WCF/Shared.Tests/Integration/OneWayTests.cs @@ -1,12 +1,12 @@ -using Microsoft.ApplicationInsights.DataContracts; -using Microsoft.ApplicationInsights.Wcf.Tests.Channels; -using Microsoft.ApplicationInsights.Wcf.Tests.Service; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Linq; - -namespace Microsoft.ApplicationInsights.Wcf.Tests.Integration +namespace Microsoft.ApplicationInsights.Wcf.Tests.Integration { + using System; + using System.Linq; + using Microsoft.ApplicationInsights.DataContracts; + using Microsoft.ApplicationInsights.Wcf.Tests.Channels; + using Microsoft.ApplicationInsights.Wcf.Tests.Service; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] public class OneWayTests { @@ -15,12 +15,13 @@ public class OneWayTests public void SuccessfulOneWayCallGeneratesRequestEvent() { TestTelemetryChannel.Clear(); - using ( var host = new HostingContext() ) + using (var host = new HostingContext()) { host.Open(); IOneWayService client = host.GetChannel(); client.SuccessfullOneWayCall(); } + var req = TestTelemetryChannel.CollectedData() .FirstOrDefault(x => x is RequestTelemetry); @@ -34,17 +35,19 @@ public void FailedOneWayCallGeneratesExceptionEvent() TestTelemetryChannel.Clear(); var host = new HostingContext() .ExpectFailure().ShouldWaitForCompletion(); - using ( host ) + using (host) { host.Open(); IOneWayService client = host.GetChannel(); try { client.FailureOneWayCall(); - } catch + } + catch { } } + var req = TestTelemetryChannel.CollectedData() .FirstOrDefault(x => x is ExceptionTelemetry); diff --git a/WCF/Shared.Tests/Integration/OperationContextExtensionsTests.cs b/WCF/Shared.Tests/Integration/OperationContextExtensionsTests.cs index 2eaaceb..834efca 100644 --- a/WCF/Shared.Tests/Integration/OperationContextExtensionsTests.cs +++ b/WCF/Shared.Tests/Integration/OperationContextExtensionsTests.cs @@ -1,12 +1,19 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.ServiceModel; - -namespace Microsoft.ApplicationInsights.Wcf.Tests.Integration +namespace Microsoft.ApplicationInsights.Wcf.Tests.Integration { + using System; + using System.ServiceModel; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] public class OperationContextExtensionsTests { + [ServiceContract] + public interface IContextCheckService + { + [OperationContract] + bool CanGetRequestTelemetry(); + } + [TestMethod] public void WhenOperationContextIsNullReturnsNull() { @@ -18,11 +25,11 @@ public void WhenOperationContextIsNullReturnsNull() [TestCategory("Integration")] public void WhenOperationContextIsClientContextReturnsNull() { - using ( var host = new HostingContext() ) + using (var host = new HostingContext()) { host.Open(); var client = host.GetChannel(); - using ( var scope = new OperationContextScope((IContextChannel)client) ) + using (var scope = new OperationContextScope((IContextChannel)client)) { Assert.IsNull(OperationContext.Current.GetRequestTelemetry()); } @@ -33,27 +40,19 @@ public void WhenOperationContextIsClientContextReturnsNull() [TestCategory("Integration")] public void WhenOperationContextIsServiceContextReturnsRequest() { - using ( var host = new HostingContext() ) + using (var host = new HostingContext()) { host.Open(); var client = host.GetChannel(); - using ( var scope = new OperationContextScope((IContextChannel)client) ) + using (var scope = new OperationContextScope((IContextChannel)client)) { Assert.IsTrue(client.CanGetRequestTelemetry()); } } } - - [ServiceContract] - public interface IContextCheckService - { - [OperationContract] - bool CanGetRequestTelemetry(); - } - [ServiceTelemetry] - class ContextCheckService : IContextCheckService + internal class ContextCheckService : IContextCheckService { public bool CanGetRequestTelemetry() { diff --git a/WCF/Shared.Tests/Integration/SyncStackTests.cs b/WCF/Shared.Tests/Integration/SyncStackTests.cs index 615002f..19be2a0 100644 --- a/WCF/Shared.Tests/Integration/SyncStackTests.cs +++ b/WCF/Shared.Tests/Integration/SyncStackTests.cs @@ -1,15 +1,15 @@ -using Microsoft.ApplicationInsights.DataContracts; -using Microsoft.ApplicationInsights.Wcf.Tests.Channels; -using Microsoft.ApplicationInsights.Wcf.Tests.Service; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Linq; -using System.ServiceModel; -using System.ServiceModel.Channels; -using System.Xml; - -namespace Microsoft.ApplicationInsights.Wcf.Tests.Integration +namespace Microsoft.ApplicationInsights.Wcf.Tests.Integration { + using System; + using System.Linq; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.Xml; + using Microsoft.ApplicationInsights.DataContracts; + using Microsoft.ApplicationInsights.Wcf.Tests.Channels; + using Microsoft.ApplicationInsights.Wcf.Tests.Service; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] public class FullStackTests { @@ -17,22 +17,23 @@ public class FullStackTests [TestCategory("Integration"), TestCategory("Sync")] public void IsClientSideContextReturnsTrueForClientChannel() { - using ( var host = new HostingContext() ) + using (var host = new HostingContext()) { host.Open(); ISimpleService client = host.GetChannel(); - using ( var scope = new OperationContextScope((IContextChannel)client) ) + using (var scope = new OperationContextScope((IContextChannel)client)) { Assert.IsTrue(OperationContext.Current.IsClientSideContext()); } } } + [TestMethod] [TestCategory("Integration"), TestCategory("Sync")] public void IsClientSideContextReturnsFalseForServerChannel() { - using ( var host = new HostingContext() ) + using (var host = new HostingContext()) { host.Open(); ISimpleService client = host.GetChannel(); @@ -46,7 +47,7 @@ public void IsClientSideContextReturnsFalseForServerChannel() public void TelemetryEventsAreGeneratedOnServiceCall() { TestTelemetryChannel.Clear(); - using ( var host = new HostingContext() ) + using (var host = new HostingContext()) { host.Open(); ISimpleService client = host.GetChannel(); @@ -60,12 +61,13 @@ public void TelemetryEventsAreGeneratedOnServiceCall() public void OperationNameIsSetBasedOnOperationCalled() { TestTelemetryChannel.Clear(); - using ( var host = new HostingContext() ) + using (var host = new HostingContext()) { host.Open(); ISimpleService client = host.GetChannel(); client.GetSimpleData(); } + var operationName = TestTelemetryChannel.CollectedData() .Select(x => x.Context.Operation.Name) .First(); @@ -78,12 +80,13 @@ public void OperationNameIsSetBasedOnOperationCalled() public void AllTelemetryEventsFromOneCallHaveSameOperationId() { TestTelemetryChannel.Clear(); - using ( var host = new HostingContext() ) + using (var host = new HostingContext()) { host.Open(); ISimpleService client = host.GetChannel(); client.GetSimpleData(); } + var ids = TestTelemetryChannel.CollectedData() .Select(x => x.Context.Operation.Id) .Distinct(); @@ -97,22 +100,22 @@ public void CallWithUnknownActionReportsCatchAllOperation() { TestTelemetryChannel.Clear(); var host = new HostingContext(); - using ( host ) + using (host) { host.Open(); ISimpleService client = host.GetChannel(); - using ( OperationContextScope scope = new OperationContextScope((IContextChannel)client) ) + using (OperationContextScope scope = new OperationContextScope((IContextChannel)client)) { OperationContext.Current.OutgoingMessageHeaders.Action = "http://someaction"; client.CatchAllOperation(); } } + var evt = TestTelemetryChannel.CollectedData().First(); Assert.AreEqual("ISimpleService.CatchAllOperation", evt.Context.Operation.Name); } - [TestMethod] [TestCategory("Integration"), TestCategory("Sync")] public void ErrorTelemetryEventsAreGeneratedOnFault() @@ -120,17 +123,19 @@ public void ErrorTelemetryEventsAreGeneratedOnFault() TestTelemetryChannel.Clear(); var host = new HostingContext() .ShouldWaitForCompletion(); - using ( host ) + using (host) { host.Open(); ISimpleService client = host.GetChannel(); try { client.CallFailsWithFault(); - } catch + } + catch { } } + var errors = from item in TestTelemetryChannel.CollectedData() where item is ExceptionTelemetry select item; @@ -144,17 +149,19 @@ public void ErrorTelemetryEventsContainDetailedInfo() TestTelemetryChannel.Clear(); var host = new HostingContext() .ShouldWaitForCompletion(); - using ( host ) + using (host) { host.Open(); ISimpleService client = host.GetChannel(); try { client.CallFailsWithFault(); - } catch + } + catch { } } + var error = (from item in TestTelemetryChannel.CollectedData() where item is ExceptionTelemetry select item).Cast().First(); @@ -170,17 +177,19 @@ public void ErrorTelemetryEventsContainDetailedInfoOnTypedFault() TestTelemetryChannel.Clear(); var host = new HostingContext() .ShouldWaitForCompletion(); - using ( host ) + using (host) { host.Open(); ISimpleService client = host.GetChannel(); try { client.CallFailsWithTypedFault(); - } catch + } + catch { } } + var error = (from item in TestTelemetryChannel.CollectedData() where item is ExceptionTelemetry select item).Cast().First(); @@ -189,7 +198,6 @@ public void ErrorTelemetryEventsContainDetailedInfoOnTypedFault() Assert.IsNotNull(error.Context.Operation.Name); } - [TestMethod] [TestCategory("Integration"), TestCategory("Sync")] public void ErrorTelemetryEventsAreGeneratedOnExceptionAndIEDIF_False() @@ -197,17 +205,19 @@ public void ErrorTelemetryEventsAreGeneratedOnExceptionAndIEDIF_False() TestTelemetryChannel.Clear(); var host = new HostingContext() .ShouldWaitForCompletion(); - using ( host ) + using (host) { host.Open(); ISimpleService client = host.GetChannel(); try { client.CallFailsWithException(); - } catch + } + catch { } } + var errors = from item in TestTelemetryChannel.CollectedData() where item is ExceptionTelemetry select item; @@ -222,7 +232,7 @@ public void ErrorTelemetryEventsAreGeneratedOnExceptionAndIEDIF_True() var host = new HostingContext() .ShouldWaitForCompletion() .IncludeDetailsInFaults(); - using ( host ) + using (host) { host.Open(); @@ -230,10 +240,12 @@ public void ErrorTelemetryEventsAreGeneratedOnExceptionAndIEDIF_True() try { client.CallFailsWithException(); - } catch + } + catch { } } + var errors = from item in TestTelemetryChannel.CollectedData() where item is ExceptionTelemetry select item; @@ -245,7 +257,7 @@ public void ErrorTelemetryEventsAreGeneratedOnExceptionAndIEDIF_True() public void TelemetryEventsEmittedInsideServiceCallContainExpectedContext() { TestTelemetryChannel.Clear(); - using ( var host = new HostingContext() ) + using (var host = new HostingContext()) { host.Open(); ISimpleService client = host.GetChannel(); @@ -264,20 +276,20 @@ public void TelemetryEventsEmittedInsideServiceCallContainExpectedContext() } } - [TestMethod] [TestCategory("Integration"), TestCategory("Sync")] public void ErrorTelemetryEventsWrittenFromMethodAreLogged() { TestTelemetryChannel.Clear(); var host = new HostingContext(); - using ( host ) + using (host) { host.Open(); ISimpleService client = host.GetChannel(); client.CallWritesExceptionEvent(); } + var request = TestTelemetryChannel.CollectedData().OfType().First(); var errors = from item in TestTelemetryChannel.CollectedData() where item is ExceptionTelemetry @@ -292,13 +304,14 @@ public void OperationMarksRequestAsFailedAndIsPropagated() { TestTelemetryChannel.Clear(); var host = new HostingContext(); - using ( host ) + using (host) { host.Open(); ISimpleService client = host.GetChannel(); client.CallMarksRequestAsFailed(); } + var request = TestTelemetryChannel.CollectedData().OfType().First(); Assert.IsFalse(request.Success.Value); } @@ -309,13 +322,14 @@ public void CallsToOpMarkedWithOperationTelemetryGeneratesEvents() { TestTelemetryChannel.Clear(); var host = new HostingContext(); - using ( host ) + using (host) { host.Open(); ISelectiveTelemetryService client = host.GetChannel(); client.OperationWithTelemetry(); } + Assert.IsTrue(TestTelemetryChannel.CollectedData().Count > 0); } @@ -325,13 +339,14 @@ public void CallsToOpWithoutOperationTelemetryGeneratesEvents() { TestTelemetryChannel.Clear(); var host = new HostingContext(); - using ( host ) + using (host) { host.Open(); ISelectiveTelemetryService client = host.GetChannel(); client.OperationWithoutTelemetry(); } + Assert.AreEqual(0, TestTelemetryChannel.CollectedData().Count); } @@ -341,12 +356,12 @@ public void CallCanFlowRootOperationId() { TestTelemetryChannel.Clear(); var host = new HostingContext(); - using ( host ) + using (host) { host.Open(); ISelectiveTelemetryService client = host.GetChannel(); - using ( var scope = new OperationContextScope((IContextChannel)client) ) + using (var scope = new OperationContextScope((IContextChannel)client)) { var rootId = new RootIdMessageHeader(); rootId.RootId = "rootId"; @@ -354,26 +369,28 @@ public void CallCanFlowRootOperationId() client.OperationWithTelemetry(); } } + Assert.AreEqual("rootId", TestTelemetryChannel.CollectedData().First().Context.Operation.Id); } public class RootIdMessageHeader : MessageHeader { - public override string Name { + public override string Name + { get { return "requestRootId"; } } - public override string Namespace { + public override string Namespace + { get { return "http://schemas.microsoft.com/application-insights"; } } - public String RootId { get; set; } + public string RootId { get; set; } protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion) { - writer.WriteString(RootId); + writer.WriteString(this.RootId); } } - } } diff --git a/WCF/Shared.Tests/Integration/TracingTests.cs b/WCF/Shared.Tests/Integration/TracingTests.cs index 19054ca..4531d88 100644 --- a/WCF/Shared.Tests/Integration/TracingTests.cs +++ b/WCF/Shared.Tests/Integration/TracingTests.cs @@ -1,13 +1,13 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Linq; -using Microsoft.ApplicationInsights.DataContracts; -using Microsoft.ApplicationInsights.Wcf.Tests.Channels; -using Microsoft.ApplicationInsights.Wcf.Tests.Service; -using System.Xml; - -namespace Microsoft.ApplicationInsights.Wcf.Tests.Integration +namespace Microsoft.ApplicationInsights.Wcf.Tests.Integration { + using System; + using System.Linq; + using System.Xml; + using Microsoft.ApplicationInsights.DataContracts; + using Microsoft.ApplicationInsights.Wcf.Tests.Channels; + using Microsoft.ApplicationInsights.Wcf.Tests.Service; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] public class TracingTests { @@ -19,23 +19,26 @@ public void RequestIsTraced() try { TestTelemetryChannel.Clear(); - using ( var host = new HostingContext() ) + using (var host = new HostingContext()) { host.Open(); ISimpleService client = host.GetChannel(); client.GetSimpleData(); } + var trace = TestTelemetryChannel.CollectedData() .OfType() .FirstOrDefault(x => x.Name == "WcfRequest"); Assert.IsNotNull(trace, "No WcfRequest trace found"); XmlDocument doc = new XmlDocument(); doc.LoadXml(trace.Properties["Body"]); - } finally + } + finally { TraceTelemetryModule.Disable(); } } + [TestMethod] [TestCategory("Integration"), TestCategory("MessageTracing")] public void ResponseIsTraced() @@ -44,19 +47,21 @@ public void ResponseIsTraced() try { TestTelemetryChannel.Clear(); - using ( var host = new HostingContext() ) + using (var host = new HostingContext()) { host.Open(); ISimpleService client = host.GetChannel(); client.GetSimpleData(); } + var trace = TestTelemetryChannel.CollectedData() .OfType() .FirstOrDefault(x => x.Name == "WcfResponse"); Assert.IsNotNull(trace, "No WcfResponse trace found"); XmlDocument doc = new XmlDocument(); doc.LoadXml(trace.Properties["Body"]); - } finally + } + finally { TraceTelemetryModule.Disable(); } diff --git a/WCF/Shared.Tests/MessageCorrelatorTests.cs b/WCF/Shared.Tests/MessageCorrelatorTests.cs index 2452e75..db893ad 100644 --- a/WCF/Shared.Tests/MessageCorrelatorTests.cs +++ b/WCF/Shared.Tests/MessageCorrelatorTests.cs @@ -1,12 +1,12 @@ -using Microsoft.ApplicationInsights.DataContracts; -using Microsoft.ApplicationInsights.Wcf.Implementation; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Threading; -using System.Xml; - -namespace Microsoft.ApplicationInsights.Wcf.Tests +namespace Microsoft.ApplicationInsights.Wcf.Tests { + using System; + using System.Threading; + using System.Xml; + using Microsoft.ApplicationInsights.DataContracts; + using Microsoft.ApplicationInsights.Wcf.Implementation; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] public class MessageCorrelatorTests { @@ -19,10 +19,12 @@ public void WhenAddIsCalledWithNullId_ArgumentNullExceptionIsThrown() try { correlator.Add(null, telemetry, TimeSpan.FromMilliseconds(100)); - } catch ( ArgumentNullException ) + } + catch (ArgumentNullException) { failed = true; } + Assert.IsTrue(failed, "ArgumentNullException was not thrown"); } @@ -34,14 +36,15 @@ public void WhenAddIsCalledWithNullTelemetry_ArgumentNullExceptionIsThrown() try { correlator.Add(new UniqueId(), null, TimeSpan.FromMilliseconds(100)); - } catch ( ArgumentNullException ) + } + catch (ArgumentNullException) { failed = true; } + Assert.IsTrue(failed, "ArgumentNullException was not thrown"); } - [TestMethod] public void WhenMessageIsAdded_TryLookupReturnsTrue() { @@ -84,8 +87,7 @@ public void WhenMessageIsAdded_AndNotRemoved_TimeoutCallbackIsFired() timeoutId = messageId; timeoutTelemetry = dependencyObj; timeoutEvent.Set(); - } - ); + }); var telemetry = new DependencyTelemetry(); var id = new UniqueId(); @@ -104,8 +106,7 @@ public void WhenMessageIsAdded_AndRemoved_TimeoutCallbackIsNotFired() (messageId, dependencyObj) => { timeoutEvent.Set(); - } - ); + }); var telemetry = new DependencyTelemetry(); // add and remove right away @@ -129,10 +130,12 @@ public void WhenDisposed_AddThrowsException() try { correlator.Add(id, new DependencyTelemetry(), TimeSpan.FromMilliseconds(100)); - } catch ( ObjectDisposedException ) + } + catch (ObjectDisposedException) { failed = true; } + Assert.IsTrue(failed, "Add did not throw ObjectDisposedException"); } } diff --git a/WCF/Shared.Tests/MockOperationContext.cs b/WCF/Shared.Tests/MockOperationContext.cs index e148e6a..025a5d8 100644 --- a/WCF/Shared.Tests/MockOperationContext.cs +++ b/WCF/Shared.Tests/MockOperationContext.cs @@ -1,35 +1,20 @@ -using Microsoft.ApplicationInsights.DataContracts; -using System; -using System.Collections.Generic; -using System.Linq; -using System.ServiceModel; -using System.ServiceModel.Channels; -using System.Text; -using System.Threading.Tasks; - -namespace Microsoft.ApplicationInsights.Wcf.Tests +namespace Microsoft.ApplicationInsights.Wcf.Tests { + using System; + using System.Collections.Generic; + using System.ServiceModel; + using System.ServiceModel.Channels; + using Microsoft.ApplicationInsights.DataContracts; + public class MockOperationContext : IOperationContext, IOperationContextState { - public IDictionary IncomingProperties { get; private set; } - public IDictionary OutgoingProperties { get; private set; } - public IDictionary IncomingHeaders { get; private set; } - private Dictionary stateDictionary; - public String OperationId { get { return Request.Id; } } - public RequestTelemetry Request { get; private set; } - public bool OwnsRequest { get; private set; } - public Uri EndpointUri { get; set; } - public Uri ToHeader { get; set; } - public String OperationName { get; set; } - public String ContractName { get; set; } - public String ContractNamespace { get; set; } - public ServiceSecurityContext SecurityContext { get; set; } + private Dictionary stateDictionary; public MockOperationContext() { - this.IncomingProperties = new Dictionary(); - this.IncomingHeaders = new Dictionary(); - this.OutgoingProperties = new Dictionary(); + this.IncomingProperties = new Dictionary(); + this.IncomingHeaders = new Dictionary(); + this.OutgoingProperties = new Dictionary(); this.ContractName = "IFakeService"; this.ContractNamespace = "urn:fake"; this.Request = new RequestTelemetry(); @@ -38,58 +23,89 @@ public MockOperationContext() this.stateDictionary = new Dictionary(); } + public IDictionary IncomingProperties { get; private set; } + + public IDictionary OutgoingProperties { get; private set; } + + public IDictionary IncomingHeaders { get; private set; } + + public string OperationId + { + get { return this.Request.Id; } + } + + public RequestTelemetry Request { get; private set; } + + public bool OwnsRequest { get; private set; } + + public Uri EndpointUri { get; set; } + + public Uri ToHeader { get; set; } + + public string OperationName { get; set; } + + public string ContractName { get; set; } + + public string ContractNamespace { get; set; } + + public ServiceSecurityContext SecurityContext { get; set; } + public bool HasIncomingMessageProperty(string propertyName) { - return IncomingProperties.ContainsKey(propertyName); + return this.IncomingProperties.ContainsKey(propertyName); } public object GetIncomingMessageProperty(string propertyName) { - return IncomingProperties[propertyName]; + return this.IncomingProperties[propertyName]; } - public bool HasOutgoingMessageProperty(String propertyName) + public bool HasOutgoingMessageProperty(string propertyName) { - return OutgoingProperties.ContainsKey(propertyName); + return this.OutgoingProperties.ContainsKey(propertyName); } - public object GetOutgoingMessageProperty(String propertyName) + + public object GetOutgoingMessageProperty(string propertyName) { - return OutgoingProperties[propertyName]; + return this.OutgoingProperties[propertyName]; } - public T GetIncomingMessageHeader(String name, String ns) + + public T GetIncomingMessageHeader(string name, string ns) { object value; - if ( IncomingHeaders.TryGetValue(ns + "#" + name, out value) ) + if (this.IncomingHeaders.TryGetValue(ns + "#" + name, out value)) { return (T)value; } + return default(T); } - public void AddIncomingMessageHeader(String name, String ns, T value) + public void AddIncomingMessageHeader(string name, string ns, T value) { - IncomingHeaders.Add(ns + "#" + name, value); + this.IncomingHeaders.Add(ns + "#" + name, value); } public void SetHttpHeaders(HttpRequestMessageProperty httpHeaders) { - IncomingProperties[HttpRequestMessageProperty.Name] = httpHeaders; + this.IncomingProperties[HttpRequestMessageProperty.Name] = httpHeaders; } public void SetState(string key, object value) { - stateDictionary.Add(key, value); + this.stateDictionary.Add(key, value); } public bool TryGetState(string key, out T value) { value = default(T); object obj = null; - if ( stateDictionary.TryGetValue(key, out obj) ) + if (this.stateDictionary.TryGetValue(key, out obj)) { value = (T)obj; return true; } + return false; } } diff --git a/WCF/Shared.Tests/OperationCorrelationTelemetryInitializerTests.cs b/WCF/Shared.Tests/OperationCorrelationTelemetryInitializerTests.cs index 54da39b..0ad84f9 100644 --- a/WCF/Shared.Tests/OperationCorrelationTelemetryInitializerTests.cs +++ b/WCF/Shared.Tests/OperationCorrelationTelemetryInitializerTests.cs @@ -1,9 +1,10 @@ -using Microsoft.ApplicationInsights.DataContracts; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.ServiceModel.Channels; - -namespace Microsoft.ApplicationInsights.Wcf.Tests +namespace Microsoft.ApplicationInsights.Wcf.Tests { + using System; + using System.ServiceModel.Channels; + using Microsoft.ApplicationInsights.DataContracts; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] public class OperationCorrelationTelemetryInitializerTests { @@ -125,6 +126,5 @@ public void InitializeDoNotMakeRequestAParentOfItself() Assert.AreEqual(null, requestTelemetry.Context.Operation.ParentId); Assert.AreEqual(requestTelemetry.Id, requestTelemetry.Context.Operation.Id); } - } } diff --git a/WCF/Shared.Tests/OperationFilterTests.cs b/WCF/Shared.Tests/OperationFilterTests.cs index 755b0b6..aa382a6 100644 --- a/WCF/Shared.Tests/OperationFilterTests.cs +++ b/WCF/Shared.Tests/OperationFilterTests.cs @@ -1,12 +1,11 @@ -using Microsoft.ApplicationInsights.Wcf.Tests.Service; -using Microsoft.ApplicationInsights.Wcf.Implementation; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Reflection; -using System.ServiceModel.Description; - -namespace Microsoft.ApplicationInsights.Wcf.Tests +namespace Microsoft.ApplicationInsights.Wcf.Tests { + using System; + using System.ServiceModel.Description; + using Microsoft.ApplicationInsights.Wcf.Implementation; + using Microsoft.ApplicationInsights.Wcf.Tests.Service; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] public class OperationFilterTests { @@ -14,16 +13,16 @@ public class OperationFilterTests public void WhenNoOpsAreInstrumentedShouldProcessReturnsTrue() { ContractDescription contract = ContractBuilder.CreateDescription( - typeof(ISimpleService), typeof(SimpleService) - ); + typeof(ISimpleService), + typeof(SimpleService)); var filter = new OperationFilter(contract); - foreach ( var operation in contract.Operations ) + foreach (var operation in contract.Operations) { Assert.IsTrue( filter.ShouldProcess(operation.Name), - "Operation {0} not processed", operation.Name - ); + "Operation {0} not processed", + operation.Name); } } @@ -31,29 +30,26 @@ public void WhenNoOpsAreInstrumentedShouldProcessReturnsTrue() public void WhenAnOpIsInstrumentedShouldProcessReturnsTrue() { ContractDescription contract = ContractBuilder.CreateDescription( - typeof(ISelectiveTelemetryService), typeof(SelectiveTelemetryService) - ); + typeof(ISelectiveTelemetryService), + typeof(SelectiveTelemetryService)); var filter = new OperationFilter(contract); Assert.IsTrue( filter.ShouldProcess("OperationWithTelemetry"), - "ShouldProcessRequest('OperationWithTelemetry') returned false" - ); + "ShouldProcessRequest('OperationWithTelemetry') returned false"); } [TestMethod] public void WhenAnOpIsNotBeenInstrumentedShouldProcessReturnsFalse() { ContractDescription contract = ContractBuilder.CreateDescription( - typeof(ISelectiveTelemetryService), typeof(SelectiveTelemetryService) - ); + typeof(ISelectiveTelemetryService), + typeof(SelectiveTelemetryService)); var filter = new OperationFilter(contract); Assert.IsFalse( filter.ShouldProcess("OperationWithoutTelemetry"), - "ShouldProcessRequest('OperationWithoutTelemetry') returned true" - ); + "ShouldProcessRequest('OperationWithoutTelemetry') returned true"); } - } } diff --git a/WCF/Shared.Tests/OperationNameTelemetryInitializerTests.cs b/WCF/Shared.Tests/OperationNameTelemetryInitializerTests.cs index 1ad0d08..01a0d8e 100644 --- a/WCF/Shared.Tests/OperationNameTelemetryInitializerTests.cs +++ b/WCF/Shared.Tests/OperationNameTelemetryInitializerTests.cs @@ -1,10 +1,10 @@ -using Microsoft.ApplicationInsights.DataContracts; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.ServiceModel.Channels; - -namespace Microsoft.ApplicationInsights.Wcf.Tests +namespace Microsoft.ApplicationInsights.Wcf.Tests { + using System; + using System.ServiceModel.Channels; + using Microsoft.ApplicationInsights.DataContracts; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] public class OperationNameTelemetryInitializerTests { @@ -19,7 +19,7 @@ public void NonHttpEndpoint() var telemetry = new RequestTelemetry(); initializer.Initialize(telemetry, context); - String name = telemetry.Context.Operation.Name; + string name = telemetry.Context.Operation.Name; Assert.AreEqual("IFakeService.GetData", name); } @@ -34,7 +34,7 @@ public void RequestNameEqualsOperationName() var telemetry = new RequestTelemetry(); initializer.Initialize(telemetry, context); - String name = telemetry.Context.Operation.Name; + string name = telemetry.Context.Operation.Name; Assert.AreEqual(name, telemetry.Name); } @@ -53,25 +53,25 @@ public void HttpEndpointDoesNotHaveMethodInName() var telemetry = new RequestTelemetry(); initializer.Initialize(telemetry, context); - String name = telemetry.Context.Operation.Name; + string name = telemetry.Context.Operation.Name; Assert.AreEqual(name, "IFakeService.GetData"); } [TestMethod] public void OperationNameIsCopiedFromRequestIfPresent() { - const String name = "MyOperationName"; + const string Name = "MyOperationName"; var context = new MockOperationContext(); context.EndpointUri = new Uri("net.tcp://localhost/Service1.svc"); context.OperationName = "GetData"; - context.Request.Context.Operation.Name = name; + context.Request.Context.Operation.Name = Name; var initializer = new OperationNameTelemetryInitializer(); var telemetry = new EventTelemetry(); initializer.Initialize(telemetry, context); - Assert.AreEqual(name, telemetry.Context.Operation.Name); + Assert.AreEqual(Name, telemetry.Context.Operation.Name); } } } diff --git a/WCF/Shared.Tests/ProfilerWcfClientProcessingTests.cs b/WCF/Shared.Tests/ProfilerWcfClientProcessingTests.cs index 343a235..43c1e9a 100644 --- a/WCF/Shared.Tests/ProfilerWcfClientProcessingTests.cs +++ b/WCF/Shared.Tests/ProfilerWcfClientProcessingTests.cs @@ -1,14 +1,14 @@ -using Microsoft.ApplicationInsights.Extensibility; -using Microsoft.ApplicationInsights.Wcf.Implementation; -using Microsoft.ApplicationInsights.Wcf.Tests.Service; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Linq; -using System.ServiceModel; -using System.ServiceModel.Description; - -namespace Microsoft.ApplicationInsights.Wcf.Tests +namespace Microsoft.ApplicationInsights.Wcf.Tests { + using System; + using System.Linq; + using System.ServiceModel; + using System.ServiceModel.Description; + using Microsoft.ApplicationInsights.Extensibility; + using Microsoft.ApplicationInsights.Wcf.Implementation; + using Microsoft.ApplicationInsights.Wcf.Tests.Service; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] public class ProfilerWcfClientProcessingTests { @@ -17,7 +17,7 @@ public void WhenInitializeEndpoint1IsCalled_BehaviorIsAdded() { ServiceEndpoint endpoint = CreateEndpoint(); endpoint.Address = new EndpointAddress("http://localhost/Service1.svc"); - using ( ChannelFactory factory = new ChannelFactory(endpoint) ) + using (ChannelFactory factory = new ChannelFactory(endpoint)) { var module = new WcfDependencyTrackingTelemetryModule(); module.Initialize(TelemetryConfiguration.Active); @@ -37,7 +37,7 @@ public void WhenInitializeEndpoint1IsCalled_AndBehaviorExists_BehaviorIsNotAdded endpoint.Address = new EndpointAddress("http://localhost/Service1.svc"); endpoint.Behaviors.Add(new ClientTelemetryEndpointBehavior(TelemetryConfiguration.Active)); - using ( ChannelFactory factory = new ChannelFactory(endpoint) ) + using (ChannelFactory factory = new ChannelFactory(endpoint)) { var module = new WcfDependencyTrackingTelemetryModule(); module.Initialize(TelemetryConfiguration.Active); @@ -55,7 +55,7 @@ public void WhenInitializeEndpoint2IsCalled_BehaviorIsAdded() { ServiceEndpoint endpoint = CreateEndpoint(); endpoint.Address = new EndpointAddress("http://localhost/Service1.svc"); - using ( ChannelFactory factory = new ChannelFactory(endpoint) ) + using (ChannelFactory factory = new ChannelFactory(endpoint)) { var module = new WcfDependencyTrackingTelemetryModule(); module.Initialize(TelemetryConfiguration.Active); @@ -73,7 +73,7 @@ public void WhenInitializeEndpoint3IsCalled_BehaviorIsAdded() { ServiceEndpoint endpoint = CreateEndpoint(); endpoint.Address = new EndpointAddress("http://localhost/Service1.svc"); - using ( ChannelFactory factory = new ChannelFactory(endpoint) ) + using (ChannelFactory factory = new ChannelFactory(endpoint)) { var module = new WcfDependencyTrackingTelemetryModule(); module.Initialize(TelemetryConfiguration.Active); @@ -86,7 +86,7 @@ public void WhenInitializeEndpoint3IsCalled_BehaviorIsAdded() } } - private ServiceEndpoint CreateEndpoint() + private static ServiceEndpoint CreateEndpoint() { var contractDescription = ContractBuilder.CreateDescription(typeof(ISimpleService), typeof(SimpleService)); return new ServiceEndpoint(contractDescription); diff --git a/WCF/Shared.Tests/Service/AsyncService.cs b/WCF/Shared.Tests/Service/AsyncService.cs index 48ed971..b799b19 100644 --- a/WCF/Shared.Tests/Service/AsyncService.cs +++ b/WCF/Shared.Tests/Service/AsyncService.cs @@ -1,44 +1,46 @@ -using System; -using System.IO; -using System.ServiceModel; -using System.Threading.Tasks; - -namespace Microsoft.ApplicationInsights.Wcf.Tests.Service +namespace Microsoft.ApplicationInsights.Wcf.Tests.Service { + using System; + using System.IO; + using System.ServiceModel; + using System.Threading.Tasks; + #if NET45 [ServiceTelemetry] public class AsyncService : IAsyncService { - public async Task GetDataAsync() + public async Task GetDataAsync() { await Task.Delay(200); return "Hello"; } - public async Task FailWithFaultAsync() + public async Task FailWithFaultAsync() { await Task.Delay(200); throw new FaultException("Call failed"); } - public async Task FailWithExceptionAsync() + public async Task FailWithExceptionAsync() { await Task.Delay(200); throw new InvalidOperationException(); } - public async Task WriteDependencyEventAsync() + + public async Task WriteDependencyEventAsync() { - String tempFile = Path.GetTempFileName(); + var tempFile = Path.GetTempFileName(); DateTimeOffset start = DateTimeOffset.Now; - using ( var file = File.CreateText(tempFile) ) + using (var file = File.CreateText(tempFile)) { - for ( int i=0; i < 10; i++ ) + for (int i = 0; i < 10; i++) { await file.WriteLineAsync("This is a line " + i); TelemetryClient client = new TelemetryClient(); client.TrackDependency("File", tempFile, start, TimeSpan.FromSeconds(1), true); } } + File.Delete(tempFile); return "Some value"; } diff --git a/WCF/Shared.Tests/Service/IAsyncService.cs b/WCF/Shared.Tests/Service/IAsyncService.cs index cc09016..a283efd 100644 --- a/WCF/Shared.Tests/Service/IAsyncService.cs +++ b/WCF/Shared.Tests/Service/IAsyncService.cs @@ -1,19 +1,19 @@ -using System; -using System.ServiceModel; -using System.Threading.Tasks; - -namespace Microsoft.ApplicationInsights.Wcf.Tests.Service +namespace Microsoft.ApplicationInsights.Wcf.Tests.Service { + using System; + using System.ServiceModel; + using System.Threading.Tasks; + [ServiceContract] public interface IAsyncService { [OperationContract] - Task GetDataAsync(); + Task GetDataAsync(); [OperationContract] - Task FailWithFaultAsync(); + Task FailWithFaultAsync(); [OperationContract] - Task FailWithExceptionAsync(); + Task FailWithExceptionAsync(); [OperationContract] - Task WriteDependencyEventAsync(); + Task WriteDependencyEventAsync(); } } diff --git a/WCF/Shared.Tests/Service/IOneWayService.cs b/WCF/Shared.Tests/Service/IOneWayService.cs index 30d7b89..ce4a2ea 100644 --- a/WCF/Shared.Tests/Service/IOneWayService.cs +++ b/WCF/Shared.Tests/Service/IOneWayService.cs @@ -1,13 +1,14 @@ -using System; -using System.ServiceModel; - -namespace Microsoft.ApplicationInsights.Wcf.Tests.Service +namespace Microsoft.ApplicationInsights.Wcf.Tests.Service { + using System; + using System.ServiceModel; + [ServiceContract] public interface IOneWayService { [OperationContract(IsOneWay = true)] void SuccessfullOneWayCall(); + [OperationContract(IsOneWay = true)] void FailureOneWayCall(); } diff --git a/WCF/Shared.Tests/Service/ISelectiveTelemetryService.cs b/WCF/Shared.Tests/Service/ISelectiveTelemetryService.cs index 3683023..56202f9 100644 --- a/WCF/Shared.Tests/Service/ISelectiveTelemetryService.cs +++ b/WCF/Shared.Tests/Service/ISelectiveTelemetryService.cs @@ -1,8 +1,8 @@ -using System; -using System.ServiceModel; - -namespace Microsoft.ApplicationInsights.Wcf.Tests.Service +namespace Microsoft.ApplicationInsights.Wcf.Tests.Service { + using System; + using System.ServiceModel; + [ServiceContract] public interface ISelectiveTelemetryService { diff --git a/WCF/Shared.Tests/Service/ISimpleService.cs b/WCF/Shared.Tests/Service/ISimpleService.cs index d739f1e..c788cb4 100644 --- a/WCF/Shared.Tests/Service/ISimpleService.cs +++ b/WCF/Shared.Tests/Service/ISimpleService.cs @@ -1,29 +1,38 @@ -using System; -using System.ServiceModel; - -namespace Microsoft.ApplicationInsights.Wcf.Tests.Service +namespace Microsoft.ApplicationInsights.Wcf.Tests.Service { + using System; + using System.ServiceModel; + [ServiceContract] public interface ISimpleService { [OperationContract] - String GetSimpleData(); + string GetSimpleData(); + [OperationContract] void CallFailsWithFault(); + [OperationContract] void CallFailsWithTypedFault(); + [OperationContract] void CallFailsWithException(); + [OperationContract] void CallWritesExceptionEvent(); + [OperationContract] void CallMarksRequestAsFailed(); - [OperationContract(Action="*")] + + [OperationContract(Action = "*")] void CatchAllOperation(); + [OperationContract] void CallThatEmitsEvent(); + [OperationContract] - void CallAnotherServiceAndLeakOperationContext(String address); + void CallAnotherServiceAndLeakOperationContext(string address); + [OperationContract] bool CallIsClientSideContext(); } diff --git a/WCF/Shared.Tests/Service/ISimpleService2.cs b/WCF/Shared.Tests/Service/ISimpleService2.cs index 8d133ad..212d1d6 100644 --- a/WCF/Shared.Tests/Service/ISimpleService2.cs +++ b/WCF/Shared.Tests/Service/ISimpleService2.cs @@ -1,8 +1,8 @@ -using System; -using System.ServiceModel; - -namespace Microsoft.ApplicationInsights.Wcf.Tests.Service +namespace Microsoft.ApplicationInsights.Wcf.Tests.Service { + using System; + using System.ServiceModel; + [ServiceContract] public interface ISimpleService2 { diff --git a/WCF/Shared.Tests/Service/OneWayService.cs b/WCF/Shared.Tests/Service/OneWayService.cs index 276ab67..837a9f0 100644 --- a/WCF/Shared.Tests/Service/OneWayService.cs +++ b/WCF/Shared.Tests/Service/OneWayService.cs @@ -1,13 +1,14 @@ -using System; - -namespace Microsoft.ApplicationInsights.Wcf.Tests.Service +namespace Microsoft.ApplicationInsights.Wcf.Tests.Service { + using System; + [ServiceTelemetry] public class OneWayService : IOneWayService { public void SuccessfullOneWayCall() { } + public void FailureOneWayCall() { throw new InvalidOperationException("Call failed"); diff --git a/WCF/Shared.Tests/Service/SelectiveTelemetryService.cs b/WCF/Shared.Tests/Service/SelectiveTelemetryService.cs index 5c7abac..99b5b09 100644 --- a/WCF/Shared.Tests/Service/SelectiveTelemetryService.cs +++ b/WCF/Shared.Tests/Service/SelectiveTelemetryService.cs @@ -1,7 +1,7 @@ -using System; - -namespace Microsoft.ApplicationInsights.Wcf.Tests.Service +namespace Microsoft.ApplicationInsights.Wcf.Tests.Service { + using System; + [ServiceTelemetry] public class SelectiveTelemetryService : ISelectiveTelemetryService { diff --git a/WCF/Shared.Tests/Service/SimpleService.cs b/WCF/Shared.Tests/Service/SimpleService.cs index 1a92c0d..5f3361d 100644 --- a/WCF/Shared.Tests/Service/SimpleService.cs +++ b/WCF/Shared.Tests/Service/SimpleService.cs @@ -1,46 +1,51 @@ -using System; -using System.ServiceModel; - -namespace Microsoft.ApplicationInsights.Wcf.Tests.Service +namespace Microsoft.ApplicationInsights.Wcf.Tests.Service { + using System; + using System.ServiceModel; + [ServiceTelemetry] public class SimpleService : ISimpleService, ISimpleService2 { - public String GetSimpleData() + public string GetSimpleData() { return "Hello world"; } - public String OtherGetSimpleData() + public string OtherGetSimpleData() { return "Hello World"; } + public void CallFailsWithFault() { throw new FaultException(); } + public void CallFailsWithTypedFault() { throw new FaultException( new TypedFault { Name = "Hello" }, - "Call failed with typed fault" - ); + "Call failed with typed fault"); } + public void CallFailsWithException() { throw new InvalidOperationException(); } + public void CallWritesExceptionEvent() { try { throw new InvalidOperationException("Some exception"); - } catch ( Exception ex ) + } + catch (Exception ex) { TelemetryClient client = new TelemetryClient(); client.TrackException(ex); } } + public void CallMarksRequestAsFailed() { var request = OperationContext.Current.GetRequestTelemetry(); @@ -54,27 +59,31 @@ public void SampleOperation() public void CatchAllOperation() { } + public void CallThatEmitsEvent() { TelemetryClient client = new TelemetryClient(); client.TrackEvent("MyCustomEvent"); } - public void CallAnotherServiceAndLeakOperationContext(String address) + public void CallAnotherServiceAndLeakOperationContext(string address) { var factory = new ChannelFactory(new NetTcpBinding(), new EndpointAddress(address)); var channel = factory.CreateChannel(); + // THIS IS INCORRECT CODE // The scope will be leaked, meaning that OperationContext.Current // will return the wrong scope later on. // We want to reproduce that behavior here so that it breaks // and we can check that the problem is fixed. var scope = new OperationContextScope((IContextChannel)channel); - //using ( scope ) + + // using ( scope ) { channel.GetSimpleData(); ((IClientChannel)channel).Close(); } + factory.Close(); } @@ -82,10 +91,10 @@ public bool CallIsClientSideContext() { return OperationContext.Current.IsClientSideContext(); } - } - public class TypedFault - { - public String Name { get; set; } + internal class TypedFault + { + public string Name { get; set; } + } } } diff --git a/WCF/Shared.Tests/SimpleAuthorizationContext.cs b/WCF/Shared.Tests/SimpleAuthorizationContext.cs index 84a8d97..af7af95 100644 --- a/WCF/Shared.Tests/SimpleAuthorizationContext.cs +++ b/WCF/Shared.Tests/SimpleAuthorizationContext.cs @@ -1,18 +1,26 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.IdentityModel.Claims; -using System.IdentityModel.Policy; -using System.Security.Principal; - -namespace Microsoft.ApplicationInsights.Wcf.Tests +namespace Microsoft.ApplicationInsights.Wcf.Tests { + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.IdentityModel.Claims; + using System.IdentityModel.Policy; + using System.Security.Principal; + public class SimpleAuthorizationContext : AuthorizationContext { private List claimSets; private DateTime expirationTime; - private String id; - private Dictionary properties; + private string id; + private Dictionary properties; + + public SimpleAuthorizationContext() + { + this.id = Guid.NewGuid().ToString(); + this.expirationTime = DateTime.Now.AddDays(1); + this.claimSets = new List(); + this.properties = new Dictionary(); + } public override ReadOnlyCollection ClaimSets { @@ -38,7 +46,7 @@ public override string Id } } - public override IDictionary Properties + public override IDictionary Properties { get { @@ -46,29 +54,22 @@ public override string Id } } - public SimpleAuthorizationContext() - { - this.id = Guid.NewGuid().ToString(); - this.expirationTime = DateTime.Now.AddDays(1); - this.claimSets = new List(); - this.properties = new Dictionary(); - } - internal void AddIdentity(IIdentity genericIdentity) { List identities = null; - if ( this.properties.ContainsKey("Identities") ) + if (this.properties.ContainsKey("Identities")) { identities = (List)this.properties["Identities"]; - } else + } + else { identities = new List(); this.properties["Identities"] = identities; } + identities.Add(genericIdentity); this.claimSets.Add(new DefaultClaimSet( - new Claim(ClaimTypes.Authentication, genericIdentity, Rights.Identity) - )); + new Claim(ClaimTypes.Authentication, genericIdentity, Rights.Identity))); } } } diff --git a/WCF/Shared.Tests/TraceTelemetryModule.cs b/WCF/Shared.Tests/TraceTelemetryModule.cs index 5a582d0..0ff5add 100644 --- a/WCF/Shared.Tests/TraceTelemetryModule.cs +++ b/WCF/Shared.Tests/TraceTelemetryModule.cs @@ -1,31 +1,32 @@ -using Microsoft.ApplicationInsights.DataContracts; -using Microsoft.ApplicationInsights.Extensibility; -using System; -using System.ServiceModel.Channels; - -namespace Microsoft.ApplicationInsights.Wcf.Tests +namespace Microsoft.ApplicationInsights.Wcf.Tests { + using System; + using System.ServiceModel.Channels; + using Microsoft.ApplicationInsights.DataContracts; + using Microsoft.ApplicationInsights.Extensibility; + public class TraceTelemetryModule : IWcfTelemetryModule, IWcfMessageTrace { private static bool enabled = false; private TelemetryClient client; - public void Initialize(TelemetryConfiguration configuration) - { - client = new TelemetryClient(configuration); - } - // needed to avoid breaking other tests // by tracking events that are not expected public static void Enable() { enabled = true; } + public static void Disable() { enabled = false; } + public void Initialize(TelemetryConfiguration configuration) + { + this.client = new TelemetryClient(configuration); + } + public void OnBeginRequest(IOperationContext operation) { } @@ -40,35 +41,36 @@ public void OnError(IOperationContext operation, Exception error) public void OnTraceRequest(IOperationContext operation, ref Message request) { - if ( enabled ) + if (enabled) { EventTelemetry ev = new EventTelemetry("WcfRequest"); ev.Properties.Add("Body", ReadMessageBody(ref request)); - client.TrackEvent(ev); + this.client.TrackEvent(ev); } } public void OnTraceResponse(IOperationContext operation, ref Message response) { - if ( enabled ) + if (enabled) { EventTelemetry ev = new EventTelemetry("WcfResponse"); ev.Properties.Add("Body", ReadMessageBody(ref response)); - client.TrackEvent(ev); + this.client.TrackEvent(ev); } } - private String ReadMessageBody(ref Message msg) + private static string ReadMessageBody(ref Message msg) { var buffer = msg.CreateBufferedCopy(int.MaxValue); - var result = ""; - using ( msg = buffer.CreateMessage() ) + var result = string.Empty; + using (msg = buffer.CreateMessage()) { - using ( var reader = msg.GetReaderAtBodyContents() ) + using (var reader = msg.GetReaderAtBodyContents()) { result = reader.ReadOuterXml(); } } + msg = buffer.CreateMessage(); return result; } diff --git a/WCF/Shared.Tests/UserAgentTelemetryInitializerTests.cs b/WCF/Shared.Tests/UserAgentTelemetryInitializerTests.cs index 9075b55..5266772 100644 --- a/WCF/Shared.Tests/UserAgentTelemetryInitializerTests.cs +++ b/WCF/Shared.Tests/UserAgentTelemetryInitializerTests.cs @@ -1,10 +1,10 @@ -using Microsoft.ApplicationInsights.DataContracts; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.ServiceModel.Channels; - -namespace Microsoft.ApplicationInsights.Wcf.Tests +namespace Microsoft.ApplicationInsights.Wcf.Tests { + using System; + using System.ServiceModel.Channels; + using Microsoft.ApplicationInsights.DataContracts; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] public class UserAgentTelemetryInitializerTests { @@ -25,6 +25,7 @@ public void ContextUserAgentIsSetIfPresent() Assert.AreEqual("MyUserAgent", telemetry.Context.User.UserAgent); } + [TestMethod] public void UserAgentIsCopiedFromRequestIfPresent() { diff --git a/WCF/Shared.Tests/UserTelemetryInitializerTests.cs b/WCF/Shared.Tests/UserTelemetryInitializerTests.cs index 22a0753..ee11dbc 100644 --- a/WCF/Shared.Tests/UserTelemetryInitializerTests.cs +++ b/WCF/Shared.Tests/UserTelemetryInitializerTests.cs @@ -1,11 +1,11 @@ -using Microsoft.ApplicationInsights.DataContracts; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Security.Principal; -using System.ServiceModel; - -namespace Microsoft.ApplicationInsights.Wcf.Tests +namespace Microsoft.ApplicationInsights.Wcf.Tests { + using System; + using System.Security.Principal; + using System.ServiceModel; + using Microsoft.ApplicationInsights.DataContracts; + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] public class UserTelemetryInitializerTests { @@ -47,18 +47,18 @@ public void AuthenticatedRequestFillsUserIdWithUserName() [TestMethod] public void UserIdCopiedFromRequestIfPresent() { - const String userName = "MyUserName"; + const string UserName = "MyUserName"; var context = new MockOperationContext(); context.EndpointUri = new Uri("http://localhost/Service1.svc"); context.OperationName = "GetData"; - context.Request.Context.User.Id = userName; + context.Request.Context.User.Id = UserName; var initializer = new UserTelemetryInitializer(); var telemetry = new EventTelemetry(); initializer.Initialize(telemetry, context); - Assert.AreEqual(userName, telemetry.Context.User.Id); + Assert.AreEqual(UserName, telemetry.Context.User.Id); } } } diff --git a/WCF/Shared.Tests/WcfEventSourceTests.cs b/WCF/Shared.Tests/WcfEventSourceTests.cs index 3316d90..153db09 100644 --- a/WCF/Shared.Tests/WcfEventSourceTests.cs +++ b/WCF/Shared.Tests/WcfEventSourceTests.cs @@ -1,177 +1,179 @@ -using Microsoft.ApplicationInsights.Wcf.Implementation; +namespace Microsoft.ApplicationInsights.Wcf.Tests +{ + using System; + using System.Collections.Generic; +#if !NET40 + using System.Diagnostics.Tracing; +#endif + using System.Globalization; + using System.Linq; + using Microsoft.ApplicationInsights.Wcf.Implementation; #if NET40 -using Microsoft.Diagnostics.Tracing; -#else -using System.Diagnostics.Tracing; + using Microsoft.Diagnostics.Tracing; #endif -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Linq; -using System.Collections.Generic; -using System.Text; -using System.Globalization; - -namespace Microsoft.ApplicationInsights.Wcf.Tests -{ + using Microsoft.VisualStudio.TestTools.UnitTesting; + [TestClass] public class WcfEventSourceTests { - private void CheckMessage(WcfEventListener listener, String format, params object[] args) - { - Assert.AreEqual(String.Format(CultureInfo.CurrentCulture, format, args), listener.FirstEventMessage); - - } [TestMethod] public void InitializationFailure_Message() { - using ( var listener = new WcfEventListener() ) + using (var listener = new WcfEventListener()) { var msg = "Exception message"; WcfEventSource.Log.InitializationFailure(msg); - CheckMessage(listener, WcfEventSource.InitializationFailure_Message, msg); + CheckMessage(listener, WcfEventSource.InitializationFailureMessage, msg); } } [TestMethod] public void TelemetryModuleExecutionStarted_Message() { - using ( var listener = new WcfEventListener() ) + using (var listener = new WcfEventListener()) { var typeName = "MyType"; var stageName = "MyStage"; WcfEventSource.Log.TelemetryModuleExecutionStarted(typeName, stageName); - CheckMessage(listener, WcfEventSource.TelemetryModuleExecutionStarted_Message, typeName, stageName); + CheckMessage(listener, WcfEventSource.TelemetryModuleExecutionStartedMessage, typeName, stageName); } } [TestMethod] public void TelemetryModuleExecutionStopped_Message() { - using ( var listener = new WcfEventListener() ) + using (var listener = new WcfEventListener()) { var typeName = "MyType"; var stageName = "MyStage"; WcfEventSource.Log.TelemetryModuleExecutionStopped(typeName, stageName); - CheckMessage(listener, WcfEventSource.TelemetryModuleExecutionStopped_Message, typeName, stageName); + CheckMessage(listener, WcfEventSource.TelemetryModuleExecutionStoppedMessage, typeName, stageName); } } [TestMethod] public void TelemetryModuleExecutionFailed_Message() { - using ( var listener = new WcfEventListener() ) + using (var listener = new WcfEventListener()) { var typeName = "MyType"; var stageName = "MyStage"; var exception = "MyException"; WcfEventSource.Log.TelemetryModuleExecutionFailed(typeName, stageName, exception); - CheckMessage(listener, WcfEventSource.TelemetryModuleExecutionFailed_Message, typeName, stageName, exception); + CheckMessage(listener, WcfEventSource.TelemetryModuleExecutionFailedMessage, typeName, stageName, exception); } } [TestMethod] public void NoOperationContextFound_Message() { - using ( var listener = new WcfEventListener() ) + using (var listener = new WcfEventListener()) { WcfEventSource.Log.NoOperationContextFound(); - CheckMessage(listener, WcfEventSource.NoOperationContextFound_Message); + CheckMessage(listener, WcfEventSource.NoOperationContextFoundMessage); } } [TestMethod] public void OperationIgnored_Message() { - using ( var listener = new WcfEventListener() ) + using (var listener = new WcfEventListener()) { var contractName = "MyContract"; var contractNamespace = "MyNS"; var operationName = "MyOperation"; WcfEventSource.Log.OperationIgnored(contractName, contractNamespace, operationName); - CheckMessage(listener, WcfEventSource.OperationIgnored_Message, contractName, contractNamespace, operationName); + CheckMessage(listener, WcfEventSource.OperationIgnoredMessage, contractName, contractNamespace, operationName); } } [TestMethod] public void WcfTelemetryInitializerLoaded_Message() { - using ( var listener = new WcfEventListener() ) + using (var listener = new WcfEventListener()) { var typeName = "MyType"; WcfEventSource.Log.WcfTelemetryInitializerLoaded(typeName); - CheckMessage(listener, WcfEventSource.WcfTelemetryInitializerLoaded_Message, typeName); + CheckMessage(listener, WcfEventSource.WcfTelemetryInitializerLoadedMessage, typeName); } } [TestMethod] public void LocationIdSet_Message() { - using ( var listener = new WcfEventListener() ) + using (var listener = new WcfEventListener()) { var ip = "10.0.0.1"; WcfEventSource.Log.LocationIdSet(ip); - CheckMessage(listener, WcfEventSource.LocationIdSet_Message, ip); + CheckMessage(listener, WcfEventSource.LocationIdSetMessage, ip); } } [TestMethod] public void OperationContextCreated_Message() { - using ( var listener = new WcfEventListener() ) + using (var listener = new WcfEventListener()) { var operationId = "abcdef"; var ownsContext = true; WcfEventSource.Log.OperationContextCreated(operationId, ownsContext); - CheckMessage(listener, WcfEventSource.OperationContextCreated_Message, operationId, ownsContext); + CheckMessage(listener, WcfEventSource.OperationContextCreatedMessage, operationId, ownsContext); } } [TestMethod] public void RequestMessageClosed_Message() { - using ( var listener = new WcfEventListener() ) + using (var listener = new WcfEventListener()) { var action = "reading property"; var argument = "Myproperty"; WcfEventSource.Log.RequestMessageClosed(action, argument); - CheckMessage(listener, WcfEventSource.RequestMessageClosed_Message, action, argument); + CheckMessage(listener, WcfEventSource.RequestMessageClosedMessage, action, argument); } } [TestMethod] public void ResponseMessageClosed_Message() { - using ( var listener = new WcfEventListener() ) + using (var listener = new WcfEventListener()) { var action = "reading property"; var argument = "Myproperty"; WcfEventSource.Log.ResponseMessageClosed(action, argument); - CheckMessage(listener, WcfEventSource.ResponseMessageClosed_Message, action, argument); + CheckMessage(listener, WcfEventSource.ResponseMessageClosedMessage, action, argument); } } - class WcfEventListener : EventListener + private static void CheckMessage(WcfEventListener listener, string format, params object[] args) + { + Assert.AreEqual(string.Format(CultureInfo.CurrentCulture, format, args), listener.FirstEventMessage); + } + + private class WcfEventListener : EventListener { - public String FirstEventMessage { get; private set; } - public List AllMessages { get; private set; } private object lockObj; public WcfEventListener() { - lockObj = new object(); - AllMessages = new List(); + this.lockObj = new object(); + this.AllMessages = new List(); this.EnableEvents(WcfEventSource.Log, EventLevel.Verbose); } + public string FirstEventMessage { get; private set; } + + public List AllMessages { get; private set; } + protected override void OnEventWritten(EventWrittenEventArgs eventData) { - lock ( lockObj ) + lock (this.lockObj) { - var str = String.Format(CultureInfo.CurrentCulture, eventData.Message, eventData.Payload.ToArray()); - AllMessages.Add(str); - if ( String.IsNullOrEmpty(FirstEventMessage) ) + var str = string.Format(CultureInfo.CurrentCulture, eventData.Message, eventData.Payload.ToArray()); + this.AllMessages.Add(str); + if (string.IsNullOrEmpty(this.FirstEventMessage)) { - FirstEventMessage = str; + this.FirstEventMessage = str; } } } diff --git a/WCF/Shared/ClientIpTelemetryInitializer.cs b/WCF/Shared/ClientIpTelemetryInitializer.cs index 559821d..af5c5d8 100644 --- a/WCF/Shared/ClientIpTelemetryInitializer.cs +++ b/WCF/Shared/ClientIpTelemetryInitializer.cs @@ -1,37 +1,38 @@ -using Microsoft.ApplicationInsights.Channel; -using Microsoft.ApplicationInsights.Extensibility.Implementation; -using Microsoft.ApplicationInsights.Wcf.Implementation; -using System; -using System.ServiceModel.Channels; - -namespace Microsoft.ApplicationInsights.Wcf +namespace Microsoft.ApplicationInsights.Wcf { + using System; + using System.ServiceModel.Channels; + using Microsoft.ApplicationInsights.Channel; + using Microsoft.ApplicationInsights.Extensibility.Implementation; + using Microsoft.ApplicationInsights.Wcf.Implementation; + /// - /// Collects the IP of the client calling the WCF service + /// Collects the IP of the client calling the WCF service. /// public sealed class ClientIpTelemetryInitializer : WcfTelemetryInitializer { /// - /// Initialize the telemetry event with the client IP if available + /// Initialize the telemetry event with the client IP if available. /// - /// The telemetry event - /// The WCF operation context + /// The telemetry event. + /// The WCF operation context. protected override void OnInitialize(ITelemetry telemetry, IOperationContext operation) { - if ( String.IsNullOrEmpty(telemetry.Context.Location.Ip) ) + if (string.IsNullOrEmpty(telemetry.Context.Location.Ip)) { var location = operation.Request.Context.Location; - if ( String.IsNullOrEmpty(location.Ip) ) + if (string.IsNullOrEmpty(location.Ip)) { - UpdateClientIp(location, operation); + this.UpdateClientIp(location, operation); } + telemetry.Context.Location.Ip = location.Ip; } } private void UpdateClientIp(LocationContext location, IOperationContext operation) { - if ( operation.HasIncomingMessageProperty(RemoteEndpointMessageProperty.Name) ) + if (operation.HasIncomingMessageProperty(RemoteEndpointMessageProperty.Name)) { var property = (RemoteEndpointMessageProperty) operation.GetIncomingMessageProperty(RemoteEndpointMessageProperty.Name); diff --git a/WCF/Shared/ClientTelemetryEndpointBehavior.cs b/WCF/Shared/ClientTelemetryEndpointBehavior.cs index 18a19ff..bff9f75 100644 --- a/WCF/Shared/ClientTelemetryEndpointBehavior.cs +++ b/WCF/Shared/ClientTelemetryEndpointBehavior.cs @@ -1,13 +1,13 @@ -using Microsoft.ApplicationInsights.Extensibility; -using Microsoft.ApplicationInsights.Extensibility.Implementation; -using Microsoft.ApplicationInsights.Wcf.Implementation; -using System; -using System.ServiceModel.Channels; -using System.ServiceModel.Description; -using System.ServiceModel.Dispatcher; - -namespace Microsoft.ApplicationInsights.Wcf +namespace Microsoft.ApplicationInsights.Wcf { + using System; + using System.ServiceModel.Channels; + using System.ServiceModel.Description; + using System.ServiceModel.Dispatcher; + using Microsoft.ApplicationInsights.Extensibility; + using Microsoft.ApplicationInsights.Extensibility.Implementation; + using Microsoft.ApplicationInsights.Wcf.Implementation; + /// /// Instruments client-side WCF endpoints to generate DependencyTelemetry /// events on calls. @@ -17,59 +17,65 @@ public sealed class ClientTelemetryEndpointBehavior : IEndpointBehavior private TelemetryClient telemetryClient; /// - /// Gets or sets the name of the HTTP header to get root operation Id from. - /// - public String RootOperationIdHeaderName { get; set; } - /// - /// Gets or sets the name of the HTTP header to get parent operation Id from. - /// - public String ParentOperationIdHeaderName { get; set; } - /// - /// Gets or sets the name of the SOAP header to get root operation Id from. - /// - public String SoapRootOperationIdHeaderName { get; set; } - /// - /// Gets or sets the name of the SOAP header to get parent operation Id from. - /// - public String SoapParentOperationIdHeaderName { get; set; } - /// - /// Gets or sets the name of the SOAP header to get parent operation Id from. - /// - public String SoapHeaderNamespace { get; set; } - - /// - /// Initializes a new instance using the default - /// Application Insights configuration + /// Initializes a new instance of the + /// class using the default Application Insights configuration. /// public ClientTelemetryEndpointBehavior() : this(TelemetryConfiguration.Active) { - } /// - /// Initializes a new instance using the specified configuration + /// Initializes a new instance of the + /// class using the specified configuration. /// - /// The Application Insights configuration to use + /// The Application Insights configuration to use. public ClientTelemetryEndpointBehavior(TelemetryConfiguration configuration) : this(configuration != null ? new TelemetryClient(configuration) : null) { } /// - /// Initializes a new instance using the specified telemetry client + /// Initializes a new instance of the class + /// using the specified telemetry client. /// - /// The TelemetryClient instance to use to emit telemetry events + /// The TelemetryClient instance to use to emit telemetry events. public ClientTelemetryEndpointBehavior(TelemetryClient client) { - if ( client == null ) + if (client == null) { - throw new ArgumentNullException(nameof(client)); + throw new ArgumentNullException(nameof(client)); } + this.telemetryClient = client; this.telemetryClient.Context.GetInternalContext().SdkVersion = "wcf: " + SdkVersionUtils.GetAssemblyVersion(); } + /// + /// Gets or sets the name of the HTTP header to get root operation Id from. + /// + public string RootOperationIdHeaderName { get; set; } + + /// + /// Gets or sets the name of the HTTP header to get parent operation Id from. + /// + public string ParentOperationIdHeaderName { get; set; } + + /// + /// Gets or sets the name of the SOAP header to get root operation Id from. + /// + public string SoapRootOperationIdHeaderName { get; set; } + + /// + /// Gets or sets the name of the SOAP header to get parent operation Id from. + /// + public string SoapParentOperationIdHeaderName { get; set; } + + /// + /// Gets or sets the name of the SOAP header to get parent operation Id from. + /// + public string SoapHeaderNamespace { get; set; } + void IEndpointBehavior.AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { var contract = endpoint.Contract.ContractType; @@ -80,13 +86,13 @@ void IEndpointBehavior.AddBindingParameters(ServiceEndpoint endpoint, BindingPar // since channel factories are cached by default in ClientBase. // We could possibly cache this to avoid the hit in other scenarios. var description = new ClientContract(endpoint.Contract); - var element = new ClientTelemetryBindingElement(telemetryClient, description) + var element = new ClientTelemetryBindingElement(this.telemetryClient, description) { - RootOperationIdHeaderName = RootOperationIdHeaderName, - ParentOperationIdHeaderName = ParentOperationIdHeaderName, - SoapRootOperationIdHeaderName = SoapRootOperationIdHeaderName, - SoapParentOperationIdHeaderName = SoapParentOperationIdHeaderName, - SoapHeaderNamespace = SoapHeaderNamespace + RootOperationIdHeaderName = this.RootOperationIdHeaderName, + ParentOperationIdHeaderName = this.ParentOperationIdHeaderName, + SoapRootOperationIdHeaderName = this.SoapRootOperationIdHeaderName, + SoapParentOperationIdHeaderName = this.SoapParentOperationIdHeaderName, + SoapHeaderNamespace = this.SoapHeaderNamespace }; var collection = endpoint.Binding.CreateBindingElements(); collection.Insert(0, element); @@ -106,5 +112,4 @@ void IEndpointBehavior.Validate(ServiceEndpoint endpoint) { } } - } diff --git a/WCF/Shared/ClientTelemetryExtensionElement.cs b/WCF/Shared/ClientTelemetryExtensionElement.cs index b7739fb..2aa8048 100644 --- a/WCF/Shared/ClientTelemetryExtensionElement.cs +++ b/WCF/Shared/ClientTelemetryExtensionElement.cs @@ -1,22 +1,21 @@ -using Microsoft.ApplicationInsights.Extensibility; -using Microsoft.ApplicationInsights.Wcf.Implementation; -using System; -using System.Configuration; -using System.ServiceModel.Configuration; - -namespace Microsoft.ApplicationInsights.Wcf +namespace Microsoft.ApplicationInsights.Wcf { + using System; + using System.Configuration; + using System.ServiceModel.Configuration; + using Microsoft.ApplicationInsights.Extensibility; + /// /// Supports generating Application Insights dependency /// events for calls to Web Services done using the - /// WCF client-side stack through the configuration file + /// WCF client-side stack through the configuration file. /// public sealed class ClientTelemetryExtensionElement : BehaviorExtensionElement { private ConfigurationPropertyCollection properties; /// - /// Gets the type of the behavior + /// Gets the type of the behavior. /// public override Type BehaviorType { @@ -26,87 +25,97 @@ public override Type BehaviorType /// /// Gets or sets the name of the HTTP header to get root operation Id from. /// - public String RootOperationIdHeaderName + public string RootOperationIdHeaderName { - get { return (String)base["rootOperationIdHeaderName"]; } + get { return (string)base["rootOperationIdHeaderName"]; } set { base["rootOperationIdHeaderName"] = value; } } + /// /// Gets or sets the name of the HTTP header to get parent operation Id from. /// - public String ParentOperationIdHeaderName + public string ParentOperationIdHeaderName { - get { return (String)base["parentOperationIdHeaderName"]; } + get { return (string)base["parentOperationIdHeaderName"]; } set { base["parentOperationIdHeaderName"] = value; } } + /// /// Gets or sets the name of the SOAP header to get root operation Id from. /// - public String SoapRootOperationIdHeaderName + public string SoapRootOperationIdHeaderName { - get { return (String)base["soapRootOperationIdHeaderName"]; } + get { return (string)base["soapRootOperationIdHeaderName"]; } set { base["soapRootOperationIdHeaderName"] = value; } } + /// /// Gets or sets the name of the SOAP header to get parent operation Id from. /// - public String SoapParentOperationIdHeaderName + public string SoapParentOperationIdHeaderName { - get { return (String)base["soapParentOperationIdHeaderName"]; } + get { return (string)base["soapParentOperationIdHeaderName"]; } set { base["soapParentOperationIdHeaderName"] = value; } } + /// /// Gets or sets the XML Namespace for the root/parent operation ID SOAP headers. /// - public String SoapHeaderNamespace + public string SoapHeaderNamespace { - get { return (String)base["soapHeaderNamespace"]; } + get { return (string)base["soapHeaderNamespace"]; } set { base["soapHeaderNamespace"] = value; } } /// - /// The list of properties supported by this behavior + /// The list of properties supported by this behavior. /// protected override ConfigurationPropertyCollection Properties { get { - if ( properties == null ) + if (this.properties == null) { - properties = CreateProperties(); + this.properties = this.CreateProperties(); } - return properties; - } - } - /// - /// Creates the ApplicationInsights behavior - /// - /// A new Endpoint Behavior that will track client-side calls - protected override object CreateBehavior() - { - return CreateBehaviorInternal(); + return this.properties; + } } internal ClientTelemetryEndpointBehavior CreateBehaviorInternal() { - var behavior = new ClientTelemetryEndpointBehavior(TelemetryConfiguration.Active); - behavior.ParentOperationIdHeaderName = ParentOperationIdHeaderName; - behavior.RootOperationIdHeaderName = RootOperationIdHeaderName; - behavior.SoapParentOperationIdHeaderName = SoapParentOperationIdHeaderName; - behavior.SoapRootOperationIdHeaderName = SoapRootOperationIdHeaderName; - behavior.SoapHeaderNamespace = SoapHeaderNamespace; + var behavior = new ClientTelemetryEndpointBehavior(TelemetryConfiguration.Active) + { + ParentOperationIdHeaderName = this.ParentOperationIdHeaderName, + RootOperationIdHeaderName = this.RootOperationIdHeaderName, + SoapParentOperationIdHeaderName = this.SoapParentOperationIdHeaderName, + SoapRootOperationIdHeaderName = this.SoapRootOperationIdHeaderName, + SoapHeaderNamespace = this.SoapHeaderNamespace + }; return behavior; } + internal ConfigurationPropertyCollection CreateProperties() { - var props = new ConfigurationPropertyCollection(); - props.Add(new ConfigurationProperty("parentOperationIdHeaderName", typeof(String), CorrelationHeaders.HttpStandardParentIdHeader)); - props.Add(new ConfigurationProperty("rootOperationIdHeaderName", typeof(String), CorrelationHeaders.HttpStandardRootIdHeader)); - props.Add(new ConfigurationProperty("soapParentOperationIdHeaderName", typeof(String), CorrelationHeaders.SoapStandardParentIdHeader)); - props.Add(new ConfigurationProperty("soapRootOperationIdHeaderName", typeof(String), CorrelationHeaders.SoapStandardRootIdHeader)); - props.Add(new ConfigurationProperty("soapHeaderNamespace", typeof(String), CorrelationHeaders.SoapStandardNamespace)); + var props = new ConfigurationPropertyCollection + { + new ConfigurationProperty("parentOperationIdHeaderName", typeof(string), CorrelationHeaders.HttpStandardParentIdHeader), + new ConfigurationProperty("rootOperationIdHeaderName", typeof(string), CorrelationHeaders.HttpStandardRootIdHeader), + new ConfigurationProperty("soapParentOperationIdHeaderName", typeof(string), CorrelationHeaders.SoapStandardParentIdHeader), + new ConfigurationProperty("soapRootOperationIdHeaderName", typeof(string), CorrelationHeaders.SoapStandardRootIdHeader), + new ConfigurationProperty("soapHeaderNamespace", typeof(string), CorrelationHeaders.SoapStandardNamespace) + }; return props; } + + /// + /// Creates the ApplicationInsights behavior. + /// + /// A new Endpoint Behavior that will track client-side calls. + protected override object CreateBehavior() + { + return this.CreateBehaviorInternal(); + } } } diff --git a/WCF/Shared/CorrelationHeaders.cs b/WCF/Shared/CorrelationHeaders.cs index ab4a044..1939cc2 100644 --- a/WCF/Shared/CorrelationHeaders.cs +++ b/WCF/Shared/CorrelationHeaders.cs @@ -1,31 +1,35 @@ -using System; - -namespace Microsoft.ApplicationInsights.Wcf +namespace Microsoft.ApplicationInsights.Wcf { + using System; + /// - /// Default correlation header names + /// Default correlation header names. /// public static class CorrelationHeaders { /// - /// Default HTTP header name for ParentId + /// Default HTTP header name for ParentId. /// - public const String HttpStandardParentIdHeader = "x-ms-request-id"; + public const string HttpStandardParentIdHeader = "x-ms-request-id"; + /// - /// Default HTTP header name for RootId + /// Default HTTP header name for RootId. /// - public const String HttpStandardRootIdHeader = "x-ms-request-root-id"; + public const string HttpStandardRootIdHeader = "x-ms-request-root-id"; + /// - /// Default SOAP header name for ParentId + /// Default SOAP header name for ParentId. /// - public const String SoapStandardParentIdHeader = "requestId"; + public const string SoapStandardParentIdHeader = "requestId"; + /// - /// Default SOAP header name for RootId + /// Default SOAP header name for RootId. /// - public const String SoapStandardRootIdHeader = "requestRootId"; + public const string SoapStandardRootIdHeader = "requestRootId"; + /// - /// Default XML namespace for SOAP headers + /// Default XML namespace for SOAP headers. /// - public const String SoapStandardNamespace = "http://schemas.microsoft.com/application-insights"; + public const string SoapStandardNamespace = "http://schemas.microsoft.com/application-insights"; } } diff --git a/WCF/Shared/ExceptionTrackingTelemetryModule.cs b/WCF/Shared/ExceptionTrackingTelemetryModule.cs index ad7dedd..a2e1c53 100644 --- a/WCF/Shared/ExceptionTrackingTelemetryModule.cs +++ b/WCF/Shared/ExceptionTrackingTelemetryModule.cs @@ -1,22 +1,22 @@ -using Microsoft.ApplicationInsights.DataContracts; -using Microsoft.ApplicationInsights.Extensibility; -using Microsoft.ApplicationInsights.Extensibility.Implementation; -using Microsoft.ApplicationInsights.Wcf.Implementation; -using System; -using System.ServiceModel; -using System.ServiceModel.Channels; - -namespace Microsoft.ApplicationInsights.Wcf +namespace Microsoft.ApplicationInsights.Wcf { + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + using Microsoft.ApplicationInsights.DataContracts; + using Microsoft.ApplicationInsights.Extensibility; + using Microsoft.ApplicationInsights.Extensibility.Implementation; + using Microsoft.ApplicationInsights.Wcf.Implementation; + /// - /// Implements exception tracking for WCF services + /// Implements exception tracking for WCF services. /// public sealed class ExceptionTrackingTelemetryModule : IWcfTelemetryModule { private TelemetryClient telemetryClient; /// - /// Initializes a new instance of this module + /// Initializes a new instance of the class. /// public ExceptionTrackingTelemetryModule() { @@ -38,22 +38,32 @@ void IWcfTelemetryModule.OnEndRequest(IOperationContext operation, Message reply void IWcfTelemetryModule.OnError(IOperationContext operation, Exception error) { - if ( operation == null ) + if (operation == null) + { throw new ArgumentNullException("operation"); - if ( error == null ) + } + + if (error == null) + { throw new ArgumentNullException("error"); - if ( telemetryClient == null ) + } + + if (this.telemetryClient == null) + { return; + } ExceptionTelemetry telemetry = new ExceptionTelemetry(error); - if ( error is FaultException ) + if (error is FaultException) { telemetry.SeverityLevel = SeverityLevel.Error; - } else + } + else { telemetry.SeverityLevel = SeverityLevel.Critical; } - telemetryClient.TrackException(error); + + this.telemetryClient.TrackException(error); } } } diff --git a/WCF/Shared/IOperationContext.cs b/WCF/Shared/IOperationContext.cs index 50959a4..81d3930 100644 --- a/WCF/Shared/IOperationContext.cs +++ b/WCF/Shared/IOperationContext.cs @@ -1,82 +1,95 @@ -using Microsoft.ApplicationInsights.DataContracts; -using System; -using System.ServiceModel; - -namespace Microsoft.ApplicationInsights.Wcf +namespace Microsoft.ApplicationInsights.Wcf { + using System; + using System.ServiceModel; + using Microsoft.ApplicationInsights.DataContracts; + /// /// Represents the context of the currently executing /// operation. Wraps the underlying - /// OperationContext + /// OperationContext. /// public interface IOperationContext { /// - /// The ID assigned to this request + /// Gets the ID assigned to this request. /// - String OperationId { get; } + string OperationId { get; } + /// - /// The URI of the service endpoint + /// Gets the URI of the service endpoint. /// Uri EndpointUri { get; } + /// - /// The URI the message was addressed to + /// Gets the URI the message was addressed to. /// Uri ToHeader { get; } + /// - /// The RequestTelemetry event + /// Gets the RequestTelemetry event. /// RequestTelemetry Request { get; } + /// - /// True if WCF owns the Request telemetry object + /// Gets a value indicating whether WCF owns the Request telemetry object. /// bool OwnsRequest { get; } + /// - /// Name of the service contract being invoked + /// Gets the name of the service contract being invoked. /// - String ContractName { get; } + string ContractName { get; } + /// - /// Namespace of the service contract being invoked + /// Gets the namespace of the service contract being invoked. /// - String ContractNamespace { get; } + string ContractNamespace { get; } + /// - /// The name of the operation being invoked + /// Gets the name of the operation being invoked. /// - String OperationName { get; } + string OperationName { get; } + /// - /// The service security context + /// Gets the service security context. /// ServiceSecurityContext SecurityContext { get; } + /// - /// Checks if the incoming message has a given property + /// Checks if the incoming message has a given property. /// - /// The name of the property to be checked - /// True if the property exists; false otherwise - bool HasIncomingMessageProperty(String propertyName); + /// The name of the property to be checked. + /// True if the property exists; false otherwise. + bool HasIncomingMessageProperty(string propertyName); + /// - /// Returns the value of the given property in the incoming message + /// Returns the value of the given property in the incoming message. /// - /// The name of the property to be checked - /// The property value - object GetIncomingMessageProperty(String propertyName); + /// The name of the property to be checked. + /// The property value. + object GetIncomingMessageProperty(string propertyName); + /// - /// Checks if the outgoing message has a given property + /// Checks if the outgoing message has a given property. /// - /// The name of the property to be checked - /// True if the property exists; false otherwise - bool HasOutgoingMessageProperty(String propertyName); + /// The name of the property to be checked. + /// True if the property exists; false otherwise. + bool HasOutgoingMessageProperty(string propertyName); + /// - /// Returns the value of the given property in the outgoing message + /// Returns the value of the given property in the outgoing message. /// - /// The name of the property - /// The property value - object GetOutgoingMessageProperty(String propertyName); + /// The name of the property. + /// The property value. + object GetOutgoingMessageProperty(string propertyName); + /// - /// Returns the specified SOAP header on the request message + /// Returns the specified SOAP header on the request message. /// - /// The header name - /// The header XML namespace - /// The header, or null if it is not present - T GetIncomingMessageHeader(String name, String ns); + /// The header name. + /// The header XML namespace. + /// The header, or null if it is not present. + T GetIncomingMessageHeader(string name, string ns); } } diff --git a/WCF/Shared/IOperationContextState.cs b/WCF/Shared/IOperationContextState.cs index 7ec0ab3..fdd229e 100644 --- a/WCF/Shared/IOperationContextState.cs +++ b/WCF/Shared/IOperationContextState.cs @@ -1,29 +1,29 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Microsoft.ApplicationInsights.Wcf +namespace Microsoft.ApplicationInsights.Wcf { + using System; + using System.Collections.Generic; + using System.Text; + /// /// Allows a TelemetryModule or TelemetryInitializer to store temporary - /// state between request/response processing + /// state between request/response processing. /// public interface IOperationContextState { /// - /// Store a value in the context + /// Store a value in the context. /// - /// The key to store the state under - /// The value to store - void SetState(String key, object value); + /// The key to store the state under. + /// The value to store. + void SetState(string key, object value); + /// - /// Retrieve a value from the context + /// Retrieve a value from the context. /// - /// The type of value - /// The key the state is stored under - /// The value, if found + /// The type of value. + /// The key the state is stored under. + /// The value, if found. /// True if the specified key was found, false otherwise. - bool TryGetState(String key, out T value); - + bool TryGetState(string key, out T value); } } diff --git a/WCF/Shared/IWcfMessageTrace.cs b/WCF/Shared/IWcfMessageTrace.cs index 476c9e6..3d3bdd3 100644 --- a/WCF/Shared/IWcfMessageTrace.cs +++ b/WCF/Shared/IWcfMessageTrace.cs @@ -1,8 +1,8 @@ -using System; -using System.ServiceModel.Channels; - -namespace Microsoft.ApplicationInsights.Wcf +namespace Microsoft.ApplicationInsights.Wcf { + using System; + using System.ServiceModel.Channels; + /// /// Represents a telemetry module that also is interested /// in tracing request messages before the request is executed. @@ -10,18 +10,18 @@ namespace Microsoft.ApplicationInsights.Wcf public interface IWcfMessageTrace { /// - /// Called after IWcfTelemetryModule.OnBeginRequest() + /// Called after /// but before the request is executed. /// - /// The operation context + /// The operation context. /// The request message. You can modify the request by returning a new, unread Message object. void OnTraceRequest(IOperationContext operation, ref Message request); /// /// Called after the request is executed, but before - /// IWcfTelemetryModule.OnEndRequest() is called. + /// is called. /// - /// The operation context + /// The operation context. /// The response message. You can modify the response by returning a new, unread Message object. void OnTraceResponse(IOperationContext operation, ref Message response); } diff --git a/WCF/Shared/IWcfTelemetryModule.cs b/WCF/Shared/IWcfTelemetryModule.cs index a443099..46322d7 100644 --- a/WCF/Shared/IWcfTelemetryModule.cs +++ b/WCF/Shared/IWcfTelemetryModule.cs @@ -1,30 +1,32 @@ -using Microsoft.ApplicationInsights.Extensibility; -using System; -using System.ServiceModel.Channels; - -namespace Microsoft.ApplicationInsights.Wcf +namespace Microsoft.ApplicationInsights.Wcf { + using System; + using System.ServiceModel.Channels; + using Microsoft.ApplicationInsights.Extensibility; + /// - /// Represents a telemetry module used during WCF processing + /// Represents a telemetry module used during WCF processing. /// public interface IWcfTelemetryModule : ITelemetryModule { /// - /// Fired when the request message arrives + /// Fired when the request message arrives. /// - /// The operation context + /// The operation context. void OnBeginRequest(IOperationContext operation); + /// - /// Fired before the response message is sent + /// Fired before the response message is sent. /// - /// The operation context - /// The response message + /// The operation context. + /// The response message. void OnEndRequest(IOperationContext operation, Message reply); + /// - /// Fired when an exception occurs + /// Fired when an exception occurs. /// - /// The operation context - /// The exception object + /// The operation context. + /// The exception object. void OnError(IOperationContext operation, Exception error); } } diff --git a/WCF/Shared/Implementation/ChannelAsyncResult.cs b/WCF/Shared/Implementation/ChannelAsyncResult.cs index 6fe68ba..a9315e5 100644 --- a/WCF/Shared/Implementation/ChannelAsyncResult.cs +++ b/WCF/Shared/Implementation/ChannelAsyncResult.cs @@ -1,291 +1,114 @@ -using Microsoft.ApplicationInsights.DataContracts; -using System; -using System.ServiceModel.Channels; -using System.Threading; -using System.Xml; - -namespace Microsoft.ApplicationInsights.Wcf.Implementation +namespace Microsoft.ApplicationInsights.Wcf.Implementation { + using System; + using System.ServiceModel.Channels; + using System.Threading; + using Microsoft.ApplicationInsights.DataContracts; + internal abstract class ChannelAsyncResult : IAsyncResult, IDisposable { + private const int Incomplete = 0; + private const int CompletedSync = 1; + private const int CompletedAsync = 2; private AsyncCallback callback; private EventWaitHandle waitHandle; private AsyncCallback channelCompletionCallback; - const int Incomplete = 0; - const int CompletedSync = 1; - const int CompletedAsync = 2; private int completed; + public ChannelAsyncResult(AsyncCallback completeCallback, AsyncCallback callback, object state, DependencyTelemetry channelState) + { + this.AsyncState = state; + this.waitHandle = new EventWaitHandle(false, EventResetMode.ManualReset); + this.Telemetry = channelState; + this.channelCompletionCallback = completeCallback; + this.callback = callback; + } + public object AsyncState { get; private set; } - public WaitHandle AsyncWaitHandle { get { return waitHandle; } } + public WaitHandle AsyncWaitHandle + { + get { return this.waitHandle; } + } public bool CompletedSynchronously { - get { return Thread.VolatileRead(ref completed) == CompletedSync; } + get { return Thread.VolatileRead(ref this.completed) == CompletedSync; } } public bool IsCompleted { - get { return Thread.VolatileRead(ref completed) != Incomplete; } + get { return Thread.VolatileRead(ref this.completed) != Incomplete; } } + // should only be read once we're complete and we don't care if // it's not read in order public Exception LastException { get; private set; } // these get set during construction, so no need for much check public DependencyTelemetry Telemetry { get; private set; } - public IAsyncResult OriginalResult { get; protected set; } - public ChannelAsyncResult(AsyncCallback completeCallback, AsyncCallback callback, object state, DependencyTelemetry channelState) - { - this.AsyncState = state; - this.waitHandle = new EventWaitHandle(false, EventResetMode.ManualReset); - this.Telemetry = channelState; - this.channelCompletionCallback = completeCallback; - this.callback = callback; - } - - protected void Complete(bool completedSync, Exception exception = null) - { - this.LastException = exception; - - Thread.VolatileWrite(ref this.completed, completedSync ? CompletedSync : CompletedAsync); - - try - { - // tell channel the async operation is done - NotifyCompletionToChannel(); - } catch ( Exception ex ) - { - LastException = ex; - } - // set the waitHandle so that when callback() calls EndWhatever() - // it doesn't hang - this.waitHandle.Set(); - try - { - callback?.Invoke(this); - } catch ( Exception ex ) - { - LastException = ex; - } - } - - protected void NotifyCompletionToChannel() - { - this.channelCompletionCallback?.Invoke(this); - } + public IAsyncResult OriginalResult { get; protected set; } public static TAsyncResult End(IAsyncResult result) where TAsyncResult : ChannelAsyncResult { - ChannelAsyncResult car = (ChannelAsyncResult)result; - if ( !car.IsCompleted ) + var car = (ChannelAsyncResult)result; + if (!car.IsCompleted) { car.AsyncWaitHandle.WaitOne(); } ((IDisposable)car).Dispose(); - if ( car.LastException != null ) + if (car.LastException != null) { throw car.LastException; } + return (TAsyncResult)car; } void IDisposable.Dispose() { // done here only to avoid CA1001 - if ( this.waitHandle != null ) + if (this.waitHandle != null) { this.waitHandle.Close(); } } - } - - internal sealed class OpenAsyncResult : ChannelAsyncResult - { - public IChannel InnerChannel { get; private set; } - - public OpenAsyncResult(IChannel innerChannel, TimeSpan timeout, AsyncCallback onOpenDone, AsyncCallback callback, object state, DependencyTelemetry telemetry) - : base(onOpenDone, callback, state, telemetry) - { - this.InnerChannel = innerChannel; - - OriginalResult = innerChannel.BeginOpen(timeout, OnComplete, this); - if ( OriginalResult.CompletedSynchronously ) - { - innerChannel.EndOpen(OriginalResult); - this.Complete(true); - } - } - - private static void OnComplete(IAsyncResult result) - { - if ( result.CompletedSynchronously ) - { - return; - } - OpenAsyncResult oar = (OpenAsyncResult)result.AsyncState; - try - { - oar.InnerChannel.EndOpen(oar.OriginalResult); - oar.Complete(false); - } catch ( Exception ex ) - { - oar.Complete(false, ex); - } - } - } - internal sealed class SendAsyncResult : ChannelAsyncResult - { - public IOutputChannel InnerChannel { get; private set; } - public UniqueId RequestId { get; private set; } - - public SendAsyncResult(IOutputChannel innerChannel, Message message, TimeSpan timeout, AsyncCallback onSendDone, AsyncCallback callback, object state, DependencyTelemetry telemetry) - : base(onSendDone, callback, state, telemetry) - { - this.InnerChannel = innerChannel; - this.RequestId = message.Headers.MessageId; - - this.OriginalResult = innerChannel.BeginSend(message, timeout, OnComplete, this); - if ( OriginalResult.CompletedSynchronously ) - { - innerChannel.EndSend(OriginalResult); - this.Complete(true); - } - } - - private static void OnComplete(IAsyncResult result) - { - if ( result.CompletedSynchronously ) - { - return; - } - SendAsyncResult sar = (SendAsyncResult)result.AsyncState; - try - { - sar.InnerChannel.EndSend(sar.OriginalResult); - sar.Complete(false); - } catch ( Exception ex ) - { - sar.Complete(false, ex); - } - } - } - - internal sealed class RequestAsyncResult : ChannelAsyncResult - { - public IRequestChannel InnerChannel { get; private set; } - public Message Reply { get; private set; } - - public RequestAsyncResult(IRequestChannel innerChannel, Message message, TimeSpan timeout, AsyncCallback onRequestDone, AsyncCallback callback, object state, DependencyTelemetry telemetry) - : base(onRequestDone, callback, state, telemetry) + protected void Complete(bool completedSync, Exception exception = null) { - this.InnerChannel = innerChannel; + this.LastException = exception; - OriginalResult = innerChannel.BeginRequest(message, timeout, OnComplete, this); - if ( OriginalResult.CompletedSynchronously ) - { - this.Reply = innerChannel.EndRequest(OriginalResult); - this.Complete(true); - } - } + Thread.VolatileWrite(ref this.completed, completedSync ? CompletedSync : CompletedAsync); - private static void OnComplete(IAsyncResult result) - { - if ( result.CompletedSynchronously ) - { - return; - } - RequestAsyncResult rar = (RequestAsyncResult)result.AsyncState; try { - rar.Reply = rar.InnerChannel.EndRequest(rar.OriginalResult); - rar.Complete(false); - } catch ( Exception ex ) - { - rar.Complete(false, ex); + // tell channel the async operation is done + this.NotifyCompletionToChannel(); } - } - } - - internal sealed class ReceiveAsyncResult : ChannelAsyncResult - { - public IInputChannel InnerChannel { get; private set; } - public Message Message { get; private set; } - - public ReceiveAsyncResult(IInputChannel innerChannel, TimeSpan timeout, AsyncCallback onReceiveDone, AsyncCallback callback, object state) - : base(onReceiveDone, callback, state, null) - { - this.InnerChannel = innerChannel; - - OriginalResult = innerChannel.BeginReceive(timeout, OnComplete, this); - if ( OriginalResult.CompletedSynchronously ) + catch (Exception ex) { - this.Message = innerChannel.EndReceive(OriginalResult); - this.Complete(true); + this.LastException = ex; } - } - private static void OnComplete(IAsyncResult result) - { - if ( result.CompletedSynchronously ) - { - return; - } - ReceiveAsyncResult rar = (ReceiveAsyncResult)result.AsyncState; + // set the waitHandle so that when callback() calls EndWhatever() + // it doesn't hang + this.waitHandle.Set(); try { - rar.Message = rar.InnerChannel.EndReceive(rar.OriginalResult); - rar.Complete(false); - } catch ( Exception ex ) - { - rar.Complete(false, ex); + this.callback?.Invoke(this); } - } - } - - internal sealed class TryReceiveAsyncResult : ChannelAsyncResult - { - public IInputChannel InnerChannel { get; private set; } - public Message Message { get; private set; } - public bool Result { get; private set; } - - public TryReceiveAsyncResult(IInputChannel innerChannel, TimeSpan timeout, AsyncCallback onReceiveDone, AsyncCallback callback, object state) - : base(onReceiveDone, callback, state, null) - { - this.InnerChannel = innerChannel; - - OriginalResult = innerChannel.BeginTryReceive(timeout, OnComplete, this); - if ( OriginalResult.CompletedSynchronously ) + catch (Exception ex) { - Message message = null; - this.Result = innerChannel.EndTryReceive(OriginalResult, out message); - this.Message = message; - this.Complete(true); + this.LastException = ex; } } - private static void OnComplete(IAsyncResult result) + protected void NotifyCompletionToChannel() { - if ( result.CompletedSynchronously ) - { - return; - } - TryReceiveAsyncResult trar = (TryReceiveAsyncResult)result.AsyncState; - try - { - Message message = null; - trar.Result = trar.InnerChannel.EndTryReceive(trar.OriginalResult, out message); - trar.Message = message; - trar.Complete(false); - } catch ( Exception ex ) - { - trar.Complete(false, ex); - } + this.channelCompletionCallback?.Invoke(this); } } } diff --git a/WCF/Shared/Implementation/ClientContract.cs b/WCF/Shared/Implementation/ClientContract.cs index a86a305..1ced296 100644 --- a/WCF/Shared/Implementation/ClientContract.cs +++ b/WCF/Shared/Implementation/ClientContract.cs @@ -1,13 +1,12 @@ -using System; -using System.Collections.Generic; -using System.ServiceModel.Description; - -namespace Microsoft.ApplicationInsights.Wcf.Implementation +namespace Microsoft.ApplicationInsights.Wcf.Implementation { + using System; + using System.Collections.Generic; + using System.ServiceModel.Description; + internal class ClientContract { - private IDictionary dictionary; - public Type ContractType { get; private set; } + private IDictionary dictionary; public ClientContract(Type contractType) : this(ContractDescription.GetContract(contractType)) @@ -16,18 +15,20 @@ public ClientContract(Type contractType) public ClientContract(ContractDescription description) { - ContractType = description.ContractType; - dictionary = new Dictionary(); - foreach ( var op in description.Operations ) + this.ContractType = description.ContractType; + this.dictionary = new Dictionary(); + foreach (var op in description.Operations) { - var opDesc = new ClientOperation(ContractType.Name, op); - dictionary.Add(opDesc.Action, opDesc); + var operationDesc = new ClientOperation(this.ContractType.Name, op); + this.dictionary.Add(operationDesc.Action, operationDesc); } } - public bool TryLookupByAction(String soapAction, out ClientOperation operation) + public Type ContractType { get; private set; } + + public bool TryLookupByAction(string soapAction, out ClientOperation operation) { - return dictionary.TryGetValue(soapAction, out operation); + return this.dictionary.TryGetValue(soapAction, out operation); } } } diff --git a/WCF/Shared/Implementation/ClientExceptionExtensions.cs b/WCF/Shared/Implementation/ClientExceptionExtensions.cs index 6853719..5ca745a 100644 --- a/WCF/Shared/Implementation/ClientExceptionExtensions.cs +++ b/WCF/Shared/Implementation/ClientExceptionExtensions.cs @@ -1,32 +1,37 @@ -using System; -using System.ServiceModel; - -namespace Microsoft.ApplicationInsights.Wcf.Implementation +namespace Microsoft.ApplicationInsights.Wcf.Implementation { + using System; + using System.ServiceModel; + internal static class ClientExceptionExtensions { - public static String ToResultCode(this Exception exception) + public static string ToResultCode(this Exception exception) { - if ( exception == null ) + if (exception == null) { throw new ArgumentNullException(nameof(exception)); } - if ( exception is TimeoutException ) + + if (exception is TimeoutException) { return "Timeout"; } - if ( exception is EndpointNotFoundException ) + + if (exception is EndpointNotFoundException) { return "EndpointNotFound"; } - if ( exception is ServerTooBusyException ) + + if (exception is ServerTooBusyException) { return "ServerTooBusy"; } - if ( exception is FaultException ) + + if (exception is FaultException) { return "SoapFault"; } + return exception.GetType().Name; } } diff --git a/WCF/Shared/Implementation/ClientOperation.cs b/WCF/Shared/Implementation/ClientOperation.cs index 0a98b01..c8a16fa 100644 --- a/WCF/Shared/Implementation/ClientOperation.cs +++ b/WCF/Shared/Implementation/ClientOperation.cs @@ -1,20 +1,23 @@ -using System; -using System.ServiceModel.Description; - -namespace Microsoft.ApplicationInsights.Wcf.Implementation +namespace Microsoft.ApplicationInsights.Wcf.Implementation { + using System; + using System.ServiceModel.Description; + internal struct ClientOperation { - public String Action { get; private set; } - public String Name { get; private set; } - public bool IsOneWay { get; private set; } - - public ClientOperation(String contractName, OperationDescription description) + public ClientOperation(string contractName, OperationDescription description) { - Action = description.Messages[0].Action; - IsOneWay = description.IsOneWay; + this.Action = description.Messages[0].Action; + this.IsOneWay = description.IsOneWay; + // Doing this here means we won't need to concatenate on each service call - Name = contractName + "." + description.Name; + this.Name = contractName + "." + description.Name; } + + public string Action { get; private set; } + + public string Name { get; private set; } + + public bool IsOneWay { get; private set; } } } diff --git a/WCF/Shared/Implementation/ClientTelemetryBindingElement.cs b/WCF/Shared/Implementation/ClientTelemetryBindingElement.cs index 69e0d62..ef872f7 100644 --- a/WCF/Shared/Implementation/ClientTelemetryBindingElement.cs +++ b/WCF/Shared/Implementation/ClientTelemetryBindingElement.cs @@ -1,36 +1,42 @@ -using System; -using System.ServiceModel.Channels; - -namespace Microsoft.ApplicationInsights.Wcf.Implementation +namespace Microsoft.ApplicationInsights.Wcf.Implementation { + using System; + using System.ServiceModel.Channels; + internal sealed class ClientTelemetryBindingElement : BindingElement { private TelemetryClient telemetryClient; private ClientContract operationMap; - public String RootOperationIdHeaderName { get; set; } - public String ParentOperationIdHeaderName { get; set; } - public String SoapRootOperationIdHeaderName { get; set; } - public String SoapParentOperationIdHeaderName { get; set; } - public String SoapHeaderNamespace { get; set; } - public ClientTelemetryBindingElement(TelemetryClient client, ClientContract map) { - if ( client == null ) + if (client == null) { throw new ArgumentNullException(nameof(client)); } - if ( map == null ) + + if (map == null) { throw new ArgumentNullException(nameof(map)); } + this.telemetryClient = client; this.operationMap = map; } + public string RootOperationIdHeaderName { get; set; } + + public string ParentOperationIdHeaderName { get; set; } + + public string SoapRootOperationIdHeaderName { get; set; } + + public string SoapParentOperationIdHeaderName { get; set; } + + public string SoapHeaderNamespace { get; set; } + public override BindingElement Clone() { - return new ClientTelemetryBindingElement(telemetryClient, operationMap); + return new ClientTelemetryBindingElement(this.telemetryClient, this.operationMap); } public override T GetProperty(BindingContext context) @@ -38,57 +44,63 @@ public override T GetProperty(BindingContext context) return context.GetInnerProperty(); } - public override bool CanBuildChannelFactory(BindingContext context) { - if ( context == null ) + if (context == null) { throw new ArgumentNullException(nameof(context)); } - if ( IsSupportedChannelShape(typeof(TChannel)) ) + + if (this.IsSupportedChannelShape(typeof(TChannel))) { return context.CanBuildInnerChannelFactory(); } + return false; } public override IChannelFactory BuildChannelFactory(BindingContext context) { - if ( context == null ) + if (context == null) { throw new ArgumentNullException(nameof(context)); } - if ( !IsSupportedChannelShape(typeof(TChannel)) ) + + if (!this.IsSupportedChannelShape(typeof(TChannel))) { throw new InvalidOperationException("Unsupported channel shape: " + typeof(TChannel)); } - var innerFactory = context.BuildInnerChannelFactory(); - var factory = new ClientTelemetryChannelFactory(context.Binding, innerFactory, telemetryClient, operationMap); - factory.RootOperationIdHeaderName = RootOperationIdHeaderName; - factory.ParentOperationIdHeaderName = ParentOperationIdHeaderName; - factory.SoapRootOperationIdHeaderName = SoapRootOperationIdHeaderName; - factory.SoapParentOperationIdHeaderName = SoapParentOperationIdHeaderName; - factory.SoapHeaderNamespace = SoapHeaderNamespace; + var innerFactory = context.BuildInnerChannelFactory(); + var factory = new ClientTelemetryChannelFactory(context.Binding, innerFactory, this.telemetryClient, this.operationMap) + { + RootOperationIdHeaderName = this.RootOperationIdHeaderName, + ParentOperationIdHeaderName = this.ParentOperationIdHeaderName, + SoapRootOperationIdHeaderName = this.SoapRootOperationIdHeaderName, + SoapParentOperationIdHeaderName = this.SoapParentOperationIdHeaderName, + SoapHeaderNamespace = this.SoapHeaderNamespace + }; return factory; } private bool IsSupportedChannelShape(Type type) { - if ( type == typeof(IRequestChannel) || type == typeof(IRequestSessionChannel) ) + if (type == typeof(IRequestChannel) || type == typeof(IRequestSessionChannel)) { return true; } - if ( type == typeof(IOutputChannel) || type == typeof(IOutputSessionChannel) ) + + if (type == typeof(IOutputChannel) || type == typeof(IOutputSessionChannel)) { return true; } - if ( type == typeof(IDuplexChannel) || type == typeof(IDuplexSessionChannel) ) + + if (type == typeof(IDuplexChannel) || type == typeof(IDuplexSessionChannel)) { return true; } + return false; } - } } diff --git a/WCF/Shared/Implementation/ClientTelemetryChannelBase.cs b/WCF/Shared/Implementation/ClientTelemetryChannelBase.cs index 29a9149..01f2e79 100644 --- a/WCF/Shared/Implementation/ClientTelemetryChannelBase.cs +++ b/WCF/Shared/Implementation/ClientTelemetryChannelBase.cs @@ -1,247 +1,182 @@ -using Microsoft.ApplicationInsights.DataContracts; -using System; -using System.ServiceModel; -using System.ServiceModel.Channels; -using System.Threading; - -namespace Microsoft.ApplicationInsights.Wcf.Implementation +namespace Microsoft.ApplicationInsights.Wcf.Implementation { + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + using Microsoft.ApplicationInsights.DataContracts; + internal abstract class ClientTelemetryChannelBase : IDisposable { - protected IChannel InnerChannel { get; private set; } - protected IChannelManager ChannelManager { get; private set; } - - public CommunicationState State - { - get { return InnerChannel.State; } - } - - public abstract EndpointAddress RemoteAddress { get; } - - public event EventHandler Closed; - public event EventHandler Closing; - public event EventHandler Faulted; - public event EventHandler Opened; - public event EventHandler Opening; - public ClientTelemetryChannelBase(IChannelManager channelManager, IChannel channel) { - if ( channelManager == null ) + if (channelManager == null) { throw new ArgumentNullException(nameof(channelManager)); } - if ( channel == null ) + + if (channel == null) { throw new ArgumentNullException(nameof(channel)); } + this.ChannelManager = channelManager; this.InnerChannel = channel; } + public event EventHandler Closed; + + public event EventHandler Closing; + + public event EventHandler Faulted; + + public event EventHandler Opened; + + public event EventHandler Opening; + + public CommunicationState State + { + get { return this.InnerChannel.State; } + } + + public abstract EndpointAddress RemoteAddress { get; } + + protected IChannel InnerChannel { get; private set; } + + protected IChannelManager ChannelManager { get; private set; } + public void Open() { - Open(ChannelManager.OpenTimeout); + this.Open(this.ChannelManager.OpenTimeout); } public void Open(TimeSpan timeout) { - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(Open)); - HookChannelEvents(); - var telemetry = this.StartOpenTelemetry(nameof(Open)); + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.Open)); + this.HookChannelEvents(); + var telemetry = this.StartOpenTelemetry(nameof(this.Open)); try { - InnerChannel.Open(timeout); - this.StopOpenTelemetry(telemetry, null, nameof(Open)); - } catch ( Exception ex ) + this.InnerChannel.Open(timeout); + this.StopOpenTelemetry(telemetry, null, nameof(this.Open)); + } + catch (Exception ex) { - this.StopOpenTelemetry(telemetry, ex, nameof(Open)); + this.StopOpenTelemetry(telemetry, ex, nameof(this.Open)); throw; } } public void Close() { - Close(ChannelManager.CloseTimeout); + this.Close(this.ChannelManager.CloseTimeout); } public void Close(TimeSpan timeout) { - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(Close)); + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.Close)); try { - InnerChannel.Close(timeout); - } finally + this.InnerChannel.Close(timeout); + } + finally { - Dispose(true); + this.Dispose(true); } } public void Abort() { - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(Abort)); + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.Abort)); try { - InnerChannel.Abort(); - } finally + this.InnerChannel.Abort(); + } + finally { - Dispose(true); + this.Dispose(true); } } public T GetProperty() where T : class { - return InnerChannel.GetProperty(); + return this.InnerChannel.GetProperty(); } - // + // ------------------------------------- // Async Methods - // + // ------------------------------------- public IAsyncResult BeginOpen(AsyncCallback callback, object state) { - return BeginOpen(this.ChannelManager.OpenTimeout, callback, state); + return this.BeginOpen(this.ChannelManager.OpenTimeout, callback, state); } public IAsyncResult BeginOpen(TimeSpan timeout, AsyncCallback callback, object state) { - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(BeginOpen)); - HookChannelEvents(); - var telemetry = StartOpenTelemetry(nameof(BeginOpen)); + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.BeginOpen)); + this.HookChannelEvents(); + var telemetry = this.StartOpenTelemetry(nameof(this.BeginOpen)); try { - return new OpenAsyncResult(InnerChannel, timeout, this.OpenCompleted, callback, state, telemetry); - } catch ( Exception ex ) + return new OpenAsyncResult(this.InnerChannel, timeout, this.OpenCompleted, callback, state, telemetry); + } + catch (Exception ex) { - StopOpenTelemetry(telemetry, ex, nameof(BeginOpen)); + this.StopOpenTelemetry(telemetry, ex, nameof(this.BeginOpen)); throw; } } public void EndOpen(IAsyncResult result) { - if ( result == null ) + if (result == null) { throw new ArgumentNullException(nameof(result)); } - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(EndOpen)); - OpenAsyncResult.End(result); - } - private void OpenCompleted(IAsyncResult result) - { - if ( result == null ) - { - throw new ArgumentNullException(nameof(result)); - } - var oar = (OpenAsyncResult)result; - StopOpenTelemetry(oar.Telemetry, oar.LastException, nameof(OpenCompleted)); + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.EndOpen)); + OpenAsyncResult.End(result); } public IAsyncResult BeginClose(AsyncCallback callback, object state) { - return BeginClose(ChannelManager.CloseTimeout, callback, state); + return this.BeginClose(this.ChannelManager.CloseTimeout, callback, state); } public IAsyncResult BeginClose(TimeSpan timeout, AsyncCallback callback, object state) { - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(BeginClose)); - return InnerChannel.BeginClose(timeout, callback, state); + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.BeginClose)); + return this.InnerChannel.BeginClose(timeout, callback, state); } public void EndClose(IAsyncResult result) { - if ( result == null ) + if (result == null) { throw new ArgumentNullException(nameof(result)); } - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(EndClose)); + + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.EndClose)); try { - InnerChannel.EndClose(result); - } finally + this.InnerChannel.EndClose(result); + } + finally { - Dispose(true); + this.Dispose(true); } } - - private void HookChannelEvents() - { - InnerChannel.Closed += OnChannelClosed; - InnerChannel.Closing += OnChannelClosing; - InnerChannel.Opened += OnChannelOpened; - InnerChannel.Opening += OnChannelOpening; - InnerChannel.Faulted += OnChannelFaulted; - } - private void UnhookChannelEvents() - { - InnerChannel.Closed -= OnChannelClosed; - InnerChannel.Closing -= OnChannelClosing; - InnerChannel.Opened -= OnChannelOpened; - InnerChannel.Opening -= OnChannelOpening; - InnerChannel.Faulted -= OnChannelFaulted; - } - private void OnChannelOpened(object sender, EventArgs e) - { - Opened?.Invoke(this, e); - } - private void OnChannelOpening(object sender, EventArgs e) - { - Opening?.Invoke(this, e); - } - private void OnChannelClosed(object sender, EventArgs e) - { - Closed?.Invoke(sender, e); - } - private void OnChannelClosing(object sender, EventArgs e) - { - Closing?.Invoke(sender, e); - } - private void OnChannelFaulted(object sender, EventArgs e) + void IDisposable.Dispose() { - Faulted?.Invoke(sender, e); + this.Dispose(true); } - // telemetry implementation - private DependencyTelemetry StartOpenTelemetry(String method) - { - try - { - var telemetry = new DependencyTelemetry(); - telemetry.Start(); - telemetry.Type = DependencyConstants.WcfChannelOpen; - telemetry.Target = RemoteAddress.Uri.Host; - telemetry.Data = RemoteAddress.Uri.ToString(); - telemetry.Name = ChannelManager.OperationMap.ContractType.Name; - return telemetry; - } catch ( Exception ex ) - { - WcfClientEventSource.Log.ClientTelemetryError(method, ex.ToString()); - return null; - } - } - private void StopOpenTelemetry(DependencyTelemetry telemetry, Exception error, String method) - { - if ( telemetry == null ) - { - return; - } - try - { - telemetry.Success = error == null; - telemetry.Stop(); - ChannelManager.TelemetryClient.TrackDependency(telemetry); - } catch ( Exception ex ) - { - WcfClientEventSource.Log.ClientTelemetryError(method, ex.ToString()); - } - } - - protected DependencyTelemetry StartSendTelemetry(Message request, String method) + protected DependencyTelemetry StartSendTelemetry(Message request, string method) { var soapAction = request.Headers.Action; ClientOperation operation; - if ( !ChannelManager.OperationMap.TryLookupByAction(soapAction, out operation) ) + if (!this.ChannelManager.OperationMap.TryLookupByAction(soapAction, out operation)) { return null; } @@ -249,113 +184,211 @@ protected DependencyTelemetry StartSendTelemetry(Message request, String method) try { var telemetry = new DependencyTelemetry(); - ChannelManager.TelemetryClient.Initialize(telemetry); + this.ChannelManager.TelemetryClient.Initialize(telemetry); telemetry.Start(); telemetry.Type = DependencyConstants.WcfClientCall; - telemetry.Target = RemoteAddress.Uri.Host; - telemetry.Data = RemoteAddress.Uri.ToString(); + telemetry.Target = this.RemoteAddress.Uri.Host; + telemetry.Data = this.RemoteAddress.Uri.ToString(); telemetry.Name = operation.Name; telemetry.Properties[DependencyConstants.SoapActionProperty] = soapAction; - if ( operation.IsOneWay ) + if (operation.IsOneWay) { - telemetry.Properties[DependencyConstants.IsOneWayProperty] = Boolean.TrueString; + telemetry.Properties[DependencyConstants.IsOneWayProperty] = bool.TrueString; } - SetCorrelationHeaders(telemetry, request); + + this.SetCorrelationHeaders(telemetry, request); return telemetry; - } catch ( Exception ex ) + } + catch (Exception ex) { WcfClientEventSource.Log.ClientTelemetryError(method, ex.ToString()); return null; } } - protected void StopSendTelemetry(DependencyTelemetry telemetry, Message response, Exception error, String method) + + protected void StopSendTelemetry(DependencyTelemetry telemetry, Message response, Exception error, string method) { - if ( telemetry == null ) + if (telemetry == null) { return; } + try { - if ( error != null ) + if (error != null) { telemetry.Success = false; telemetry.ResultCode = error.ToResultCode(); } - if ( response != null && response.IsFault ) + + if (response != null && response.IsFault) { telemetry.Success = false; telemetry.ResultCode = "SoapFault"; } + telemetry.Stop(); - ChannelManager.TelemetryClient.TrackDependency(telemetry); - } catch ( Exception ex ) + this.ChannelManager.TelemetryClient.TrackDependency(telemetry); + } + catch (Exception ex) { WcfClientEventSource.Log.ClientTelemetryError(method, ex.ToString()); } } + protected bool IsOneWay(DependencyTelemetry telemetry) { - String value; - if ( telemetry.Properties.TryGetValue(DependencyConstants.IsOneWayProperty, out value) ) + string value; + if (telemetry.Properties.TryGetValue(DependencyConstants.IsOneWayProperty, out value)) { - return Boolean.Parse(value); + return bool.Parse(value); } + return false; } - void IDisposable.Dispose() + protected void Dispose(bool disposing) { - this.Dispose(true); + this.UnhookChannelEvents(); + this.OnClosed(); } - protected void Dispose(bool disposing) + protected virtual void OnClosed() { - UnhookChannelEvents(); - OnClosed(); } - protected virtual void OnClosed() + + private static void SetSoapHeader(Message message, string soapNS, string header, string value) { + var currentHeader = message.Headers.FindHeader(header, soapNS); + if (currentHeader < 0) + { + var soapHeader = MessageHeader.CreateHeader(header, soapNS, value); + message.Headers.Add(soapHeader); + } + } + + private static string ValueOrDefault(string value, string defaultValue) + { + return string.IsNullOrEmpty(value) ? defaultValue : value; + } + + private void HookChannelEvents() + { + this.InnerChannel.Closed += this.OnChannelClosed; + this.InnerChannel.Closing += this.OnChannelClosing; + this.InnerChannel.Opened += this.OnChannelOpened; + this.InnerChannel.Opening += this.OnChannelOpening; + this.InnerChannel.Faulted += this.OnChannelFaulted; + } + + private void UnhookChannelEvents() + { + this.InnerChannel.Closed -= this.OnChannelClosed; + this.InnerChannel.Closing -= this.OnChannelClosing; + this.InnerChannel.Opened -= this.OnChannelOpened; + this.InnerChannel.Opening -= this.OnChannelOpening; + this.InnerChannel.Faulted -= this.OnChannelFaulted; + } + + private void OpenCompleted(IAsyncResult result) + { + if (result == null) + { + throw new ArgumentNullException(nameof(result)); + } + + var oar = (OpenAsyncResult)result; + this.StopOpenTelemetry(oar.Telemetry, oar.LastException, nameof(this.OpenCompleted)); + } + + private void OnChannelOpened(object sender, EventArgs e) + { + this.Opened?.Invoke(this, e); + } + + private void OnChannelOpening(object sender, EventArgs e) + { + this.Opening?.Invoke(this, e); + } + + private void OnChannelClosed(object sender, EventArgs e) + { + this.Closed?.Invoke(sender, e); + } + + private void OnChannelClosing(object sender, EventArgs e) + { + this.Closing?.Invoke(sender, e); + } + + private void OnChannelFaulted(object sender, EventArgs e) + { + this.Faulted?.Invoke(sender, e); + } + + private DependencyTelemetry StartOpenTelemetry(string method) + { + try + { + var telemetry = new DependencyTelemetry(); + telemetry.Start(); + telemetry.Type = DependencyConstants.WcfChannelOpen; + telemetry.Target = this.RemoteAddress.Uri.Host; + telemetry.Data = this.RemoteAddress.Uri.ToString(); + telemetry.Name = this.ChannelManager.OperationMap.ContractType.Name; + return telemetry; + } + catch (Exception ex) + { + WcfClientEventSource.Log.ClientTelemetryError(method, ex.ToString()); + return null; + } + } + + private void StopOpenTelemetry(DependencyTelemetry telemetry, Exception error, string method) + { + if (telemetry == null) + { + return; + } + + try + { + telemetry.Success = error == null; + telemetry.Stop(); + this.ChannelManager.TelemetryClient.TrackDependency(telemetry); + } + catch (Exception ex) + { + WcfClientEventSource.Log.ClientTelemetryError(method, ex.ToString()); + } } private void SetCorrelationHeaders(DependencyTelemetry telemetry, Message message) { var httpHeaders = message.GetHttpRequestHeaders(); - var rootIdHttpHeader = ValueOrDefault(ChannelManager.RootOperationIdHeaderName, CorrelationHeaders.HttpStandardRootIdHeader); - var rootIdSoapHeader = ValueOrDefault(ChannelManager.SoapRootOperationIdHeaderName, CorrelationHeaders.SoapStandardRootIdHeader); - var parentIdHttpHeader = ValueOrDefault(ChannelManager.ParentOperationIdHeaderName, CorrelationHeaders.HttpStandardParentIdHeader); - var parentIdSoapHeader = ValueOrDefault(ChannelManager.SoapParentOperationIdHeaderName, CorrelationHeaders.SoapStandardParentIdHeader); + var rootIdHttpHeader = ValueOrDefault(this.ChannelManager.RootOperationIdHeaderName, CorrelationHeaders.HttpStandardRootIdHeader); + var rootIdSoapHeader = ValueOrDefault(this.ChannelManager.SoapRootOperationIdHeaderName, CorrelationHeaders.SoapStandardRootIdHeader); + var parentIdHttpHeader = ValueOrDefault(this.ChannelManager.ParentOperationIdHeaderName, CorrelationHeaders.HttpStandardParentIdHeader); + var parentIdSoapHeader = ValueOrDefault(this.ChannelManager.SoapParentOperationIdHeaderName, CorrelationHeaders.SoapStandardParentIdHeader); + // "" is a valid value for the namespace - var soapNS = ChannelManager.SoapHeaderNamespace != null ? ChannelManager.SoapHeaderNamespace : CorrelationHeaders.SoapStandardNamespace; + var soapNS = this.ChannelManager.SoapHeaderNamespace ?? CorrelationHeaders.SoapStandardNamespace; var rootId = telemetry.Context.Operation.Id; - if ( !String.IsNullOrEmpty(rootId) ) + if (!string.IsNullOrEmpty(rootId)) { httpHeaders.Headers[rootIdHttpHeader] = rootId; SetSoapHeader(message, soapNS, rootIdSoapHeader, rootId); } var parentId = telemetry.Id; - if ( !String.IsNullOrEmpty(parentId) ) + if (!string.IsNullOrEmpty(parentId)) { httpHeaders.Headers[parentIdHttpHeader] = parentId; SetSoapHeader(message, soapNS, parentIdSoapHeader, parentId); } } - - private void SetSoapHeader(Message message, String soapNS, String header, String value) - { - int currentHeader = message.Headers.FindHeader(header, soapNS); - if ( currentHeader < 0 ) - { - var soapHeader = MessageHeader.CreateHeader(header, soapNS, value); - message.Headers.Add(soapHeader); - } - } - - private String ValueOrDefault(String value, String defaultValue) - { - return String.IsNullOrEmpty(value) ? defaultValue : value; - } } } diff --git a/WCF/Shared/Implementation/ClientTelemetryChannelFactory.cs b/WCF/Shared/Implementation/ClientTelemetryChannelFactory.cs index fd011dd..43d9b41 100644 --- a/WCF/Shared/Implementation/ClientTelemetryChannelFactory.cs +++ b/WCF/Shared/Implementation/ClientTelemetryChannelFactory.cs @@ -1,35 +1,31 @@ -using System; -using System.ServiceModel; -using System.ServiceModel.Channels; - -namespace Microsoft.ApplicationInsights.Wcf.Implementation +namespace Microsoft.ApplicationInsights.Wcf.Implementation { + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + internal sealed class ClientTelemetryChannelFactory : ChannelFactoryBase, IChannelManager { private IChannelFactory innerFactory; - public TelemetryClient TelemetryClient { get; private set; } - public ClientContract OperationMap { get; set; } - public String RootOperationIdHeaderName { get; set; } - public String ParentOperationIdHeaderName { get; set; } - public String SoapRootOperationIdHeaderName { get; set; } - public String SoapParentOperationIdHeaderName { get; set; } - public String SoapHeaderNamespace { get; set; } public ClientTelemetryChannelFactory(Binding binding, IChannelFactory factory, TelemetryClient client, ClientContract map) : base(binding) { - if ( factory == null ) + if (factory == null) { - throw new ArgumentNullException(nameof(factory)); + throw new ArgumentNullException(nameof(factory)); } - if ( client == null ) + + if (client == null) { throw new ArgumentNullException(nameof(client)); } - if ( map == null ) + + if (map == null) { throw new ArgumentNullException(nameof(map)); } + this.innerFactory = factory; this.TelemetryClient = client; this.OperationMap = map; @@ -37,25 +33,42 @@ public ClientTelemetryChannelFactory(Binding binding, IChannelFactory WcfClientEventSource.Log.ChannelFactoryCreated(typeof(TChannel).FullName); } + public TelemetryClient TelemetryClient { get; private set; } + + public ClientContract OperationMap { get; set; } + + public string RootOperationIdHeaderName { get; set; } + + public string ParentOperationIdHeaderName { get; set; } + + public string SoapRootOperationIdHeaderName { get; set; } + + public string SoapParentOperationIdHeaderName { get; set; } + + public string SoapHeaderNamespace { get; set; } + public override T GetProperty() { - return innerFactory.GetProperty(); + return this.innerFactory.GetProperty(); } protected override void OnOpen(TimeSpan timeout) { this.innerFactory.Open(timeout); } + protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state) { return this.innerFactory.BeginOpen(timeout, callback, state); } + protected override void OnEndOpen(IAsyncResult result) { - if ( result == null ) + if (result == null) { throw new ArgumentNullException(nameof(result)); } + this.innerFactory.EndOpen(result); } @@ -63,18 +76,22 @@ protected override void OnClose(TimeSpan timeout) { this.innerFactory.Close(timeout); } + protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state) { return this.innerFactory.BeginClose(timeout, callback, state); } + protected override void OnEndClose(IAsyncResult result) { - if ( result == null ) + if (result == null) { throw new ArgumentNullException(nameof(result)); } + this.innerFactory.EndClose(result); } + protected override void OnAbort() { this.innerFactory.Abort(); @@ -82,28 +99,31 @@ protected override void OnAbort() protected override TChannel OnCreateChannel(EndpointAddress address, Uri via) { - if ( address == null ) + if (address == null) { throw new ArgumentNullException(nameof(address)); } var channel = this.innerFactory.CreateChannel(address, via); IChannel newChannel = null; - if ( typeof(TChannel) == typeof(IRequestChannel) || typeof(TChannel) == typeof(IRequestSessionChannel) ) + if (typeof(TChannel) == typeof(IRequestChannel) || typeof(TChannel) == typeof(IRequestSessionChannel)) { newChannel = new ClientTelemetryRequestChannel(this, (IChannel)channel); - } else if ( typeof(TChannel) == typeof(IOutputChannel) || typeof(TChannel) == typeof(IOutputSessionChannel) ) + } + else if (typeof(TChannel) == typeof(IOutputChannel) || typeof(TChannel) == typeof(IOutputSessionChannel)) { newChannel = new ClientTelemetryOutputChannel(this, (IChannel)channel); - } else if ( typeof(TChannel) == typeof(IDuplexChannel) || typeof(TChannel) == typeof(IDuplexSessionChannel) ) + } + else if (typeof(TChannel) == typeof(IDuplexChannel) || typeof(TChannel) == typeof(IDuplexSessionChannel)) { newChannel = new ClientTelemetryDuplexChannel(this, (IChannel)channel); - } else + } + else { throw new NotSupportedException("Channel shape is not supported: " + typeof(TChannel)); } + return (TChannel)(object)newChannel; } - } } diff --git a/WCF/Shared/Implementation/ClientTelemetryDuplexChannel.cs b/WCF/Shared/Implementation/ClientTelemetryDuplexChannel.cs index a714c2d..1459815 100644 --- a/WCF/Shared/Implementation/ClientTelemetryDuplexChannel.cs +++ b/WCF/Shared/Implementation/ClientTelemetryDuplexChannel.cs @@ -1,13 +1,10 @@ -using Microsoft.ApplicationInsights.DataContracts; -using System; -using System.Collections.Generic; -using System.ServiceModel; -using System.ServiceModel.Channels; -using System.Text; -using System.Xml; - -namespace Microsoft.ApplicationInsights.Wcf.Implementation +namespace Microsoft.ApplicationInsights.Wcf.Implementation { + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.Xml; + using Microsoft.ApplicationInsights.DataContracts; // An IRequestChannel is simulated on top of a duplex channel is // done by DuplexChannelBinder.Request() by correlating messages based on the message id @@ -22,238 +19,255 @@ internal sealed class ClientTelemetryDuplexChannel : ClientTelemetryChannelBase, { private MessageCorrelator correlator; - private IDuplexChannel DuplexChannel + public ClientTelemetryDuplexChannel(IChannelManager channelManager, IChannel channel) + : base(channelManager, channel) { - get { return (IDuplexChannel)InnerChannel; } + this.correlator = new MessageCorrelator(this.OnRequestTimeout); } + public EndpointAddress LocalAddress { - get { return DuplexChannel.LocalAddress; } + get { return this.DuplexChannel.LocalAddress; } } + public IDuplexSession Session { - get { return ((IDuplexSessionChannel)DuplexChannel).Session; } + get { return ((IDuplexSessionChannel)this.DuplexChannel).Session; } } + public Uri Via { - get { return DuplexChannel.Via; } + get { return this.DuplexChannel.Via; } } public override EndpointAddress RemoteAddress { - get { return DuplexChannel.RemoteAddress; } + get { return this.DuplexChannel.RemoteAddress; } } - public ClientTelemetryDuplexChannel(IChannelManager channelManager, IChannel channel) - : base(channelManager, channel) + private IDuplexChannel DuplexChannel { - correlator = new MessageCorrelator(this.OnRequestTimeout); + get { return (IDuplexChannel)this.InnerChannel; } } - // + // ------------------------ // Send side - // + // ------------------------ public void Send(Message message) { - Send(message, ChannelManager.SendTimeout); + this.Send(message, this.ChannelManager.SendTimeout); } public void Send(Message message, TimeSpan timeout) { - if ( message == null ) + if (message == null) { throw new ArgumentNullException(nameof(message)); } - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(Send)); - var telemetry = StartSendTelemetry(message, nameof(Send)); + + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.Send)); + var telemetry = StartSendTelemetry(message, nameof(this.Send)); try { - bool isOneWay = IsOneWay(telemetry); - DuplexChannel.Send(message, timeout); - if ( isOneWay ) + var isOneWay = IsOneWay(telemetry); + this.DuplexChannel.Send(message, timeout); + if (isOneWay) { // no matching receive - StopSendTelemetry(telemetry, null, null, nameof(Send)); - } else + this.StopSendTelemetry(telemetry, null, null, nameof(this.Send)); + } + else { - correlator.Add(message.Headers.MessageId, telemetry, timeout); + this.correlator.Add(message.Headers.MessageId, telemetry, timeout); } - } catch ( Exception ex ) + } + catch (Exception ex) { - StopSendTelemetry(telemetry, null, ex, nameof(Send)); + this.StopSendTelemetry(telemetry, null, ex, nameof(this.Send)); throw; } } public IAsyncResult BeginSend(Message message, AsyncCallback callback, object state) { - return BeginSend(message, ChannelManager.SendTimeout, callback, state); + return this.BeginSend(message, this.ChannelManager.SendTimeout, callback, state); } public IAsyncResult BeginSend(Message message, TimeSpan timeout, AsyncCallback callback, object state) { - if ( message == null ) + if (message == null) { throw new ArgumentNullException(nameof(message)); } - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(BeginSend)); - var telemetry = StartSendTelemetry(message, nameof(BeginSend)); + + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.BeginSend)); + var telemetry = StartSendTelemetry(message, nameof(this.BeginSend)); try { - var result = new SendAsyncResult(DuplexChannel, message, timeout, this.OnSendDone, callback, state, telemetry); - correlator.Add(message.Headers.MessageId, telemetry, timeout); + var result = new SendAsyncResult(this.DuplexChannel, message, timeout, this.OnSendDone, callback, state, telemetry); + this.correlator.Add(message.Headers.MessageId, telemetry, timeout); return result; - } catch ( Exception ex ) + } + catch (Exception ex) { - StopSendTelemetry(telemetry, null, ex, nameof(BeginSend)); + this.StopSendTelemetry(telemetry, null, ex, nameof(this.BeginSend)); throw; } } public void EndSend(IAsyncResult result) { - if ( result == null ) + if (result == null) { throw new ArgumentNullException(nameof(result)); } - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(EndSend)); - SendAsyncResult.End(result); - } - private void OnSendDone(IAsyncResult result) - { - SendAsyncResult sar = (SendAsyncResult)result; - - if ( IsOneWay(sar.Telemetry) || sar.LastException != null ) - { - // not expecting reply - correlator.Remove(sar.RequestId); - StopSendTelemetry(sar.Telemetry, null, sar.LastException, nameof(OnSendDone)); - } + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.EndSend)); + SendAsyncResult.End(result); } - - // + // ------------------------------------------- // Receive Side - // - + // ------------------------------------------- // Both Receive and Receive(timeout) should fail with a // if we get a timeout, we have no way of knowing which // outstanding message it closed :( public Message Receive() { - return Receive(ChannelManager.ReceiveTimeout); + return this.Receive(this.ChannelManager.ReceiveTimeout); } public Message Receive(TimeSpan timeout) { - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(Receive)); - var response = DuplexChannel.Receive(timeout); - if ( response != null ) + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.Receive)); + var response = this.DuplexChannel.Receive(timeout); + if (response != null) { - HandleReply(response); + this.HandleReply(response); } + return response; } public bool TryReceive(TimeSpan timeout, out Message message) { - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(TryReceive)); - bool success = DuplexChannel.TryReceive(timeout, out message); - if ( success && message != null ) + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.TryReceive)); + var success = this.DuplexChannel.TryReceive(timeout, out message); + if (success && message != null) { - HandleReply(message); + this.HandleReply(message); } + return success; } public bool WaitForMessage(TimeSpan timeout) { - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(WaitForMessage)); - return DuplexChannel.WaitForMessage(timeout); + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.WaitForMessage)); + return this.DuplexChannel.WaitForMessage(timeout); } public IAsyncResult BeginReceive(AsyncCallback callback, object state) { - return BeginReceive(ChannelManager.ReceiveTimeout, callback, state); + return this.BeginReceive(this.ChannelManager.ReceiveTimeout, callback, state); } public IAsyncResult BeginReceive(TimeSpan timeout, AsyncCallback callback, object state) { - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(BeginReceive)); + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.BeginReceive)); return new ReceiveAsyncResult(this.DuplexChannel, timeout, null, callback, state); } + public Message EndReceive(IAsyncResult result) { - if ( result == null ) + if (result == null) { throw new ArgumentNullException(nameof(result)); } - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(EndReceive)); + + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.EndReceive)); var rar = ReceiveAsyncResult.End(result); - if ( rar.Message != null ) + if (rar.Message != null) { this.HandleReply(rar.Message); } + return rar.Message; } public IAsyncResult BeginTryReceive(TimeSpan timeout, AsyncCallback callback, object state) { - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(BeginTryReceive)); - return new TryReceiveAsyncResult(DuplexChannel, timeout, null, callback, state); + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.BeginTryReceive)); + return new TryReceiveAsyncResult(this.DuplexChannel, timeout, null, callback, state); } + public bool EndTryReceive(IAsyncResult result, out Message message) { - if ( result == null ) + if (result == null) { throw new ArgumentNullException(nameof(result)); } - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(EndTryReceive)); + + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.EndTryReceive)); var trar = TryReceiveAsyncResult.End(result); message = trar.Message; - if ( trar.Result && message != null ) + if (trar.Result && message != null) { this.HandleReply(message); } + return trar.Result; } - public IAsyncResult BeginWaitForMessage(TimeSpan timeout, AsyncCallback callback, object state) { - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(BeginWaitForMessage)); - return DuplexChannel.BeginWaitForMessage(timeout, callback, state); + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.BeginWaitForMessage)); + return this.DuplexChannel.BeginWaitForMessage(timeout, callback, state); } + public bool EndWaitForMessage(IAsyncResult result) { - if ( result == null ) + if (result == null) { throw new ArgumentNullException(nameof(result)); } - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(EndWaitForMessage)); - return DuplexChannel.EndWaitForMessage(result); - } - + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.EndWaitForMessage)); + return this.DuplexChannel.EndWaitForMessage(result); + } protected override void OnClosed() { - correlator.Dispose(); + this.correlator.Dispose(); base.OnClosed(); } + + private void OnSendDone(IAsyncResult result) + { + var sar = (SendAsyncResult)result; + + if (this.IsOneWay(sar.Telemetry) || sar.LastException != null) + { + // not expecting reply + this.correlator.Remove(sar.RequestId); + this.StopSendTelemetry(sar.Telemetry, null, sar.LastException, nameof(this.OnSendDone)); + } + } + private void HandleReply(Message reply) { DependencyTelemetry telemetry = null; - if ( correlator.TryLookup(reply.Headers.RelatesTo, out telemetry) ) + if (this.correlator.TryLookup(reply.Headers.RelatesTo, out telemetry)) { - StopSendTelemetry(telemetry, reply, null, nameof(HandleReply)); + this.StopSendTelemetry(telemetry, reply, null, nameof(this.HandleReply)); } + // not our message, leave it be } + private void OnRequestTimeout(UniqueId messageId, DependencyTelemetry telemetry) { - StopSendTelemetry(telemetry, null, new TimeoutException(), nameof(OnRequestTimeout)); + this.StopSendTelemetry(telemetry, null, new TimeoutException(), nameof(this.OnRequestTimeout)); } } } diff --git a/WCF/Shared/Implementation/ClientTelemetryOutputChannel.cs b/WCF/Shared/Implementation/ClientTelemetryOutputChannel.cs index 8f368f9..df7ec2b 100644 --- a/WCF/Shared/Implementation/ClientTelemetryOutputChannel.cs +++ b/WCF/Shared/Implementation/ClientTelemetryOutputChannel.cs @@ -1,100 +1,107 @@ -using System; -using System.ServiceModel; -using System.ServiceModel.Channels; - -namespace Microsoft.ApplicationInsights.Wcf.Implementation +namespace Microsoft.ApplicationInsights.Wcf.Implementation { - sealed class ClientTelemetryOutputChannel : ClientTelemetryChannelBase, IOutputChannel, IOutputSessionChannel + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + + internal sealed class ClientTelemetryOutputChannel : ClientTelemetryChannelBase, IOutputChannel, IOutputSessionChannel { - private IOutputChannel OutputChannel + public ClientTelemetryOutputChannel(IChannelManager channelManager, IChannel channel) + : base(channelManager, channel) { - get { return (IOutputChannel)InnerChannel; } } public override EndpointAddress RemoteAddress { - get { return OutputChannel.RemoteAddress; } + get { return this.OutputChannel.RemoteAddress; } } + public Uri Via { - get { return OutputChannel.Via; } + get { return this.OutputChannel.Via; } } public IOutputSession Session { - get { return ((IOutputSessionChannel)InnerChannel).Session; } + get { return ((IOutputSessionChannel)this.InnerChannel).Session; } } - public ClientTelemetryOutputChannel(IChannelManager channelManager, IChannel channel) - : base(channelManager, channel) + private IOutputChannel OutputChannel { + get { return (IOutputChannel)this.InnerChannel; } } public void Send(Message message) { - Send(message, ChannelManager.SendTimeout); + this.Send(message, this.ChannelManager.SendTimeout); } public void Send(Message message, TimeSpan timeout) { - if ( message == null ) + if (message == null) { throw new ArgumentNullException(nameof(message)); } - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(Send)); - var telemetry = StartSendTelemetry(message, nameof(Send)); + + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.Send)); + var telemetry = StartSendTelemetry(message, nameof(this.Send)); try { - OutputChannel.Send(message, timeout); - StopSendTelemetry(telemetry, null, null, nameof(Send)); - } catch ( Exception ex ) + this.OutputChannel.Send(message, timeout); + this.StopSendTelemetry(telemetry, null, null, nameof(this.Send)); + } + catch (Exception ex) { - StopSendTelemetry(telemetry, null, ex, nameof(Send)); + this.StopSendTelemetry(telemetry, null, ex, nameof(this.Send)); throw; } } public IAsyncResult BeginSend(Message message, AsyncCallback callback, object state) { - return BeginSend(message, ChannelManager.SendTimeout, callback, state); + return this.BeginSend(message, this.ChannelManager.SendTimeout, callback, state); } public IAsyncResult BeginSend(Message message, TimeSpan timeout, AsyncCallback callback, object state) { - if ( message == null ) + if (message == null) { throw new ArgumentNullException(nameof(message)); } - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(BeginSend)); - var telemetry = StartSendTelemetry(message, nameof(BeginSend)); + + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.BeginSend)); + var telemetry = StartSendTelemetry(message, nameof(this.BeginSend)); try { - return new SendAsyncResult(OutputChannel, message, timeout, this.OnSendComplete, callback, state, telemetry); - } catch ( Exception ex ) + return new SendAsyncResult(this.OutputChannel, message, timeout, this.OnSendComplete, callback, state, telemetry); + } + catch (Exception ex) { - StopSendTelemetry(telemetry, null, ex, nameof(BeginSend)); + this.StopSendTelemetry(telemetry, null, ex, nameof(this.BeginSend)); throw; } } public void EndSend(IAsyncResult result) { - if ( result == null ) + if (result == null) { throw new ArgumentNullException(nameof(result)); } - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(EndSend)); + + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.EndSend)); SendAsyncResult.End(result); } private void OnSendComplete(IAsyncResult result) { - if ( result == null ) + if (result == null) { throw new ArgumentNullException(nameof(result)); } + var sar = (SendAsyncResult)result; - StopSendTelemetry(sar.Telemetry, null, sar.LastException, nameof(OnSendComplete)); + this.StopSendTelemetry(sar.Telemetry, null, sar.LastException, nameof(this.OnSendComplete)); } } } diff --git a/WCF/Shared/Implementation/ClientTelemetryRequestChannel.cs b/WCF/Shared/Implementation/ClientTelemetryRequestChannel.cs index 8c0653f..6f3af29 100644 --- a/WCF/Shared/Implementation/ClientTelemetryRequestChannel.cs +++ b/WCF/Shared/Implementation/ClientTelemetryRequestChannel.cs @@ -1,98 +1,103 @@ -using System; -using System.ServiceModel; -using System.ServiceModel.Channels; - -namespace Microsoft.ApplicationInsights.Wcf.Implementation +namespace Microsoft.ApplicationInsights.Wcf.Implementation { - sealed class ClientTelemetryRequestChannel : ClientTelemetryChannelBase, IRequestChannel, IRequestSessionChannel + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + + internal sealed class ClientTelemetryRequestChannel : ClientTelemetryChannelBase, IRequestChannel, IRequestSessionChannel { - private IRequestChannel RequestChannel + public ClientTelemetryRequestChannel(IChannelManager channelManager, IChannel channel) + : base(channelManager, channel) { - get { return (IRequestChannel)InnerChannel; } } public override EndpointAddress RemoteAddress { - get { return RequestChannel.RemoteAddress; } + get { return this.RequestChannel.RemoteAddress; } } + public Uri Via { - get { return RequestChannel.Via; } + get { return this.RequestChannel.Via; } } public IOutputSession Session { - get { return ((IRequestSessionChannel)InnerChannel).Session; } + get { return ((IRequestSessionChannel)this.InnerChannel).Session; } } - public ClientTelemetryRequestChannel(IChannelManager channelManager, IChannel channel) - : base(channelManager, channel) + private IRequestChannel RequestChannel { + get { return (IRequestChannel)this.InnerChannel; } } public Message Request(Message message) { - return Request(message, ChannelManager.SendTimeout); + return this.Request(message, this.ChannelManager.SendTimeout); } public Message Request(Message message, TimeSpan timeout) { - if ( message == null ) + if (message == null) { throw new ArgumentNullException(nameof(message)); } - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(Request)); - var telemetry = StartSendTelemetry(message, nameof(Request)); + + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.Request)); + var telemetry = this.StartSendTelemetry(message, nameof(this.Request)); try { - var response = RequestChannel.Request(message, timeout); - StopSendTelemetry(telemetry, response, null, nameof(Request)); + var response = this.RequestChannel.Request(message, timeout); + this.StopSendTelemetry(telemetry, response, null, nameof(this.Request)); return response; - } catch ( Exception ex ) + } + catch (Exception ex) { - StopSendTelemetry(telemetry, null, ex, nameof(Request)); + this.StopSendTelemetry(telemetry, null, ex, nameof(this.Request)); throw; } } public IAsyncResult BeginRequest(Message message, AsyncCallback callback, object state) { - return BeginRequest(message, ChannelManager.SendTimeout, callback, state); + return this.BeginRequest(message, ChannelManager.SendTimeout, callback, state); } public IAsyncResult BeginRequest(Message message, TimeSpan timeout, AsyncCallback callback, object state) { - if ( message == null ) + if (message == null) { throw new ArgumentNullException(nameof(message)); } - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(BeginRequest)); - var telemetry = StartSendTelemetry(message, nameof(BeginRequest)); + + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.BeginRequest)); + var telemetry = this.StartSendTelemetry(message, nameof(this.BeginRequest)); try { - return new RequestAsyncResult(RequestChannel, message, timeout, this.OnRequestDone, callback, state, telemetry); - } catch ( Exception ex ) + return new RequestAsyncResult(this.RequestChannel, message, timeout, this.OnRequestDone, callback, state, telemetry); + } + catch (Exception ex) { - StopSendTelemetry(telemetry, null, ex, nameof(BeginRequest)); + this.StopSendTelemetry(telemetry, null, ex, nameof(this.BeginRequest)); throw; } } public Message EndRequest(IAsyncResult result) { - if ( result == null ) + if (result == null) { throw new ArgumentNullException(nameof(result)); } - WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(EndRequest)); + + WcfClientEventSource.Log.ChannelCalled(GetType().FullName, nameof(this.EndRequest)); return RequestAsyncResult.End(result).Reply; } private void OnRequestDone(IAsyncResult result) { - RequestAsyncResult rar = (RequestAsyncResult)result; - StopSendTelemetry(rar.Telemetry, rar.Reply, rar.LastException, nameof(OnRequestDone)); + var rar = (RequestAsyncResult)result; + this.StopSendTelemetry(rar.Telemetry, rar.Reply, rar.LastException, nameof(this.OnRequestDone)); } - } } diff --git a/WCF/Shared/Implementation/ContractFilter.cs b/WCF/Shared/Implementation/ContractFilter.cs index 998ca2e..84dd357 100644 --- a/WCF/Shared/Implementation/ContractFilter.cs +++ b/WCF/Shared/Implementation/ContractFilter.cs @@ -1,41 +1,42 @@ -using System; -using System.Collections.Generic; -using System.ServiceModel.Description; - -namespace Microsoft.ApplicationInsights.Wcf.Implementation +namespace Microsoft.ApplicationInsights.Wcf.Implementation { - class ContractFilter + using System; + using System.Collections.Generic; + using System.ServiceModel.Description; + + internal class ContractFilter { - private Dictionary filters; + private Dictionary filters; public ContractFilter(IEnumerable contracts) { - filters = new Dictionary(); - foreach ( var contract in contracts ) + this.filters = new Dictionary(); + foreach (var contract in contracts) { - String key = GetKeyForContract(contract.Name, contract.Namespace); - if ( !filters.ContainsKey(key) ) + var key = this.GetKeyForContract(contract.Name, contract.Namespace); + if (!this.filters.ContainsKey(key)) { - filters[key] = new OperationFilter(contract); + this.filters[key] = new OperationFilter(contract); } } } - public bool ShouldProcess(String contractName, String contractNamespace, String operationName) + public bool ShouldProcess(string contractName, string contractNamespace, string operationName) { - String key = GetKeyForContract(contractName, contractNamespace); + var key = this.GetKeyForContract(contractName, contractNamespace); OperationFilter filter = null; - if ( filters.TryGetValue(key, out filter) ) + if (this.filters.TryGetValue(key, out filter)) { return filter.ShouldProcess(operationName); } + // if unknown contract, err on the safe side return true; } // TODO: Avoid string concatenation to avoid extra allocation - private String GetKeyForContract(String contractName, String contractNamespace) + private string GetKeyForContract(string contractName, string contractNamespace) { return contractNamespace + '#' + contractName; } diff --git a/WCF/Shared/Implementation/DependencyConstants.cs b/WCF/Shared/Implementation/DependencyConstants.cs index 5036a07..f5f8014 100644 --- a/WCF/Shared/Implementation/DependencyConstants.cs +++ b/WCF/Shared/Implementation/DependencyConstants.cs @@ -1,13 +1,13 @@ -using System; - -namespace Microsoft.ApplicationInsights.Wcf.Implementation +namespace Microsoft.ApplicationInsights.Wcf.Implementation { + using System; + internal static class DependencyConstants { - public const String WcfClientCall = "WCF Service Call"; - public const String WcfChannelOpen = "WCF Channel Open"; + public const string WcfClientCall = "WCF Service Call"; + public const string WcfChannelOpen = "WCF Channel Open"; - public const String IsOneWayProperty = "isOneWay"; - public const String SoapActionProperty = "soapAction"; + public const string IsOneWayProperty = "isOneWay"; + public const string SoapActionProperty = "soapAction"; } } diff --git a/WCF/Shared/Implementation/Executor.cs b/WCF/Shared/Implementation/Executor.cs index 2b3f1f2..9613e92 100644 --- a/WCF/Shared/Implementation/Executor.cs +++ b/WCF/Shared/Implementation/Executor.cs @@ -1,43 +1,48 @@ -using System; - -namespace Microsoft.ApplicationInsights.Wcf.Implementation +namespace Microsoft.ApplicationInsights.Wcf.Implementation { + using System; + internal static class Executor { public delegate void LogAction(TCtxt ctxt, ref TParam param); - public static void ExceptionSafe(String moduleName, String activityName, Action action, TCtxt context) + public static void ExceptionSafe(string moduleName, string activityName, Action action, TCtxt context) { WcfEventSource.Log.TelemetryModuleExecutionStarted(moduleName, activityName); try { action(context); WcfEventSource.Log.TelemetryModuleExecutionStopped(moduleName, activityName); - } catch ( Exception ex ) + } + catch (Exception ex) { WcfEventSource.Log.TelemetryModuleExecutionFailed(moduleName, activityName, ex.ToString()); } } - public static void ExceptionSafe(String moduleName, String activityName, LogAction action, TCtxt context, ref TParam param) + + public static void ExceptionSafe(string moduleName, string activityName, LogAction action, TCtxt context, ref TParam param) { WcfEventSource.Log.TelemetryModuleExecutionStarted(moduleName, activityName); try { action(context, ref param); WcfEventSource.Log.TelemetryModuleExecutionStopped(moduleName, activityName); - } catch ( Exception ex ) + } + catch (Exception ex) { WcfEventSource.Log.TelemetryModuleExecutionFailed(moduleName, activityName, ex.ToString()); } } - public static void ExceptionSafe(String moduleName, String activityName, Action action, TCtxt context, TValue value) + + public static void ExceptionSafe(string moduleName, string activityName, Action action, TCtxt context, TValue value) { WcfEventSource.Log.TelemetryModuleExecutionStarted(moduleName, activityName); try { action(context, value); WcfEventSource.Log.TelemetryModuleExecutionStopped(moduleName, activityName); - } catch ( Exception ex ) + } + catch (Exception ex) { WcfEventSource.Log.TelemetryModuleExecutionFailed(moduleName, activityName, ex.ToString()); } diff --git a/WCF/Shared/Implementation/IChannelManager.cs b/WCF/Shared/Implementation/IChannelManager.cs index 68562b9..0fef700 100644 --- a/WCF/Shared/Implementation/IChannelManager.cs +++ b/WCF/Shared/Implementation/IChannelManager.cs @@ -1,17 +1,22 @@ -using System; -using System.ServiceModel; - -namespace Microsoft.ApplicationInsights.Wcf.Implementation +namespace Microsoft.ApplicationInsights.Wcf.Implementation { + using System; + using System.ServiceModel; + internal interface IChannelManager : IDefaultCommunicationTimeouts { TelemetryClient TelemetryClient { get; } + ClientContract OperationMap { get; } - String RootOperationIdHeaderName { get; } - String ParentOperationIdHeaderName { get; } - String SoapRootOperationIdHeaderName { get; } - String SoapParentOperationIdHeaderName { get; } - String SoapHeaderNamespace { get; } + string RootOperationIdHeaderName { get; } + + string ParentOperationIdHeaderName { get; } + + string SoapRootOperationIdHeaderName { get; } + + string SoapParentOperationIdHeaderName { get; } + + string SoapHeaderNamespace { get; } } } diff --git a/WCF/Shared/Implementation/MessageCorrelator.cs b/WCF/Shared/Implementation/MessageCorrelator.cs index 4afe304..cfe09a9 100644 --- a/WCF/Shared/Implementation/MessageCorrelator.cs +++ b/WCF/Shared/Implementation/MessageCorrelator.cs @@ -1,14 +1,14 @@ -using Microsoft.ApplicationInsights.DataContracts; -using System; -using System.Collections.Generic; -using System.Threading; -using System.Xml; - -namespace Microsoft.ApplicationInsights.Wcf.Implementation +namespace Microsoft.ApplicationInsights.Wcf.Implementation { + using System; + using System.Collections.Generic; + using System.Threading; + using System.Xml; + using Microsoft.ApplicationInsights.DataContracts; + internal sealed class MessageCorrelator : IDisposable { - private Dictionary pendingMessages; + private Dictionary pendingMessages; private Action timeoutCallback; private object lockObject; private bool disposed; @@ -16,29 +16,32 @@ internal sealed class MessageCorrelator : IDisposable public MessageCorrelator(Action callback = null) { this.timeoutCallback = callback; - this.pendingMessages = new Dictionary(); + this.pendingMessages = new Dictionary(); this.lockObject = new object(); this.disposed = false; } public void Add(UniqueId messageId, DependencyTelemetry telemetry, TimeSpan timeout) { - if ( messageId == null ) + if (messageId == null) { throw new ArgumentNullException(nameof(messageId)); } - if ( telemetry == null ) + + if (telemetry == null) { throw new ArgumentNullException(nameof(telemetry)); } - lock ( lockObject ) + + lock (this.lockObject) { - if ( disposed ) + if (this.disposed) { throw new ObjectDisposedException(nameof(MessageCorrelator)); } + var pending = new PendingMessage(messageId, telemetry); - pendingMessages[messageId.ToString()] = pending; + this.pendingMessages[messageId.ToString()] = pending; pending.Start(timeout, this.OnTimeout); } } @@ -46,83 +49,91 @@ public void Add(UniqueId messageId, DependencyTelemetry telemetry, TimeSpan time public bool TryLookup(UniqueId messageId, out DependencyTelemetry telemetry) { telemetry = null; - lock ( this.lockObject ) + lock (this.lockObject) { PendingMessage pending = null; - if ( pendingMessages.TryGetValue(messageId.ToString(), out pending) ) + if (this.pendingMessages.TryGetValue(messageId.ToString(), out pending)) { - pendingMessages.Remove(messageId.ToString()); + this.pendingMessages.Remove(messageId.ToString()); telemetry = pending.Telemetry; pending.Dispose(); return true; } } + return false; } public void Remove(UniqueId messageId) { DependencyTelemetry telemetry; - TryLookup(messageId, out telemetry); + this.TryLookup(messageId, out telemetry); } public void Dispose() { - lock ( this.lockObject ) + lock (this.lockObject) { - foreach ( var obj in pendingMessages.Values ) + foreach (var obj in this.pendingMessages.Values) { obj.Dispose(); } - pendingMessages.Clear(); - disposed = true; + + this.pendingMessages.Clear(); + this.disposed = true; } } private void OnTimeout(object state) { var pending = (PendingMessage)state; - Remove(pending.Id); + this.Remove(pending.Id); this.timeoutCallback?.Invoke(pending.Id, pending.Telemetry); } private class PendingMessage : IDisposable { - public UniqueId Id { get; private set; } - public DependencyTelemetry Telemetry { get; private set; } - public Timer TimeoutTimer { get; private set; } - public PendingMessage(UniqueId id, DependencyTelemetry telemetry) { - if ( id == null ) + if (id == null) { throw new ArgumentNullException(nameof(id)); } - if ( telemetry == null ) + + if (telemetry == null) { throw new ArgumentNullException(nameof(telemetry)); } + this.Id = id; this.Telemetry = telemetry; } + public UniqueId Id { get; private set; } + + public DependencyTelemetry Telemetry { get; private set; } + + public Timer TimeoutTimer { get; private set; } + public void Start(TimeSpan timeout, TimerCallback callback) { - if ( TimeoutTimer != null ) + if (this.TimeoutTimer != null) { throw new InvalidOperationException("Start cannot be called twice"); } - if ( timeout > TimeSpan.Zero ) + + if (timeout > TimeSpan.Zero) { this.TimeoutTimer = new Timer(callback, this, timeout, TimeSpan.FromMilliseconds(-1)); } } + public void Dispose() { - if ( TimeoutTimer != null ) + if (this.TimeoutTimer != null) { - TimeoutTimer.Dispose(); - TimeoutTimer = null; + this.TimeoutTimer.Dispose(); + this.TimeoutTimer = null; } } } diff --git a/WCF/Shared/Implementation/OpenAsyncResult.cs b/WCF/Shared/Implementation/OpenAsyncResult.cs new file mode 100644 index 0000000..6615416 --- /dev/null +++ b/WCF/Shared/Implementation/OpenAsyncResult.cs @@ -0,0 +1,43 @@ +namespace Microsoft.ApplicationInsights.Wcf.Implementation +{ + using System; + using System.ServiceModel.Channels; + using Microsoft.ApplicationInsights.DataContracts; + + internal sealed class OpenAsyncResult : ChannelAsyncResult + { + public OpenAsyncResult(IChannel innerChannel, TimeSpan timeout, AsyncCallback onOpenDone, AsyncCallback callback, object state, DependencyTelemetry telemetry) + : base(onOpenDone, callback, state, telemetry) + { + this.InnerChannel = innerChannel; + + this.OriginalResult = innerChannel.BeginOpen(timeout, OnComplete, this); + if (this.OriginalResult.CompletedSynchronously) + { + innerChannel.EndOpen(this.OriginalResult); + this.Complete(true); + } + } + + public IChannel InnerChannel { get; private set; } + + private static void OnComplete(IAsyncResult result) + { + if (result.CompletedSynchronously) + { + return; + } + + var oar = (OpenAsyncResult)result.AsyncState; + try + { + oar.InnerChannel.EndOpen(oar.OriginalResult); + oar.Complete(false); + } + catch (Exception ex) + { + oar.Complete(false, ex); + } + } + } +} diff --git a/WCF/Shared/Implementation/OperationFilter.cs b/WCF/Shared/Implementation/OperationFilter.cs index 5732af9..5f8e62c 100644 --- a/WCF/Shared/Implementation/OperationFilter.cs +++ b/WCF/Shared/Implementation/OperationFilter.cs @@ -1,39 +1,46 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.ServiceModel.Description; - -namespace Microsoft.ApplicationInsights.Wcf.Implementation +namespace Microsoft.ApplicationInsights.Wcf.Implementation { - class OperationFilter + using System; + using System.Collections.Generic; + using System.Linq; + using System.ServiceModel.Description; + + internal class OperationFilter { - private HashSet instrumentedOperations; + private HashSet instrumentedOperations; public OperationFilter(ContractDescription serviceContract) { - instrumentedOperations = InspectContract(serviceContract); + this.instrumentedOperations = this.InspectContract(serviceContract); } - public bool ShouldProcess(String operationName) + public bool ShouldProcess(string operationName) { // if no operations had [OperationTelemetry], all are instrumented - if ( instrumentedOperations == null || instrumentedOperations.Count == 0 ) + if (this.instrumentedOperations == null || this.instrumentedOperations.Count == 0) + { return true; - return instrumentedOperations.Contains(operationName); + } + + return this.instrumentedOperations.Contains(operationName); } - private HashSet InspectContract(ContractDescription serviceContract) + private HashSet InspectContract(ContractDescription serviceContract) { - HashSet result = null; - foreach ( var op in serviceContract.Operations ) + HashSet result = null; + foreach (var op in serviceContract.Operations) { - if ( ShouldInstrument(op) ) + if (this.ShouldInstrument(op)) { - if ( result == null ) - result = new HashSet(); + if (result == null) + { + result = new HashSet(); + } + result.Add(op.Name); } } + return result; } diff --git a/WCF/Shared/Implementation/PlatformContext.cs b/WCF/Shared/Implementation/PlatformContext.cs index 67aaa27..edc8282 100644 --- a/WCF/Shared/Implementation/PlatformContext.cs +++ b/WCF/Shared/Implementation/PlatformContext.cs @@ -1,28 +1,42 @@ -using Microsoft.ApplicationInsights.DataContracts; -using System; -using System.Runtime.CompilerServices; -using System.ServiceModel; -using System.Web; - -namespace Microsoft.ApplicationInsights.Wcf.Implementation +namespace Microsoft.ApplicationInsights.Wcf.Implementation { + using System; + using System.Runtime.CompilerServices; + using System.ServiceModel; + using System.Web; + using Microsoft.ApplicationInsights.DataContracts; + /// /// Helper class used to speed up checks for RequestTelemetry - /// created by the Web SDK, which is stored in HttpContext + /// created by the Web SDK, which is stored in HttpContext. /// internal static class PlatformContext { - static readonly bool aspNetCompat = ServiceHostingEnvironment.AspNetCompatibilityEnabled; + private static readonly bool AspNetCompat = ServiceHostingEnvironment.AspNetCompatibilityEnabled; internal static RequestTelemetry RequestFromHttpContext() { // only check HttpContext if WCF hosting is configured with // // See: https://msdn.microsoft.com/en-us/library/aa702682(v=vs.110).aspx - if ( aspNetCompat ) + if (AspNetCompat) { return GetRequestTelemetryFromHttpContext(); } + + return null; + } + + internal static HttpContext GetContext() + { + // only check HttpContext if WCF hosting is configured with + // + // See: https://msdn.microsoft.com/en-us/library/aa702682(v=vs.110).aspx + if (AspNetCompat) + { + return HttpContext.Current; + } + return null; } @@ -32,26 +46,12 @@ internal static RequestTelemetry RequestFromHttpContext() private static RequestTelemetry GetRequestTelemetryFromHttpContext() { var context = HttpContext.Current; - if ( context != null ) + if (context != null) { return context.Items[RequestTrackingConstants.HttpContextRequestTelemetryName] as RequestTelemetry; } - return null; - } - /// - /// Returns the HttpContext for the current thread - /// - /// Null if not running on IIS or not available - internal static HttpContext GetContext() - { - // only check HttpContext if WCF hosting is configured with - // - // See: https://msdn.microsoft.com/en-us/library/aa702682(v=vs.110).aspx - if ( aspNetCompat ) - { - return HttpContext.Current; - } + return null; } } diff --git a/WCF/Shared/Implementation/ProfilerWcfClientProcessing.cs b/WCF/Shared/Implementation/ProfilerWcfClientProcessing.cs index 2e94756..e363584 100644 --- a/WCF/Shared/Implementation/ProfilerWcfClientProcessing.cs +++ b/WCF/Shared/Implementation/ProfilerWcfClientProcessing.cs @@ -1,34 +1,38 @@ -using System; -using System.Linq; -using System.ServiceModel; -using System.ServiceModel.Description; - -namespace Microsoft.ApplicationInsights.Wcf.Implementation +namespace Microsoft.ApplicationInsights.Wcf.Implementation { + using System; + using System.Linq; + using System.ServiceModel; + using System.ServiceModel.Description; + internal sealed class ProfilerWcfClientProcessing { private WcfDependencyTrackingTelemetryModule trackingModule; public ProfilerWcfClientProcessing(WcfDependencyTrackingTelemetryModule module) { - if ( module == null ) + if (module == null) { throw new ArgumentNullException(nameof(module)); } + this.trackingModule = module; } + public object OnStartInitializeEndpoint1(object thisObj, object serviceEndpoint) { return null; } + public object OnEndInitializeEndpoint1(object context, object returnValue, object thisObj, object serviceEndpoint) { - if ( thisObj == null ) + if (thisObj == null) { - WcfClientEventSource.Log.NotExpectedCallback(0, nameof(OnEndInitializeEndpoint1), "thisObj == null"); + WcfClientEventSource.Log.NotExpectedCallback(0, nameof(this.OnEndInitializeEndpoint1), "thisObj == null"); return returnValue; } - AddBehaviorIfNotPresent(((ChannelFactory)thisObj).Endpoint); + + this.AddBehaviorIfNotPresent(((ChannelFactory)thisObj).Endpoint); return returnValue; } @@ -37,14 +41,16 @@ public object OnStartInitializeEndpoint2(object thisObj, object configuratioName Console.WriteLine("onStartInitializeEndpoint2"); return null; } + public object OnEndInitializeEndpoint2(object context, object returnValue, object thisObj, object configuratioNameOrBinding, object address) { - if ( thisObj == null ) + if (thisObj == null) { - WcfClientEventSource.Log.NotExpectedCallback(0, nameof(OnEndInitializeEndpoint2), "thisObj == null"); + WcfClientEventSource.Log.NotExpectedCallback(0, nameof(this.OnEndInitializeEndpoint2), "thisObj == null"); return returnValue; } - AddBehaviorIfNotPresent(((ChannelFactory)thisObj).Endpoint); + + this.AddBehaviorIfNotPresent(((ChannelFactory)thisObj).Endpoint); return returnValue; } @@ -52,25 +58,28 @@ public object OnStartInitializeEndpoint3(object thisObj, object configurationNam { return null; } + public object OnEndInitializeEndpoint3(object context, object returnValue, object thisObj, object configurationName, object address, object configuration) { - if ( thisObj == null ) + if (thisObj == null) { - WcfClientEventSource.Log.NotExpectedCallback(0, nameof(OnEndInitializeEndpoint3), "thisObj == null"); + WcfClientEventSource.Log.NotExpectedCallback(0, nameof(this.OnEndInitializeEndpoint3), "thisObj == null"); return returnValue; } - AddBehaviorIfNotPresent(((ChannelFactory)thisObj).Endpoint); + + this.AddBehaviorIfNotPresent(((ChannelFactory)thisObj).Endpoint); return returnValue; } private void AddBehaviorIfNotPresent(ServiceEndpoint endpoint) { - if ( endpoint.Behaviors.OfType().Any() ) + if (endpoint.Behaviors.OfType().Any()) { // don't add behavior if it's already been added by user code // or the configuration return; } + var behavior = new ClientTelemetryEndpointBehavior(this.trackingModule.TelemetryClient) { RootOperationIdHeaderName = this.trackingModule.RootOperationIdHeaderName, diff --git a/WCF/Shared/Implementation/ReceiveAsyncResult.cs b/WCF/Shared/Implementation/ReceiveAsyncResult.cs new file mode 100644 index 0000000..0629c31 --- /dev/null +++ b/WCF/Shared/Implementation/ReceiveAsyncResult.cs @@ -0,0 +1,44 @@ +namespace Microsoft.ApplicationInsights.Wcf.Implementation +{ + using System; + using System.ServiceModel.Channels; + + internal sealed class ReceiveAsyncResult : ChannelAsyncResult + { + public ReceiveAsyncResult(IInputChannel innerChannel, TimeSpan timeout, AsyncCallback onReceiveDone, AsyncCallback callback, object state) + : base(onReceiveDone, callback, state, null) + { + this.InnerChannel = innerChannel; + + this.OriginalResult = innerChannel.BeginReceive(timeout, OnComplete, this); + if (this.OriginalResult.CompletedSynchronously) + { + this.Message = innerChannel.EndReceive(this.OriginalResult); + this.Complete(true); + } + } + + public IInputChannel InnerChannel { get; private set; } + + public Message Message { get; private set; } + + private static void OnComplete(IAsyncResult result) + { + if (result.CompletedSynchronously) + { + return; + } + + var rar = (ReceiveAsyncResult)result.AsyncState; + try + { + rar.Message = rar.InnerChannel.EndReceive(rar.OriginalResult); + rar.Complete(false); + } + catch (Exception ex) + { + rar.Complete(false, ex); + } + } + } +} diff --git a/WCF/Shared/Implementation/RequestAsyncResult.cs b/WCF/Shared/Implementation/RequestAsyncResult.cs new file mode 100644 index 0000000..10607d0 --- /dev/null +++ b/WCF/Shared/Implementation/RequestAsyncResult.cs @@ -0,0 +1,45 @@ +namespace Microsoft.ApplicationInsights.Wcf.Implementation +{ + using System; + using System.ServiceModel.Channels; + using Microsoft.ApplicationInsights.DataContracts; + + internal sealed class RequestAsyncResult : ChannelAsyncResult + { + public RequestAsyncResult(IRequestChannel innerChannel, Message message, TimeSpan timeout, AsyncCallback onRequestDone, AsyncCallback callback, object state, DependencyTelemetry telemetry) + : base(onRequestDone, callback, state, telemetry) + { + this.InnerChannel = innerChannel; + + this.OriginalResult = innerChannel.BeginRequest(message, timeout, OnComplete, this); + if (this.OriginalResult.CompletedSynchronously) + { + this.Reply = innerChannel.EndRequest(this.OriginalResult); + this.Complete(true); + } + } + + public IRequestChannel InnerChannel { get; private set; } + + public Message Reply { get; private set; } + + private static void OnComplete(IAsyncResult result) + { + if (result.CompletedSynchronously) + { + return; + } + + var rar = (RequestAsyncResult)result.AsyncState; + try + { + rar.Reply = rar.InnerChannel.EndRequest(rar.OriginalResult); + rar.Complete(false); + } + catch (Exception ex) + { + rar.Complete(false, ex); + } + } + } +} diff --git a/WCF/Shared/Implementation/RequestTrackingConstants.cs b/WCF/Shared/Implementation/RequestTrackingConstants.cs index f37346f..2a5c512 100644 --- a/WCF/Shared/Implementation/RequestTrackingConstants.cs +++ b/WCF/Shared/Implementation/RequestTrackingConstants.cs @@ -1,7 +1,7 @@ -using System; - -namespace Microsoft.ApplicationInsights.Wcf.Implementation +namespace Microsoft.ApplicationInsights.Wcf.Implementation { + using System; + internal static class RequestTrackingConstants { internal const string HttpContextRequestTelemetryName = "Microsoft.ApplicationInsights.RequestTelemetry"; diff --git a/WCF/Shared/Implementation/SdkVersionUtils.cs b/WCF/Shared/Implementation/SdkVersionUtils.cs index 31f3462..15d1bbd 100644 --- a/WCF/Shared/Implementation/SdkVersionUtils.cs +++ b/WCF/Shared/Implementation/SdkVersionUtils.cs @@ -1,12 +1,12 @@ -using System; -using System.Linq; -using System.Reflection; - -namespace Microsoft.ApplicationInsights.Wcf.Implementation +namespace Microsoft.ApplicationInsights.Wcf.Implementation { + using System; + using System.Linq; + using System.Reflection; + internal static class SdkVersionUtils { - public static String GetAssemblyVersion() + public static string GetAssemblyVersion() { return typeof(SdkVersionUtils).Assembly.GetCustomAttributes(false) .OfType() diff --git a/WCF/Shared/Implementation/SendAsyncResult.cs b/WCF/Shared/Implementation/SendAsyncResult.cs new file mode 100644 index 0000000..caa917b --- /dev/null +++ b/WCF/Shared/Implementation/SendAsyncResult.cs @@ -0,0 +1,47 @@ +namespace Microsoft.ApplicationInsights.Wcf.Implementation +{ + using System; + using System.ServiceModel.Channels; + using System.Xml; + using Microsoft.ApplicationInsights.DataContracts; + + internal sealed class SendAsyncResult : ChannelAsyncResult + { + public SendAsyncResult(IOutputChannel innerChannel, Message message, TimeSpan timeout, AsyncCallback onSendDone, AsyncCallback callback, object state, DependencyTelemetry telemetry) + : base(onSendDone, callback, state, telemetry) + { + this.InnerChannel = innerChannel; + this.RequestId = message.Headers.MessageId; + + this.OriginalResult = innerChannel.BeginSend(message, timeout, OnComplete, this); + if (this.OriginalResult.CompletedSynchronously) + { + innerChannel.EndSend(this.OriginalResult); + this.Complete(true); + } + } + + public IOutputChannel InnerChannel { get; private set; } + + public UniqueId RequestId { get; private set; } + + private static void OnComplete(IAsyncResult result) + { + if (result.CompletedSynchronously) + { + return; + } + + var sar = (SendAsyncResult)result.AsyncState; + try + { + sar.InnerChannel.EndSend(sar.OriginalResult); + sar.Complete(false); + } + catch (Exception ex) + { + sar.Complete(false, ex); + } + } + } +} diff --git a/WCF/Shared/Implementation/TryReceiveAsyncResult.cs b/WCF/Shared/Implementation/TryReceiveAsyncResult.cs new file mode 100644 index 0000000..786965e --- /dev/null +++ b/WCF/Shared/Implementation/TryReceiveAsyncResult.cs @@ -0,0 +1,50 @@ +namespace Microsoft.ApplicationInsights.Wcf.Implementation +{ + using System; + using System.ServiceModel.Channels; + + internal sealed class TryReceiveAsyncResult : ChannelAsyncResult + { + public TryReceiveAsyncResult(IInputChannel innerChannel, TimeSpan timeout, AsyncCallback onReceiveDone, AsyncCallback callback, object state) + : base(onReceiveDone, callback, state, null) + { + this.InnerChannel = innerChannel; + + this.OriginalResult = innerChannel.BeginTryReceive(timeout, OnComplete, this); + if (this.OriginalResult.CompletedSynchronously) + { + Message message = null; + this.Result = innerChannel.EndTryReceive(this.OriginalResult, out message); + this.Message = message; + this.Complete(true); + } + } + + public IInputChannel InnerChannel { get; private set; } + + public Message Message { get; private set; } + + public bool Result { get; private set; } + + private static void OnComplete(IAsyncResult result) + { + if (result.CompletedSynchronously) + { + return; + } + + var trar = (TryReceiveAsyncResult)result.AsyncState; + try + { + Message message = null; + trar.Result = trar.InnerChannel.EndTryReceive(trar.OriginalResult, out message); + trar.Message = message; + trar.Complete(false); + } + catch (Exception ex) + { + trar.Complete(false, ex); + } + } + } +} diff --git a/WCF/Shared/Implementation/WcfClientEventSource.cs b/WCF/Shared/Implementation/WcfClientEventSource.cs index 754366b..8214459 100644 --- a/WCF/Shared/Implementation/WcfClientEventSource.cs +++ b/WCF/Shared/Implementation/WcfClientEventSource.cs @@ -1,55 +1,50 @@ -#if NET40 -using Microsoft.Diagnostics.Tracing; +namespace Microsoft.ApplicationInsights.Wcf.Implementation +{ + using System; +#if NET40 + using Microsoft.Diagnostics.Tracing; #else -using System.Diagnostics.Tracing; + using System.Diagnostics.Tracing; #endif -using System; -namespace Microsoft.ApplicationInsights.Wcf.Implementation -{ [EventSource(Name = "Microsoft-ApplicationInsights-Wcf-Client")] internal sealed class WcfClientEventSource : EventSource { - public sealed class Keywords - { - public const EventKeywords DependencyTracking = (EventKeywords)0x10; - } - public static readonly WcfClientEventSource Log = new WcfClientEventSource(); - public String ApplicationName { [NonEvent] get; [NonEvent] private set; } - public WcfClientEventSource() { - this.ApplicationName = GetApplicationName(); + this.ApplicationName = this.GetApplicationName(); } + public string ApplicationName { [NonEvent] get; [NonEvent] private set; } + [Event(1, Keywords = Keywords.DependencyTracking, Message = "Client Telemetry applied to contract: {0}", Level = EventLevel.Informational)] - public void ClientTelemetryApplied(String contract, String appDomainName = "Invalid") + public void ClientTelemetryApplied(string contract, string appDomainName = "Invalid") { this.WriteEvent(1, contract, this.ApplicationName); } [Event(2, Keywords = Keywords.DependencyTracking, Message = "{0}", Level = EventLevel.Informational)] - public void ClientDependencyTrackingInfo(String info, String appDomainName = "Invalid") + public void ClientDependencyTrackingInfo(string info, string appDomainName = "Invalid") { this.WriteEvent(2, info, this.ApplicationName); } [Event(3, Keywords = Keywords.DependencyTracking, Message = "ChannelFactory created for channel shape {0}", Level = EventLevel.Informational)] - public void ChannelFactoryCreated(String channelShape, String appDomainName = "Invalid") + public void ChannelFactoryCreated(string channelShape, string appDomainName = "Invalid") { this.WriteEvent(3, channelShape, this.ApplicationName); } [Event(4, Keywords = Keywords.DependencyTracking, Message = "{0}.{1} callback called.", Level = EventLevel.Informational)] - public void ChannelCalled(String channel, String method, String appDomainName = "Invalid") + public void ChannelCalled(string channel, string method, string appDomainName = "Invalid") { this.WriteEvent(4, channel, method, this.ApplicationName); } [Event(5, Keywords = Keywords.DependencyTracking, Message = "Exception while processing {0}: {1}", Level = EventLevel.Error)] - public void ClientTelemetryError(String method, String exception, String appDomainName = "Invalid") + public void ClientTelemetryError(string method, string exception, string appDomainName = "Invalid") { this.WriteEvent(5, method, exception, this.ApplicationName); } @@ -61,15 +56,21 @@ public void NotExpectedCallback(long id, string callbackName, string reason, str } [NonEvent] - private String GetApplicationName() + private string GetApplicationName() { try { return AppDomain.CurrentDomain.FriendlyName; - } catch + } + catch { return "Undefined"; } } + + public sealed class Keywords + { + public const EventKeywords DependencyTracking = (EventKeywords)0x10; + } } } diff --git a/WCF/Shared/Implementation/WcfEventSource.cs b/WCF/Shared/Implementation/WcfEventSource.cs index 0041fbb..8f12394 100644 --- a/WCF/Shared/Implementation/WcfEventSource.cs +++ b/WCF/Shared/Implementation/WcfEventSource.cs @@ -1,120 +1,122 @@ -#if NET40 -using Microsoft.Diagnostics.Tracing; +namespace Microsoft.ApplicationInsights.Wcf.Implementation +{ + using System; +#if NET40 + using Microsoft.Diagnostics.Tracing; #else -using System.Diagnostics.Tracing; + using System.Diagnostics.Tracing; #endif -using System; -namespace Microsoft.ApplicationInsights.Wcf.Implementation -{ [EventSource(Name = "Microsoft-ApplicationInsights-Wcf")] internal sealed class WcfEventSource : EventSource { - // Keywords must be powers of 2 since they are used as bitmasks - public sealed class Keywords - { - public const EventKeywords WcfModule = (EventKeywords)0x10; - public const EventKeywords RequestTelemetry = (EventKeywords)0x20; - public const EventKeywords ExceptionTelemetry = (EventKeywords)0x40; - public const EventKeywords OperationContext = (EventKeywords)0x80; - } + public const string InitializationFailureMessage = "ServiceTelemetry module failed to initialize with exception: {0}"; + public const string TelemetryModuleExecutionStartedMessage = "WCF Telemetry Module {0} started stage {1}"; + public const string TelemetryModuleExecutionStoppedMessage = "WCF Telemetry Module {0} stopped stage {1}"; + public const string TelemetryModuleExecutionFailedMessage = "WCF Telemetry Module {0} failed stage {1} with exception: {2}"; + public const string NoOperationContextFoundMessage = "No OperationContext found on thread"; + public const string OperationIgnoredMessage = "Request ignored because operation is not marked with [OperationTelemetry]: {0}#{1} - {2}"; + public const string WcfTelemetryInitializerLoadedMessage = "WCF TelemetryInitializer loaded: {0}"; + public const string LocationIdSetMessage = "Location.Id set to: {0}"; + public const string OperationContextCreatedMessage = "WcfOperationContext created. OpId={0}; OwnRequest={1}"; + public const string RequestMessageClosedMessage = "Request message closed while attempting action '{0}' ({1})"; + public const string ResponseMessageClosedMessage = "Response message closed while attempting action '{0}' ({1})"; public static readonly WcfEventSource Log = new WcfEventSource(); - public String ApplicationName { [NonEvent] get; [NonEvent] private set; } - public WcfEventSource() { - this.ApplicationName = GetApplicationName(); + this.ApplicationName = this.GetApplicationName(); } - public const String InitializationFailure_Message = "ServiceTelemetry module failed to initialize with exception: {0}"; - [Event(1, Keywords = Keywords.WcfModule, Message = InitializationFailure_Message, Level = EventLevel.Verbose)] - public void InitializationFailure(String exception, String appDomainName = "Invalid") + public string ApplicationName { [NonEvent] get; [NonEvent] private set; } + + [Event(1, Keywords = Keywords.WcfModule, Message = InitializationFailureMessage, Level = EventLevel.Verbose)] + public void InitializationFailure(string exception, string appDomainName = "Invalid") { - this.WriteEvent(1, exception, ApplicationName); + this.WriteEvent(1, exception, this.ApplicationName); } - public const String TelemetryModuleExecutionStarted_Message = "WCF Telemetry Module {0} started stage {1}"; - [Event(4, Keywords = Keywords.WcfModule, Message = TelemetryModuleExecutionStarted_Message, Level = EventLevel.Verbose)] - public void TelemetryModuleExecutionStarted(String typeName, String stageName, String appDomainName = "Invalid") + [Event(4, Keywords = Keywords.WcfModule, Message = TelemetryModuleExecutionStartedMessage, Level = EventLevel.Verbose)] + public void TelemetryModuleExecutionStarted(string typeName, string stageName, string appDomainName = "Invalid") { - this.WriteEvent(4, typeName, stageName, ApplicationName); + this.WriteEvent(4, typeName, stageName, this.ApplicationName); } - public const String TelemetryModuleExecutionStopped_Message = "WCF Telemetry Module {0} stopped stage {1}"; - [Event(5, Keywords = Keywords.WcfModule, Message = TelemetryModuleExecutionStopped_Message, Level = EventLevel.Verbose)] - public void TelemetryModuleExecutionStopped(String typeName, String stageName, String appDomainName = "Invalid") + [Event(5, Keywords = Keywords.WcfModule, Message = TelemetryModuleExecutionStoppedMessage, Level = EventLevel.Verbose)] + public void TelemetryModuleExecutionStopped(string typeName, string stageName, string appDomainName = "Invalid") { - this.WriteEvent(5, typeName, stageName, ApplicationName); + this.WriteEvent(5, typeName, stageName, this.ApplicationName); } - public const String TelemetryModuleExecutionFailed_Message = "WCF Telemetry Module {0} failed stage {1} with exception: {2}"; - [Event(6, Keywords = Keywords.WcfModule, Message = TelemetryModuleExecutionFailed_Message, Level = EventLevel.Error)] - public void TelemetryModuleExecutionFailed(String typeName, String stageName, String exception, String appDomainName = "Invalid") + [Event(6, Keywords = Keywords.WcfModule, Message = TelemetryModuleExecutionFailedMessage, Level = EventLevel.Error)] + public void TelemetryModuleExecutionFailed(string typeName, string stageName, string exception, string appDomainName = "Invalid") { - this.WriteEvent(6, typeName, stageName, exception, ApplicationName); + this.WriteEvent(6, typeName, stageName, exception, this.ApplicationName); } - public const String NoOperationContextFound_Message = "No OperationContext found on thread"; - [Event(7, Keywords = Keywords.WcfModule, Message = NoOperationContextFound_Message, Level = EventLevel.Warning)] - public void NoOperationContextFound(String appDomainName = "Invalid") + [Event(7, Keywords = Keywords.WcfModule, Message = NoOperationContextFoundMessage, Level = EventLevel.Warning)] + public void NoOperationContextFound(string appDomainName = "Invalid") { - this.WriteEvent(7, ApplicationName); + this.WriteEvent(7, this.ApplicationName); } - public const String OperationIgnored_Message = "Request ignored because operation is not marked with [OperationTelemetry]: {0}#{1} - {2}"; - [Event(8, Keywords = Keywords.WcfModule, Message = OperationIgnored_Message, Level = EventLevel.Verbose)] - public void OperationIgnored(String contractName, String contractNamespace, String operationName, String appDomainName = "Invalid") + [Event(8, Keywords = Keywords.WcfModule, Message = OperationIgnoredMessage, Level = EventLevel.Verbose)] + public void OperationIgnored(string contractName, string contractNamespace, string operationName, string appDomainName = "Invalid") { - this.WriteEvent(8, contractName, contractNamespace, operationName, ApplicationName); + this.WriteEvent(8, contractName, contractNamespace, operationName, this.ApplicationName); } - public const String WcfTelemetryInitializerLoaded_Message = "WCF TelemetryInitializer loaded: {0}"; - [Event(9, Keywords = Keywords.RequestTelemetry, Message = WcfTelemetryInitializerLoaded_Message, Level = EventLevel.Verbose)] - public void WcfTelemetryInitializerLoaded(String typeName, String appDomainName = "Invalid") + [Event(9, Keywords = Keywords.RequestTelemetry, Message = WcfTelemetryInitializerLoadedMessage, Level = EventLevel.Verbose)] + public void WcfTelemetryInitializerLoaded(string typeName, string appDomainName = "Invalid") { - this.WriteEvent(9, typeName, ApplicationName); + this.WriteEvent(9, typeName, this.ApplicationName); } - public const String LocationIdSet_Message = "Location.Id set to: {0}"; - [Event(15, Keywords = Keywords.WcfModule, Message = LocationIdSet_Message, Level = EventLevel.Verbose)] - public void LocationIdSet(String ip, String appDomainName = "Invalid") + [Event(15, Keywords = Keywords.WcfModule, Message = LocationIdSetMessage, Level = EventLevel.Verbose)] + public void LocationIdSet(string ip, string appDomainName = "Invalid") { this.WriteEvent(15, ip ?? "NULL", this.ApplicationName); } - public const String OperationContextCreated_Message = "WcfOperationContext created. OpId={0}; OwnRequest={1}"; - [Event(30, Keywords = Keywords.OperationContext, Message = OperationContextCreated_Message, Level = EventLevel.Verbose)] - public void OperationContextCreated(String operationId, bool ownsRequest, String appDomainName = "Invalid") + [Event(30, Keywords = Keywords.OperationContext, Message = OperationContextCreatedMessage, Level = EventLevel.Verbose)] + public void OperationContextCreated(string operationId, bool ownsRequest, string appDomainName = "Invalid") { this.WriteEvent(30, operationId ?? "NULL", ownsRequest, this.ApplicationName); } - public const String RequestMessageClosed_Message = "Request message closed while attempting action '{0}' ({1})"; - [Event(35, Keywords = Keywords.OperationContext, Message = RequestMessageClosed_Message, Level = EventLevel.Warning)] - public void RequestMessageClosed(String action, String argument, String appDomainName = "Invalid") + [Event(35, Keywords = Keywords.OperationContext, Message = RequestMessageClosedMessage, Level = EventLevel.Warning)] + public void RequestMessageClosed(string action, string argument, string appDomainName = "Invalid") { this.WriteEvent(35, action, argument, this.ApplicationName); } - public const String ResponseMessageClosed_Message = "Response message closed while attempting action '{0}' ({1})"; - [Event(36, Keywords = Keywords.OperationContext, Message = ResponseMessageClosed_Message, Level = EventLevel.Warning)] - public void ResponseMessageClosed(String action, String argument, String appDomainName = "Invalid") + [Event(36, Keywords = Keywords.OperationContext, Message = ResponseMessageClosedMessage, Level = EventLevel.Warning)] + public void ResponseMessageClosed(string action, string argument, string appDomainName = "Invalid") { this.WriteEvent(36, action, argument, this.ApplicationName); } [NonEvent] - private String GetApplicationName() + private string GetApplicationName() { try { return AppDomain.CurrentDomain.FriendlyName; - } catch + } + catch { return "Undefined"; } } + + // Keywords must be powers of 2 since they are used as bitmasks + public sealed class Keywords + { + public const EventKeywords WcfModule = (EventKeywords)0x10; + public const EventKeywords RequestTelemetry = (EventKeywords)0x20; + public const EventKeywords ExceptionTelemetry = (EventKeywords)0x40; + public const EventKeywords OperationContext = (EventKeywords)0x80; + } } } diff --git a/WCF/Shared/Implementation/WcfExtensions.cs b/WCF/Shared/Implementation/WcfExtensions.cs index 1c295a0..b0470f9 100644 --- a/WCF/Shared/Implementation/WcfExtensions.cs +++ b/WCF/Shared/Implementation/WcfExtensions.cs @@ -1,47 +1,53 @@ -using System; -using System.ServiceModel.Channels; - -namespace Microsoft.ApplicationInsights.Wcf.Implementation +namespace Microsoft.ApplicationInsights.Wcf.Implementation { + using System; + using System.ServiceModel.Channels; + internal static class WcfExtensions { public static HttpRequestMessageProperty GetHttpRequestHeaders(this IOperationContext operation) { - if ( operation.HasIncomingMessageProperty(HttpRequestMessageProperty.Name) ) + if (operation.HasIncomingMessageProperty(HttpRequestMessageProperty.Name)) { return (HttpRequestMessageProperty)operation.GetIncomingMessageProperty(HttpRequestMessageProperty.Name); } + return null; } + public static HttpResponseMessageProperty GetHttpResponseHeaders(this IOperationContext operation) { - if ( operation.HasOutgoingMessageProperty(HttpResponseMessageProperty.Name) ) + if (operation.HasOutgoingMessageProperty(HttpResponseMessageProperty.Name)) { return (HttpResponseMessageProperty)operation.GetOutgoingMessageProperty(HttpResponseMessageProperty.Name); } + return null; } public static HttpRequestMessageProperty GetHttpRequestHeaders(this Message message) { HttpRequestMessageProperty headers = null; - if ( message.Properties.ContainsKey(HttpRequestMessageProperty.Name) ) + if (message.Properties.ContainsKey(HttpRequestMessageProperty.Name)) { headers = (HttpRequestMessageProperty)message.Properties[HttpRequestMessageProperty.Name]; - } else + } + else { headers = new HttpRequestMessageProperty(); message.Properties.Add(HttpRequestMessageProperty.Name, headers); } + return headers; } public static bool IsClosed(this Message message) { - if ( message == null ) + if (message == null) { throw new ArgumentNullException(nameof(message)); } + return message.State == MessageState.Closed; } } diff --git a/WCF/Shared/Implementation/WcfInterceptor.cs b/WCF/Shared/Implementation/WcfInterceptor.cs index a9f18ff..2378f9a 100644 --- a/WCF/Shared/Implementation/WcfInterceptor.cs +++ b/WCF/Shared/Implementation/WcfInterceptor.cs @@ -1,14 +1,14 @@ -using TelemetryModules = Microsoft.ApplicationInsights.Extensibility.Implementation.TelemetryModules; -using System; -using System.Collections.Generic; -using System.Linq; -using System.ServiceModel; -using System.ServiceModel.Channels; -using System.ServiceModel.Dispatcher; -using Microsoft.ApplicationInsights.Extensibility; - -namespace Microsoft.ApplicationInsights.Wcf.Implementation +namespace Microsoft.ApplicationInsights.Wcf.Implementation { + using System; + using System.Collections.Generic; + using System.Linq; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.ServiceModel.Dispatcher; + using Microsoft.ApplicationInsights.Extensibility; + using TelemetryModules = Microsoft.ApplicationInsights.Extensibility.Implementation.TelemetryModules; + internal class WcfInterceptor : IDispatchMessageInspector, IErrorHandler { private TelemetryConfiguration configuration; @@ -18,11 +18,14 @@ public WcfInterceptor(TelemetryConfiguration configuration) : this(configuration, null) { } + public WcfInterceptor(TelemetryConfiguration configuration, ContractFilter filter) { - if ( configuration == null ) + if (configuration == null) + { throw new ArgumentNullException("configuration"); - + } + this.configuration = configuration; this.contractFilter = filter; } @@ -30,75 +33,81 @@ public WcfInterceptor(TelemetryConfiguration configuration, ContractFilter filte public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) { IOperationContext context = WcfOperationContext.Current; - if ( context != null ) + if (context != null) { - if ( !LogTelemetryFor(context) ) + if (!this.LogTelemetryFor(context)) { WcfEventSource.Log.OperationIgnored(context.ContractName, context.ContractNamespace, context.OperationName); return null; } - foreach ( var mod in GetModules() ) + + foreach (var mod in this.GetModules()) { Executor.ExceptionSafe( mod.GetType().Name, "OnBeginRequest", mod.OnBeginRequest, - context - ); + context); } - foreach ( var mod in GetModules() ) + + foreach (var mod in this.GetModules()) { var tracer = mod as IWcfMessageTrace; - if ( tracer != null ) + if (tracer != null) { Executor.ExceptionSafe( mod.GetType().Name, "OnTraceRequest", tracer.OnTraceRequest, - context, ref request - ); + context, + ref request); } } - } else + } + else { WcfEventSource.Log.NoOperationContextFound(); } + return null; } public void BeforeSendReply(ref Message reply, object correlationState) { var context = WcfOperationContext.Current; - if ( context != null ) + if (context != null) { // Do not run OnEndRequest for stuff we're not interested in! - if ( !LogTelemetryFor(context) ) + if (!this.LogTelemetryFor(context)) { return; } - foreach ( var mod in GetModules() ) + + foreach (var mod in this.GetModules()) { var tracer = mod as IWcfMessageTrace; - if ( tracer != null ) + if (tracer != null) { Executor.ExceptionSafe( mod.GetType().Name, "OnTraceResponse", tracer.OnTraceResponse, - context, ref reply - ); + context, + ref reply); } } - foreach ( var mod in GetModules() ) + + foreach (var mod in this.GetModules()) { Executor.ExceptionSafe( mod.GetType().Name, "OnEndRequest", mod.OnEndRequest, - context, reply - ); + context, + reply); } - } else + } + else { WcfEventSource.Log.NoOperationContextFound(); } @@ -112,18 +121,19 @@ public bool HandleError(Exception error) public void ProvideFault(Exception error, MessageVersion version, ref Message fault) { var context = WcfOperationContext.Current; - if ( context != null ) + if (context != null) { - foreach ( var mod in GetModules() ) + foreach (var mod in this.GetModules()) { Executor.ExceptionSafe( mod.GetType().Name, "OnError", mod.OnError, - context, error - ); + context, + error); } - } else + } + else { WcfEventSource.Log.NoOperationContextFound(); } @@ -136,13 +146,15 @@ private IEnumerable GetModules() private bool LogTelemetryFor(IOperationContext context) { - if ( contractFilter == null ) + if (this.contractFilter == null) + { return true; - return contractFilter.ShouldProcess( + } + + return this.contractFilter.ShouldProcess( context.ContractName, context.ContractNamespace, - context.OperationName - ); + context.OperationName); } } } diff --git a/WCF/Shared/Implementation/WcfOperationContext.cs b/WCF/Shared/Implementation/WcfOperationContext.cs index 7f7260f..e3c42cc 100644 --- a/WCF/Shared/Implementation/WcfOperationContext.cs +++ b/WCF/Shared/Implementation/WcfOperationContext.cs @@ -1,82 +1,126 @@ -using Microsoft.ApplicationInsights.DataContracts; -using System; -using System.Collections.Concurrent; -using System.Runtime.Remoting; -using System.Runtime.Remoting.Messaging; -using System.ServiceModel; -using System.ServiceModel.Dispatcher; - -namespace Microsoft.ApplicationInsights.Wcf.Implementation +namespace Microsoft.ApplicationInsights.Wcf.Implementation { + using System; + using System.Collections.Concurrent; + using System.Runtime.Remoting; + using System.Runtime.Remoting.Messaging; + using System.ServiceModel; + using System.ServiceModel.Dispatcher; + using Microsoft.ApplicationInsights.DataContracts; + internal class WcfOperationContext : IOperationContext, IExtension, IOperationContextState { + private const string CallContextProperty = "AIWcfOperationContext"; private OperationContext context; - private ConcurrentDictionary stateDicctionary; + private ConcurrentDictionary stateDicctionary; + + private WcfOperationContext(OperationContext operationContext, RequestTelemetry httpCtxTelemetry) + { + this.context = operationContext; + this.stateDicctionary = new ConcurrentDictionary(); + this.OperationName = this.DiscoverOperationName(operationContext); + if (httpCtxTelemetry != null) + { + this.Request = httpCtxTelemetry; + this.OwnsRequest = false; + } + else + { + this.Request = new RequestTelemetry(); + this.Request.GenerateOperationId(); + this.OwnsRequest = true; + } + + WcfEventSource.Log.OperationContextCreated(this.Request.Id, this.OwnsRequest); + } - const String CallContextProperty = "AIWcfOperationContext"; + public static IOperationContext Current + { + get { return GetContext(); } + } - public String OperationId + public string OperationId { - get { return Request.Id; } + get { return this.Request.Id; } } - public String OperationName { get; private set; } + + public string OperationName { get; private set; } + public RequestTelemetry Request { get; private set; } + public bool OwnsRequest { get; private set; } - public String ContractName + public string ContractName { - get { return context.EndpointDispatcher.ContractName; } + get { return this.context.EndpointDispatcher.ContractName; } } - public String ContractNamespace + + public string ContractNamespace { - get { return context.EndpointDispatcher.ContractNamespace; } + get { return this.context.EndpointDispatcher.ContractNamespace; } } + public Uri EndpointUri { - get { return context.EndpointDispatcher.EndpointAddress.Uri; } + get { return this.context.EndpointDispatcher.EndpointAddress.Uri; } } + public Uri ToHeader { - get { return context.IncomingMessageHeaders.To; } + get { return this.context.IncomingMessageHeaders.To; } } + public ServiceSecurityContext SecurityContext { - get { return GetSecurityContext(); } + get { return this.GetSecurityContext(); } } - public static IOperationContext Current + // In the normal case, we'll use the OperationContext + // found in the local thread. However, there are cases this won't work: + // - When async calls have been done and .NET < 4.6 + // - Within a *client* side OperationContextScope + // To work around this, we store a copy of our context on the + // thread's LogicalCallContext, so that it gets moved from thread to thread + // Because this field is not serializable, we store an + // ObjectHandle instead. + public static WcfOperationContext FindContext(OperationContext owner) { - get { return GetContext(); } - } + // don't retrieve a context for a client-side OperationContext + if (owner != null && owner.IsClientSideContext()) + { + return null; + } - private WcfOperationContext(OperationContext operationContext, RequestTelemetry httpCtxTelemetry) - { - context = operationContext; - stateDicctionary = new ConcurrentDictionary(); - OperationName = DiscoverOperationName(operationContext); - if ( httpCtxTelemetry != null ) + WcfOperationContext context = null; + if (context == null && owner != null) { - Request = httpCtxTelemetry; - OwnsRequest = false; - } else + context = owner.Extensions.Find(); + } + + if (context == null) { - Request = new RequestTelemetry(); - Request.GenerateOperationId(); - OwnsRequest = true; + var handle = CallContext.LogicalGetData(CallContextProperty) as ObjectHandle; + if (handle != null) + { + context = handle.Unwrap() as WcfOperationContext; + } } - WcfEventSource.Log.OperationContextCreated(Request.Id, OwnsRequest); + + return context; } public bool HasIncomingMessageProperty(string propertyName) { - if ( String.IsNullOrEmpty(propertyName) ) + if (string.IsNullOrEmpty(propertyName)) { throw new ArgumentNullException(nameof(propertyName)); } + try { - return context.IncomingMessageProperties.ContainsKey(propertyName); - } catch ( ObjectDisposedException ) + return this.context.IncomingMessageProperties.ContainsKey(propertyName); + } + catch (ObjectDisposedException) { // WCF message has been closed already WcfEventSource.Log.RequestMessageClosed("reading property", propertyName); @@ -86,14 +130,16 @@ public bool HasIncomingMessageProperty(string propertyName) public object GetIncomingMessageProperty(string propertyName) { - if ( String.IsNullOrEmpty(propertyName) ) + if (string.IsNullOrEmpty(propertyName)) { throw new ArgumentNullException(nameof(propertyName)); } + try { - return context.IncomingMessageProperties[propertyName]; - } catch ( ObjectDisposedException ) + return this.context.IncomingMessageProperties[propertyName]; + } + catch (ObjectDisposedException) { // WCF message has been closed already WcfEventSource.Log.RequestMessageClosed("reading property", propertyName); @@ -101,16 +147,18 @@ public object GetIncomingMessageProperty(string propertyName) } } - public bool HasOutgoingMessageProperty(String propertyName) + public bool HasOutgoingMessageProperty(string propertyName) { - if ( String.IsNullOrEmpty(propertyName) ) + if (string.IsNullOrEmpty(propertyName)) { throw new ArgumentNullException(nameof(propertyName)); } + try { - return context.OutgoingMessageProperties.ContainsKey(propertyName); - } catch ( ObjectDisposedException ) + return this.context.OutgoingMessageProperties.ContainsKey(propertyName); + } + catch (ObjectDisposedException) { // WCF message has been closed already WcfEventSource.Log.ResponseMessageClosed("reading property", propertyName); @@ -118,16 +166,18 @@ public bool HasOutgoingMessageProperty(String propertyName) } } - public object GetOutgoingMessageProperty(String propertyName) + public object GetOutgoingMessageProperty(string propertyName) { - if ( String.IsNullOrEmpty(propertyName) ) + if (string.IsNullOrEmpty(propertyName)) { throw new ArgumentNullException(nameof(propertyName)); } + try { - return context.OutgoingMessageProperties[propertyName]; - } catch ( ObjectDisposedException ) + return this.context.OutgoingMessageProperties[propertyName]; + } + catch (ObjectDisposedException) { // WCF message has been closed already WcfEventSource.Log.ResponseMessageClosed("reading property", propertyName); @@ -135,161 +185,140 @@ public object GetOutgoingMessageProperty(String propertyName) } } - public T GetIncomingMessageHeader(String name, String ns) + public T GetIncomingMessageHeader(string name, string ns) { - if ( String.IsNullOrEmpty(name) ) + if (string.IsNullOrEmpty(name)) { throw new ArgumentNullException(nameof(name)); } - if ( String.IsNullOrEmpty(ns) ) + + if (string.IsNullOrEmpty(ns)) { throw new ArgumentNullException(nameof(ns)); } + try { - int index = context.IncomingMessageHeaders.FindHeader(name, ns); - if ( index >= 0 ) + var index = this.context.IncomingMessageHeaders.FindHeader(name, ns); + if (index >= 0) { - return context.IncomingMessageHeaders.GetHeader(index); + return this.context.IncomingMessageHeaders.GetHeader(index); } - } catch ( ObjectDisposedException ) + } + catch (ObjectDisposedException) { // WCF message has been closed already WcfEventSource.Log.RequestMessageClosed("reading header", ns + "#" + name); } + return default(T); } - // - // In the normal case, we'll use the OperationContext - // found in the local thread. However, there are cases this won't work: - // - When async calls have been done and .NET < 4.6 - // - Within a *client* side OperationContextScope - // To work around this, we store a copy of our context on the - // thread's LogicalCallContext, so that it gets moved from thread to thread - // Because this field is not serializable, we store an - // ObjectHandle instead. - // - public static WcfOperationContext FindContext(OperationContext owner) + void IExtension.Attach(OperationContext owner) { - // don't retrieve a context for a client-side OperationContext - if ( owner != null && owner.IsClientSideContext() ) - { - return null; - } + } - WcfOperationContext context = null; - if ( context == null && owner != null ) - { - context = owner.Extensions.Find(); - } - if ( context == null ) - { - var handle = CallContext.LogicalGetData(CallContextProperty) as ObjectHandle; - if ( handle != null ) - { - context = handle.Unwrap() as WcfOperationContext; - } - } - return context; + void IExtension.Detach(OperationContext owner) + { } - private ServiceSecurityContext GetSecurityContext() + void IOperationContextState.SetState(string key, object value) { - try - { - return this.context.ServiceSecurityContext; - } catch ( ObjectDisposedException ) + this.stateDicctionary[key] = value; + } + + bool IOperationContextState.TryGetState(string key, out T value) + { + value = default(T); + object storedValue = null; + + if (this.stateDicctionary.TryGetValue(key, out storedValue)) { - // WCF message has been closed already - WcfEventSource.Log.RequestMessageClosed("reading", "ServiceSecurityContext"); - return null; + value = (T)storedValue; + return true; } + + return false; } private static IOperationContext GetContext() { var owner = OperationContext.Current; - if ( owner != null && owner.IsClientSideContext() ) + if (owner != null && owner.IsClientSideContext()) { owner = null; } - WcfOperationContext context = FindContext(owner); - if ( context == null ) + var context = FindContext(owner); + if (context == null) { - if ( owner != null ) + if (owner != null) { context = new WcfOperationContext(owner, PlatformContext.RequestFromHttpContext()); owner.Extensions.Add(context); + // backup in case we can't get to the server-side OperationContext later CallContext.LogicalSetData(CallContextProperty, new ObjectHandle(context)); } + // no server-side OperationContext to attach to } - return context; - } - - void IExtension.Attach(OperationContext owner) - { - } - void IExtension.Detach(OperationContext owner) - { - } - - void IOperationContextState.SetState(string key, object value) - { - stateDicctionary[key] = value; + return context; } - bool IOperationContextState.TryGetState(string key, out T value) + private ServiceSecurityContext GetSecurityContext() { - value = default(T); - object storedValue = null; - - if ( stateDicctionary.TryGetValue(key, out storedValue) ) + try { - value = (T)storedValue; - return true; + return this.context.ServiceSecurityContext; + } + catch (ObjectDisposedException) + { + // WCF message has been closed already + WcfEventSource.Log.RequestMessageClosed("reading", "ServiceSecurityContext"); + return null; } - return false; } - private String DiscoverOperationName(OperationContext operationContext) + private string DiscoverOperationName(OperationContext operationContext) { var runtime = operationContext.EndpointDispatcher.DispatchRuntime; - String action = operationContext.IncomingMessageHeaders.Action; - if ( !String.IsNullOrEmpty(action) ) + string action = operationContext.IncomingMessageHeaders.Action; + if (!string.IsNullOrEmpty(action)) { - foreach ( var op in runtime.Operations ) + foreach (var op in runtime.Operations) { - if ( op.Action == action ) + if (op.Action == action) { return op.Name; } } - } else + } + else { // WebHttpDispatchOperationSelector will stick the // selected operation name into a message property - return GetWebHttpOperationName(operationContext); + return this.GetWebHttpOperationName(operationContext); } + var catchAll = runtime.UnhandledDispatchOperation; - if ( catchAll != null ) + if (catchAll != null) { return catchAll.Name; } + return "*"; } private string GetWebHttpOperationName(OperationContext operationContext) { var name = WebHttpDispatchOperationSelector.HttpOperationNamePropertyName; - if ( HasIncomingMessageProperty(name) ) + if (this.HasIncomingMessageProperty(name)) { - return GetIncomingMessageProperty(name) as String; + return this.GetIncomingMessageProperty(name) as string; } + return ""; } } diff --git a/WCF/Shared/Microsoft.AI.Wcf.projitems b/WCF/Shared/Microsoft.AI.Wcf.projitems index 32136d2..2f262c6 100644 --- a/WCF/Shared/Microsoft.AI.Wcf.projitems +++ b/WCF/Shared/Microsoft.AI.Wcf.projitems @@ -13,6 +13,11 @@ + + + + + diff --git a/WCF/Shared/OperationContextExtensions.cs b/WCF/Shared/OperationContextExtensions.cs index 157eab4..b75fda5 100644 --- a/WCF/Shared/OperationContextExtensions.cs +++ b/WCF/Shared/OperationContextExtensions.cs @@ -1,12 +1,12 @@ -using Microsoft.ApplicationInsights.DataContracts; -using Microsoft.ApplicationInsights.Wcf.Implementation; -using System; -using System.ServiceModel; - -namespace Microsoft.ApplicationInsights.Wcf +namespace Microsoft.ApplicationInsights.Wcf { + using System; + using System.ServiceModel; + using Microsoft.ApplicationInsights.DataContracts; + using Microsoft.ApplicationInsights.Wcf.Implementation; + /// - /// Provides extensions methods for accessing Application Insights Objects + /// Provides extensions methods for accessing Application Insights Objects. /// public static class OperationContextExtensions { @@ -15,29 +15,32 @@ public static class OperationContextExtensions /// the Application Insights WCF SDK. /// /// The WCF OperationContext instance - /// associated with the current request - /// The request object or null + /// associated with the current request. + /// The request object or null. public static RequestTelemetry GetRequestTelemetry(this OperationContext context) { - if ( context == null ) + if (context == null) + { return null; - var icontext = WcfOperationContext.FindContext(context); + } - return icontext != null ? icontext.Request : null; + var icontext = WcfOperationContext.FindContext(context); + return icontext?.Request; } /// /// Returns true if the OperationContext object is associated with - /// a client-side channel + /// a client-side channel. /// - /// The WCF operation context instance + /// The WCF operation context instance. /// True for a client-side channel, false otherwise. internal static bool IsClientSideContext(this OperationContext context) { - if ( context == null ) + if (context == null) { throw new ArgumentNullException("context"); } + // OperationContext.IsUserContext probably does the same thing // but exact semantics are not documented. return context.Host == null; diff --git a/WCF/Shared/OperationCorrelationTelemetryInitializer.cs b/WCF/Shared/OperationCorrelationTelemetryInitializer.cs index 86412ca..91574ea 100644 --- a/WCF/Shared/OperationCorrelationTelemetryInitializer.cs +++ b/WCF/Shared/OperationCorrelationTelemetryInitializer.cs @@ -1,102 +1,111 @@ -using System; -using Microsoft.ApplicationInsights.Channel; -using Microsoft.ApplicationInsights.Wcf.Implementation; - -namespace Microsoft.ApplicationInsights.Wcf +namespace Microsoft.ApplicationInsights.Wcf { + using System; + using Microsoft.ApplicationInsights.Channel; + using Microsoft.ApplicationInsights.Wcf.Implementation; + /// /// A telemetry initializer that will set the correlation context for all telemetry items in web application. /// public sealed class OperationCorrelationTelemetryInitializer : WcfTelemetryInitializer { + private const string RequestHeadersChecked = "OCTI_RequestHeadersChecked"; + private static readonly object Checked = new object(); + + /// + /// Initializes a new instance of the class. + /// + public OperationCorrelationTelemetryInitializer() + { + this.RootOperationIdHeaderName = CorrelationHeaders.HttpStandardRootIdHeader; + this.ParentOperationIdHeaderName = CorrelationHeaders.HttpStandardParentIdHeader; + this.SoapHeaderNamespace = CorrelationHeaders.SoapStandardNamespace; + this.SoapParentOperationIdHeaderName = CorrelationHeaders.SoapStandardParentIdHeader; + this.SoapRootOperationIdHeaderName = CorrelationHeaders.SoapStandardRootIdHeader; + } + /// /// Gets or sets the name of the HTTP header to get root operation Id from. /// public string RootOperationIdHeaderName { get; set; } + /// /// Gets or sets the name of the HTTP header to get parent operation Id from. /// public string ParentOperationIdHeaderName { get; set; } + /// /// Gets or sets the name of the SOAP header to get root operation Id from. /// public string SoapRootOperationIdHeaderName { get; set; } + /// /// Gets or sets the name of the SOAP header to get parent operation Id from. /// public string SoapParentOperationIdHeaderName { get; set; } + /// /// Gets or sets the XML Namespace for the root/parent operation ID SOAP headers. /// public string SoapHeaderNamespace { get; set; } - private const String RequestHeadersChecked = "OCTI_RequestHeadersChecked"; - private static readonly object Checked = new object(); - /// - /// Initializes a new instance of the class. + /// Initialize the telemetry event. /// - public OperationCorrelationTelemetryInitializer() - { - this.RootOperationIdHeaderName = CorrelationHeaders.HttpStandardRootIdHeader; - this.ParentOperationIdHeaderName = CorrelationHeaders.HttpStandardParentIdHeader; - this.SoapHeaderNamespace = CorrelationHeaders.SoapStandardNamespace; - this.SoapParentOperationIdHeaderName = CorrelationHeaders.SoapStandardParentIdHeader; - this.SoapRootOperationIdHeaderName = CorrelationHeaders.SoapStandardRootIdHeader; - } - - /// - /// Initialize the telemetry event - /// - /// The telemetry event - /// WCF operation context + /// The telemetry event. + /// WCF operation context. protected override void OnInitialize(ITelemetry telemetry, IOperationContext operation) { var parentContext = operation.Request.Context.Operation; // if the parent operation ID is specified in the header // set it on the current request - if ( String.IsNullOrEmpty(parentContext.ParentId) ) + if (string.IsNullOrEmpty(parentContext.ParentId)) { - if ( !String.IsNullOrEmpty(ParentOperationIdHeaderName) ) + if (!string.IsNullOrEmpty(this.ParentOperationIdHeaderName)) { - var parentId = GetHeader(operation, ParentOperationIdHeaderName, SoapParentOperationIdHeaderName); - if ( !String.IsNullOrEmpty(parentId) ) + var parentId = this.GetHeader(operation, this.ParentOperationIdHeaderName, this.SoapParentOperationIdHeaderName); + if (!string.IsNullOrEmpty(parentId)) { parentContext.ParentId = parentId; } } } + // if the root operation ID is specified in the header // set it on the current request - if ( String.IsNullOrEmpty(parentContext.Id) ) + if (string.IsNullOrEmpty(parentContext.Id)) { - if ( !String.IsNullOrWhiteSpace(RootOperationIdHeaderName) ) + if (!string.IsNullOrWhiteSpace(this.RootOperationIdHeaderName)) { - var rootId = GetHeader(operation, RootOperationIdHeaderName, SoapRootOperationIdHeaderName); - if ( !String.IsNullOrEmpty(rootId) ) + var rootId = this.GetHeader(operation, this.RootOperationIdHeaderName, this.SoapRootOperationIdHeaderName); + if (!string.IsNullOrEmpty(rootId)) { parentContext.Id = rootId; } } + // if the root ID has not been set, set it now - if ( String.IsNullOrEmpty(parentContext.Id) ) + if (string.IsNullOrEmpty(parentContext.Id)) { parentContext.Id = operation.Request.Id; } } - if ( telemetry != operation.Request ) + + if (telemetry != operation.Request) { // tie the current telemetry event to the parent request - if ( String.IsNullOrEmpty(telemetry.Context.Operation.ParentId) ) + if (string.IsNullOrEmpty(telemetry.Context.Operation.ParentId)) { telemetry.Context.Operation.ParentId = operation.Request.Id; } - if ( String.IsNullOrEmpty(telemetry.Context.Operation.Id) ) + + if (string.IsNullOrEmpty(telemetry.Context.Operation.Id)) { telemetry.Context.Operation.Id = parentContext.Id; } - } else + } + else { // we've initialized the correlation headers for // this request object. We want to initialize @@ -109,29 +118,32 @@ protected override void OnInitialize(ITelemetry telemetry, IOperationContext ope } } - private String GetHeader(IOperationContext context, String httpHeader, String soapHeader) + private string GetHeader(IOperationContext context, string httpHeader, string soapHeader) { - if ( RequestAlreadyChecked(context) ) + if (this.RequestAlreadyChecked(context)) { return null; } + var httpHeaders = context.GetHttpRequestHeaders(); - if ( httpHeaders != null ) + if (httpHeaders != null) { return httpHeaders.Headers[httpHeader]; - } else + } + else { - return context.GetIncomingMessageHeader(soapHeader, SoapHeaderNamespace); + return context.GetIncomingMessageHeader(soapHeader, this.SoapHeaderNamespace); } } private bool RequestAlreadyChecked(IOperationContext context) { object checkedValue = null; - if ( ((IOperationContextState)context).TryGetState(RequestHeadersChecked, out checkedValue) ) + if (((IOperationContextState)context).TryGetState(RequestHeadersChecked, out checkedValue)) { return checkedValue == Checked; } + return false; } } diff --git a/WCF/Shared/OperationNameTelemetryInitializer.cs b/WCF/Shared/OperationNameTelemetryInitializer.cs index 33da625..b4d2b48 100644 --- a/WCF/Shared/OperationNameTelemetryInitializer.cs +++ b/WCF/Shared/OperationNameTelemetryInitializer.cs @@ -1,42 +1,43 @@ -using Microsoft.ApplicationInsights.Channel; -using Microsoft.ApplicationInsights.DataContracts; -using Microsoft.ApplicationInsights.Extensibility.Implementation; -using System; - -namespace Microsoft.ApplicationInsights.Wcf +namespace Microsoft.ApplicationInsights.Wcf { + using System; + using Microsoft.ApplicationInsights.Channel; + using Microsoft.ApplicationInsights.DataContracts; + using Microsoft.ApplicationInsights.Extensibility.Implementation; + /// - /// Telemetry initializer that collects the operation name + /// Telemetry initializer that collects the operation name. /// public sealed class OperationNameTelemetryInitializer : WcfTelemetryInitializer { /// - /// Called when a telemetry item is available + /// Called when a telemetry item is available. /// - /// The telemetry item to augment - /// The operation context + /// The telemetry item to augment. + /// The operation context. protected override void OnInitialize(ITelemetry telemetry, IOperationContext operation) { - if ( String.IsNullOrEmpty(telemetry.Context.Operation.Name) ) + if (string.IsNullOrEmpty(telemetry.Context.Operation.Name)) { - var opContext = operation.Request.Context.Operation; - if ( String.IsNullOrEmpty(opContext.Name) ) + var operationContext = operation.Request.Context.Operation; + if (string.IsNullOrEmpty(operationContext.Name)) { - UpdateOperationContext(operation, opContext); + this.UpdateOperationContext(operation, operationContext); } - telemetry.Context.Operation.Name = opContext.Name; - RequestTelemetry request = telemetry as RequestTelemetry; - if ( request != null ) + telemetry.Context.Operation.Name = operationContext.Name; + + var request = telemetry as RequestTelemetry; + if (request != null) { - request.Name = opContext.Name; + request.Name = operationContext.Name; } } } - private void UpdateOperationContext(IOperationContext operation, OperationContext opContext) + private void UpdateOperationContext(IOperationContext operation, OperationContext context) { - opContext.Name = operation.ContractName + '.' + operation.OperationName; + context.Name = operation.ContractName + '.' + operation.OperationName; } } } diff --git a/WCF/Shared/OperationTelemetryAttribute.cs b/WCF/Shared/OperationTelemetryAttribute.cs index 8c2dee4..9d2acae 100644 --- a/WCF/Shared/OperationTelemetryAttribute.cs +++ b/WCF/Shared/OperationTelemetryAttribute.cs @@ -1,26 +1,29 @@ -using System; -using System.ServiceModel.Channels; -using System.ServiceModel.Description; -using System.ServiceModel.Dispatcher; - -namespace Microsoft.ApplicationInsights.Wcf +namespace Microsoft.ApplicationInsights.Wcf { + using System; + using System.ServiceModel.Channels; + using System.ServiceModel.Description; + using System.ServiceModel.Dispatcher; + /// - /// Marks an OperationContract as an instrumented method + /// Marks an OperationContract as an instrumented method. /// /// /// By default, when a WCF service has been instrumented through - /// the , requests + /// the , requests /// to any service operation will be tracked. - /// + /// /// However, in some cases you might want finer control and only /// record telemetry data for requests to certain operations. - /// + /// + /// /// You can use the [OperationTelemetry] attribute on an operation contract method /// (that is, in the service contract interface, not on the service implementation) /// to tell Application Insights to only record telemetry for this method. - /// - /// Any operation contract methods without an [OperationTelemetry] attribute will be ignored + /// + /// + /// Any operation contract methods without an [OperationTelemetry] attribute will be ignored. + /// /// [AttributeUsage(AttributeTargets.Method)] public sealed class OperationTelemetryAttribute : Attribute, IOperationBehavior diff --git a/WCF/Shared/Properties/AssemblyInfo.cs b/WCF/Shared/Properties/AssemblyInfo.cs index ab832bd..0d5c6f3 100644 --- a/WCF/Shared/Properties/AssemblyInfo.cs +++ b/WCF/Shared/Properties/AssemblyInfo.cs @@ -9,7 +9,6 @@ [assembly: InternalsVisibleTo("Microsoft.AI.Wcf.Tests.Net40, PublicKey=" + AssemblyInfo.PublicKey)] [assembly: InternalsVisibleTo("Microsoft.AI.Wcf.Tests.Net45, PublicKey=" + AssemblyInfo.PublicKey)] - internal static class AssemblyInfo { #if PUBLIC_RELEASE diff --git a/WCF/Shared/RequestTrackingTelemetryModule.cs b/WCF/Shared/RequestTrackingTelemetryModule.cs index f12ed68..f772445 100644 --- a/WCF/Shared/RequestTrackingTelemetryModule.cs +++ b/WCF/Shared/RequestTrackingTelemetryModule.cs @@ -1,22 +1,22 @@ -using Microsoft.ApplicationInsights.DataContracts; -using Microsoft.ApplicationInsights.Extensibility; -using Microsoft.ApplicationInsights.Extensibility.Implementation; -using Microsoft.ApplicationInsights.Wcf.Implementation; -using System; -using System.Net; -using System.ServiceModel.Channels; - -namespace Microsoft.ApplicationInsights.Wcf +namespace Microsoft.ApplicationInsights.Wcf { + using System; + using System.Net; + using System.ServiceModel.Channels; + using Microsoft.ApplicationInsights.DataContracts; + using Microsoft.ApplicationInsights.Extensibility; + using Microsoft.ApplicationInsights.Extensibility.Implementation; + using Microsoft.ApplicationInsights.Wcf.Implementation; + /// - /// Telemetry module that collects requests to WCF services + /// Telemetry module that collects requests to WCF services. /// public sealed class RequestTrackingTelemetryModule : IWcfTelemetryModule { private TelemetryClient telemetryClient; /// - /// Initializes a new instance + /// Initializes a new instance of the class. /// public RequestTrackingTelemetryModule() { @@ -30,14 +30,20 @@ void ITelemetryModule.Initialize(TelemetryConfiguration configuration) void IWcfTelemetryModule.OnBeginRequest(IOperationContext operation) { - if ( operation == null ) + if (operation == null) + { throw new ArgumentNullException("operation"); - if ( telemetryClient == null ) + } + + if (this.telemetryClient == null) + { return; + } RequestTelemetry telemetry = operation.Request; + // if ASP.NET has already started the request, leave the start time alone. - if ( operation.OwnsRequest ) + if (operation.OwnsRequest) { telemetry.Start(); } @@ -46,10 +52,10 @@ void IWcfTelemetryModule.OnBeginRequest(IOperationContext operation) telemetry.Name = operation.OperationName; var httpHeaders = operation.GetHttpRequestHeaders(); - if ( httpHeaders != null ) + if (httpHeaders != null) { telemetry.Properties["httpMethod"] = httpHeaders.Method; - if ( operation.ToHeader != null ) + if (operation.ToHeader != null) { // overwrite it for WebHttpBinding requests telemetry.Url = operation.ToHeader; @@ -62,12 +68,17 @@ void IWcfTelemetryModule.OnBeginRequest(IOperationContext operation) void IWcfTelemetryModule.OnEndRequest(IOperationContext operation, Message reply) { - if ( operation == null ) + if (operation == null) + { throw new ArgumentNullException("operation"); - if ( telemetryClient == null ) + } + + if (this.telemetryClient == null) + { return; + } - if ( reply != null && reply.IsClosed() ) + if (reply != null && reply.IsClosed()) { WcfEventSource.Log.ResponseMessageClosed(nameof(RequestTrackingTelemetryModule), "OnEndRequest"); } @@ -75,46 +86,49 @@ void IWcfTelemetryModule.OnEndRequest(IOperationContext operation, Message reply RequestTelemetry telemetry = operation.Request; // make some assumptions - bool isFault = false; - HttpStatusCode responseCode = HttpStatusCode.OK; + var isFault = false; + var responseCode = HttpStatusCode.OK; - if ( reply != null && !reply.IsClosed() ) + if (reply != null && !reply.IsClosed()) { isFault = reply.IsFault; } HttpResponseMessageProperty httpHeaders = operation.GetHttpResponseHeaders(); - if ( httpHeaders != null ) + if (httpHeaders != null) { responseCode = httpHeaders.StatusCode; - if ( (int)responseCode >= 400 ) + if ((int)responseCode >= 400) { isFault = true; } - } else if ( isFault ) + } + else if (isFault) { responseCode = HttpStatusCode.InternalServerError; } // if the operation code has already marked the request as failed // don't overwrite the value if we think it was successful - if ( isFault || !telemetry.Success.HasValue ) + if (isFault || !telemetry.Success.HasValue) { telemetry.Success = !isFault; } + telemetry.ResponseCode = responseCode.ToString("d"); - if ( telemetry.Url != null ) + if (telemetry.Url != null) { telemetry.Properties["Protocol"] = telemetry.Url.Scheme; } + // if the Microsoft.ApplicationInsights.Web package started // tracking this request before WCF handled it, we // don't want to track it because it would duplicate the event. // Let the HttpModule instead write it later on. - if ( operation.OwnsRequest ) + if (operation.OwnsRequest) { telemetry.Stop(); - telemetryClient.TrackRequest(telemetry); + this.telemetryClient.TrackRequest(telemetry); } } diff --git a/WCF/Shared/ServiceTelemetryAttribute.cs b/WCF/Shared/ServiceTelemetryAttribute.cs index 387a355..99bc84d 100644 --- a/WCF/Shared/ServiceTelemetryAttribute.cs +++ b/WCF/Shared/ServiceTelemetryAttribute.cs @@ -1,15 +1,15 @@ -using Microsoft.ApplicationInsights.Extensibility; -using Microsoft.ApplicationInsights.Wcf.Implementation; -using System; -using System.Collections.ObjectModel; -using System.Linq; -using System.ServiceModel; -using System.ServiceModel.Channels; -using System.ServiceModel.Description; -using System.ServiceModel.Dispatcher; - -namespace Microsoft.ApplicationInsights.Wcf +namespace Microsoft.ApplicationInsights.Wcf { + using System; + using System.Collections.ObjectModel; + using System.Linq; + using System.ServiceModel; + using System.ServiceModel.Channels; + using System.ServiceModel.Description; + using System.ServiceModel.Dispatcher; + using Microsoft.ApplicationInsights.Extensibility; + using Microsoft.ApplicationInsights.Wcf.Implementation; + /// /// Enables Application Insights telemetry when applied on a /// WCF service class. @@ -18,12 +18,12 @@ namespace Microsoft.ApplicationInsights.Wcf public sealed class ServiceTelemetryAttribute : Attribute, IServiceBehavior { /// - /// The Application Insights instrumentation key. + /// Gets or sets the Application Insights instrumentation key. /// /// /// You can use this as an alternative to setting the key in the ApplicationInsights.config file. /// - public String InstrumentationKey { get; set; } + public string InstrumentationKey { get; set; } void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection endpoints, BindingParameterCollection bindingParameters) { @@ -34,34 +34,37 @@ void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescriptio try { TelemetryConfiguration configuration = TelemetryConfiguration.Active; - if ( !String.IsNullOrEmpty(InstrumentationKey) ) + if (!string.IsNullOrEmpty(this.InstrumentationKey)) { - configuration.InstrumentationKey = InstrumentationKey; + configuration.InstrumentationKey = this.InstrumentationKey; } var contractFilter = BuildFilter(serviceDescription); - WcfInterceptor interceptor = null; - foreach ( ChannelDispatcher channelDisp in serviceHost.ChannelDispatchers ) + WcfInterceptor interceptor = null; + foreach (ChannelDispatcher channelDisp in serviceHost.ChannelDispatchers) { - if ( channelDisp.ErrorHandlers.OfType().Any() ) + if (channelDisp.ErrorHandlers.OfType().Any()) { // already added, ignore continue; } - if ( interceptor == null ) + + if (interceptor == null) { interceptor = new WcfInterceptor(configuration, contractFilter); } + channelDisp.ErrorHandlers.Insert(0, interceptor); - foreach ( var ep in channelDisp.Endpoints ) + foreach (var ep in channelDisp.Endpoints) { - if ( !ep.IsSystemEndpoint ) + if (!ep.IsSystemEndpoint) { ep.DispatchRuntime.MessageInspectors.Insert(0, interceptor); } } } - } catch ( Exception ex ) + } + catch (Exception ex) { WcfEventSource.Log.InitializationFailure(ex.ToString()); } diff --git a/WCF/Shared/ServiceTelemetryExtensionElement.cs b/WCF/Shared/ServiceTelemetryExtensionElement.cs index b9aa27d..c92d399 100644 --- a/WCF/Shared/ServiceTelemetryExtensionElement.cs +++ b/WCF/Shared/ServiceTelemetryExtensionElement.cs @@ -1,31 +1,31 @@ -using System; -using System.Configuration; -using System.ServiceModel.Configuration; - -namespace Microsoft.ApplicationInsights.Wcf +namespace Microsoft.ApplicationInsights.Wcf { + using System; + using System.Configuration; + using System.ServiceModel.Configuration; + /// /// Supports adding Application Insights telemetry to WCF services - /// through the configuration file + /// through the configuration file. /// public class ServiceTelemetryExtensionElement : BehaviorExtensionElement { private ConfigurationPropertyCollection properties; /// - /// The Application Insights instrumentation key + /// Gets or sets the Application Insights instrumentation key. /// /// - /// You can use this as an alternative to setting the instrumentation key in the ApplicationInsights.config file + /// You can use this as an alternative to setting the instrumentation key in the ApplicationInsights.config file. /// - public String InstrumentationKey + public string InstrumentationKey { - get { return (String)base["instrumentationKey"]; } + get { return (string)base["instrumentationKey"]; } set { base["instrumentationKey"] = value; } } /// - /// Gets the type of the behavior + /// Gets the type of the behavior. /// public override Type BehaviorType { @@ -33,29 +33,34 @@ public override Type BehaviorType } /// - /// The list of properties supported by this behavior + /// Gets the list of properties supported by this behavior. /// protected override System.Configuration.ConfigurationPropertyCollection Properties { get { - if ( properties == null ) + if (this.properties == null) { - properties = new ConfigurationPropertyCollection(); - properties.Add(new ConfigurationProperty("instrumentationKey", typeof(String), "", ConfigurationPropertyOptions.None)); + this.properties = new ConfigurationPropertyCollection + { + new ConfigurationProperty("instrumentationKey", typeof(string), string.Empty, ConfigurationPropertyOptions.None) + }; } - return properties; + + return this.properties; } } /// - /// Creates the ApplicationInsights behavior + /// Creates the behavior. /// - /// A new instance of ApplicationInsightsAttribute + /// A new instance of class.. protected override object CreateBehavior() { - var behavior = new ServiceTelemetryAttribute(); - behavior.InstrumentationKey = InstrumentationKey; + var behavior = new ServiceTelemetryAttribute() + { + InstrumentationKey = this.InstrumentationKey + }; return behavior; } } diff --git a/WCF/Shared/UserAgentTelemetryInitializer.cs b/WCF/Shared/UserAgentTelemetryInitializer.cs index 98b4a98..6ab0b5f 100644 --- a/WCF/Shared/UserAgentTelemetryInitializer.cs +++ b/WCF/Shared/UserAgentTelemetryInitializer.cs @@ -1,31 +1,32 @@ -using Microsoft.ApplicationInsights.Channel; -using Microsoft.ApplicationInsights.Extensibility.Implementation; -using Microsoft.ApplicationInsights.Wcf.Implementation; -using System; - -namespace Microsoft.ApplicationInsights.Wcf +namespace Microsoft.ApplicationInsights.Wcf { + using System; + using Microsoft.ApplicationInsights.Channel; + using Microsoft.ApplicationInsights.Extensibility.Implementation; + using Microsoft.ApplicationInsights.Wcf.Implementation; + /// - /// Telemetry initializer that collects User-Agent information + /// Telemetry initializer that collects User-Agent information. /// public sealed class UserAgentTelemetryInitializer : WcfTelemetryInitializer { - private const String UserAgent = "UATI_UserAgent"; + private const string UserAgent = "UATI_UserAgent"; /// - /// Called when a telemetry item is available + /// Called when a telemetry item is available. /// - /// The telemetry item to augment - /// The operation context + /// The telemetry item to augment. + /// The operation context. protected override void OnInitialize(ITelemetry telemetry, IOperationContext operation) { - if ( String.IsNullOrEmpty(telemetry.Context.User.UserAgent) ) + if (string.IsNullOrEmpty(telemetry.Context.User.UserAgent)) { var requestContext = operation.Request.Context.User; - if ( String.IsNullOrEmpty(requestContext.UserAgent) ) + if (string.IsNullOrEmpty(requestContext.UserAgent)) { - UpdateUserAgent(operation, requestContext); + this.UpdateUserAgent(operation, requestContext); } + var userContext = telemetry.Context.User; telemetry.Context.User.UserAgent = requestContext.UserAgent; } @@ -34,26 +35,27 @@ protected override void OnInitialize(ITelemetry telemetry, IOperationContext ope private void UpdateUserAgent(IOperationContext operation, UserContext userContext) { var contextState = (IOperationContextState)operation; - String knownAgent = null; - if ( contextState.TryGetState(UserAgent, out knownAgent) ) + string knownAgent = null; + if (contextState.TryGetState(UserAgent, out knownAgent)) { userContext.Id = knownAgent; return; } var httpHeaders = operation.GetHttpRequestHeaders(); - if ( httpHeaders != null ) + if (httpHeaders != null) { var userAgent = httpHeaders.Headers["User-Agent"]; - if ( !String.IsNullOrEmpty(userAgent) ) + if (!string.IsNullOrEmpty(userAgent)) { userContext.UserAgent = userAgent; } } + // we store this here (even if it's null), to avoid // having to check the message headers later on // when it might no longer be available. - contextState.SetState(UserAgent, userContext.UserAgent ?? ""); + contextState.SetState(UserAgent, userContext.UserAgent ?? string.Empty); } } } diff --git a/WCF/Shared/UserTelemetryInitializer.cs b/WCF/Shared/UserTelemetryInitializer.cs index de1d728..a2b424f 100644 --- a/WCF/Shared/UserTelemetryInitializer.cs +++ b/WCF/Shared/UserTelemetryInitializer.cs @@ -1,30 +1,31 @@ -using Microsoft.ApplicationInsights.Channel; -using Microsoft.ApplicationInsights.Extensibility.Implementation; -using System; - -namespace Microsoft.ApplicationInsights.Wcf +namespace Microsoft.ApplicationInsights.Wcf { + using System; + using Microsoft.ApplicationInsights.Channel; + using Microsoft.ApplicationInsights.Extensibility.Implementation; + /// - /// Telemetry initializer that collects user information from the security context + /// Telemetry initializer that collects user information from the security context. /// public sealed class UserTelemetryInitializer : WcfTelemetryInitializer { - private const String IdentityName = "UTI_IdentityName"; + private const string IdentityName = "UTI_IdentityName"; /// - /// Called when a telemetry item is available + /// Called when a telemetry item is available. /// - /// The telemetry item to augment - /// The operation context + /// The telemetry item to augment. + /// The operation context. protected override void OnInitialize(ITelemetry telemetry, IOperationContext operation) { - if ( String.IsNullOrEmpty(telemetry.Context.User.Id) ) + if (string.IsNullOrEmpty(telemetry.Context.User.Id)) { var userContext = operation.Request.Context.User; - if ( String.IsNullOrEmpty(userContext.Id) ) + if (string.IsNullOrEmpty(userContext.Id)) { - UpdateUserContext(operation, userContext); + this.UpdateUserContext(operation, userContext); } + telemetry.Context.User.Id = userContext.Id; } } @@ -32,15 +33,15 @@ protected override void OnInitialize(ITelemetry telemetry, IOperationContext ope private void UpdateUserContext(IOperationContext operation, UserContext userContext) { var contextState = (IOperationContextState)operation; - String knownIdentity = null; - if ( contextState.TryGetState(IdentityName, out knownIdentity) ) + string knownIdentity = null; + if (contextState.TryGetState(IdentityName, out knownIdentity)) { userContext.Id = knownIdentity; return; } var ctxt = operation.SecurityContext; - if ( ctxt != null && !ctxt.IsAnonymous && ctxt.PrimaryIdentity != null ) + if (ctxt != null && !ctxt.IsAnonymous && ctxt.PrimaryIdentity != null) { userContext.Id = ctxt.PrimaryIdentity.Name; } @@ -48,7 +49,7 @@ private void UpdateUserContext(IOperationContext operation, UserContext userCont // we store this here (even if it's null), to avoid // having to check the request security context later on // when it might no longer be available. - contextState.SetState(IdentityName, userContext.Id ?? ""); + contextState.SetState(IdentityName, userContext.Id ?? string.Empty); } } } diff --git a/WCF/Shared/WcfDependencyTrackingTelemetryModule.cs b/WCF/Shared/WcfDependencyTrackingTelemetryModule.cs index 10b0d9f..84151ff 100644 --- a/WCF/Shared/WcfDependencyTrackingTelemetryModule.cs +++ b/WCF/Shared/WcfDependencyTrackingTelemetryModule.cs @@ -1,10 +1,10 @@ -using Microsoft.ApplicationInsights.Extensibility; -using Microsoft.ApplicationInsights.Wcf.Implementation; -using Microsoft.Diagnostics.Instrumentation.Extensions.Intercept; -using System; - -namespace Microsoft.ApplicationInsights.Wcf +namespace Microsoft.ApplicationInsights.Wcf { + using System; + using Microsoft.ApplicationInsights.Extensibility; + using Microsoft.ApplicationInsights.Wcf.Implementation; + using Microsoft.Diagnostics.Instrumentation.Extensions.Intercept; + /// /// Provides telemetry for web service calls done through the /// WCF client-side stack. @@ -18,69 +18,76 @@ namespace Microsoft.ApplicationInsights.Wcf /// public sealed class WcfDependencyTrackingTelemetryModule : ITelemetryModule { + private readonly object lockObject = new object(); private ProfilerWcfClientProcessing wcfClientProcessing = null; private bool initialized = false; - private readonly object lockObject = new object(); /// /// Gets or sets the name of the HTTP header to get root operation Id from. /// public string RootOperationIdHeaderName { get; set; } + /// /// Gets or sets the name of the HTTP header to get parent operation Id from. /// public string ParentOperationIdHeaderName { get; set; } + /// /// Gets or sets the name of the SOAP header to get root operation Id from. /// public string SoapRootOperationIdHeaderName { get; set; } + /// /// Gets or sets the name of the SOAP header to get parent operation Id from. /// public string SoapParentOperationIdHeaderName { get; set; } + /// /// Gets or sets the XML Namespace for the root/parent operation ID SOAP headers. /// public string SoapHeaderNamespace { get; set; } + /// /// Gets or sets a value indicating whether to disable runtime instrumentation. /// public bool DisableRuntimeInstrumentation { get; set; } + /// - /// Telemetry Client based on configuration we were initialized with + /// Gets the Telemetry Client based on configuration we were initialized with. /// internal TelemetryClient TelemetryClient { get; private set; } /// - /// Initializes this telemetry module + /// Initializes this telemetry module. /// - /// Application Insights configuration + /// Application Insights configuration. public void Initialize(TelemetryConfiguration configuration) { - if ( configuration == null ) + if (configuration == null) { throw new ArgumentNullException(nameof(configuration)); } - if ( !initialized ) + if (!this.initialized) { - lock ( lockObject ) + lock (this.lockObject) { - if ( !initialized ) + if (!this.initialized) { try { this.TelemetryClient = new TelemetryClient(configuration); - DoInitialization(configuration); - } catch ( Exception ex ) + this.DoInitialization(configuration); + } + catch (Exception ex) { WcfEventSource.Log.InitializationFailure(ex.ToString()); } - initialized = true; + + this.initialized = true; } } } - } private void DoInitialization(TelemetryConfiguration configuration) @@ -91,15 +98,16 @@ private void DoInitialization(TelemetryConfiguration configuration) this.SoapParentOperationIdHeaderName = CorrelationHeaders.SoapStandardParentIdHeader; this.SoapRootOperationIdHeaderName = CorrelationHeaders.SoapStandardRootIdHeader; - if ( Decorator.IsHostEnabled() ) + if (Decorator.IsHostEnabled()) { WcfClientEventSource.Log.ClientDependencyTrackingInfo("Profiler is attached"); WcfClientEventSource.Log.ClientDependencyTrackingInfo("Agent version: " + Decorator.GetAgentVersion()); - if ( !DisableRuntimeInstrumentation ) + if (!this.DisableRuntimeInstrumentation) { this.wcfClientProcessing = new ProfilerWcfClientProcessing(this); - DecorateProfilerForWcfClientProcessing(); - } else + this.DecorateProfilerForWcfClientProcessing(); + } + else { WcfClientEventSource.Log.ClientDependencyTrackingInfo("Runtime Instrumentation is disabled."); } @@ -108,14 +116,15 @@ private void DoInitialization(TelemetryConfiguration configuration) private void DecorateProfilerForWcfClientProcessing() { - const String assembly = "System.ServiceModel"; - const String module = "System.ServiceModel.dll"; - const String className = "System.ServiceModel.ChannelFactory"; + const string Assembly = "System.ServiceModel"; + const string Module = "System.ServiceModel.dll"; + const string ClassName = "System.ServiceModel.ChannelFactory"; // void InitializeEndpoint(ServiceEndpoint endpoint) Decorator.Decorate( - assembly, module, - className + ".InitializeEndpoint", + Assembly, + Module, + ClassName + ".InitializeEndpoint", 1, null, this.wcfClientProcessing.OnEndInitializeEndpoint1, @@ -124,8 +133,9 @@ private void DecorateProfilerForWcfClientProcessing() // void InitializeEndpoint(Binding binding, EndpointAddress address) // void InitializeEndpoint(string configurationName, EndpointAddress address) Decorator.Decorate( - assembly, module, - className + ".InitializeEndpoint", + Assembly, + Module, + ClassName + ".InitializeEndpoint", 2, this.wcfClientProcessing.OnStartInitializeEndpoint2, this.wcfClientProcessing.OnEndInitializeEndpoint2, @@ -133,8 +143,9 @@ private void DecorateProfilerForWcfClientProcessing() // void InitializeEndpoint(string configurationName, EndpointAddress address, Configuration configuration) Decorator.Decorate( - assembly, module, - className + ".InitializeEndpoint", + Assembly, + Module, + ClassName + ".InitializeEndpoint", 3, null, this.wcfClientProcessing.OnEndInitializeEndpoint3, diff --git a/WCF/Shared/WcfTelemetryInitializer.cs b/WCF/Shared/WcfTelemetryInitializer.cs index 4eedb08..1c9d324 100644 --- a/WCF/Shared/WcfTelemetryInitializer.cs +++ b/WCF/Shared/WcfTelemetryInitializer.cs @@ -1,18 +1,18 @@ -using Microsoft.ApplicationInsights.Channel; -using Microsoft.ApplicationInsights.Extensibility; -using Microsoft.ApplicationInsights.Wcf.Implementation; -using System; - -namespace Microsoft.ApplicationInsights.Wcf +namespace Microsoft.ApplicationInsights.Wcf { + using System; + using Microsoft.ApplicationInsights.Channel; + using Microsoft.ApplicationInsights.Extensibility; + using Microsoft.ApplicationInsights.Wcf.Implementation; + /// /// Base class for ITelemetryInitializer implementations used - /// to instrument WCF services + /// to instrument WCF services. /// public abstract class WcfTelemetryInitializer : ITelemetryInitializer { /// - /// Constructs a new instance + /// Initializes a new instance of the class. /// public WcfTelemetryInitializer() { @@ -20,41 +20,42 @@ public WcfTelemetryInitializer() } /// - /// Initializes the telemetry context for a telemetry item + /// Initializes the telemetry context for a telemetry item. /// - /// The telemetry item to augment + /// The telemetry item to augment. public void Initialize(ITelemetry telemetry) { - IOperationContext context = WcfOperationContext.Current; - if ( context != null ) + var context = WcfOperationContext.Current; + if (context != null) { - OnInitialize(telemetry, context); - } else + this.OnInitialize(telemetry, context); + } + else { WcfEventSource.Log.NoOperationContextFound(); } } /// - /// Initializes the telemetry context for a telemetry item + /// Initializes the telemetry context for a telemetry item. /// - /// The telemetry item to augment - /// The operation context + /// The telemetry item to augment. + /// The operation context. /// /// This variant is used to support easier testability by providing - /// the operation context explicitly + /// the operation context explicitly. /// public void Initialize(ITelemetry telemetry, IOperationContext context) { - OnInitialize(telemetry, context); + this.OnInitialize(telemetry, context); } /// /// Method that subclasses can override to augment - /// a telemetry item + /// a telemetry item. /// - /// The telemetry item - /// The operation context + /// The telemetry item. + /// The operation context. protected abstract void OnInitialize(ITelemetry telemetry, IOperationContext operation); } } diff --git a/WCF/WCF.sln b/WCF/WCF.sln index 4d455f9..34e34d6 100644 --- a/WCF/WCF.sln +++ b/WCF/WCF.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.24720.0 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D928C78D-DB5B-4145-A174-DCF0A4A1D64C}" EndProject @@ -9,7 +9,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{EBFB5CC5 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{74E830EA-5350-408B-9971-15B42E6225B0}" ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig readme.md = readme.md + Settings.StyleCop = Settings.StyleCop Version.props = Version.props EndProjectSection EndProject @@ -31,12 +33,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AI.Wcf.Tests.Net4 EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution - Shared\Microsoft.AI.Wcf.projitems*{a9362b74-ddda-4eaf-8bd8-e165be426883}*SharedItemsImports = 13 - Shared.Tests\Microsoft.AI.Wcf.Tests.projitems*{fa34505d-857b-4cc2-9d5d-c5fb74a75cb1}*SharedItemsImports = 13 Shared\Microsoft.AI.Wcf.projitems*{2dbb5fb7-f219-4999-bdbb-25eb6355c808}*SharedItemsImports = 4 + Shared.Tests\Microsoft.AI.Wcf.Tests.projitems*{62bc21bd-6a9b-4cef-8f31-5df2570a0eea}*SharedItemsImports = 4 Shared\Microsoft.AI.Wcf.projitems*{a43c7e00-0f6d-41ea-982b-870dbc08b8c9}*SharedItemsImports = 4 + Shared\Microsoft.AI.Wcf.projitems*{a9362b74-ddda-4eaf-8bd8-e165be426883}*SharedItemsImports = 13 + Shared.Tests\Microsoft.AI.Wcf.Tests.projitems*{fa34505d-857b-4cc2-9d5d-c5fb74a75cb1}*SharedItemsImports = 13 Shared.Tests\Microsoft.AI.Wcf.Tests.projitems*{fc52ad4b-4679-44b8-838c-9780fc459488}*SharedItemsImports = 4 - Shared.Tests\Microsoft.AI.Wcf.Tests.projitems*{62bc21bd-6a9b-4cef-8f31-5df2570a0eea}*SharedItemsImports = 4 EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/WCF/net40.tests/Microsoft.AI.Wcf.Tests.Net40.csproj b/WCF/net40.tests/Microsoft.AI.Wcf.Tests.Net40.csproj index 9be56ed..d09850c 100644 --- a/WCF/net40.tests/Microsoft.AI.Wcf.Tests.Net40.csproj +++ b/WCF/net40.tests/Microsoft.AI.Wcf.Tests.Net40.csproj @@ -94,8 +94,10 @@ + +