private async Task <IEnumerable <AzureSupportCenterInsight> > GetInsightsFromDetector(OperationContext <TResource> context, Definition detector, List <Definition> detectorsRun) { Response response = null; detectorsRun.Add(detector); try { var fullResponse = await GetDetectorInternal(detector.Id, context); if (fullResponse != null) { response = fullResponse.Item1; } } catch (Exception ex) { DiagnosticsETWProvider.Instance.LogRuntimeHostHandledException(context.RequestId, "GetInsightsFromDetector", context.Resource.SubscriptionId, context.Resource.ResourceGroup, context.Resource.Name, ex.GetType().ToString(), ex.ToString()); } // Handle Exception or Not Found // Not found can occur if invalid detector is put in detector list if (response == null) { return(null); } List <AzureSupportCenterInsight> supportCenterInsights = new List <AzureSupportCenterInsight>(); // Take max one insight per detector, only critical or warning, pick the most critical var mostCriticalInsight = response.Insights.OrderBy(insight => insight.Status).FirstOrDefault(); //TODO: Add Logging Per Detector Here AzureSupportCenterInsight ascInsight = null; if (mostCriticalInsight != null) { ascInsight = AzureSupportCenterInsightUtilites.CreateInsight(mostCriticalInsight, context, detector); supportCenterInsights.Add(ascInsight); } DiagnosticsETWProvider.Instance.LogRuntimeHostDetectorAscInsight(context.RequestId, detector.Id, ascInsight?.ImportanceLevel.ToString()); var detectorLists = response.Dataset .Where(diagnosicData => diagnosicData.RenderingProperties.Type == RenderingType.Detector) .SelectMany(diagnosticData => ((DetectorCollectionRendering)diagnosticData.RenderingProperties).DetectorIds) .Distinct(); if (detectorLists.Any()) { var applicableDetectorMetaData = (await this.ListDetectorsInternal(context)).Where(detectorResponse => detectorLists.Contains(detectorResponse.Metadata.Id)); var detectorListResponses = await Task.WhenAll(applicableDetectorMetaData.Select(detectorResponse => GetInsightsFromDetector(context, detectorResponse.Metadata, detectorsRun))); supportCenterInsights.AddRange(detectorListResponses.Where(detectorInsights => detectorInsights != null).SelectMany(detectorInsights => detectorInsights)); } return(supportCenterInsights); }
/// <summary> /// Adds an ASC Insight that will be rendered as a regular insight. /// It will flow automatically to Azure Support Center if your detector is enabled for a support topic. /// </summary> /// <param name="res">Response object for extension method</param> /// <param name="title">Title of insight. This should be a constant string, without any part of it changing depending on the resource.</param> /// <param name="status">Status of the insight. All insight status types are available, but None and Success with be changes to Info in Azure Support Center.</param> /// <param name="description">The description of the insight. This should not contain anything that is instructing the reader to do anything. /// It should only expand on any relevant information about the insight.</param> /// <param name="recommendedAction">Recommended action that is specifically addressing what steps thesupport engineer should take. /// If you only want to have the support engineer to send the customer ready content, then that is all you would have to include here.</param> /// <param name="customerReadyContent">This is a response that is meant for the support engineer to paste directly into an email to the customer. /// It is meant to completely solve the problem, and if that is not the case you may not want to use this field, by passing a null value. /// Other good information to have is additional resources for the customer so that they can solve the problem in the future.</param> /// <param name="context">Operation context which is passed into detector run method.</param> /// <param name="ascOnly">Only show the insight in Azure Support Center.</param> /// <param name="extraNameValuePairs">Additional name value pairs that you want to display in Applens/App Service Diagnostics. These will not be added in Azure Support Center. /// For markdown, wrap your text in markdown tags. HTML or plain text also allowed</param> /// <param name="isExpanded">Whether you want to Applens/App Service Diagnostics to expand the insight initially.</param> /// <returns>Azure Support Center Insight Object</returns> /// <example> /// This sample shows how to use the <see cref="AddAscInsight"/> method. /// <code> /// public async static Task<![CDATA[<Response>]]> Run(DataProviders dp, OperationContext cxt, Response res) /// { /// var descriptionMarkdown = /// @"###A scale operation failed because there was already a scale operation underway. Here is the error: /// /// ``` /// Operation failed because a current scale operation is ongoing. /// ```"; /// /// var recommendedActionPlainText = "Copy and paste the Customer Ready Content and send to the customer"; /// /// var customerReadyActionMarkdown = /// @$"Your scale operation for site ***{cxt.Resource.Name}*** failed because there was a current scale operation underway. /// /// Please wait for the current operation to finish and then try again to scale your app service plan. "; /// /// res.AddAscInsight( /// "Failed Scale Operation Detected", /// InsightStatus.Critical, /// new Text(descriptionMarkdown, true), /// new Text(recommendedActionPlainText), /// new Text(customerReadyActionMarkdown, true), /// cxt); /// } /// </code> /// </example> public static AzureSupportCenterInsight AddAscInsight <TResource>(this Response res, string title, InsightStatus status, Text description, Text recommendedAction, Text customerReadyContent, OperationContext <TResource> context, Dictionary <string, string> extraNameValuePairs = null, bool ascOnly = false, bool isExpanded = false) where TResource : IResource { var insight = AzureSupportCenterInsightUtilites.CreateInsight(title, status, description, recommendedAction, customerReadyContent, res.Metadata, context); res.AscInsights.Add(insight); // If you designate an insight as ASC only, it will not show in applens // If this is an external call, it will not be added as an insight that renders in the UI if (ascOnly || !context.IsInternalCall) { return(insight); } var nameValuePairs = new Dictionary <string, string>(); if (description != null) { nameValuePairs.Add("Description", GetContentStringForApplens(description)); } // Recommended Action is only for internal if (context.IsInternalCall && recommendedAction != null) { nameValuePairs.Add("Recommended Action", GetContentStringForApplens(recommendedAction)); } // For consistency with ASC, we will convert Customer Ready Content to HTML on the server side, // so that we ensure that it renders the same in both ASC and applens // For external requests, we will put this content as 'Recommended Action' if (customerReadyContent != null) { nameValuePairs.Add(context.IsInternalCall ? "Customer Ready Content" : "Recommended Action", CommonMark.CommonMarkConverter.Convert(customerReadyContent.Value)); } if (extraNameValuePairs != null) { foreach (var key in extraNameValuePairs.Keys) { nameValuePairs.Add(key, extraNameValuePairs[key]); } } res.AddInsight(new Insight(status, title, nameValuePairs, isExpanded)); return(insight); }
protected async Task <IActionResult> GetInsights(TResource resource, string supportTopicId, string minimumSeverity, string startTime, string endTime, string timeGrain) { if (!DateTimeHelper.PrepareStartEndTimeWithTimeGrain(startTime, endTime, timeGrain, out DateTime startTimeUtc, out DateTime endTimeUtc, out TimeSpan timeGrainTimeSpan, out string errorMessage)) { return(BadRequest(errorMessage)); } OperationContext <TResource> cxt = PrepareContext(resource, startTimeUtc, endTimeUtc, forceInternal: true); List <AzureSupportCenterInsight> insights = null; string error = null; List <Definition> detectorsRun = new List <Definition>(); try { supportTopicId = ParseCorrectSupportTopicId(supportTopicId); var allDetectors = (await ListDetectorsInternal(cxt)).Select(detectorResponse => detectorResponse.Metadata); var applicableDetectors = allDetectors .Where(detector => string.IsNullOrWhiteSpace(supportTopicId) || detector.SupportTopicList.FirstOrDefault(supportTopic => supportTopic.Id == supportTopicId) != null); var insightGroups = await Task.WhenAll(applicableDetectors.Select(detector => GetInsightsFromDetector(cxt, detector, detectorsRun))); insights = insightGroups.Where(group => group != null).SelectMany(group => group).ToList(); } catch (Exception ex) { error = ex.GetType().ToString(); DiagnosticsETWProvider.Instance.LogRuntimeHostHandledException(cxt.RequestId, "GetInsights", cxt.Resource.SubscriptionId, cxt.Resource.ResourceGroup, cxt.Resource.Name, ex.GetType().ToString(), ex.ToString()); } var correlationId = Guid.NewGuid(); var insightInfo = new { Total = insights.Count, Critical = insights.Count(insight => insight.ImportanceLevel == ImportanceLevel.Critical), Warning = insights.Count(insight => insight.ImportanceLevel == ImportanceLevel.Warning), Info = insights.Count(insight => insight.ImportanceLevel == ImportanceLevel.Info), Default = detectorsRun.Any() && !insights.Any() ? 1 : 0 }; DiagnosticsETWProvider.Instance.LogRuntimeHostInsightCorrelation(cxt.RequestId, "ControllerBase.GetInsights", cxt.Resource.SubscriptionId, cxt.Resource.ResourceGroup, cxt.Resource.Name, correlationId.ToString(), JsonConvert.SerializeObject(insightInfo)); if (!insights.Any() && detectorsRun.Any()) { insights.Add(AzureSupportCenterInsightUtilites.CreateDefaultInsight(cxt, detectorsRun)); } var response = new AzureSupportCenterInsightEnvelope() { CorrelationId = correlationId, ErrorMessage = error, TotalInsightsFound = insights != null?insights.Count() : 0, Insights = insights }; return(Ok(response)); }
private async Task <IEnumerable <AzureSupportCenterInsight> > GetInsightsFromDetector(RuntimeContext <TResource> context, Definition detector, List <Definition> detectorsRun) { Response response = null; detectorsRun.Add(detector); try { var fullResponse = await GetDetectorInternal(detector.Id, context); if (fullResponse != null) { response = fullResponse.Item1; } } catch (Exception ex) { DiagnosticsETWProvider.Instance.LogRuntimeHostHandledException(context.OperationContext.RequestId, "GetInsightsFromDetector", context.OperationContext.Resource.SubscriptionId, context.OperationContext.Resource.ResourceGroup, context.OperationContext.Resource.Name, ex.GetType().ToString(), ex.ToString()); } // Handle Exception or Not Found // Not found can occur if invalid detector is put in detector list if (response == null) { return(null); } List <AzureSupportCenterInsight> supportCenterInsights = new List <AzureSupportCenterInsight>(); if (response.AscInsights.Any()) { foreach (var ascInsight in response.AscInsights) { logAscInsight(context, detector, ascInsight); supportCenterInsights.Add(ascInsight); } } else { var regularToAscInsights = response.Insights.Select(insight => { var ascInsight = AzureSupportCenterInsightUtilites.CreateInsight(insight, context.OperationContext, detector); logAscInsight(context, detector, ascInsight); return(ascInsight); }); supportCenterInsights.AddRange(regularToAscInsights); } var detectorLists = response.Dataset .Where(diagnosicData => diagnosicData.RenderingProperties.Type == RenderingType.Detector) .SelectMany(diagnosticData => ((DetectorCollectionRendering)diagnosticData.RenderingProperties).DetectorIds) .Distinct(); if (detectorLists.Any()) { var applicableDetectorMetaData = (await this.ListDetectorsInternal(context)).Where(detectorResponse => detectorLists.Contains(detectorResponse.Metadata.Id)); var detectorListResponses = await Task.WhenAll(applicableDetectorMetaData.Select(detectorResponse => GetInsightsFromDetector(context, detectorResponse.Metadata, detectorsRun))); supportCenterInsights.AddRange(detectorListResponses.Where(detectorInsights => detectorInsights != null).SelectMany(detectorInsights => detectorInsights)); } return(supportCenterInsights); }