diff --git a/.gitignore b/.gitignore index b7dfc574..97d76923 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ *.user *.sln.docstates .vs +.vscode *.sln.dotsettings # Build results diff --git a/CHANGELOG.md b/CHANGELOG.md index db8e62c6..c95e896f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog + ## Version 2.7.0-beta1 +- [Add operation details for HTTP and SQL operation to the dependency telemetry.](https://github.com/Microsoft/ApplicationInsights-dotnet-server/issues/900) - [Fix: Do not call base HandleErrorAttribute.OnException in MVC unhandled exception filter](https://github.com/Microsoft/ApplicationInsights-dotnet-server/issues/921) - [Send UserActionable event about correlation issue with HTTP request with body when .NET 4.7.1 is not installed](https://github.com/Microsoft/ApplicationInsights-dotnet-server/pull/903) - [Added support to collect Perf Counters for .NET Core Apps if running inside Azure WebApps](https://github.com/Microsoft/ApplicationInsights-dotnet-server/issues/889) @@ -8,6 +10,7 @@ - [Fix: Correlation is not working for POST requests](https://github.com/Microsoft/ApplicationInsights-dotnet-server/pull/898) when .NET 4.7.1 runtime is installed. - [Fix: Tracking mixed HTTP responses with and without content](https://github.com/Microsoft/ApplicationInsights-dotnet-server/pull/919) + ## Version 2.6.0-beta4 - [Remove CorrelationIdLookupHelper. Use TelemetryConfiguration.ApplicationIdProvider instead.](https://github.com/Microsoft/ApplicationInsights-dotnet-server/pull/880) With this change you can update URL to query application ID from which enables environments with reverse proxy configuration to access Application Insights ednpoints. - [Update Microsoft.AspNet.TelemetryCorrelation package to 1.0.1: Fix endless loop when activity stack is broken](https://github.com/aspnet/Microsoft.AspNet.TelemetryCorrelation/issues/22) diff --git a/Src/DependencyCollector/Net45.Tests/DependencyTrackingTelemetryModuleHttpTest.cs b/Src/DependencyCollector/Net45.Tests/DependencyTrackingTelemetryModuleHttpTest.cs index 5eebcb36..e7f0c6fb 100644 --- a/Src/DependencyCollector/Net45.Tests/DependencyTrackingTelemetryModuleHttpTest.cs +++ b/Src/DependencyCollector/Net45.Tests/DependencyTrackingTelemetryModuleHttpTest.cs @@ -35,12 +35,18 @@ private StubTelemetryChannel channel; private TelemetryConfiguration config; private List sentTelemetry; + private object request; + private object response; + private object responseHeaders; [TestInitialize] public void Initialize() { ServicePointManager.DefaultConnectionLimit = 1000; this.sentTelemetry = new List(); + this.request = null; + this.response = null; + this.responseHeaders = null; this.channel = new StubTelemetryChannel { @@ -51,6 +57,9 @@ if (depTelemetry != null) { this.sentTelemetry.Add(depTelemetry); + depTelemetry.TryGetOperationDetail(RemoteDependencyConstants.HttpRequestOperationDetailName, out this.request); + depTelemetry.TryGetOperationDetail(RemoteDependencyConstants.HttpResponseOperationDetailName, out this.response); + depTelemetry.TryGetOperationDetail(RemoteDependencyConstants.HttpResponseOperationDetailName, out this.responseHeaders); } }, EndpointAddress = FakeProfileApiEndpoint @@ -382,7 +391,7 @@ } } - this.ValidateTelemetry(enableDiagnosticSource, this.sentTelemetry.Single(), new Uri(url), request, statusCode >= 200 && statusCode < 300, statusCode.ToString(CultureInfo.InvariantCulture), injectLegacyHeaders); + this.ValidateTelemetry(enableDiagnosticSource, this.sentTelemetry.Single(), new Uri(url), request, statusCode >= 200 && statusCode < 300, statusCode.ToString(CultureInfo.InvariantCulture), expectLegacyHeaders: injectLegacyHeaders); } } @@ -412,7 +421,7 @@ } } - this.ValidateTelemetry(true, this.sentTelemetry.Single(), new Uri(url), null, statusCode >= 200 && statusCode < 300, statusCode.ToString(CultureInfo.InvariantCulture)); + this.ValidateTelemetry(true, this.sentTelemetry.Single(), new Uri(url), null, statusCode >= 200 && statusCode < 300, statusCode.ToString(CultureInfo.InvariantCulture), responseExpected: contentLength != 0); } } @@ -468,7 +477,7 @@ } Assert.AreEqual(2, this.sentTelemetry.Count); - this.ValidateTelemetry(true, this.sentTelemetry.Last(), new Uri(url), null, statusCode >= 200 && statusCode < 300, statusCode.ToString(CultureInfo.InvariantCulture)); + this.ValidateTelemetry(true, this.sentTelemetry.Last(), new Uri(url), null, statusCode >= 200 && statusCode < 300, statusCode.ToString(CultureInfo.InvariantCulture), responseExpected: false); } } @@ -498,7 +507,7 @@ await httpClient.GetAsync(url, cts.Token).ContinueWith(t => { }); } - this.ValidateTelemetry(enableDiagnosticSource, this.sentTelemetry.Single(), new Uri(url), null, false, string.Empty); + this.ValidateTelemetry(enableDiagnosticSource, this.sentTelemetry.Single(), new Uri(url), null, false, string.Empty, responseExpected: false); } } @@ -515,7 +524,7 @@ // here the start of dependency is tracked with HttpDesktopDiagnosticSourceListener, // so the expected SDK version should have DiagnosticSource 'rdddsd' prefix. // however the end is tracked by FrameworkHttpEventListener - this.ValidateTelemetry(true, this.sentTelemetry.Single(), url, null, false, string.Empty); + this.ValidateTelemetry(true, this.sentTelemetry.Single(), url, null, false, string.Empty, responseExpected: false); } else { @@ -526,7 +535,7 @@ } } - private void ValidateTelemetry(bool diagnosticSource, DependencyTelemetry item, Uri url, WebRequest request, bool success, string resultCode, bool expectLegacyHeaders = false) + private void ValidateTelemetry(bool diagnosticSource, DependencyTelemetry item, Uri url, WebRequest request, bool success, string resultCode, bool responseExpected = true, bool headersExpected = false, bool expectLegacyHeaders = false) { Assert.AreEqual(url, item.Data); @@ -555,6 +564,30 @@ Assert.AreEqual(Activity.Current?.Id, item.Context.Operation.ParentId); Assert.IsTrue(item.Id.StartsWith('|' + item.Context.Operation.Id + '.')); + // Validate the http request was captured + if (diagnosticSource) + { + Assert.IsNotNull(this.request, "Http request was not found within the operation details."); + var webRequest = this.request as WebRequest; + Assert.IsNotNull(webRequest, "Http request was not the expected type."); + } + + // If expected -- validate the response was captured + if (diagnosticSource && responseExpected) + { + Assert.IsNotNull(this.response, "Http response was not found within the operation details."); + var webResponse = this.response as WebResponse; + Assert.IsNotNull(webResponse, "Http response was not the expected type."); + } + + // If expected -- validate the headers were captured + if (diagnosticSource && headersExpected) + { + Assert.IsNotNull(this.responseHeaders, "Http response headers were not found within the operation details."); + var headers = this.responseHeaders as WebHeaderCollection; + Assert.IsNotNull(headers, "Http response headers were not the expected type."); + } + if (diagnosticSource) { this.ValidateTelemetryForDiagnosticSource(item, url, request, expectLegacyHeaders); diff --git a/Src/DependencyCollector/NetCore.Tests/DependencyTrackingTelemetryModuleTestNetCore.cs b/Src/DependencyCollector/NetCore.Tests/DependencyTrackingTelemetryModuleTestNetCore.cs index 8036fece..5e2e7899 100644 --- a/Src/DependencyCollector/NetCore.Tests/DependencyTrackingTelemetryModuleTestNetCore.cs +++ b/Src/DependencyCollector/NetCore.Tests/DependencyTrackingTelemetryModuleTestNetCore.cs @@ -33,6 +33,9 @@ private StubTelemetryChannel channel; private TelemetryConfiguration config; private List sentTelemetry; + private object request; + private object response; + private object responseHeaders; /// /// Initialize. @@ -41,6 +44,9 @@ public void Initialize() { this.sentTelemetry = new List(); + this.request = null; + this.response = null; + this.responseHeaders = null; this.channel = new StubTelemetryChannel { @@ -51,6 +57,9 @@ if (depTelemetry != null) { this.sentTelemetry.Add(depTelemetry); + depTelemetry.TryGetOperationDetail(RemoteDependencyConstants.HttpRequestOperationDetailName, out this.request); + depTelemetry.TryGetOperationDetail(RemoteDependencyConstants.HttpResponseOperationDetailName, out this.response); + depTelemetry.TryGetOperationDetail(RemoteDependencyConstants.HttpResponseHeadersOperationDetailName, out this.responseHeaders); } }, EndpointAddress = FakeProfileApiEndpoint @@ -220,6 +229,19 @@ Assert.IsFalse(request.Headers.Contains(RequestResponseHeaders.StandardParentIdHeader)); } } + + // Validate the http request was captured + Assert.IsNotNull(this.request, "Http request was not found within the operation details."); + var webRequest = this.request as HttpRequestMessage; + Assert.IsNotNull(webRequest, "Http request was not the expected type."); + + // Validate the http response was captured + Assert.IsNotNull(this.response, "Http response was not found within the operation details."); + var webResponse = this.response as HttpResponseMessage; + Assert.IsNotNull(webResponse, "Http response was not the expected type."); + + // Validate the http response headers were not captured + Assert.IsNull(this.responseHeaders, "Http response headers were not found within the operation details."); } private sealed class LocalServer : IDisposable diff --git a/Src/DependencyCollector/Shared.Tests/Implementation/DependencyCollectorDiagnosticListenerTests.Netstandard16.cs b/Src/DependencyCollector/Shared.Tests/Implementation/DependencyCollectorDiagnosticListenerTests.Netstandard16.cs index 02b75b16..e9d7ad62 100644 --- a/Src/DependencyCollector/Shared.Tests/Implementation/DependencyCollectorDiagnosticListenerTests.Netstandard16.cs +++ b/Src/DependencyCollector/Shared.Tests/Implementation/DependencyCollectorDiagnosticListenerTests.Netstandard16.cs @@ -29,7 +29,10 @@ namespace Microsoft.ApplicationInsights.Tests private const string HttpOkResultCode = "200"; private const string NotFoundResultCode = "404"; - private readonly List sentTelemetry = new List(); + private List sentTelemetry; + private object request; + private object response; + private object responseHeaders; private TelemetryConfiguration configuration; private string testInstrumentationKey1 = nameof(testInstrumentationKey1); @@ -44,10 +47,27 @@ namespace Microsoft.ApplicationInsights.Tests [TestInitialize] public void Initialize() { + this.sentTelemetry = new List(); + this.request = null; + this.response = null; + this.responseHeaders = null; + this.telemetryChannel = new StubTelemetryChannel() { EndpointAddress = "https://endpointaddress", - OnSend = this.sentTelemetry.Add + OnSend = telemetry => + { + this.sentTelemetry.Add(telemetry); + + // The correlation id lookup service also makes http call, just make sure we skip that + DependencyTelemetry depTelemetry = telemetry as DependencyTelemetry; + if (depTelemetry != null) + { + depTelemetry.TryGetOperationDetail(RemoteDependencyConstants.HttpRequestOperationDetailName, out this.request); + depTelemetry.TryGetOperationDetail(RemoteDependencyConstants.HttpResponseOperationDetailName, out this.response); + depTelemetry.TryGetOperationDetail(RemoteDependencyConstants.HttpResponseHeadersOperationDetailName, out this.responseHeaders); + } + }, }; this.testInstrumentationKey1 = Guid.NewGuid().ToString(); @@ -314,6 +334,9 @@ namespace Microsoft.ApplicationInsights.Tests string expectedVersion = SdkVersionHelper.GetExpectedSdkVersion(typeof(DependencyTrackingTelemetryModule), prefix: "rdddsc:"); Assert.AreEqual(expectedVersion, telemetry.Context.GetInternalContext().SdkVersion); + + // Check the operation details + this.ValidateOperationDetails(telemetry); } /// @@ -349,6 +372,9 @@ namespace Microsoft.ApplicationInsights.Tests Assert.AreEqual(RequestUrl, telemetry.Target); Assert.AreEqual(NotFoundResultCode, telemetry.ResultCode); Assert.AreEqual(false, telemetry.Success); + + // Check the operation details + this.ValidateOperationDetails(telemetry); } /// @@ -385,6 +411,9 @@ namespace Microsoft.ApplicationInsights.Tests Assert.AreEqual(RequestUrl, telemetry.Target); Assert.AreEqual(HttpOkResultCode, telemetry.ResultCode); Assert.AreEqual(true, telemetry.Success, "response was not successful"); + + // Check the operation details + this.ValidateOperationDetails(telemetry); } /// @@ -420,6 +449,9 @@ namespace Microsoft.ApplicationInsights.Tests Assert.AreEqual(RequestUrl, telemetry.Target); Assert.AreEqual(NotFoundResultCode, telemetry.ResultCode); Assert.AreEqual(false, telemetry.Success); + + // Check the operation details + this.ValidateOperationDetails(telemetry); } /// @@ -456,6 +488,9 @@ namespace Microsoft.ApplicationInsights.Tests Assert.AreEqual(GetApplicationInsightsTarget(targetApplicationId), telemetry.Target); Assert.AreEqual(HttpOkResultCode, telemetry.ResultCode); Assert.AreEqual(true, telemetry.Success); + + // Check the operation details + this.ValidateOperationDetails(telemetry); } /// @@ -492,6 +527,9 @@ namespace Microsoft.ApplicationInsights.Tests Assert.AreEqual(GetApplicationInsightsTarget(targetApplicationId), telemetry.Target); Assert.AreEqual(NotFoundResultCode, telemetry.ResultCode); Assert.AreEqual(false, telemetry.Success); + + // Check the operation details + this.ValidateOperationDetails(telemetry); } /// @@ -534,6 +572,9 @@ namespace Microsoft.ApplicationInsights.Tests Assert.AreEqual(parentActivity.RootId, telemetry.Context.Operation.Id); Assert.AreEqual(parentActivity.Id, telemetry.Context.Operation.ParentId); + // Check the operation details + this.ValidateOperationDetails(telemetry); + parentActivity.Stop(); } @@ -551,5 +592,21 @@ namespace Microsoft.ApplicationInsights.Tests { return HttpHeadersUtilities.GetRequestContextKeyValue(request.Headers, keyName); } + + private void ValidateOperationDetails(DependencyTelemetry telemetry, bool responseExpected = true) + { + Assert.IsNotNull(this.request, "Request was not present and expected."); + Assert.IsNotNull(this.request as HttpRequestMessage, "Request was not the expected type."); + Assert.IsNull(this.responseHeaders, "Response headers were present and not expected."); + if (responseExpected) + { + Assert.IsNotNull(this.response, "Response was not present and expected."); + Assert.IsNotNull(this.response as HttpResponseMessage, "Response was not the expected type."); + } + else + { + Assert.IsNull(this.response, "Response was present and not expected."); + } + } } } \ No newline at end of file diff --git a/Src/DependencyCollector/Shared.Tests/Implementation/DependencyCollectorDiagnosticListenerTests.Netstandard20.cs b/Src/DependencyCollector/Shared.Tests/Implementation/DependencyCollectorDiagnosticListenerTests.Netstandard20.cs index b99cd103..88a00832 100644 --- a/Src/DependencyCollector/Shared.Tests/Implementation/DependencyCollectorDiagnosticListenerTests.Netstandard20.cs +++ b/Src/DependencyCollector/Shared.Tests/Implementation/DependencyCollectorDiagnosticListenerTests.Netstandard20.cs @@ -106,6 +106,9 @@ namespace Microsoft.ApplicationInsights.Tests string expectedVersion = SdkVersionHelper.GetExpectedSdkVersion(typeof(DependencyTrackingTelemetryModule), prefix: "rdddsc:"); Assert.AreEqual(expectedVersion, telemetry.Context.GetInternalContext().SdkVersion); + + // Check the operation details + this.ValidateOperationDetails(telemetry); } /// @@ -126,6 +129,9 @@ namespace Microsoft.ApplicationInsights.Tests Assert.AreEqual("Canceled", telemetry.ResultCode); Assert.AreEqual(false, telemetry.Success); + + // Check the operation details + this.ValidateOperationDetails(telemetry, responseExpected: false); } /// @@ -146,6 +152,9 @@ namespace Microsoft.ApplicationInsights.Tests Assert.AreEqual("Faulted", telemetry.ResultCode); Assert.AreEqual(false, telemetry.Success); + + // Check the operation details + this.ValidateOperationDetails(telemetry, responseExpected: false); } /// @@ -172,6 +181,9 @@ namespace Microsoft.ApplicationInsights.Tests Assert.AreEqual(exceptionTelemetry.Context.Operation.Id, dependencyTelemetry.Context.Operation.Id); Assert.AreEqual(exceptionTelemetry.Context.Operation.ParentId, dependencyTelemetry.Id); Assert.AreEqual("The server name or address could not be resolved", dependencyTelemetry.Context.Properties["Error"]); + + // Check the operation details + this.ValidateOperationDetails(dependencyTelemetry, responseExpected: false); } /// diff --git a/Src/DependencyCollector/Shared.Tests/Implementation/DesktopDiagnosticSourceHttpProcessingTests.cs b/Src/DependencyCollector/Shared.Tests/Implementation/DesktopDiagnosticSourceHttpProcessingTests.cs index e08490d7..a151a6c7 100644 --- a/Src/DependencyCollector/Shared.Tests/Implementation/DesktopDiagnosticSourceHttpProcessingTests.cs +++ b/Src/DependencyCollector/Shared.Tests/Implementation/DesktopDiagnosticSourceHttpProcessingTests.cs @@ -30,7 +30,10 @@ namespace Microsoft.ApplicationInsights.Tests private Uri testUrl = new Uri("http://www.microsoft.com/"); private int sleepTimeMsecBetweenBeginAndEnd = 100; private TelemetryConfiguration configuration; - private List sendItems = new List(); + private List sendItems; + private object request; + private object response; + private object responseHeaders; private DesktopDiagnosticSourceHttpProcessing httpDesktopProcessingFramework; #endregion //Fields @@ -39,9 +42,29 @@ namespace Microsoft.ApplicationInsights.Tests [TestInitialize] public void TestInitialize() { + this.sendItems = new List(); + this.request = null; + this.response = null; + this.responseHeaders = null; + this.configuration = new TelemetryConfiguration() { - TelemetryChannel = new StubTelemetryChannel { OnSend = item => this.sendItems.Add(item) }, + TelemetryChannel = new StubTelemetryChannel + { + OnSend = telemetry => + { + this.sendItems.Add(telemetry); + + // The correlation id lookup service also makes http call, just make sure we skip that + DependencyTelemetry depTelemetry = telemetry as DependencyTelemetry; + if (depTelemetry != null) + { + depTelemetry.TryGetOperationDetail(RemoteDependencyConstants.HttpRequestOperationDetailName, out this.request); + depTelemetry.TryGetOperationDetail(RemoteDependencyConstants.HttpResponseOperationDetailName, out this.response); + depTelemetry.TryGetOperationDetail(RemoteDependencyConstants.HttpResponseHeadersOperationDetailName, out this.responseHeaders); + } + }, + }, InstrumentationKey = TestInstrumentationKey, ApplicationIdProvider = new MockApplicationIdProvider(TestInstrumentationKey, TestApplicationId) }; @@ -74,7 +97,7 @@ namespace Microsoft.ApplicationInsights.Tests stopwatch.Stop(); Assert.AreEqual(1, this.sendItems.Count, "Only one telemetry item should be sent"); - ValidateTelemetryPacketForOnRequestSend(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, true, stopwatch.Elapsed.TotalMilliseconds, "200"); + this.ValidateTelemetryPacketForOnRequestSend(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, true, stopwatch.Elapsed.TotalMilliseconds, "200"); } /// @@ -106,11 +129,11 @@ namespace Microsoft.ApplicationInsights.Tests var successResponse = TestUtils.GenerateHttpWebResponse(HttpStatusCode.OK); Stopwatch stopwatch = Stopwatch.StartNew(); - this.httpDesktopProcessingFramework.OnBegin(request); - this.httpDesktopProcessingFramework.OnBegin(request); - this.httpDesktopProcessingFramework.OnBegin(request); - this.httpDesktopProcessingFramework.OnBegin(request); - this.httpDesktopProcessingFramework.OnBegin(request); + this.httpDesktopProcessingFramework.OnBegin(request); + this.httpDesktopProcessingFramework.OnBegin(request); + this.httpDesktopProcessingFramework.OnBegin(request); + this.httpDesktopProcessingFramework.OnBegin(request); + this.httpDesktopProcessingFramework.OnBegin(request); Thread.Sleep(this.sleepTimeMsecBetweenBeginAndEnd); Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); this.httpDesktopProcessingFramework.OnEndResponse(request, redirectResponse); @@ -121,7 +144,7 @@ namespace Microsoft.ApplicationInsights.Tests this.httpDesktopProcessingFramework.OnEndResponse(request, successResponse); Assert.AreEqual(1, this.sendItems.Count, "Only one telemetry item should be sent"); - ValidateTelemetryPacketForOnRequestSend(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, true, stopwatch.Elapsed.TotalMilliseconds, "302"); + this.ValidateTelemetryPacketForOnRequestSend(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, true, stopwatch.Elapsed.TotalMilliseconds, "302"); } /// @@ -305,15 +328,15 @@ namespace Microsoft.ApplicationInsights.Tests Assert.AreNotEqual(sampleHeaderValueWithAppId, actualHeaderValue); } - private static void ValidateTelemetryPacketForOnRequestSend(DependencyTelemetry remoteDependencyTelemetryActual, Uri url, string kind, bool? success, double valueMin, string statusCode) + private void ValidateTelemetryPacketForOnRequestSend(DependencyTelemetry remoteDependencyTelemetryActual, Uri url, string kind, bool? success, double valueMin, string statusCode) { Assert.AreEqual("GET " + url.AbsolutePath, remoteDependencyTelemetryActual.Name, true, "Resource name in the sent telemetry is wrong"); string expectedVersion = SdkVersionHelper.GetExpectedSdkVersion(typeof(DependencyTrackingTelemetryModule), prefix: "rdddsd:"); - ValidateTelemetryPacket(remoteDependencyTelemetryActual, url, kind, success, valueMin, statusCode, expectedVersion); + this.ValidateTelemetryPacket(remoteDependencyTelemetryActual, url, kind, success, valueMin, statusCode, expectedVersion); } - private static void ValidateTelemetryPacket(DependencyTelemetry remoteDependencyTelemetryActual, Uri url, string kind, bool? success, double valueMin, string statusCode, string expectedVersion) + private void ValidateTelemetryPacket(DependencyTelemetry remoteDependencyTelemetryActual, Uri url, string kind, bool? success, double valueMin, string statusCode, string expectedVersion, bool responseExpected = true) { Assert.AreEqual(url.Host, remoteDependencyTelemetryActual.Target, true, "Resource target in the sent telemetry is wrong"); Assert.AreEqual(url.OriginalString, remoteDependencyTelemetryActual.Data, true, "Resource data in the sent telemetry is wrong"); @@ -321,6 +344,18 @@ namespace Microsoft.ApplicationInsights.Tests Assert.AreEqual(success, remoteDependencyTelemetryActual.Success, "Success in the sent telemetry is wrong"); Assert.AreEqual(statusCode, remoteDependencyTelemetryActual.ResultCode, "ResultCode in the sent telemetry is wrong"); + // Validate the http request was captured + Assert.IsNotNull(this.request, "Http request was not found within the operation details."); + Assert.IsNotNull(this.request as WebRequest, "Http request was not the expected type."); + + // If expected -- validate the response was captured + if (responseExpected) + { + Assert.IsNotNull(this.response, "Http response was not found within the operation details."); + Assert.IsNotNull(this.response as WebResponse, "Http response was not the expected type."); + Assert.IsNull(this.responseHeaders, "Http response headers were not found within the operation details."); + } + var valueMinRelaxed = valueMin - TimeAccuracyMilliseconds; Assert.IsTrue( remoteDependencyTelemetryActual.Duration >= TimeSpan.FromMilliseconds(valueMinRelaxed), diff --git a/Src/DependencyCollector/Shared.Tests/Implementation/ProfilerHttpProcessingTest.cs b/Src/DependencyCollector/Shared.Tests/Implementation/ProfilerHttpProcessingTest.cs index ee602457..ec12a233 100644 --- a/Src/DependencyCollector/Shared.Tests/Implementation/ProfilerHttpProcessingTest.cs +++ b/Src/DependencyCollector/Shared.Tests/Implementation/ProfilerHttpProcessingTest.cs @@ -35,7 +35,10 @@ private TelemetryConfiguration configuration; private Uri testUrl = new Uri("http://www.microsoft.com/"); private Uri testUrlNonStandardPort = new Uri("http://www.microsoft.com:911/"); - private List sendItems = new List(); + private List sendItems; + private object request; + private object response; + private object responseHeaders; private int sleepTimeMsecBetweenBeginAndEnd = 100; private Exception ex = new Exception(); private ProfilerHttpProcessing httpProcessingProfiler; @@ -46,9 +49,29 @@ [TestInitialize] public void TestInitialize() { + this.sendItems = new List(); + this.request = null; + this.response = null; + this.responseHeaders = null; + this.configuration = new TelemetryConfiguration() { - TelemetryChannel = new StubTelemetryChannel { OnSend = item => this.sendItems.Add(item) }, + TelemetryChannel = new StubTelemetryChannel + { + OnSend = telemetry => + { + this.sendItems.Add(telemetry); + + // The correlation id lookup service also makes http call, just make sure we skip that + DependencyTelemetry depTelemetry = telemetry as DependencyTelemetry; + if (depTelemetry != null) + { + depTelemetry.TryGetOperationDetail(RemoteDependencyConstants.HttpRequestOperationDetailName, out this.request); + depTelemetry.TryGetOperationDetail(RemoteDependencyConstants.HttpResponseOperationDetailName, out this.response); + depTelemetry.TryGetOperationDetail(RemoteDependencyConstants.HttpResponseHeadersOperationDetailName, out this.responseHeaders); + } + }, + }, InstrumentationKey = TestInstrumentationKey, ApplicationIdProvider = new MockApplicationIdProvider(TestInstrumentationKey, TestApplicationId) }; @@ -83,11 +106,11 @@ [Owner("cithomas")] [TestCategory("CVT")] public void RddTestHttpProcessingProfilerOnBeginForGetResponse() - { + { var request = WebRequest.Create(this.testUrl); DependencyTelemetry operationReturned = (DependencyTelemetry)this.httpProcessingProfiler.OnBeginForGetResponse(request); Assert.IsNull(operationReturned, "Operation returned should be null as all context is maintained internally"); - Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); + Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); } /// @@ -98,20 +121,20 @@ [Owner("cithomas")] [TestCategory("CVT")] public void RddTestHttpProcessingProfilerOnEndForGetResponse() - { + { var request = WebRequest.Create(this.testUrl); var returnObjectPassed = TestUtils.GenerateHttpWebResponse(HttpStatusCode.OK); this.httpProcessingProfiler.OnBeginForGetResponse(request); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); Thread.Sleep(this.sleepTimeMsecBetweenBeginAndEnd); - Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); + Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); var objectReturned = this.httpProcessingProfiler.OnEndForGetResponse(null, returnObjectPassed, request); stopwatch.Stop(); Assert.AreSame(returnObjectPassed, objectReturned, "Object returned from OnEndForGetResponse processor is not the same as expected return object"); Assert.AreEqual(1, this.sendItems.Count, "Only one telemetry item should be sent"); - ValidateTelemetryPacket(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, true, stopwatch.Elapsed.TotalMilliseconds, "200"); + this.ValidateTelemetryPacket(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, true, stopwatch.Elapsed.TotalMilliseconds, "200"); } /// @@ -327,12 +350,12 @@ this.httpProcessingProfiler.OnBeginForGetResponse(request); Thread.Sleep(this.sleepTimeMsecBetweenBeginAndEnd); Exception exc = new Exception(); - Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); + Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); this.httpProcessingProfiler.OnExceptionForGetResponse(null, exc, request); stopwatch.Stop(); Assert.AreEqual(1, this.sendItems.Count, "Only one telemetry item should be sent"); - ValidateTelemetryPacket(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, false, stopwatch.Elapsed.TotalMilliseconds, string.Empty); + this.ValidateTelemetryPacket(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, false, stopwatch.Elapsed.TotalMilliseconds, string.Empty, responseExpected: false); } /// @@ -356,7 +379,7 @@ stopwatch.Stop(); Assert.AreEqual(1, this.sendItems.Count, "Only one telemetry item should be sent"); - ValidateTelemetryPacket(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, false, stopwatch.Elapsed.TotalMilliseconds, "404"); + this.ValidateTelemetryPacket(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, false, stopwatch.Elapsed.TotalMilliseconds, "404"); } /// @@ -400,7 +423,7 @@ DependencyTelemetry operationReturned = (DependencyTelemetry)this.httpProcessingProfiler.OnBeginForGetResponse(request); var objectReturned = this.httpProcessingProfiler.OnEndForGetResponse(null, returnObjectPassed, null); Assert.AreSame(returnObjectPassed, objectReturned, "Object returned from OnEndForGetResponse processor is not the same as expected return object"); - Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); + Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); var message = listener.Messages.First(item => item.EventId == 14); Assert.IsNotNull(message); @@ -423,7 +446,7 @@ var request = WebRequest.Create(this.testUrl); DependencyTelemetry operationReturned = (DependencyTelemetry)this.httpProcessingProfiler.OnBeginForGetRequestStream(request, null); Assert.IsNull(operationReturned, "Operation returned should be null as all context is maintained internally"); - Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); + Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); } /// @@ -441,13 +464,13 @@ stopwatch.Start(); this.httpProcessingProfiler.OnBeginForGetResponse(request); Thread.Sleep(this.sleepTimeMsecBetweenBeginAndEnd); - Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); + Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); Exception exc = new Exception(); this.httpProcessingProfiler.OnExceptionForGetResponse(null, exc, request); stopwatch.Stop(); Assert.AreEqual(1, this.sendItems.Count, "Only one telemetry item should be sent"); - ValidateTelemetryPacket(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, false, stopwatch.Elapsed.TotalMilliseconds, string.Empty); + this.ValidateTelemetryPacket(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, false, stopwatch.Elapsed.TotalMilliseconds, string.Empty, responseExpected: false); } #endregion //GetRequestStream @@ -466,7 +489,7 @@ var request = WebRequest.Create(this.testUrl); DependencyTelemetry operationReturned = (DependencyTelemetry)this.httpProcessingProfiler.OnBeginForBeginGetResponse(request, null, null); Assert.IsNull(operationReturned, "For async methods, operation returned should be null as correlation is done internally using WeakTables."); - Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); + Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); } /// @@ -484,13 +507,13 @@ stopwatch.Start(); DependencyTelemetry operationReturned = (DependencyTelemetry)this.httpProcessingProfiler.OnBeginForBeginGetResponse(request, null, null); Thread.Sleep(this.sleepTimeMsecBetweenBeginAndEnd); - Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); + Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); var objectReturned = this.httpProcessingProfiler.OnEndForEndGetResponse(operationReturned, returnObjectPassed, request, null); stopwatch.Stop(); Assert.AreSame(returnObjectPassed, objectReturned, "Object returned from OnEndForEndGetResponse processor is not the same as expected return object"); Assert.AreEqual(1, this.sendItems.Count, "Only one telemetry item should be sent"); - ValidateTelemetryPacket(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, true, stopwatch.Elapsed.TotalMilliseconds, "200"); + this.ValidateTelemetryPacket(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, true, stopwatch.Elapsed.TotalMilliseconds, "200"); } /// @@ -514,7 +537,7 @@ Assert.AreSame(returnObjectPassed, objectReturned, "Object returned from OnEndForEndGetResponse processor is not the same as expected return object"); Assert.AreEqual(1, this.sendItems.Count, "Only one telemetry item should be sent"); - ValidateTelemetryPacket(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, false, stopwatch.Elapsed.TotalMilliseconds, string.Empty); + this.ValidateTelemetryPacket(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, false, stopwatch.Elapsed.TotalMilliseconds, string.Empty, responseExpected: false); } /// @@ -532,13 +555,13 @@ stopwatch.Start(); DependencyTelemetry operationReturned = (DependencyTelemetry)this.httpProcessingProfiler.OnBeginForBeginGetResponse(request, null, null); Thread.Sleep(this.sleepTimeMsecBetweenBeginAndEnd); - Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); + Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); Exception exc = new Exception(); this.httpProcessingProfiler.OnExceptionForEndGetResponse(operationReturned, exc, request, null); stopwatch.Stop(); Assert.AreEqual(1, this.sendItems.Count, "Only one telemetry item should be sent"); - ValidateTelemetryPacket(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, false, stopwatch.Elapsed.TotalMilliseconds, string.Empty); + this.ValidateTelemetryPacket(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, false, stopwatch.Elapsed.TotalMilliseconds, string.Empty, responseExpected: false); } #endregion //BeginGetResponse-EndGetResponse @@ -557,7 +580,7 @@ var request = WebRequest.Create(this.testUrl); DependencyTelemetry operationReturned = (DependencyTelemetry)this.httpProcessingProfiler.OnBeginForBeginGetRequestStream(request, null, null); Assert.IsNull(operationReturned, "For async methods, operation returned should be null as correlation is done internally using WeakTables."); - Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); + Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); } /// @@ -575,13 +598,13 @@ stopwatch.Start(); DependencyTelemetry operationReturned = (DependencyTelemetry)this.httpProcessingProfiler.OnBeginForBeginGetRequestStream(request, null, null); Thread.Sleep(this.sleepTimeMsecBetweenBeginAndEnd); - Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); + Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); Exception exc = new Exception(); this.httpProcessingProfiler.OnExceptionForEndGetRequestStream(operationReturned, exc, request, null, null); stopwatch.Stop(); Assert.AreEqual(1, this.sendItems.Count, "Only one telemetry item should be sent"); - ValidateTelemetryPacket(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, false, stopwatch.Elapsed.TotalMilliseconds, string.Empty); + this.ValidateTelemetryPacket(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, false, stopwatch.Elapsed.TotalMilliseconds, string.Empty, responseExpected: false); } #endregion //BeginGetRequestStream-EndGetRequestStream @@ -609,13 +632,13 @@ Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); this.httpProcessingProfiler.OnBeginForGetRequestStream(request, null); - this.httpProcessingProfiler.OnBeginForGetRequestStream(request, null); + this.httpProcessingProfiler.OnBeginForGetRequestStream(request, null); Thread.Sleep(this.sleepTimeMsecBetweenBeginAndEnd); this.httpProcessingProfiler.OnBeginForGetRequestStream(request, null); Thread.Sleep(this.sleepTimeMsecBetweenBeginAndEnd); this.httpProcessingProfiler.OnBeginForGetResponse(request); - Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); + Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); Thread.Sleep(this.sleepTimeMsecBetweenBeginAndEnd); this.httpProcessingProfiler.OnEndForGetResponse(null, returnObjectPassed, request); stopwatch.Stop(); @@ -625,13 +648,13 @@ Thread.Sleep(this.sleepTimeMsecBetweenBeginAndEnd); Assert.AreEqual(1, this.sendItems.Count, "Exactly one telemetry item should be sent"); - ValidateTelemetryPacket(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, true, stopwatch.Elapsed.TotalMilliseconds, "200"); + this.ValidateTelemetryPacket(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, true, stopwatch.Elapsed.TotalMilliseconds, "200"); } - /// + /// /// Validates that HttpProcessingProfiler will sent RDD telemetry when GetRequestStream fails and GetResponse is not invoked /// 1.create request - /// 2.request.GetRequestStream fails. + /// 2.request.GetRequestStream fails. /// [TestMethod] [Description("Validates that HttpProcessingProfiler will sent RDD telemetry when GetRequestStream fails and GetResponse is not invoked.")] @@ -642,11 +665,11 @@ var request = WebRequest.Create(this.testUrl); this.httpProcessingProfiler.OnBeginForGetRequestStream(request, null); - Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); + Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); this.httpProcessingProfiler.OnExceptionForGetRequestStream(null, this.ex, request, null); Assert.AreEqual(1, this.sendItems.Count, "Exactly one telemetry item should be sent"); - ValidateTelemetryPacket(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, false, 0, string.Empty); + this.ValidateTelemetryPacket(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, false, 0, string.Empty, responseExpected: false); } #endregion //SyncScenarios @@ -677,13 +700,13 @@ Thread.Sleep(this.sleepTimeMsecBetweenBeginAndEnd); this.httpProcessingProfiler.OnBeginForBeginGetResponse(request, null, null); Thread.Sleep(this.sleepTimeMsecBetweenBeginAndEnd); - Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); + Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); this.httpProcessingProfiler.OnEndForEndGetResponse(null, returnObjectPassed, request, null); stopwatch.Stop(); Assert.AreEqual(1, this.sendItems.Count, "Exactly one telemetry item should be sent"); - ValidateTelemetryPacket(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, true, stopwatch.Elapsed.TotalMilliseconds, "200"); - } + this.ValidateTelemetryPacket(this.sendItems[0] as DependencyTelemetry, this.testUrl, RemoteDependencyConstants.HTTP, true, stopwatch.Elapsed.TotalMilliseconds, "200"); + } #endregion AsyncScenarios @@ -812,7 +835,7 @@ Assert.AreEqual(expectedTarget, receivedItem.Target, "HttpProcessingProfiler returned incorrect target for non standard port."); } - #endregion //Misc + #endregion //Misc #region LoggingTests @@ -835,7 +858,7 @@ DependencyTelemetry operationReturned = (DependencyTelemetry)this.httpProcessingProfiler.OnBeginForGetResponse(request); Assert.AreEqual(0, this.sendItems.Count, "No telemetry item should be processed without calling End"); - TestUtils.ValidateEventLogMessage(listener, "UnexpectedCallbackParameter", EventLevel.Warning); + TestUtils.ValidateEventLogMessage(listener, "UnexpectedCallbackParameter", EventLevel.Warning); } } @@ -851,8 +874,14 @@ #region Helpers - private static void ValidateTelemetryPacket( - DependencyTelemetry remoteDependencyTelemetryActual, Uri uri, string type, bool success, double expectedValue, string resultCode) + private void ValidateTelemetryPacket( + DependencyTelemetry remoteDependencyTelemetryActual, + Uri uri, + string type, + bool success, + double expectedValue, + string resultCode, + bool responseExpected = true) { Assert.AreEqual("GET " + uri.AbsolutePath, remoteDependencyTelemetryActual.Name, true, "Resource name in the sent telemetry is wrong"); Assert.AreEqual(uri.Host, remoteDependencyTelemetryActual.Target, true, "Resource target in the sent telemetry is wrong"); @@ -861,6 +890,18 @@ Assert.AreEqual(success, remoteDependencyTelemetryActual.Success, "Success in the sent telemetry is wrong"); Assert.AreEqual(resultCode, remoteDependencyTelemetryActual.ResultCode, "ResultCode in the sent telemetry is wrong"); + // Validate the http request is present + Assert.IsNotNull(this.request, "Http request was not found within the operation details."); + Assert.IsNotNull(this.request as WebRequest, "Http request was not the expected type."); + + // If expected -- validate the response + if (responseExpected) + { + Assert.IsNotNull(this.response, "Http response was not found within the operation details."); + Assert.IsNotNull(this.response as HttpWebResponse, "Http response was not the expected type."); + Assert.IsNull(this.responseHeaders, "Http response headers were not found within the operation details."); + } + var valueMinRelaxed = expectedValue - TimeAccuracyMilliseconds; Assert.IsTrue( remoteDependencyTelemetryActual.Duration >= TimeSpan.FromMilliseconds(valueMinRelaxed), diff --git a/Src/DependencyCollector/Shared/HttpCoreDiagnosticSourceListener.cs b/Src/DependencyCollector/Shared/HttpCoreDiagnosticSourceListener.cs index ceac1af0..f50bf9dd 100644 --- a/Src/DependencyCollector/Shared/HttpCoreDiagnosticSourceListener.cs +++ b/Src/DependencyCollector/Shared/HttpCoreDiagnosticSourceListener.cs @@ -328,6 +328,7 @@ namespace Microsoft.ApplicationInsights.DependencyCollector.Implementation var resourceName = request.Method.Method + " " + requestUri.AbsolutePath; DependencyTelemetry telemetry = new DependencyTelemetry(); + telemetry.SetOperationDetail(RemoteDependencyConstants.HttpRequestOperationDetailName, request); // properly fill dependency telemetry operation context: OperationCorrelationTelemetryInitializer initializes child telemetry telemetry.Context.Operation.Id = currentActivity.RootId; @@ -352,6 +353,7 @@ namespace Microsoft.ApplicationInsights.DependencyCollector.Implementation if (response != null) { this.ParseResponse(response, telemetry); + telemetry.SetOperationDetail(RemoteDependencyConstants.HttpResponseOperationDetailName, response); } else { @@ -364,7 +366,7 @@ namespace Microsoft.ApplicationInsights.DependencyCollector.Implementation telemetry.Success = false; } - this.client.Track(telemetry); + this.client.TrackDependency(telemetry); } //// netcoreapp1.1 and prior event. See https://github.com/dotnet/corefx/blob/release/1.0.0-rc2/src/Common/src/System/Net/Http/HttpHandlerDiagnosticListenerExtensions.cs. @@ -386,6 +388,7 @@ namespace Microsoft.ApplicationInsights.DependencyCollector.Implementation dependency.Telemetry.Target = requestUri.Host; dependency.Telemetry.Type = RemoteDependencyConstants.HTTP; dependency.Telemetry.Data = requestUri.OriginalString; + dependency.Telemetry.SetOperationDetail(RemoteDependencyConstants.HttpRequestOperationDetailName, request); this.pendingTelemetry.AddIfNotExists(request, dependency); this.InjectRequestHeaders(request, dependency.Telemetry.Context.InstrumentationKey, true); @@ -404,11 +407,16 @@ namespace Microsoft.ApplicationInsights.DependencyCollector.Implementation { DependencyCollectorEventSource.Log.HttpCoreDiagnosticSourceListenerResponse(loggingRequestId); var request = response.RequestMessage; - if (request != null && this.pendingTelemetry.TryGetValue(request, out IOperationHolder dependency)) + + if (this.pendingTelemetry.TryGetValue(request, out IOperationHolder dependency)) { - this.ParseResponse(response, dependency.Telemetry); - this.client.StopOperation(dependency); - this.pendingTelemetry.Remove(request); + dependency.Telemetry.SetOperationDetail(RemoteDependencyConstants.HttpResponseOperationDetailName, response); + if (request != null) + { + this.ParseResponse(response, dependency.Telemetry); + this.client.StopOperation(dependency); + this.pendingTelemetry.Remove(request); + } } } } diff --git a/Src/DependencyCollector/Shared/Implementation/ClientServerDependencyTracker.cs b/Src/DependencyCollector/Shared/Implementation/ClientServerDependencyTracker.cs index 0aeb5d35..82523685 100644 --- a/Src/DependencyCollector/Shared/Implementation/ClientServerDependencyTracker.cs +++ b/Src/DependencyCollector/Shared/Implementation/ClientServerDependencyTracker.cs @@ -99,7 +99,7 @@ internal static void EndTracking(TelemetryClient telemetryClient, DependencyTelemetry telemetry) { telemetry.Stop(); - telemetryClient.Track(telemetry); + telemetryClient.TrackDependency(telemetry); } /// diff --git a/Src/DependencyCollector/Shared/Implementation/HttpProcessing.cs b/Src/DependencyCollector/Shared/Implementation/HttpProcessing.cs index 48023f20..7e2df2a9 100644 --- a/Src/DependencyCollector/Shared/Implementation/HttpProcessing.cs +++ b/Src/DependencyCollector/Shared/Implementation/HttpProcessing.cs @@ -72,7 +72,7 @@ namespace Microsoft.ApplicationInsights.DependencyCollector.Implementation internal object OnBegin(object thisObj, bool injectCorrelationHeaders = true) { try - { + { if (thisObj == null) { DependencyCollectorEventSource.Log.NotExpectedCallback(0, "OnBeginHttp", "thisObj == null"); @@ -144,6 +144,7 @@ namespace Microsoft.ApplicationInsights.DependencyCollector.Implementation telemetry.Target = DependencyTargetNameHelper.GetDependencyTargetName(url); telemetry.Type = RemoteDependencyConstants.HTTP; telemetry.Data = url.OriginalString; + telemetry.SetOperationDetail(RemoteDependencyConstants.HttpRequestOperationDetailName, webRequest); // Add the source instrumentation key header if collection is enabled, the request host is not in the excluded list and the same header doesn't already exist if (this.setCorrelationHeaders && !this.correlationDomainExclusionList.Contains(url.Host)) @@ -215,9 +216,9 @@ namespace Microsoft.ApplicationInsights.DependencyCollector.Implementation /// /// Common helper for all End Callbacks. - /// + /// /// The HttpWebRequest instance. - /// The HttpWebResponse instance. + /// The HttpWebResponse instance. internal void OnEndResponse(object request, object response) { try @@ -232,6 +233,9 @@ namespace Microsoft.ApplicationInsights.DependencyCollector.Implementation { statusCode = (int)responseObj.StatusCode; this.SetTarget(telemetry, responseObj.Headers); + + // Set the operation details for the response + telemetry.SetOperationDetail(RemoteDependencyConstants.HttpResponseOperationDetailName, responseObj); } catch (ObjectDisposedException) { @@ -253,9 +257,9 @@ namespace Microsoft.ApplicationInsights.DependencyCollector.Implementation /// /// Common helper for all End Callbacks. - /// + /// /// The exception object if any. - /// HttpWebRequest instance. + /// HttpWebRequest instance. internal void OnEndException(object exception, object request) { try @@ -272,6 +276,9 @@ namespace Microsoft.ApplicationInsights.DependencyCollector.Implementation { statusCode = (int)responseObj.StatusCode; this.SetTarget(telemetry, responseObj.Headers); + + // Set the operation details for the response + telemetry.SetOperationDetail(RemoteDependencyConstants.HttpResponseOperationDetailName, responseObj); } catch (ObjectDisposedException) { @@ -302,9 +309,9 @@ namespace Microsoft.ApplicationInsights.DependencyCollector.Implementation /// /// Common helper for all End Callbacks. - /// + /// /// WebRequest object. - /// HttpStatusCode from response. + /// HttpStatusCode from response. /// Response headers. internal void OnEndResponse(object request, object statusCode, object responseHeaders) { @@ -318,6 +325,7 @@ namespace Microsoft.ApplicationInsights.DependencyCollector.Implementation } this.SetTarget(telemetry, (WebHeaderCollection)responseHeaders); + telemetry.SetOperationDetail(RemoteDependencyConstants.HttpResponseHeadersOperationDetailName, responseHeaders); ClientServerDependencyTracker.EndTracking(this.telemetryClient, telemetry); } diff --git a/Src/DependencyCollector/Shared/Implementation/ProfilerSqlProcessingBase.cs b/Src/DependencyCollector/Shared/Implementation/ProfilerSqlProcessingBase.cs index f124ac45..caded080 100644 --- a/Src/DependencyCollector/Shared/Implementation/ProfilerSqlProcessingBase.cs +++ b/Src/DependencyCollector/Shared/Implementation/ProfilerSqlProcessingBase.cs @@ -245,6 +245,7 @@ telemetry.Type = RemoteDependencyConstants.SQL; telemetry.Target = this.GetDependencyTarget(thisObj); telemetry.Data = commandText; + telemetry.SetOperationDetail(RemoteDependencyConstants.SqlCommandOperationDetailName, thisObj); // We use weaktables to store the thisObj for correlating begin with end call. this.TelemetryTable.Store(thisObj, new Tuple(telemetry, isCustomCreated)); @@ -299,7 +300,7 @@ DependencyCollectorEventSource.Log.CallbackError(thisObj == null ? 0 : thisObj.GetHashCode(), "OnEndAsyncSql", ex); } }); - } + } catch (Exception ex) { DependencyCollectorEventSource.Log.CallbackError(thisObj == null ? 0 : thisObj.GetHashCode(), "OnEndAsyncSql", ex); diff --git a/Src/DependencyCollector/Shared/Implementation/RemoteDependencyConstants.cs b/Src/DependencyCollector/Shared/Implementation/RemoteDependencyConstants.cs index 47cd2ec4..4c7779bb 100644 --- a/Src/DependencyCollector/Shared/Implementation/RemoteDependencyConstants.cs +++ b/Src/DependencyCollector/Shared/Implementation/RemoteDependencyConstants.cs @@ -16,5 +16,11 @@ public const string WcfService = "WCF Service"; public const string WebService = "Web Service"; + + public const string HttpRequestOperationDetailName = "HttpRequest"; + public const string HttpResponseOperationDetailName = "HttpResponse"; + public const string HttpResponseHeadersOperationDetailName = "HttpResponseHeaders"; + + public const string SqlCommandOperationDetailName = "SqlCommand"; } } diff --git a/Src/DependencyCollector/Shared/Implementation/SqlClientDiagnostics/SqlClientDiagnosticSourceListener.cs b/Src/DependencyCollector/Shared/Implementation/SqlClientDiagnostics/SqlClientDiagnosticSourceListener.cs index 3ac77cc4..a9cffb81 100644 --- a/Src/DependencyCollector/Shared/Implementation/SqlClientDiagnostics/SqlClientDiagnosticSourceListener.cs +++ b/Src/DependencyCollector/Shared/Implementation/SqlClientDiagnostics/SqlClientDiagnosticSourceListener.cs @@ -90,18 +90,20 @@ namespace Microsoft.ApplicationInsights.DependencyCollector.Implementation.SqlCl { var dependencyName = string.Empty; var target = string.Empty; + SqlConnection connection = null; if (command.Connection != null) { - target = string.Join(" | ", command.Connection.DataSource, command.Connection.Database); + connection = command.Connection; + target = string.Join(" | ", connection.DataSource, connection.Database); var commandName = command.CommandType == CommandType.StoredProcedure ? command.CommandText : string.Empty; dependencyName = string.IsNullOrEmpty(commandName) - ? string.Join(" | ", command.Connection.DataSource, command.Connection.Database) - : string.Join(" | ", command.Connection.DataSource, command.Connection.Database, commandName); + ? string.Join(" | ", connection.DataSource, connection.Database) + : string.Join(" | ", connection.DataSource, connection.Database, commandName); } var timestamp = CommandBefore.Timestamp.Fetch(evnt.Value) as long? @@ -116,7 +118,10 @@ namespace Microsoft.ApplicationInsights.DependencyCollector.Implementation.SqlCl Data = command.CommandText, Success = true }; - + + // Populate the operation details for intializers + telemetry.SetOperationDetail(RemoteDependencyConstants.SqlCommandOperationDetailName, command); + InitializeTelemetry(telemetry, operationId, timestamp); this.operationHolder.Store(command, Tuple.Create(telemetry, /* isCustomCreated: */ false)); @@ -148,7 +153,7 @@ namespace Microsoft.ApplicationInsights.DependencyCollector.Implementation.SqlCl telemetry.Stop(timestamp); - this.client.Track(telemetry); + this.client.TrackDependency(telemetry); } else { @@ -181,7 +186,7 @@ namespace Microsoft.ApplicationInsights.DependencyCollector.Implementation.SqlCl ConfigureExceptionTelemetry(telemetry, exception); - this.client.Track(telemetry); + this.client.TrackDependency(telemetry); } else { @@ -213,7 +218,7 @@ namespace Microsoft.ApplicationInsights.DependencyCollector.Implementation.SqlCl Data = operation, Success = true }; - + InitializeTelemetry(telemetry, operationId, timestamp); this.operationHolder.Store(connection, Tuple.Create(telemetry, /* isCustomCreated: */ false)); @@ -270,7 +275,7 @@ namespace Microsoft.ApplicationInsights.DependencyCollector.Implementation.SqlCl ConfigureExceptionTelemetry(telemetry, exception); - this.client.Track(telemetry); + this.client.TrackDependency(telemetry); } else { @@ -372,7 +377,7 @@ namespace Microsoft.ApplicationInsights.DependencyCollector.Implementation.SqlCl telemetry.Stop(timestamp); - this.client.Track(telemetry); + this.client.TrackDependency(telemetry); } else { @@ -406,7 +411,7 @@ namespace Microsoft.ApplicationInsights.DependencyCollector.Implementation.SqlCl ConfigureExceptionTelemetry(telemetry, exception); - this.client.Track(telemetry); + this.client.TrackDependency(telemetry); } else { diff --git a/Src/DependencyCollector/Shared/TelemetryDiagnosticSourceListener.cs b/Src/DependencyCollector/Shared/TelemetryDiagnosticSourceListener.cs index f46d5f05..cfe8d6aa 100644 --- a/Src/DependencyCollector/Shared/TelemetryDiagnosticSourceListener.cs +++ b/Src/DependencyCollector/Shared/TelemetryDiagnosticSourceListener.cs @@ -44,7 +44,7 @@ namespace Microsoft.ApplicationInsights.DependencyCollector.Implementation Activity currentActivity = Activity.Current; // extensibility point - can chain more telemetry extraction methods here - ITelemetry telemetry = this.ExtractDependencyTelemetry(diagnosticListener, currentActivity); + var telemetry = this.ExtractDependencyTelemetry(diagnosticListener, currentActivity); if (telemetry == null) { return; @@ -58,7 +58,7 @@ namespace Microsoft.ApplicationInsights.DependencyCollector.Implementation telemetry.Context.Properties["DiagnosticSource"] = diagnosticListener.Name; telemetry.Context.Properties["Activity"] = currentActivity.OperationName; - this.Client.Track(telemetry); + this.Client.TrackDependency(telemetry); } internal void RegisterHandler(string diagnosticSourceName, IDiagnosticEventHandler eventHandler) diff --git a/runUnitTests.cmd b/runUnitTests.cmd index 3115dcb3..ebb5909c 100644 --- a/runUnitTests.cmd +++ b/runUnitTests.cmd @@ -7,7 +7,7 @@ set BuildRoot=%~dp0..\bin\debug\ set VSTestPath=%PROGRAMFILES(x86)%\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe -CALL "%VSTestPath%" /UseVsixExtensions:true "%BuildRoot%Src\PerformanceCollector\Xdt.Tests\Xdt.Tests.dll" "%BuildRoot%Src\PerformanceCollector\Perf.NetFull.Tests\Microsoft.AI.PerformanceCollector.NetFull.Tests.dll" "%BuildRoot%Src\DependencyCollector\Net45.Tests\Microsoft.ApplicationInsights.DependencyCollector.Net45.Tests.dll" "%BuildRoot%Src\DependencyCollector\Nuget.Tests\Microsoft.ApplicationInsights.DependencyCollector.NuGet.Tests.dll" "%BuildRoot%Src\Web\Web.Net45.Tests\Microsoft.ApplicationInsights.Web.Net45.Tests.dll" "%BuildRoot%Src\Web\Web.Nuget.Tests\Microsoft.ApplicationInsights.Platform.Web.Nuget.Tests.dll" "%BuildRoot%Src\WindowsServer\WindowsServer.Net45.Tests\WindowsServer.Net45.Tests.dll" "%BuildRoot%Src\WindowsServer\WindowsServer.Nuget.Tests\WindowsServer.Nuget.Tests.dll" /logger:trx +CALL "%VSTestPath%" "%BuildRoot%Src\PerformanceCollector\Xdt.Tests\Xdt.Tests.dll" "%BuildRoot%Src\PerformanceCollector\Perf.NetFull.Tests\Microsoft.AI.PerformanceCollector.NetFull.Tests.dll" "%BuildRoot%Src\DependencyCollector\Net45.Tests\Microsoft.ApplicationInsights.DependencyCollector.Net45.Tests.dll" "%BuildRoot%Src\DependencyCollector\Nuget.Tests\Microsoft.ApplicationInsights.DependencyCollector.NuGet.Tests.dll" "%BuildRoot%Src\Web\Web.Net45.Tests\Microsoft.ApplicationInsights.Web.Net45.Tests.dll" "%BuildRoot%Src\Web\Web.Nuget.Tests\Microsoft.ApplicationInsights.Platform.Web.Nuget.Tests.dll" "%BuildRoot%Src\WindowsServer\WindowsServer.Net45.Tests\WindowsServer.Net45.Tests.dll" "%BuildRoot%Src\WindowsServer\WindowsServer.Nuget.Tests\WindowsServer.Nuget.Tests.dll" /logger:trx set VSTestNetCorePath=%PROGRAMFILES(x86)%\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\Extensions\TestPlatform\vstest.console.exe