From 05e634bc85dc134fe2a7b826d6299203033a4ddd Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Thu, 20 Jun 2019 18:30:11 -0700 Subject: [PATCH 1/4] 2.7.1 Hotfix (#909) * ApplicationInsights StartupFilter should not swallow exceptions from downstream ApplicationBuilder (#899) * bump to 2.7.1 hotfix --- CHANGELOG.md | 3 +++ .../Implementation/ApplicationInsightsStartupFilter.cs | 6 ++++-- .../Microsoft.ApplicationInsights.AspNetCore.csproj | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a7af33..660a32b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## Version 2.7.1 +- [Fix - ApplicationInsights StartupFilter should not swallow exceptions from downstream ApplicationBuilder.](https://github.com/microsoft/ApplicationInsights-aspnetcore/issues/897) + ## Version 2.7.0 - Updated Web/Base SDK version dependency to 2.10.0 - [Remove unused reference to System.Net.Http](https://github.com/microsoft/ApplicationInsights-aspnetcore/pull/879) diff --git a/src/Microsoft.ApplicationInsights.AspNetCore/Implementation/ApplicationInsightsStartupFilter.cs b/src/Microsoft.ApplicationInsights.AspNetCore/Implementation/ApplicationInsightsStartupFilter.cs index 614fefe..44289d5 100644 --- a/src/Microsoft.ApplicationInsights.AspNetCore/Implementation/ApplicationInsightsStartupFilter.cs +++ b/src/Microsoft.ApplicationInsights.AspNetCore/Implementation/ApplicationInsightsStartupFilter.cs @@ -23,12 +23,14 @@ // via class which triggers // initialization of TelemetryModules and construction of TelemetryProcessor pipeline. var tc = app.ApplicationServices.GetService(); - next(app); } catch (Exception ex) { AspNetCoreEventSource.Instance.LogWarning(ex.Message); - } + } + + // Invoking next builder is not wrapped in try catch to ensure any exceptions gets propogated up. + next(app); }; } } diff --git a/src/Microsoft.ApplicationInsights.AspNetCore/Microsoft.ApplicationInsights.AspNetCore.csproj b/src/Microsoft.ApplicationInsights.AspNetCore/Microsoft.ApplicationInsights.AspNetCore.csproj index f4f989d..949d17e 100644 --- a/src/Microsoft.ApplicationInsights.AspNetCore/Microsoft.ApplicationInsights.AspNetCore.csproj +++ b/src/Microsoft.ApplicationInsights.AspNetCore/Microsoft.ApplicationInsights.AspNetCore.csproj @@ -1,7 +1,7 @@  Microsoft.ApplicationInsights.AspNetCore - 2.7.0 + 2.7.1 Microsoft © Microsoft Corporation. All rights reserved. Application Insights for ASP.NET Core Web Applications From e2f7731d960eb6f29fabf83be429f5d737dcfcd2 Mon Sep 17 00:00:00 2001 From: Dmitry-Matveev Date: Wed, 10 Jul 2019 19:17:23 -0700 Subject: [PATCH 2/4] WIP ASP.NET Core Pert Fixes - 2 (#907) * Remove DiagnosticAdapter and use DiagnosticListener directly * minor fixes * switch * Initialize IKey * Fix typo * GetUri with Concat * Set Header fixes, use Properties for AspNetCoreEnv. * SetHeader fixes * Test Fixes - 1 * fix tests * Keep Public vs. Internal * Changelog update * Listeners, SetAppId, Ifs vs. Switch, RequestPool * Join listeners. * Refactor pre-sampling * Remove request pool * Update AI SDK * Remove unnecessary code and files * Repalce MVC Listener in tests * Fix unit tests * Fix Nuget Config * Fix tests - 2 * Code Review - 1 * Fix merge * Fix tests after merge - 2 * Finish merge * Fix AppId perf back * Use experimental feature flag for proactive sampling * Keep source for unsampled items (it only does one check for header internally anyway) * Cijo CR - 1 * SetAppId feature flag * Rename head sampling, fix predicate * Changelog and version * AppId as a method * Remove unnecessary settings for release --- CHANGELOG.md | 1 + .../HostingDiagnosticListener.cs | 357 +++++++++++++----- .../Implementation/MvcDiagnosticsListener.cs | 1 + .../Tracing/AspNetCoreEventSource.cs | 10 + .../TelemetryConfigurationOptionsSetup.cs | 14 +- .../RequestTrackingTelemetryModule.cs | 44 ++- .../Generated/AvailabilityData_types.cs | 12 +- .../Generated/Base_types.cs | 4 +- .../Generated/ContextTagKeys_types.cs | 16 +- .../Generated/DataPointType_types.cs | 4 +- .../Generated/DataPoint_types.cs | 4 +- .../Generated/Data_types.cs | 4 +- .../Generated/Domain_types.cs | 4 +- .../Generated/Envelope_types.cs | 8 +- .../Generated/EventData_types.cs | 12 +- .../Generated/ExceptionData_types.cs | 16 +- .../Generated/ExceptionDetails_types.cs | 10 +- .../Generated/MessageData_types.cs | 10 +- .../Generated/MetricData_types.cs | 12 +- .../Generated/PageViewData_types.cs | 4 +- .../Generated/PageViewPerfData_types.cs | 4 +- .../Generated/RemoteDependencyData_types.cs | 14 +- .../Generated/RequestData_types.cs | 20 +- .../Generated/SeverityLevel_types.cs | 4 +- .../Generated/StackFrame_types.cs | 4 +- .../Generated/bonddefaultcodegen.in | 38 +- .../RequestTelemetryEmptyAppTests.cs | 17 +- .../RequestTelemetryMvcTests.cs | 25 +- .../HostingDiagnosticListenerTest.cs | 117 ++++-- .../OperationNameTelemetryInitializerTests.cs | 87 +++-- .../RequestTelemetryWebApiTests.cs | 26 +- 31 files changed, 602 insertions(+), 301 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23e26fd..c11c97d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Version 2.8.0-beta1 - [Add EventCounter collection.](https://github.com/microsoft/ApplicationInsights-aspnetcore/issues/913) +- [Performance fixes: One DiagSource Listener; Head Sampling Feature; No Concurrent Dictionary; etc...](https://github.com/microsoft/ApplicationInsights-aspnetcore/pull/907) - [Fix: Add `IJavaScriptSnippet` service interface and update the `IServiceCollection` extension to register it for `JavaScriptSnippet`.](https://github.com/microsoft/ApplicationInsights-aspnetcore/issues/890) - [Make JavaScriptEncoder optional and Fallback to JavaScriptEncoder.Default.](https://github.com/microsoft/ApplicationInsights-aspnetcore/pull/918) diff --git a/src/Microsoft.ApplicationInsights.AspNetCore/DiagnosticListeners/Implementation/HostingDiagnosticListener.cs b/src/Microsoft.ApplicationInsights.AspNetCore/DiagnosticListeners/Implementation/HostingDiagnosticListener.cs index 0e6ce26..71025ed 100644 --- a/src/Microsoft.ApplicationInsights.AspNetCore/DiagnosticListeners/Implementation/HostingDiagnosticListener.cs +++ b/src/Microsoft.ApplicationInsights.AspNetCore/DiagnosticListeners/Implementation/HostingDiagnosticListener.cs @@ -6,13 +6,15 @@ using System.Globalization; using System.Linq; using System.Text; - using Extensibility.Implementation.Tracing; using Microsoft.ApplicationInsights.AspNetCore.DiagnosticListeners.Implementation; + using Microsoft.ApplicationInsights.AspNetCore.Extensibility.Implementation.Tracing; using Microsoft.ApplicationInsights.AspNetCore.Extensions; + using Microsoft.ApplicationInsights.Channel; using Microsoft.ApplicationInsights.Common; using Microsoft.ApplicationInsights.DataContracts; using Microsoft.ApplicationInsights.Extensibility; using Microsoft.ApplicationInsights.Extensibility.Implementation; + using Microsoft.ApplicationInsights.Extensibility.Implementation.Experimental; using Microsoft.ApplicationInsights.Extensibility.W3C; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Primitives; @@ -23,6 +25,8 @@ internal class HostingDiagnosticListener : IApplicationInsightDiagnosticListener { private const string ActivityCreatedByHostingDiagnosticListener = "ActivityCreatedByHostingDiagnosticListener"; + private const string ProactiveSamplingFeatureFlagName = "proactiveSampling"; + private const string ConditionalAppIdFeatureFlagName = "conditionalAppId"; /// /// Determine whether the running AspNetCore Hosting version is 2.0 or higher. This will affect what DiagnosticSource events we receive. @@ -31,6 +35,10 @@ /// private readonly bool enableNewDiagnosticEvents; + private readonly bool proactiveSamplingEnabled = false; + private readonly bool conditionalAppIdEnabled = false; + + private readonly TelemetryConfiguration configuration; private readonly TelemetryClient client; private readonly IApplicationIdProvider applicationIdProvider; private readonly string sdkVersion = SdkVersionUtils.GetVersion(); @@ -39,7 +47,12 @@ private readonly bool enableW3CHeaders; private static readonly ActiveSubsciptionManager SubscriptionManager = new ActiveSubsciptionManager(); + #region fetchers + // fetch is unique per event and per property + private readonly PropertyFetcher httpContextFetcherOnBeforeAction = new PropertyFetcher("httpContext"); + private readonly PropertyFetcher routeDataFetcher = new PropertyFetcher("routeData"); + private readonly PropertyFetcher routeValuesFetcher = new PropertyFetcher("Values"); private readonly PropertyFetcher httpContextFetcherStart = new PropertyFetcher("HttpContext"); private readonly PropertyFetcher httpContextFetcherStop = new PropertyFetcher("HttpContext"); private readonly PropertyFetcher httpContextFetcherBeginRequest = new PropertyFetcher("httpContext"); @@ -53,6 +66,10 @@ private readonly PropertyFetcher timestampFetcherBeginRequest = new PropertyFetcher("timestamp"); private readonly PropertyFetcher timestampFetcherEndRequest = new PropertyFetcher("timestamp"); + #endregion + + private string lastIKeyLookedUp; + private string lastAppIdUsed; /// /// Initializes a new instance of the class. @@ -79,6 +96,31 @@ this.enableW3CHeaders = enableW3CHeaders; } + /// + /// Initializes a new instance of the class. + /// + /// as a settings source. + /// to post traces to. + /// Provider for resolving application Id to be used in multiple instruemntation keys scenarios. + /// Flag that indicates that response headers should be injected. + /// Flag that indicates that exceptions should be tracked. + /// Flag that indicates that W3C header parsing should be enabled. + /// Flag that indicates that new diagnostic events are supported by AspNetCore + public HostingDiagnosticListener( + TelemetryConfiguration configuration, + TelemetryClient client, + IApplicationIdProvider applicationIdProvider, + bool injectResponseHeaders, + bool trackExceptions, + bool enableW3CHeaders, + bool enableNewDiagnosticEvents = true) + : this(client, applicationIdProvider, injectResponseHeaders, trackExceptions, enableW3CHeaders, enableNewDiagnosticEvents) + { + this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + this.proactiveSamplingEnabled = this.configuration.EvaluateExperimentalFeature(ProactiveSamplingFeatureFlagName); + this.conditionalAppIdEnabled = this.configuration.EvaluateExperimentalFeature(ConditionalAppIdFeatureFlagName); + } + /// public void OnSubscribe() { @@ -88,6 +130,77 @@ /// public string ListenerName { get; } = "Microsoft.AspNetCore"; + /// + /// Diagnostic event handler method for 'Microsoft.AspNetCore.Mvc.BeforeAction' event + /// + public void OnBeforeAction(HttpContext httpContext, IDictionary routeValues) + { + var telemetry = httpContext.Features.Get(); + + if (telemetry != null && string.IsNullOrEmpty(telemetry.Name)) + { + string name = this.GetNameFromRouteContext(routeValues); + + if (!string.IsNullOrEmpty(name)) + { + name = httpContext.Request.Method + " " + name; + telemetry.Name = name; + } + } + } + + private string GetNameFromRouteContext(IDictionary routeValues) + { + string name = null; + + if (routeValues.Count > 0) + { + object controller; + routeValues.TryGetValue("controller", out controller); + string controllerString = (controller == null) ? string.Empty : controller.ToString(); + + if (!string.IsNullOrEmpty(controllerString)) + { + name = controllerString; + + if (routeValues.TryGetValue("action", out var action) && action != null) + { + name += "/" + action.ToString(); + } + + if (routeValues.Keys.Count > 2) + { + // Add parameters + var sortedKeys = routeValues.Keys + .Where(key => + !string.Equals(key, "controller", StringComparison.OrdinalIgnoreCase) && + !string.Equals(key, "action", StringComparison.OrdinalIgnoreCase) && + !string.Equals(key, "!__route_group", StringComparison.OrdinalIgnoreCase)) + .OrderBy(key => key, StringComparer.OrdinalIgnoreCase) + .ToArray(); + + if (sortedKeys.Length > 0) + { + string arguments = string.Join(@"/", sortedKeys); + name += " [" + arguments + "]"; + } + } + } + else + { + object page; + routeValues.TryGetValue("page", out page); + string pageString = (page == null) ? string.Empty : page.ToString(); + if (!string.IsNullOrEmpty(pageString)) + { + name = pageString; + } + } + } + + return name; + } + /// /// Diagnostic event handler method for 'Microsoft.AspNetCore.Hosting.HttpRequestIn.Start' event. /// @@ -183,7 +296,8 @@ } requestTelemetry.Context.Operation.ParentId = originalParentId; - this.SetAppIdInResponseHeader(httpContext, requestTelemetry); + + this.AddAppIdToResponseIfRequired(httpContext, requestTelemetry); } } @@ -296,7 +410,8 @@ // fix parent that may be modified by non-W3C operation correlation requestTelemetry.Context.Operation.ParentId = originalParentId; - this.SetAppIdInResponseHeader(httpContext, requestTelemetry); + + this.AddAppIdToResponseIfRequired(httpContext, requestTelemetry); } } @@ -342,98 +457,22 @@ this.OnException(httpContext, exception); } - public void Dispose() + private void AddAppIdToResponseIfRequired(HttpContext httpContext, RequestTelemetry requestTelemetry) { - SubscriptionManager.Detach(this); - } - - public void OnNext(KeyValuePair value) - { - HttpContext httpContext = null; - Exception exception = null; - long? timestamp = null; - try + if (this.conditionalAppIdEnabled) { - switch (value.Key) + // Only reply back with AppId if we got an indication that we need to set one + if (!string.IsNullOrWhiteSpace(requestTelemetry.Source)) { - case "Microsoft.AspNetCore.Hosting.HttpRequestIn.Start": - httpContext = this.httpContextFetcherStart.Fetch(value.Value) as HttpContext; - if (httpContext != null) - { - this.OnHttpRequestInStart(httpContext); - } - - break; - case "Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop": - httpContext = this.httpContextFetcherStop.Fetch(value.Value) as HttpContext; - if (httpContext != null) - { - this.OnHttpRequestInStop(httpContext); - } - - break; - case "Microsoft.AspNetCore.Hosting.BeginRequest": - httpContext = this.httpContextFetcherBeginRequest.Fetch(value.Value) as HttpContext; - timestamp = this.timestampFetcherBeginRequest.Fetch(value.Value) as long?; - if (httpContext != null && timestamp.HasValue) - { - this.OnBeginRequest(httpContext, timestamp.Value); - } - - break; - case "Microsoft.AspNetCore.Hosting.EndRequest": - httpContext = this.httpContextFetcherEndRequest.Fetch(value.Value) as HttpContext; - timestamp = this.timestampFetcherEndRequest.Fetch(value.Value) as long?; - if (httpContext != null && timestamp.HasValue) - { - this.OnEndRequest(httpContext, timestamp.Value); - } - - break; - case "Microsoft.AspNetCore.Diagnostics.UnhandledException": - httpContext = this.httpContextFetcherDiagExceptionUnhandled.Fetch(value.Value) as HttpContext; - exception = this.exceptionFetcherDiagExceptionUnhandled.Fetch(value.Value) as Exception; - if (httpContext != null && exception != null) - { - this.OnDiagnosticsUnhandledException(httpContext, exception); - } - break; - case "Microsoft.AspNetCore.Diagnostics.HandledException": - httpContext = this.httpContextFetcherDiagExceptionHandled.Fetch(value.Value) as HttpContext; - exception = this.exceptionFetcherDiagExceptionHandled.Fetch(value.Value) as Exception; - if (httpContext != null && exception != null) - { - this.OnDiagnosticsHandledException(httpContext, exception); - } - - break; - case "Microsoft.AspNetCore.Hosting.UnhandledException": - httpContext = this.httpContextFetcherHostingExceptionUnhandled.Fetch(value.Value) as HttpContext; - exception = this.exceptionFetcherHostingExceptionUnhandled.Fetch(value.Value) as Exception; - if (httpContext != null && exception != null) - { - this.OnHostingException(httpContext, exception); - } - - break; + this.SetAppIdInResponseHeader(httpContext, requestTelemetry); } } - catch (Exception ex) + else { - AspNetCoreEventSource.Instance.DiagnosticListenerWarning("HostingDiagnosticListener", value.Key, ex.Message); + this.SetAppIdInResponseHeader(httpContext, requestTelemetry); } } - /// - public void OnError(Exception error) - { - } - - /// - public void OnCompleted() - { - } - private RequestTelemetry InitializeRequestTelemetry(HttpContext httpContext, Activity activity, long timestamp) { var requestTelemetry = new RequestTelemetry(); @@ -448,17 +487,31 @@ activity.UpdateTelemetry(requestTelemetry, false); } - foreach (var prop in activity.Baggage) + if (this.proactiveSamplingEnabled + && this.configuration != null + && !string.IsNullOrEmpty(requestTelemetry.Context.Operation.Id) + && SamplingScoreGenerator.GetSamplingScore(requestTelemetry.Context.Operation.Id) >= this.configuration.GetLastObservedSamplingPercentage(requestTelemetry.ItemTypeFlag)) { - if (!requestTelemetry.Properties.ContainsKey(prop.Key)) + requestTelemetry.IsSampledOutAtHead = true; + AspNetCoreEventSource.Instance.TelemetryItemWasSampledOutAtHead(requestTelemetry.Context.Operation.Id); + } + + //// When the item is proactively sampled out, we can avoid heavy operations that do not have known dependency later in the pipeline. + //// We mostly exclude operations that were deemed heavy as per the corresponding profiler trace of this code path. + + if (!requestTelemetry.IsSampledOutAtHead) + { + foreach (var prop in activity.Baggage) { - requestTelemetry.Properties[prop.Key] = prop.Value; + if (!requestTelemetry.Properties.ContainsKey(prop.Key)) + { + requestTelemetry.Properties[prop.Key] = prop.Value; + } } } this.client.InitializeInstrumentationKey(requestTelemetry); - - requestTelemetry.Source = this.GetAppIdFromRequestHeader(httpContext.Request.Headers, requestTelemetry.Context.InstrumentationKey); + requestTelemetry.Source = GetAppIdFromRequestHeader(httpContext.Request.Headers, requestTelemetry.Context.InstrumentationKey); requestTelemetry.Start(timestamp); httpContext.Features.Set(requestTelemetry); @@ -503,16 +556,14 @@ responseHeaders, RequestResponseHeaders.RequestContextTargetKey))) { - string applicationId = null; - if (this.applicationIdProvider?.TryGetApplicationId( - requestTelemetry.Context.InstrumentationKey, - out applicationId) ?? false) + if (this.lastIKeyLookedUp != requestTelemetry.Context.InstrumentationKey) { - HttpHeadersUtilities.SetRequestContextKeyValue( - responseHeaders, - RequestResponseHeaders.RequestContextTargetKey, - applicationId); + this.lastIKeyLookedUp = requestTelemetry.Context.InstrumentationKey; + this.applicationIdProvider?.TryGetApplicationId(requestTelemetry.Context.InstrumentationKey, out this.lastAppIdUsed); } + + HttpHeadersUtilities.SetRequestContextKeyValue(responseHeaders, + RequestResponseHeaders.RequestContextTargetKey, this.lastAppIdUsed); } } } @@ -556,8 +607,12 @@ telemetry.Name = httpContext.Request.Method + " " + httpContext.Request.Path.Value; } - telemetry.Url = httpContext.Request.GetUri(); - telemetry.Context.GetInternalContext().SdkVersion = this.sdkVersion; + if (!telemetry.IsSampledOutAtHead) + { + telemetry.Url = httpContext.Request.GetUri(); + telemetry.Context.GetInternalContext().SdkVersion = this.sdkVersion; + } + this.client.TrackRequest(telemetry); var activity = httpContext?.Features.Get(); @@ -672,5 +727,105 @@ appId = appIds[0]; return true; } + + public void Dispose() + { + SubscriptionManager.Detach(this); + } + + public void OnNext(KeyValuePair value) + { + HttpContext httpContext = null; + Exception exception = null; + long? timestamp = null; + + //// Top messages in if-else are the most often used messages. + //// It starts with ASP.NET Core 2.0 events, then 1.0 events, then exception events. + //// Switch is compiled into GetHashCode() and binary search, if-else without GetHashCode() is faster if 2.0 events are used. + if (value.Key == "Microsoft.AspNetCore.Hosting.HttpRequestIn.Start") + { + httpContext = this.httpContextFetcherStart.Fetch(value.Value) as HttpContext; + if (httpContext != null) + { + this.OnHttpRequestInStart(httpContext); + } + } + else if (value.Key == "Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop") + { + httpContext = this.httpContextFetcherStop.Fetch(value.Value) as HttpContext; + if (httpContext != null) + { + this.OnHttpRequestInStop(httpContext); + } + } + else if (value.Key == "Microsoft.AspNetCore.Mvc.BeforeAction") + { + var context = this.httpContextFetcherOnBeforeAction.Fetch(value.Value) as HttpContext; + var routeData = this.routeDataFetcher.Fetch(value.Value); + var routeValues = this.routeValuesFetcher.Fetch(routeData) as IDictionary; + + if (context != null && routeValues != null) + { + this.OnBeforeAction(context, routeValues); + } + + } + else if (value.Key == "Microsoft.AspNetCore.Hosting.BeginRequest") + { + httpContext = this.httpContextFetcherBeginRequest.Fetch(value.Value) as HttpContext; + timestamp = this.timestampFetcherBeginRequest.Fetch(value.Value) as long?; + if (httpContext != null && timestamp.HasValue) + { + this.OnBeginRequest(httpContext, timestamp.Value); + } + } + else if (value.Key == "Microsoft.AspNetCore.Hosting.EndRequest") + { + httpContext = this.httpContextFetcherEndRequest.Fetch(value.Value) as HttpContext; + timestamp = this.timestampFetcherEndRequest.Fetch(value.Value) as long?; + if (httpContext != null && timestamp.HasValue) + { + this.OnEndRequest(httpContext, timestamp.Value); + } + } + else if (value.Key == "Microsoft.AspNetCore.Diagnostics.UnhandledException") + { + httpContext = this.httpContextFetcherDiagExceptionUnhandled.Fetch(value.Value) as HttpContext; + exception = this.exceptionFetcherDiagExceptionUnhandled.Fetch(value.Value) as Exception; + if (httpContext != null && exception != null) + { + this.OnDiagnosticsUnhandledException(httpContext, exception); + } + } + else if (value.Key == "Microsoft.AspNetCore.Diagnostics.HandledException") + { + httpContext = this.httpContextFetcherDiagExceptionHandled.Fetch(value.Value) as HttpContext; + exception = this.exceptionFetcherDiagExceptionHandled.Fetch(value.Value) as Exception; + if (httpContext != null && exception != null) + { + this.OnDiagnosticsHandledException(httpContext, exception); + } + } + else if (value.Key == "Microsoft.AspNetCore.Hosting.UnhandledException") + { + httpContext = this.httpContextFetcherHostingExceptionUnhandled.Fetch(value.Value) as HttpContext; + exception = this.exceptionFetcherHostingExceptionUnhandled.Fetch(value.Value) as Exception; + if (httpContext != null && exception != null) + { + this.OnHostingException(httpContext, exception); + } + } + } + + /// + public void OnError(Exception error) + { + } + + /// + public void OnCompleted() + { + } + } } \ No newline at end of file diff --git a/src/Microsoft.ApplicationInsights.AspNetCore/DiagnosticListeners/Implementation/MvcDiagnosticsListener.cs b/src/Microsoft.ApplicationInsights.AspNetCore/DiagnosticListeners/Implementation/MvcDiagnosticsListener.cs index f64caf9..ffeb716 100644 --- a/src/Microsoft.ApplicationInsights.AspNetCore/DiagnosticListeners/Implementation/MvcDiagnosticsListener.cs +++ b/src/Microsoft.ApplicationInsights.AspNetCore/DiagnosticListeners/Implementation/MvcDiagnosticsListener.cs @@ -11,6 +11,7 @@ namespace Microsoft.ApplicationInsights.AspNetCore.DiagnosticListeners /// /// implementation that listens for evens specific to AspNetCore Mvc layer /// + [Obsolete("This class was merged with HostingDiagnosticsListener to optimize Diagnostics Source subscription performance")] public class MvcDiagnosticsListener : IApplicationInsightDiagnosticListener { /// diff --git a/src/Microsoft.ApplicationInsights.AspNetCore/Extensibility/Implementation/Tracing/AspNetCoreEventSource.cs b/src/Microsoft.ApplicationInsights.AspNetCore/Extensibility/Implementation/Tracing/AspNetCoreEventSource.cs index 67a5d1d..e5c9415 100644 --- a/src/Microsoft.ApplicationInsights.AspNetCore/Extensibility/Implementation/Tracing/AspNetCoreEventSource.cs +++ b/src/Microsoft.ApplicationInsights.AspNetCore/Extensibility/Implementation/Tracing/AspNetCoreEventSource.cs @@ -173,6 +173,16 @@ namespace Microsoft.ApplicationInsights.AspNetCore.Extensibility.Implementation. this.WriteEvent(17, errorMessage, this.ApplicationName); } + [Event( + 18, + Keywords = Keywords.Diagnostics, + Message = "Telemetry item was sampled out at head, OperationId: '{0}'", + Level = EventLevel.Verbose)] + public void TelemetryItemWasSampledOutAtHead(string operationId, string appDomainName = "Incorrect") + { + this.WriteEvent(18, operationId, this.ApplicationName); + } + /// /// Keywords for the AspNetEventSource. /// diff --git a/src/Microsoft.ApplicationInsights.AspNetCore/Implementation/TelemetryConfigurationOptionsSetup.cs b/src/Microsoft.ApplicationInsights.AspNetCore/Implementation/TelemetryConfigurationOptionsSetup.cs index 463ce6b..a37c73e 100644 --- a/src/Microsoft.ApplicationInsights.AspNetCore/Implementation/TelemetryConfigurationOptionsSetup.cs +++ b/src/Microsoft.ApplicationInsights.AspNetCore/Implementation/TelemetryConfigurationOptionsSetup.cs @@ -13,6 +13,8 @@ namespace Microsoft.Extensions.DependencyInjection using Microsoft.ApplicationInsights.Extensibility.PerfCounterCollector.QuickPulse; using Microsoft.ApplicationInsights.Extensibility.W3C; using Microsoft.Extensions.Options; + using Microsoft.ApplicationInsights.WindowsServer.Channel.Implementation; + using Microsoft.ApplicationInsights.DataContracts; /// /// Initializes TelemetryConfiguration based on values in @@ -163,7 +165,17 @@ namespace Microsoft.Extensions.DependencyInjection { if (this.applicationInsightsServiceOptions.EnableAdaptiveSampling) { - configuration.DefaultTelemetrySink.TelemetryProcessorChainBuilder.UseAdaptiveSampling(5, excludedTypes: "Event"); + AdaptiveSamplingPercentageEvaluatedCallback samplingCallback = (ratePerSecond, currentPercentage, newPercentage, isChanged, estimatorSettings) => + { + if (isChanged) + { + configuration.SetLastObservedSamplingPercentage(SamplingTelemetryItemTypes.Request, newPercentage); + } + }; + + SamplingPercentageEstimatorSettings settings = new SamplingPercentageEstimatorSettings(); + settings.MaxTelemetryItemsPerSecond = 5; + configuration.DefaultTelemetrySink.TelemetryProcessorChainBuilder.UseAdaptiveSampling(settings, samplingCallback, excludedTypes: "Event"); configuration.DefaultTelemetrySink.TelemetryProcessorChainBuilder.UseAdaptiveSampling(5, includedTypes: "Event"); } } diff --git a/src/Microsoft.ApplicationInsights.AspNetCore/RequestTrackingTelemetryModule.cs b/src/Microsoft.ApplicationInsights.AspNetCore/RequestTrackingTelemetryModule.cs index bee9525..67639c7 100644 --- a/src/Microsoft.ApplicationInsights.AspNetCore/RequestTrackingTelemetryModule.cs +++ b/src/Microsoft.ApplicationInsights.AspNetCore/RequestTrackingTelemetryModule.cs @@ -2,7 +2,6 @@ namespace Microsoft.ApplicationInsights.AspNetCore { using System; using System.Collections.Concurrent; - using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Threading; @@ -17,15 +16,20 @@ namespace Microsoft.ApplicationInsights.AspNetCore /// public class RequestTrackingTelemetryModule : ITelemetryModule, IObserver, IDisposable { - private TelemetryClient telemetryClient; - private readonly IApplicationIdProvider applicationIdProvider; - private ConcurrentBag subscriptions; - private readonly List diagnosticListeners; - private bool isInitialized = false; + // We are only interested in BeforeAction event from Microsoft.AspNetCore.Mvc source. + // We are interested in Microsoft.AspNetCore.Hosting and Microsoft.AspNetCore.Diagnostics as well. + // Below filter achieves acceptable performance, character 22 shoudl not be M unless event is BeforeAction. + private static readonly Predicate HostingPredicate = (string eventName) => (eventName != null) ? !(eventName[21] == 'M') || eventName == "Microsoft.AspNetCore.Mvc.BeforeAction" : false; private readonly object lockObject = new object(); + private readonly IApplicationIdProvider applicationIdProvider; + + private TelemetryClient telemetryClient; + private ConcurrentBag subscriptions; + private HostingDiagnosticListener diagnosticListener; + private bool isInitialized = false; /// - /// RequestTrackingTelemetryModule. + /// Initializes a new instance of the class. /// public RequestTrackingTelemetryModule() : this(null) @@ -34,14 +38,13 @@ namespace Microsoft.ApplicationInsights.AspNetCore } /// - /// Creates RequestTrackingTelemetryModule. + /// Initializes a new instance of the class. /// - /// + /// Provider to resolve Application Id public RequestTrackingTelemetryModule(IApplicationIdProvider applicationIdProvider) { this.applicationIdProvider = applicationIdProvider; this.subscriptions = new ConcurrentBag(); - this.diagnosticListeners = new List(); } /// @@ -75,16 +78,14 @@ namespace Microsoft.ApplicationInsights.AspNetCore // ignore any errors } - this.diagnosticListeners.Add(new HostingDiagnosticListener( + this.diagnosticListener = new HostingDiagnosticListener( + configuration, this.telemetryClient, this.applicationIdProvider, this.CollectionOptions.InjectResponseHeaders, this.CollectionOptions.TrackExceptions, this.CollectionOptions.EnableW3CDistributedTracing, - enableNewDiagnosticEvents)); - - this.diagnosticListeners.Add - (new MvcDiagnosticsListener()); + enableNewDiagnosticEvents); this.subscriptions?.Add(DiagnosticListener.AllListeners.Subscribe(this)); @@ -108,13 +109,10 @@ namespace Microsoft.ApplicationInsights.AspNetCore return; } - foreach (var applicationInsightDiagnosticListener in this.diagnosticListeners) + if (this.diagnosticListener.ListenerName == value.Name) { - if (applicationInsightDiagnosticListener.ListenerName == value.Name) - { - subs.Add(value.Subscribe(applicationInsightDiagnosticListener)); - applicationInsightDiagnosticListener.OnSubscribe(); - } + subs.Add(value.Subscribe(this.diagnosticListener, HostingPredicate)); + this.diagnosticListener.OnSubscribe(); } } @@ -156,9 +154,9 @@ namespace Microsoft.ApplicationInsights.AspNetCore subscription.Dispose(); } - foreach (var listener in this.diagnosticListeners) + if (this.diagnosticListener != null) { - listener.Dispose(); + this.diagnosticListener.Dispose(); } } } diff --git a/test/ApplicationInsightsTypes/Generated/AvailabilityData_types.cs b/test/ApplicationInsightsTypes/Generated/AvailabilityData_types.cs index 84aad0a..afafa74 100644 --- a/test/ApplicationInsightsTypes/Generated/AvailabilityData_types.cs +++ b/test/ApplicationInsightsTypes/Generated/AvailabilityData_types.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // This code was generated by a tool. // -// Tool : Bond Compiler 0.10.0.0 +// Tool : Bond Compiler 0.10.1.0 // File : AvailabilityData_types.cs // // Changes to this file may cause incorrect behavior and will be lost when @@ -30,7 +30,7 @@ namespace AI [global::Bond.Attribute("Description", "Instances of AvailabilityData represent the result of executing an availability test.")] [global::Bond.Schema] - [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.0.0")] + [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.1.0")] public partial class AvailabilityData : Domain { @@ -73,13 +73,13 @@ namespace AI [global::Bond.Attribute("Description", "Collection of custom properties.")] [global::Bond.Attribute("MaxKeyLength", "150")] [global::Bond.Attribute("MaxValueLength", "8192")] - [global::Bond.Id(100), global::Bond.Type(typeof(Dictionary))] - public IDictionary properties { get; set; } + [global::Bond.Id(100)] + public Dictionary properties { get; set; } [global::Bond.Attribute("Description", "Collection of custom measurements.")] [global::Bond.Attribute("MaxKeyLength", "150")] - [global::Bond.Id(200), global::Bond.Type(typeof(Dictionary))] - public IDictionary measurements { get; set; } + [global::Bond.Id(200)] + public Dictionary measurements { get; set; } public AvailabilityData() : this("AI.AvailabilityData", "AvailabilityData") diff --git a/test/ApplicationInsightsTypes/Generated/Base_types.cs b/test/ApplicationInsightsTypes/Generated/Base_types.cs index f323fc4..5fd1837 100644 --- a/test/ApplicationInsightsTypes/Generated/Base_types.cs +++ b/test/ApplicationInsightsTypes/Generated/Base_types.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // This code was generated by a tool. // -// Tool : Bond Compiler 0.10.0.0 +// Tool : Bond Compiler 0.10.1.0 // File : Base_types.cs // // Changes to this file may cause incorrect behavior and will be lost when @@ -30,7 +30,7 @@ namespace AI [global::Bond.Attribute("Description", "Data struct to contain only C section with custom fields.")] [global::Bond.Schema] - [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.0.0")] + [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.1.0")] public partial class Base { [global::Bond.Attribute("Name", "ItemTypeName")] diff --git a/test/ApplicationInsightsTypes/Generated/ContextTagKeys_types.cs b/test/ApplicationInsightsTypes/Generated/ContextTagKeys_types.cs index c5ec662..0079914 100644 --- a/test/ApplicationInsightsTypes/Generated/ContextTagKeys_types.cs +++ b/test/ApplicationInsightsTypes/Generated/ContextTagKeys_types.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // This code was generated by a tool. // -// Tool : Bond Compiler 0.10.0.0 +// Tool : Bond Compiler 0.10.1.0 // File : ContextTagKeys_types.cs // // Changes to this file may cause incorrect behavior and will be lost when @@ -31,7 +31,7 @@ namespace AI [global::Bond.Attribute("ContextContract", "Emit")] [global::Bond.Attribute("PseudoType", "JSMap")] [global::Bond.Schema] - [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.0.0")] + [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.1.0")] public partial class ContextTagKeys { [global::Bond.Attribute("Description", "Application version. Information in the application context fields is always about the application that is sending the telemetry.")] @@ -69,7 +69,7 @@ namespace AI [global::Bond.Id(160)] public string DeviceType { get; set; } - [global::Bond.Attribute("Description", "The IP address of the client device. IPv4 and IPv6 is supported. Information in the location context fields is always about the end user. When telemetry is sent from a service, the location context is about the user that initiated the operation in the service.")] + [global::Bond.Attribute("Description", "The IPv4 IP address of the client device. IPv6 is not currently supported. Information in the location context fields is always about the end user. When telemetry is sent from a service, the location context is about the user that initiated the operation in the service.")] [global::Bond.Attribute("MaxStringLength", "45")] [global::Bond.Id(200)] public string LocationIp { get; set; } @@ -115,7 +115,7 @@ namespace AI [global::Bond.Id(505)] public string UserAccountId { get; set; } - [global::Bond.Attribute("Description", "The browser's user agent string as reported by the browser. This property will be used to extract information regarding the customer's browser but will not be stored. Use custom properties to store the original user agent.")] + [global::Bond.Attribute("Description", "The browser's user agent string as reported by the browser. This property will be used to extract informaiton regarding the customer's browser but will not be stored. Use custom properties to store the original user agent.")] [global::Bond.Attribute("MaxStringLength", "2048")] [global::Bond.Id(510)] public string UserAgent { get; set; } @@ -135,7 +135,7 @@ namespace AI [global::Bond.Id(705)] public string CloudRole { get; set; } - [global::Bond.Attribute("Description", "Name of the instance where the application is running. Computer name for on-premises, instance name for Azure.")] + [global::Bond.Attribute("Description", "Name of the instance where the application is running. Computer name for on-premisis, instance name for Azure.")] [global::Bond.Attribute("MaxStringLength", "256")] [global::Bond.Id(715)] public string CloudRoleInstance { get; set; } @@ -150,11 +150,6 @@ namespace AI [global::Bond.Id(1001)] public string InternalAgentVersion { get; set; } - [global::Bond.Attribute("Description", "This is the node name used for billing purposes. Use it to override the standard detection of nodes.")] - [global::Bond.Attribute("MaxStringLength", "256")] - [global::Bond.Id(1002)] - public string InternalNodeName { get; set; } - public ContextTagKeys() : this("AI.ContextTagKeys", "ContextTagKeys") {} @@ -184,7 +179,6 @@ namespace AI CloudRoleInstance = "ai.cloud.roleInstance"; InternalSdkVersion = "ai.internal.sdkVersion"; InternalAgentVersion = "ai.internal.agentVersion"; - InternalNodeName = "ai.internal.nodeName"; } } } // AI diff --git a/test/ApplicationInsightsTypes/Generated/DataPointType_types.cs b/test/ApplicationInsightsTypes/Generated/DataPointType_types.cs index 1be5635..01daac1 100644 --- a/test/ApplicationInsightsTypes/Generated/DataPointType_types.cs +++ b/test/ApplicationInsightsTypes/Generated/DataPointType_types.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // This code was generated by a tool. // -// Tool : Bond Compiler 0.10.0.0 +// Tool : Bond Compiler 0.10.1.0 // File : DataPointType_types.cs // // Changes to this file may cause incorrect behavior and will be lost when @@ -29,7 +29,7 @@ namespace AI using System.Collections.Generic; [global::Bond.Attribute("Description", "Type of the metric data measurement.")] - [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.0.0")] + [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.1.0")] public enum DataPointType { Measurement, diff --git a/test/ApplicationInsightsTypes/Generated/DataPoint_types.cs b/test/ApplicationInsightsTypes/Generated/DataPoint_types.cs index 8e5d94f..311e742 100644 --- a/test/ApplicationInsightsTypes/Generated/DataPoint_types.cs +++ b/test/ApplicationInsightsTypes/Generated/DataPoint_types.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // This code was generated by a tool. // -// Tool : Bond Compiler 0.10.0.0 +// Tool : Bond Compiler 0.10.1.0 // File : DataPoint_types.cs // // Changes to this file may cause incorrect behavior and will be lost when @@ -30,7 +30,7 @@ namespace AI [global::Bond.Attribute("Description", "Metric data single measurement.")] [global::Bond.Schema] - [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.0.0")] + [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.1.0")] public partial class DataPoint { [global::Bond.Attribute("Description", "Name of the metric.")] diff --git a/test/ApplicationInsightsTypes/Generated/Data_types.cs b/test/ApplicationInsightsTypes/Generated/Data_types.cs index ee3ef84..f35b519 100644 --- a/test/ApplicationInsightsTypes/Generated/Data_types.cs +++ b/test/ApplicationInsightsTypes/Generated/Data_types.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // This code was generated by a tool. // -// Tool : Bond Compiler 0.10.0.0 +// Tool : Bond Compiler 0.10.1.0 // File : Data_types.cs // // Changes to this file may cause incorrect behavior and will be lost when @@ -30,7 +30,7 @@ namespace AI [global::Bond.Attribute("Description", "Data struct to contain both B and C sections.")] [global::Bond.Schema] - [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.0.0")] + [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.1.0")] public partial class Data : Base { diff --git a/test/ApplicationInsightsTypes/Generated/Domain_types.cs b/test/ApplicationInsightsTypes/Generated/Domain_types.cs index b1dcae0..0b8b565 100644 --- a/test/ApplicationInsightsTypes/Generated/Domain_types.cs +++ b/test/ApplicationInsightsTypes/Generated/Domain_types.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // This code was generated by a tool. // -// Tool : Bond Compiler 0.10.0.0 +// Tool : Bond Compiler 0.10.1.0 // File : Domain_types.cs // // Changes to this file may cause incorrect behavior and will be lost when @@ -30,7 +30,7 @@ namespace AI [global::Bond.Attribute("Description", "The abstract common base of all domains.")] [global::Bond.Schema] - [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.0.0")] + [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.1.0")] public partial class Domain { diff --git a/test/ApplicationInsightsTypes/Generated/Envelope_types.cs b/test/ApplicationInsightsTypes/Generated/Envelope_types.cs index f8bf262..c15d74f 100644 --- a/test/ApplicationInsightsTypes/Generated/Envelope_types.cs +++ b/test/ApplicationInsightsTypes/Generated/Envelope_types.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // This code was generated by a tool. // -// Tool : Bond Compiler 0.10.0.0 +// Tool : Bond Compiler 0.10.1.0 // File : Envelope_types.cs // // Changes to this file may cause incorrect behavior and will be lost when @@ -30,7 +30,7 @@ namespace AI [global::Bond.Attribute("Description", "System variables for a telemetry item.")] [global::Bond.Schema] - [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.0.0")] + [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.1.0")] public partial class Envelope { [global::Bond.Attribute("Description", "Envelope version. For internal use only. By assigning this the default, it will not be serialized within the payload unless changed to a value other than #1.")] @@ -74,8 +74,8 @@ namespace AI [global::Bond.Attribute("Name", "Tags")] [global::Bond.Attribute("TypeAlias", "ContextTagKeys")] [global::Bond.Attribute("Description", "Key/value collection of context properties. See ContextTagKeys for information on available properties.")] - [global::Bond.Id(500), global::Bond.Type(typeof(Dictionary))] - public IDictionary tags { get; set; } + [global::Bond.Id(500)] + public Dictionary tags { get; set; } [global::Bond.Attribute("Name", "TelemetryData")] [global::Bond.Attribute("Description", "Telemetry data item.")] diff --git a/test/ApplicationInsightsTypes/Generated/EventData_types.cs b/test/ApplicationInsightsTypes/Generated/EventData_types.cs index 6546812..02909a6 100644 --- a/test/ApplicationInsightsTypes/Generated/EventData_types.cs +++ b/test/ApplicationInsightsTypes/Generated/EventData_types.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // This code was generated by a tool. // -// Tool : Bond Compiler 0.10.0.0 +// Tool : Bond Compiler 0.10.1.0 // File : EventData_types.cs // // Changes to this file may cause incorrect behavior and will be lost when @@ -30,7 +30,7 @@ namespace AI [global::Bond.Attribute("Description", "Instances of Event represent structured event records that can be grouped and searched by their properties. Event data item also creates a metric of event count by name.")] [global::Bond.Schema] - [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.0.0")] + [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.1.0")] public partial class EventData : Domain { @@ -47,13 +47,13 @@ namespace AI [global::Bond.Attribute("Description", "Collection of custom properties.")] [global::Bond.Attribute("MaxKeyLength", "150")] [global::Bond.Attribute("MaxValueLength", "8192")] - [global::Bond.Id(100), global::Bond.Type(typeof(Dictionary))] - public IDictionary properties { get; set; } + [global::Bond.Id(100)] + public Dictionary properties { get; set; } [global::Bond.Attribute("Description", "Collection of custom measurements.")] [global::Bond.Attribute("MaxKeyLength", "150")] - [global::Bond.Id(200), global::Bond.Type(typeof(Dictionary))] - public IDictionary measurements { get; set; } + [global::Bond.Id(200)] + public Dictionary measurements { get; set; } public EventData() : this("AI.EventData", "EventData") diff --git a/test/ApplicationInsightsTypes/Generated/ExceptionData_types.cs b/test/ApplicationInsightsTypes/Generated/ExceptionData_types.cs index e469ddb..2d5b66b 100644 --- a/test/ApplicationInsightsTypes/Generated/ExceptionData_types.cs +++ b/test/ApplicationInsightsTypes/Generated/ExceptionData_types.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // This code was generated by a tool. // -// Tool : Bond Compiler 0.10.0.0 +// Tool : Bond Compiler 0.10.1.0 // File : ExceptionData_types.cs // // Changes to this file may cause incorrect behavior and will be lost when @@ -30,7 +30,7 @@ namespace AI [global::Bond.Attribute("Description", "An instance of Exception represents a handled or unhandled exception that occurred during execution of the monitored application.")] [global::Bond.Schema] - [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.0.0")] + [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.1.0")] public partial class ExceptionData : Domain { @@ -39,8 +39,8 @@ namespace AI public int ver { get; set; } [global::Bond.Attribute("Description", "Exception chain - list of inner exceptions.")] - [global::Bond.Id(50), global::Bond.Type(typeof(List)), global::Bond.Required] - public IList exceptions { get; set; } + [global::Bond.Id(50), global::Bond.Required] + public List exceptions { get; set; } [global::Bond.Attribute("Description", "Severity level. Mostly used to indicate exception severity level when it is reported by logging library.")] [global::Bond.Id(60), global::Bond.Type(typeof(global::Bond.Tag.nullable))] @@ -54,13 +54,13 @@ namespace AI [global::Bond.Attribute("Description", "Collection of custom properties.")] [global::Bond.Attribute("MaxKeyLength", "150")] [global::Bond.Attribute("MaxValueLength", "8192")] - [global::Bond.Id(100), global::Bond.Type(typeof(Dictionary))] - public IDictionary properties { get; set; } + [global::Bond.Id(100)] + public Dictionary properties { get; set; } [global::Bond.Attribute("Description", "Collection of custom measurements.")] [global::Bond.Attribute("MaxKeyLength", "150")] - [global::Bond.Id(200), global::Bond.Type(typeof(Dictionary))] - public IDictionary measurements { get; set; } + [global::Bond.Id(200)] + public Dictionary measurements { get; set; } public ExceptionData() : this("AI.ExceptionData", "ExceptionData") diff --git a/test/ApplicationInsightsTypes/Generated/ExceptionDetails_types.cs b/test/ApplicationInsightsTypes/Generated/ExceptionDetails_types.cs index a6c1aa7..129c68c 100644 --- a/test/ApplicationInsightsTypes/Generated/ExceptionDetails_types.cs +++ b/test/ApplicationInsightsTypes/Generated/ExceptionDetails_types.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // This code was generated by a tool. // -// Tool : Bond Compiler 0.10.0.0 +// Tool : Bond Compiler 0.10.1.0 // File : ExceptionDetails_types.cs // // Changes to this file may cause incorrect behavior and will be lost when @@ -30,7 +30,7 @@ namespace AI [global::Bond.Attribute("Description", "Exception details of the exception in a chain.")] [global::Bond.Schema] - [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.0.0")] + [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.1.0")] public partial class ExceptionDetails { [global::Bond.Attribute("Description", "In case exception is nested (outer exception contains inner one), the id and outerId properties are used to represent the nesting.")] @@ -47,7 +47,7 @@ namespace AI public string typeName { get; set; } [global::Bond.Attribute("Description", "Exception message.")] - [global::Bond.Attribute("MaxStringLength", "32768")] + [global::Bond.Attribute("MaxStringLength", "1024")] [global::Bond.Id(40), global::Bond.Required] public string message { get; set; } @@ -61,8 +61,8 @@ namespace AI public string stack { get; set; } [global::Bond.Attribute("Description", "List of stack frames. Either stack or parsedStack should have a value.")] - [global::Bond.Id(70), global::Bond.Type(typeof(List))] - public IList parsedStack { get; set; } + [global::Bond.Id(70)] + public List parsedStack { get; set; } public ExceptionDetails() : this("AI.ExceptionDetails", "ExceptionDetails") diff --git a/test/ApplicationInsightsTypes/Generated/MessageData_types.cs b/test/ApplicationInsightsTypes/Generated/MessageData_types.cs index 89ba47a..46c0653 100644 --- a/test/ApplicationInsightsTypes/Generated/MessageData_types.cs +++ b/test/ApplicationInsightsTypes/Generated/MessageData_types.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // This code was generated by a tool. // -// Tool : Bond Compiler 0.10.0.0 +// Tool : Bond Compiler 0.10.1.0 // File : MessageData_types.cs // // Changes to this file may cause incorrect behavior and will be lost when @@ -28,9 +28,9 @@ namespace AI { using System.Collections.Generic; - [global::Bond.Attribute("Description", "Instances of Message represent printf-like trace statements that are text-searched. Log4Net, NLog and other text-based log file entries are translated into instances of this type. The message does not have measurements.")] + [global::Bond.Attribute("Description", "Instances of Message represent printf-like trace statements that are text-searched. Log4Net, NLog and other text-based log file entries are translated into intances of this type. The message does not have measurements.")] [global::Bond.Schema] - [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.0.0")] + [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.1.0")] public partial class MessageData : Domain { @@ -50,8 +50,8 @@ namespace AI [global::Bond.Attribute("Description", "Collection of custom properties.")] [global::Bond.Attribute("MaxKeyLength", "150")] [global::Bond.Attribute("MaxValueLength", "8192")] - [global::Bond.Id(100), global::Bond.Type(typeof(Dictionary))] - public IDictionary properties { get; set; } + [global::Bond.Id(100)] + public Dictionary properties { get; set; } public MessageData() : this("AI.MessageData", "MessageData") diff --git a/test/ApplicationInsightsTypes/Generated/MetricData_types.cs b/test/ApplicationInsightsTypes/Generated/MetricData_types.cs index e4ea6a4..81da19f 100644 --- a/test/ApplicationInsightsTypes/Generated/MetricData_types.cs +++ b/test/ApplicationInsightsTypes/Generated/MetricData_types.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // This code was generated by a tool. // -// Tool : Bond Compiler 0.10.0.0 +// Tool : Bond Compiler 0.10.1.0 // File : MetricData_types.cs // // Changes to this file may cause incorrect behavior and will be lost when @@ -30,7 +30,7 @@ namespace AI [global::Bond.Attribute("Description", "An instance of the Metric item is a list of measurements (single data points) and/or aggregations.")] [global::Bond.Schema] - [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.0.0")] + [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.1.0")] public partial class MetricData : Domain { @@ -39,14 +39,14 @@ namespace AI public int ver { get; set; } [global::Bond.Attribute("Description", "List of metrics.")] - [global::Bond.Id(20), global::Bond.Type(typeof(List)), global::Bond.Required] - public IList metrics { get; set; } + [global::Bond.Id(20), global::Bond.Required] + public List metrics { get; set; } [global::Bond.Attribute("Description", "Collection of custom properties.")] [global::Bond.Attribute("MaxKeyLength", "150")] [global::Bond.Attribute("MaxValueLength", "8192")] - [global::Bond.Id(100), global::Bond.Type(typeof(Dictionary))] - public IDictionary properties { get; set; } + [global::Bond.Id(100)] + public Dictionary properties { get; set; } public MetricData() : this("AI.MetricData", "MetricData") diff --git a/test/ApplicationInsightsTypes/Generated/PageViewData_types.cs b/test/ApplicationInsightsTypes/Generated/PageViewData_types.cs index aea0032..9c6a90d 100644 --- a/test/ApplicationInsightsTypes/Generated/PageViewData_types.cs +++ b/test/ApplicationInsightsTypes/Generated/PageViewData_types.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // This code was generated by a tool. // -// Tool : Bond Compiler 0.10.0.0 +// Tool : Bond Compiler 0.10.1.0 // File : PageViewData_types.cs // // Changes to this file may cause incorrect behavior and will be lost when @@ -31,7 +31,7 @@ namespace AI [global::Bond.Attribute("Description", "An instance of PageView represents a generic action on a page like a button click. It is also the base type for PageView.")] [global::Bond.Attribute("Alias", "PageviewData;PageEventData")] [global::Bond.Schema] - [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.0.0")] + [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.1.0")] public partial class PageViewData : EventData { diff --git a/test/ApplicationInsightsTypes/Generated/PageViewPerfData_types.cs b/test/ApplicationInsightsTypes/Generated/PageViewPerfData_types.cs index 7e84097..76dca47 100644 --- a/test/ApplicationInsightsTypes/Generated/PageViewPerfData_types.cs +++ b/test/ApplicationInsightsTypes/Generated/PageViewPerfData_types.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // This code was generated by a tool. // -// Tool : Bond Compiler 0.10.0.0 +// Tool : Bond Compiler 0.10.1.0 // File : PageViewPerfData_types.cs // // Changes to this file may cause incorrect behavior and will be lost when @@ -31,7 +31,7 @@ namespace AI [global::Bond.Attribute("Description", "An instance of PageViewPerf represents: a page view with no performance data, a page view with performance data, or just the performance data of an earlier page request.")] [global::Bond.Attribute("Alias", "PageViewPerformanceData;PageviewPerformanceData")] [global::Bond.Schema] - [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.0.0")] + [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.1.0")] public partial class PageViewPerfData : PageViewData { diff --git a/test/ApplicationInsightsTypes/Generated/RemoteDependencyData_types.cs b/test/ApplicationInsightsTypes/Generated/RemoteDependencyData_types.cs index 970b578..65f2430 100644 --- a/test/ApplicationInsightsTypes/Generated/RemoteDependencyData_types.cs +++ b/test/ApplicationInsightsTypes/Generated/RemoteDependencyData_types.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // This code was generated by a tool. // -// Tool : Bond Compiler 0.10.0.0 +// Tool : Bond Compiler 0.10.1.0 // File : RemoteDependencyData_types.cs // // Changes to this file may cause incorrect behavior and will be lost when @@ -30,7 +30,7 @@ namespace AI [global::Bond.Attribute("Description", "An instance of Remote Dependency represents an interaction of the monitored component with a remote component/service like SQL or an HTTP endpoint.")] [global::Bond.Schema] - [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.0.0")] + [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.1.0")] public partial class RemoteDependencyData : Domain { @@ -59,7 +59,7 @@ namespace AI [global::Bond.Id(61), global::Bond.Required] public string duration { get; set; } - [global::Bond.Attribute("Description", "Indication of successful or unsuccessful call.")] + [global::Bond.Attribute("Description", "Indication of successfull or unsuccessfull call.")] [global::Bond.Id(120)] public bool success { get; set; } @@ -81,13 +81,13 @@ namespace AI [global::Bond.Attribute("Description", "Collection of custom properties.")] [global::Bond.Attribute("MaxKeyLength", "150")] [global::Bond.Attribute("MaxValueLength", "8192")] - [global::Bond.Id(200), global::Bond.Type(typeof(Dictionary))] - public IDictionary properties { get; set; } + [global::Bond.Id(200)] + public Dictionary properties { get; set; } [global::Bond.Attribute("Description", "Collection of custom measurements.")] [global::Bond.Attribute("MaxKeyLength", "150")] - [global::Bond.Id(300), global::Bond.Type(typeof(Dictionary))] - public IDictionary measurements { get; set; } + [global::Bond.Id(300)] + public Dictionary measurements { get; set; } public RemoteDependencyData() : this("AI.RemoteDependencyData", "RemoteDependencyData") diff --git a/test/ApplicationInsightsTypes/Generated/RequestData_types.cs b/test/ApplicationInsightsTypes/Generated/RequestData_types.cs index cde6828..1d0a426 100644 --- a/test/ApplicationInsightsTypes/Generated/RequestData_types.cs +++ b/test/ApplicationInsightsTypes/Generated/RequestData_types.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // This code was generated by a tool. // -// Tool : Bond Compiler 0.10.0.0 +// Tool : Bond Compiler 0.10.1.0 // File : RequestData_types.cs // // Changes to this file may cause incorrect behavior and will be lost when @@ -30,7 +30,7 @@ namespace AI [global::Bond.Attribute("Description", "An instance of Request represents completion of an external request to the application to do work and contains a summary of that request execution and the results.")] [global::Bond.Schema] - [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.0.0")] + [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.1.0")] public partial class RequestData : Domain { @@ -43,11 +43,6 @@ namespace AI [global::Bond.Id(20), global::Bond.Required] public string id { get; set; } - [global::Bond.Attribute("MaxStringLength", "1024")] - [global::Bond.Attribute("Description", "Source of the request. Examples are the instrumentation key of the caller or the ip address of the caller.")] - [global::Bond.Id(29)] - public string source { get; set; } - [global::Bond.Attribute("MaxStringLength", "1024")] [global::Bond.Attribute("Description", "Name of the request. Represents code path taken to process request. Low cardinality value to allow better grouping of requests. For HTTP requests it represents the HTTP method and URL path template like 'GET /values/{id}'.")] [global::Bond.Id(30)] @@ -63,7 +58,7 @@ namespace AI [global::Bond.Id(60), global::Bond.Required] public string responseCode { get; set; } - [global::Bond.Attribute("Description", "Indication of successful or unsuccessful call.")] + [global::Bond.Attribute("Description", "Indication of successfull or unsuccessfull call.")] [global::Bond.Id(70), global::Bond.Required] public bool success { get; set; } @@ -75,13 +70,13 @@ namespace AI [global::Bond.Attribute("Description", "Collection of custom properties.")] [global::Bond.Attribute("MaxKeyLength", "150")] [global::Bond.Attribute("MaxValueLength", "8192")] - [global::Bond.Id(100), global::Bond.Type(typeof(Dictionary))] - public IDictionary properties { get; set; } + [global::Bond.Id(100)] + public Dictionary properties { get; set; } [global::Bond.Attribute("Description", "Collection of custom measurements.")] [global::Bond.Attribute("MaxKeyLength", "150")] - [global::Bond.Id(200), global::Bond.Type(typeof(Dictionary))] - public IDictionary measurements { get; set; } + [global::Bond.Id(200)] + public Dictionary measurements { get; set; } public RequestData() : this("AI.RequestData", "RequestData") @@ -91,7 +86,6 @@ namespace AI { ver = 2; id = ""; - source = ""; this.name = ""; duration = ""; responseCode = ""; diff --git a/test/ApplicationInsightsTypes/Generated/SeverityLevel_types.cs b/test/ApplicationInsightsTypes/Generated/SeverityLevel_types.cs index fca5e9b..1a847f8 100644 --- a/test/ApplicationInsightsTypes/Generated/SeverityLevel_types.cs +++ b/test/ApplicationInsightsTypes/Generated/SeverityLevel_types.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // This code was generated by a tool. // -// Tool : Bond Compiler 0.10.0.0 +// Tool : Bond Compiler 0.10.1.0 // File : SeverityLevel_types.cs // // Changes to this file may cause incorrect behavior and will be lost when @@ -29,7 +29,7 @@ namespace AI using System.Collections.Generic; [global::Bond.Attribute("Description", "Defines the level of severity for the event.")] - [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.0.0")] + [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.1.0")] public enum SeverityLevel { Verbose, diff --git a/test/ApplicationInsightsTypes/Generated/StackFrame_types.cs b/test/ApplicationInsightsTypes/Generated/StackFrame_types.cs index 0faf908..8581c6f 100644 --- a/test/ApplicationInsightsTypes/Generated/StackFrame_types.cs +++ b/test/ApplicationInsightsTypes/Generated/StackFrame_types.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // This code was generated by a tool. // -// Tool : Bond Compiler 0.10.0.0 +// Tool : Bond Compiler 0.10.1.0 // File : StackFrame_types.cs // // Changes to this file may cause incorrect behavior and will be lost when @@ -30,7 +30,7 @@ namespace AI [global::Bond.Attribute("Description", "Stack frame information.")] [global::Bond.Schema] - [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.0.0")] + [System.CodeDom.Compiler.GeneratedCode("gbc", "0.10.1.0")] public partial class StackFrame { [global::Bond.Attribute("Description", "Level in the call stack. For the long stacks SDK may not report every function in a call stack.")] diff --git a/test/ApplicationInsightsTypes/Generated/bonddefaultcodegen.in b/test/ApplicationInsightsTypes/Generated/bonddefaultcodegen.in index 6b9e035..df5850e 100644 --- a/test/ApplicationInsightsTypes/Generated/bonddefaultcodegen.in +++ b/test/ApplicationInsightsTypes/Generated/bonddefaultcodegen.in @@ -1,19 +1,19 @@ -..\..\..\Schema\PublicSchema\AvailabilityData.bond -..\..\..\Schema\PublicSchema\Base.bond -..\..\..\Schema\PublicSchema\ContextTagKeys.bond -..\..\..\Schema\PublicSchema\Data.bond -..\..\..\Schema\PublicSchema\DataPoint.bond -..\..\..\Schema\PublicSchema\DataPointType.bond -..\..\..\Schema\PublicSchema\Domain.bond -..\..\..\Schema\PublicSchema\Envelope.bond -..\..\..\Schema\PublicSchema\EventData.bond -..\..\..\Schema\PublicSchema\ExceptionData.bond -..\..\..\Schema\PublicSchema\ExceptionDetails.bond -..\..\..\Schema\PublicSchema\MessageData.bond -..\..\..\Schema\PublicSchema\MetricData.bond -..\..\..\Schema\PublicSchema\PageViewData.bond -..\..\..\Schema\PublicSchema\PageViewPerfData.bond -..\..\..\Schema\PublicSchema\RemoteDependencyData.bond -..\..\..\Schema\PublicSchema\RequestData.bond -..\..\..\Schema\PublicSchema\SeverityLevel.bond -..\..\..\Schema\PublicSchema\StackFrame.bond +..\..\Schema\PublicSchema\AvailabilityData.bond +..\..\Schema\PublicSchema\Base.bond +..\..\Schema\PublicSchema\ContextTagKeys.bond +..\..\Schema\PublicSchema\Data.bond +..\..\Schema\PublicSchema\DataPoint.bond +..\..\Schema\PublicSchema\DataPointType.bond +..\..\Schema\PublicSchema\Domain.bond +..\..\Schema\PublicSchema\Envelope.bond +..\..\Schema\PublicSchema\EventData.bond +..\..\Schema\PublicSchema\ExceptionData.bond +..\..\Schema\PublicSchema\ExceptionDetails.bond +..\..\Schema\PublicSchema\MessageData.bond +..\..\Schema\PublicSchema\MetricData.bond +..\..\Schema\PublicSchema\PageViewData.bond +..\..\Schema\PublicSchema\PageViewPerfData.bond +..\..\Schema\PublicSchema\RemoteDependencyData.bond +..\..\Schema\PublicSchema\RequestData.bond +..\..\Schema\PublicSchema\SeverityLevel.bond +..\..\Schema\PublicSchema\StackFrame.bond diff --git a/test/EmptyApp20.FunctionalTests/FunctionalTest/RequestTelemetryEmptyAppTests.cs b/test/EmptyApp20.FunctionalTests/FunctionalTest/RequestTelemetryEmptyAppTests.cs index 8de10d8..b60a9bd 100644 --- a/test/EmptyApp20.FunctionalTests/FunctionalTest/RequestTelemetryEmptyAppTests.cs +++ b/test/EmptyApp20.FunctionalTests/FunctionalTest/RequestTelemetryEmptyAppTests.cs @@ -1,5 +1,6 @@ namespace EmptyApp20.FunctionalTests.FunctionalTest { + using System.Collections.Generic; using System.Linq; using System.Net.Http; using AI; @@ -29,7 +30,13 @@ expectedRequestTelemetry.Success = true; expectedRequestTelemetry.Url = new System.Uri(server.BaseHost + RequestPath); - this.ValidateBasicRequest(server, RequestPath, expectedRequestTelemetry); + Dictionary requestHeaders = new Dictionary() + { + { "Request-Id", ""}, + { "Request-Context", "appId=value"}, + }; + + this.ValidateRequestWithHeaders(server, RequestPath, requestHeaders, expectedRequestTelemetry, expectRequestContextInResponse: true); } } @@ -46,7 +53,13 @@ expectedRequestTelemetry.Success = false; expectedRequestTelemetry.Url = new System.Uri(server.BaseHost + RequestPath); - this.ValidateBasicRequest(server, RequestPath, expectedRequestTelemetry); + Dictionary requestHeaders = new Dictionary() + { + { "Request-Id", ""}, + { "Request-Context", "appId=value"}, + }; + + this.ValidateRequestWithHeaders(server, RequestPath, requestHeaders, expectedRequestTelemetry, expectRequestContextInResponse: true); } } diff --git a/test/MVCFramework20.FunctionalTests/FunctionalTest/RequestTelemetryMvcTests.cs b/test/MVCFramework20.FunctionalTests/FunctionalTest/RequestTelemetryMvcTests.cs index 1fef0ea..beff275 100644 --- a/test/MVCFramework20.FunctionalTests/FunctionalTest/RequestTelemetryMvcTests.cs +++ b/test/MVCFramework20.FunctionalTests/FunctionalTest/RequestTelemetryMvcTests.cs @@ -1,5 +1,6 @@ namespace MVCFramework20.FunctionalTests.FunctionalTest { + using System.Collections.Generic; using System.Linq; using System.Net.Http; @@ -30,7 +31,13 @@ expectedRequestTelemetry.Success = true; expectedRequestTelemetry.Url = new System.Uri(server.BaseHost + RequestPath); - this.ValidateBasicRequest(server, RequestPath, expectedRequestTelemetry); + Dictionary requestHeaders = new Dictionary() + { + { "Request-Id", ""}, + { "Request-Context", "appId=value"}, + }; + + this.ValidateRequestWithHeaders(server, RequestPath, requestHeaders, expectedRequestTelemetry, expectRequestContextInResponse: true); } } @@ -47,7 +54,13 @@ expectedRequestTelemetry.Success = true; expectedRequestTelemetry.Url = new System.Uri(server.BaseHost + RequestPath); - this.ValidateBasicRequest(server, RequestPath, expectedRequestTelemetry); + Dictionary requestHeaders = new Dictionary() + { + { "Request-Id", ""}, + { "Request-Context", "appId=value"}, + }; + + this.ValidateRequestWithHeaders(server, RequestPath, requestHeaders, expectedRequestTelemetry, expectRequestContextInResponse: true); } } @@ -64,7 +77,13 @@ expectedRequestTelemetry.Success = false; expectedRequestTelemetry.Url = new System.Uri(server.BaseHost + RequestPath); - this.ValidateBasicRequest(server, RequestPath, expectedRequestTelemetry); + Dictionary requestHeaders = new Dictionary() + { + { "Request-Id", ""}, + { "Request-Context", "appId=value"}, + }; + + this.ValidateRequestWithHeaders(server, RequestPath, requestHeaders, expectedRequestTelemetry, expectRequestContextInResponse: true); } } diff --git a/test/Microsoft.ApplicationInsights.AspNetCore.Tests/HostingDiagnosticListenerTest.cs b/test/Microsoft.ApplicationInsights.AspNetCore.Tests/HostingDiagnosticListenerTest.cs index bec2516..db7d39f 100644 --- a/test/Microsoft.ApplicationInsights.AspNetCore.Tests/HostingDiagnosticListenerTest.cs +++ b/test/Microsoft.ApplicationInsights.AspNetCore.Tests/HostingDiagnosticListenerTest.cs @@ -70,17 +70,32 @@ private ConcurrentQueue sentTelemetry = new ConcurrentQueue(); private ActiveSubsciptionManager subscriptionManager; - private HostingDiagnosticListener CreateHostingListener(bool aspNetCore2) + private HostingDiagnosticListener CreateHostingListener(bool aspNetCore2, TelemetryConfiguration config = null) { - var hostingListener = new HostingDiagnosticListener( - CommonMocks.MockTelemetryClient(telemetry => this.sentTelemetry.Enqueue(telemetry)), - CommonMocks.GetMockApplicationIdProvider(), - injectResponseHeaders: true, - trackExceptions: true, - enableW3CHeaders: false, - enableNewDiagnosticEvents: aspNetCore2); - hostingListener.OnSubscribe(); + HostingDiagnosticListener hostingListener; + if (config != null) + { + hostingListener = new HostingDiagnosticListener( + config, + CommonMocks.MockTelemetryClient(telemetry => this.sentTelemetry.Enqueue(telemetry)), + CommonMocks.GetMockApplicationIdProvider(), + injectResponseHeaders: true, + trackExceptions: true, + enableW3CHeaders: false, + enableNewDiagnosticEvents: aspNetCore2); + } + else + { + hostingListener = new HostingDiagnosticListener( + CommonMocks.MockTelemetryClient(telemetry => this.sentTelemetry.Enqueue(telemetry)), + CommonMocks.GetMockApplicationIdProvider(), + injectResponseHeaders: true, + trackExceptions: true, + enableW3CHeaders: false, + enableNewDiagnosticEvents: aspNetCore2); + } + hostingListener.OnSubscribe(); return hostingListener; } @@ -90,15 +105,16 @@ public void TestSdkVersionIsPopulatedByMiddleware(bool isAspNetCore2) { HttpContext context = CreateContext(HttpRequestScheme, HttpRequestHost); + TelemetryConfiguration config = TelemetryConfiguration.CreateDefault(); + config.ExperimentalFeatures.Add("conditionalAppId"); - using (var hostingListener = CreateHostingListener(isAspNetCore2)) + using (var hostingListener = CreateHostingListener(isAspNetCore2, config)) { HandleRequestBegin(hostingListener, context, 0, isAspNetCore2); Assert.NotNull(context.Features.Get()); - Assert.Equal(CommonMocks.TestApplicationId, - HttpHeadersUtilities.GetRequestContextKeyValue(context.Response.Headers, + Assert.Null(HttpHeadersUtilities.GetRequestContextKeyValue(context.Response.Headers, RequestResponseHeaders.RequestContextTargetKey)); HandleRequestEnd(hostingListener, context, 0, isAspNetCore2); @@ -130,8 +146,7 @@ Assert.NotNull(context.Features.Get()); Assert.Equal(CommonMocks.TestApplicationId, - HttpHeadersUtilities.GetRequestContextKeyValue(context.Response.Headers, - RequestResponseHeaders.RequestContextTargetKey)); + HttpHeadersUtilities.GetRequestContextKeyValue(context.Response.Headers, RequestResponseHeaders.RequestContextTargetKey)); HandleRequestEnd(hostingListener, context, 0, isAspNetCore2); } @@ -162,8 +177,7 @@ Assert.NotNull(context.Features.Get()); Assert.Equal(CommonMocks.TestApplicationId, - HttpHeadersUtilities.GetRequestContextKeyValue(context.Response.Headers, - RequestResponseHeaders.RequestContextTargetKey)); + HttpHeadersUtilities.GetRequestContextKeyValue(context.Response.Headers, RequestResponseHeaders.RequestContextTargetKey)); hostingListener.OnDiagnosticsUnhandledException(context, null); HandleRequestEnd(hostingListener, context, 0, isAspNetCore2); @@ -345,8 +359,7 @@ Assert.NotNull(context.Features.Get()); Assert.Equal(CommonMocks.TestApplicationId, - HttpHeadersUtilities.GetRequestContextKeyValue(context.Response.Headers, - RequestResponseHeaders.RequestContextTargetKey)); + HttpHeadersUtilities.GetRequestContextKeyValue(context.Response.Headers, RequestResponseHeaders.RequestContextTargetKey)); HandleRequestEnd(hostingListener, context, 0, isAspNetCore2); } @@ -370,13 +383,15 @@ public void OnEndRequestSetsRequestNameToMethodAndPath(bool isAspNetCore2) { HttpContext context = CreateContext(HttpRequestScheme, HttpRequestHost, "/Test", method: "GET"); - using (var hostingListener = CreateHostingListener(isAspNetCore2)) + TelemetryConfiguration config = TelemetryConfiguration.CreateDefault(); + config.ExperimentalFeatures.Add("conditionalAppId"); + + using (var hostingListener = CreateHostingListener(isAspNetCore2, config)) { HandleRequestBegin(hostingListener, context, 0, isAspNetCore2); Assert.NotNull(context.Features.Get()); - Assert.Equal(CommonMocks.TestApplicationId, - HttpHeadersUtilities.GetRequestContextKeyValue(context.Response.Headers, + Assert.Null(HttpHeadersUtilities.GetRequestContextKeyValue(context.Response.Headers, RequestResponseHeaders.RequestContextTargetKey)); HandleRequestEnd(hostingListener, context, 0, isAspNetCore2); @@ -401,17 +416,15 @@ public void OnEndRequestFromSameInstrumentationKey(bool isAspNetCore2) { HttpContext context = CreateContext(HttpRequestScheme, HttpRequestHost, "/Test", method: "GET"); - HttpHeadersUtilities.SetRequestContextKeyValue(context.Request.Headers, RequestResponseHeaders.RequestContextSourceKey, CommonMocks.TestApplicationId); + HttpHeadersUtilities.SetRequestContextKeyValue(context.Request.Headers, RequestResponseHeaders.RequestContextSourceKey, CommonMocks.TestApplicationId); using (var hostingListener = CreateHostingListener(isAspNetCore2)) { HandleRequestBegin(hostingListener, context, 0, isAspNetCore2); Assert.NotNull(context.Features.Get()); - Assert.Equal(CommonMocks.TestApplicationId, - HttpHeadersUtilities.GetRequestContextKeyValue(context.Response.Headers, - RequestResponseHeaders.RequestContextTargetKey)); + HttpHeadersUtilities.GetRequestContextKeyValue(context.Response.Headers, RequestResponseHeaders.RequestContextTargetKey)); HandleRequestEnd(hostingListener, context, 0, isAspNetCore2); } @@ -682,6 +695,7 @@ "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"; context.Request.Headers[W3C.W3CConstants.TraceStateHeader] = "state=some"; context.Request.Headers[RequestResponseHeaders.CorrelationContextHeader] = "k=v"; + context.Request.Headers[RequestResponseHeaders.RequestContextHeader] = "appId=something"; HandleRequestBegin(hostingListener, context, 0, isAspNetCore2); @@ -732,6 +746,7 @@ "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"; context.Request.Headers[W3C.W3CConstants.TraceStateHeader] = "state=some"; context.Request.Headers[RequestResponseHeaders.CorrelationContextHeader] = "k=v"; + context.Request.Headers[RequestResponseHeaders.RequestContextHeader] = "appId=something"; HandleRequestBegin(hostingListener, context, 0, isAspNetCore2); @@ -831,6 +846,7 @@ hostingListener.OnSubscribe(); var context = CreateContext(HttpRequestScheme, HttpRequestHost, "/Test", method: "POST"); + context.Request.Headers[RequestResponseHeaders.RequestContextHeader] = "appId=something"; HandleRequestBegin(hostingListener, context, 0, isAspNetCore2); @@ -904,6 +920,57 @@ } } + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RequestTelemetryIsProactivelySampledOutIfFeatureFlagIsOn(bool isAspNetCore2) + { + TelemetryConfiguration config = TelemetryConfiguration.CreateDefault(); + config.ExperimentalFeatures.Add("proactiveSampling"); + config.SetLastObservedSamplingPercentage(SamplingTelemetryItemTypes.Request, 0); + + HttpContext context = CreateContext(HttpRequestScheme, HttpRequestHost, "/Test", method: "POST"); + + using (var hostingListener = CreateHostingListener(isAspNetCore2, config)) + { + HandleRequestBegin(hostingListener, context, 0, isAspNetCore2); + + Assert.NotNull(Activity.Current); + + var requestTelemetry = context.Features.Get(); + Assert.NotNull(requestTelemetry); + Assert.Equal(requestTelemetry.Id, Activity.Current.Id); + Assert.Equal(requestTelemetry.Context.Operation.Id, Activity.Current.RootId); + Assert.Null(requestTelemetry.Context.Operation.ParentId); + Assert.True(requestTelemetry.IsSampledOutAtHead); + } + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RequestTelemetryIsNotProactivelySampledOutIfFeatureFlasIfOff(bool isAspNetCore2) + { + TelemetryConfiguration config = TelemetryConfiguration.CreateDefault(); + config.SetLastObservedSamplingPercentage(SamplingTelemetryItemTypes.Request, 0); + + HttpContext context = CreateContext(HttpRequestScheme, HttpRequestHost, "/Test", method: "POST"); + + using (var hostingListener = CreateHostingListener(isAspNetCore2, config)) + { + HandleRequestBegin(hostingListener, context, 0, isAspNetCore2); + + Assert.NotNull(Activity.Current); + + var requestTelemetry = context.Features.Get(); + Assert.NotNull(requestTelemetry); + Assert.Equal(requestTelemetry.Id, Activity.Current.Id); + Assert.Equal(requestTelemetry.Context.Operation.Id, Activity.Current.RootId); + Assert.Null(requestTelemetry.Context.Operation.ParentId); + Assert.False(requestTelemetry.IsSampledOutAtHead); + } + } + private void HandleRequestBegin(HostingDiagnosticListener hostingListener, HttpContext context, long timestamp, bool isAspNetCore2) { if (isAspNetCore2) diff --git a/test/Microsoft.ApplicationInsights.AspNetCore.Tests/TelemetryInitializers/OperationNameTelemetryInitializerTests.cs b/test/Microsoft.ApplicationInsights.AspNetCore.Tests/TelemetryInitializers/OperationNameTelemetryInitializerTests.cs index 27d25e6..ef3e3d6 100644 --- a/test/Microsoft.ApplicationInsights.AspNetCore.Tests/TelemetryInitializers/OperationNameTelemetryInitializerTests.cs +++ b/test/Microsoft.ApplicationInsights.AspNetCore.Tests/TelemetryInitializers/OperationNameTelemetryInitializerTests.cs @@ -4,21 +4,33 @@ namespace Microsoft.ApplicationInsights.AspNetCore.Tests.TelemetryInitializers { using System; using System.Diagnostics; - using System.Diagnostics.Tracing; using Microsoft.ApplicationInsights.AspNetCore.TelemetryInitializers; using Microsoft.ApplicationInsights.AspNetCore.Tests.Helpers; using Microsoft.ApplicationInsights.DataContracts; - using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Http.Internal; using Microsoft.AspNetCore.Mvc; - using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Routing; using Xunit; using Microsoft.AspNetCore.Routing.Tree; using Microsoft.AspNetCore.Http; + public class OperationNameTelemetryInitializerTests { private const string TestListenerName = "TestListener"; + + private HostingDiagnosticListener CreateHostingListener(bool aspNetCore2) + { + var hostingListener = new HostingDiagnosticListener( + CommonMocks.MockTelemetryClient(telemetry => {}), + CommonMocks.GetMockApplicationIdProvider(), + injectResponseHeaders: true, + trackExceptions: true, + enableW3CHeaders: false, + enableNewDiagnosticEvents: aspNetCore2); + hostingListener.OnSubscribe(); + + return hostingListener; + } + [Fact] public void InitializeThrowIfHttpContextAccessorIsNull() { @@ -85,11 +97,12 @@ namespace Microsoft.ApplicationInsights.AspNetCore.Tests.TelemetryInitializers var contextAccessor = HttpContextAccessorHelper.CreateHttpContextAccessor(new RequestTelemetry(), actionContext); var telemetryListener = new DiagnosticListener(TestListenerName); - var initializer = new MvcDiagnosticsListener(); - telemetryListener.Subscribe(initializer); - telemetryListener.Write("Microsoft.AspNetCore.Mvc.BeforeAction", - new { httpContext = contextAccessor.HttpContext, routeData = actionContext.RouteData }); - + using (var listener = CreateHostingListener(false)) + { + telemetryListener.Subscribe(listener); + telemetryListener.Write("Microsoft.AspNetCore.Mvc.BeforeAction", + new { httpContext = contextAccessor.HttpContext, routeData = actionContext.RouteData }); + } var telemetry = contextAccessor.HttpContext.Features.Get(); Assert.Equal("GET home", telemetry.Name); @@ -106,11 +119,12 @@ namespace Microsoft.ApplicationInsights.AspNetCore.Tests.TelemetryInitializers var contextAccessor = HttpContextAccessorHelper.CreateHttpContextAccessor(new RequestTelemetry(), actionContext); var telemetryListener = new DiagnosticListener(TestListenerName); - var initializer = new MvcDiagnosticsListener(); - telemetryListener.Subscribe(initializer); - telemetryListener.Write("Microsoft.AspNetCore.Mvc.BeforeAction", - new { httpContext = contextAccessor.HttpContext, routeData = actionContext.RouteData }); - + using (var listener = CreateHostingListener(false)) + { + telemetryListener.Subscribe(listener); + telemetryListener.Write("Microsoft.AspNetCore.Mvc.BeforeAction", + new { httpContext = contextAccessor.HttpContext, routeData = actionContext.RouteData }); + } var telemetry = contextAccessor.HttpContext.Features.Get(); Assert.Equal("GET account/login", telemetry.Name); @@ -126,11 +140,12 @@ namespace Microsoft.ApplicationInsights.AspNetCore.Tests.TelemetryInitializers var contextAccessor = HttpContextAccessorHelper.CreateHttpContextAccessor(new RequestTelemetry(), actionContext); var telemetryListener = new DiagnosticListener(TestListenerName); - var initializer = new MvcDiagnosticsListener(); - telemetryListener.Subscribe(initializer); - telemetryListener.Write("Microsoft.AspNetCore.Mvc.BeforeAction", - new { httpContext = contextAccessor.HttpContext, routeData = actionContext.RouteData }); - + using (var listener = CreateHostingListener(false)) + { + telemetryListener.Subscribe(listener); + telemetryListener.Write("Microsoft.AspNetCore.Mvc.BeforeAction", + new { httpContext = contextAccessor.HttpContext, routeData = actionContext.RouteData }); + } var telemetry = contextAccessor.HttpContext.Features.Get(); Assert.Equal("GET /Index", telemetry.Name); @@ -148,10 +163,12 @@ namespace Microsoft.ApplicationInsights.AspNetCore.Tests.TelemetryInitializers var contextAccessor = HttpContextAccessorHelper.CreateHttpContextAccessor(new RequestTelemetry(), actionContext); var telemetryListener = new DiagnosticListener(TestListenerName); - var initializer = new MvcDiagnosticsListener(); - telemetryListener.Subscribe(initializer); - telemetryListener.Write("Microsoft.AspNetCore.Mvc.BeforeAction", - new { httpContext = contextAccessor.HttpContext, routeData = actionContext.RouteData }); + using (var listener = CreateHostingListener(false)) + { + telemetryListener.Subscribe(listener); + telemetryListener.Write("Microsoft.AspNetCore.Mvc.BeforeAction", + new { httpContext = contextAccessor.HttpContext, routeData = actionContext.RouteData }); + } var telemetry = contextAccessor.HttpContext.Features.Get(); @@ -172,11 +189,12 @@ namespace Microsoft.ApplicationInsights.AspNetCore.Tests.TelemetryInitializers var contextAccessor = HttpContextAccessorHelper.CreateHttpContextAccessor(new RequestTelemetry(), actionContext); var telemetryListener = new DiagnosticListener(TestListenerName); - var initializer = new MvcDiagnosticsListener(); - telemetryListener.Subscribe(initializer); - telemetryListener.Write("Microsoft.AspNetCore.Mvc.BeforeAction", - new { httpContext = contextAccessor.HttpContext, routeData = actionContext.RouteData }); - + using (var listener = CreateHostingListener(false)) + { + telemetryListener.Subscribe(listener); + telemetryListener.Write("Microsoft.AspNetCore.Mvc.BeforeAction", + new { httpContext = contextAccessor.HttpContext, routeData = actionContext.RouteData }); + } var telemetry = contextAccessor.HttpContext.Features.Get(); Assert.Equal("GET account/login [parameterA/parameterN/parameterZ]", telemetry.Name); @@ -193,13 +211,14 @@ namespace Microsoft.ApplicationInsights.AspNetCore.Tests.TelemetryInitializers var contextAccessor = HttpContextAccessorHelper.CreateHttpContextAccessor(new RequestTelemetry(), actionContext); var telemetryListener = new DiagnosticListener(TestListenerName); - var initializer = new MvcDiagnosticsListener(); - telemetryListener.Subscribe(initializer); - telemetryListener.Write("Microsoft.AspNetCore.Mvc.BeforeAction", - new { httpContext = contextAccessor.HttpContext, routeData = actionContext.RouteData }); - + using (var listener = CreateHostingListener(false)) + { + telemetryListener.Subscribe(listener); + telemetryListener.Write("Microsoft.AspNetCore.Mvc.BeforeAction", + new { httpContext = contextAccessor.HttpContext, routeData = actionContext.RouteData }); + } var telemetry = contextAccessor.HttpContext.Features.Get(); - + Assert.Equal("GET account/login", telemetry.Name); } diff --git a/test/WebApi20.FunctionalTests/FunctionalTest/RequestTelemetryWebApiTests.cs b/test/WebApi20.FunctionalTests/FunctionalTest/RequestTelemetryWebApiTests.cs index 75b3c55..c87d899 100644 --- a/test/WebApi20.FunctionalTests/FunctionalTest/RequestTelemetryWebApiTests.cs +++ b/test/WebApi20.FunctionalTests/FunctionalTest/RequestTelemetryWebApiTests.cs @@ -34,7 +34,13 @@ expectedRequestTelemetry.Success = true; expectedRequestTelemetry.Url = new System.Uri(server.BaseHost + RequestPath); - this.ValidateBasicRequest(server, RequestPath, expectedRequestTelemetry); + Dictionary requestHeaders = new Dictionary() + { + { "Request-Id", ""}, + { "Request-Context", "appId=value"}, + }; + + this.ValidateRequestWithHeaders(server, RequestPath, requestHeaders, expectedRequestTelemetry, expectRequestContextInResponse: true); } } @@ -51,7 +57,13 @@ expectedRequestTelemetry.Success = false; expectedRequestTelemetry.Url = new System.Uri(server.BaseHost + RequestPath); - this.ValidateBasicRequest(server, RequestPath, expectedRequestTelemetry); + Dictionary requestHeaders = new Dictionary() + { + { "Request-Id", ""}, + { "Request-Context", "appId=value"}, + }; + + this.ValidateRequestWithHeaders(server, RequestPath, requestHeaders, expectedRequestTelemetry, expectRequestContextInResponse: true); } } @@ -68,7 +80,13 @@ expectedRequestTelemetry.Success = true; expectedRequestTelemetry.Url = new System.Uri(server.BaseHost + RequestPath); - this.ValidateBasicRequest(server, RequestPath, expectedRequestTelemetry); + Dictionary requestHeaders = new Dictionary() + { + { "Request-Id", ""}, + { "Request-Context", "appId=value"}, + }; + + this.ValidateRequestWithHeaders(server, RequestPath, requestHeaders, expectedRequestTelemetry, expectRequestContextInResponse: true); } } @@ -121,7 +139,7 @@ expectedRequestTelemetry.Success = true; expectedRequestTelemetry.Url = new System.Uri(server.BaseHost + RequestPath); - var item = this.ValidateBasicRequest(server, RequestPath, expectedRequestTelemetry, true); + var item = this.ValidateBasicRequest(server, RequestPath, expectedRequestTelemetry); // W3C compatible-Id ( should go away when W3C is implemented in .NET https://github.com/dotnet/corefx/issues/30331) Assert.Equal(32, item.tags["ai.operation.id"].Length);