/// <summary> /// Always log, possibly inform the user, possibly throw the exception /// </summary> /// <param name="modalThreshold">Will show a modal dialog if the channel is this or lower</param> /// <param name="passiveThreshold">Will toast if channel is this or lower (and didn't modal) and shortUserLevelMessage is defined.</param> /// <param name="shortUserLevelMessage">Simple message that fits in small toast notification</param> /// <param name="moreDetails">Info adds information about the problem, which we get if they report the problem</param> /// <param name="exception"></param> public static void Report(ModalIf modalThreshold, PassiveIf passiveThreshold, string shortUserLevelMessage = null, string moreDetails = null, Exception exception = null) { s_expectedByUnitTest?.ProblemWasReported(); // Simplify some checks below by tweaking the channel name on Linux. var channel = ApplicationUpdateSupport.ChannelName.ToLowerInvariant(); if (channel.EndsWith("-unstable")) { channel = channel.Replace("unstable", "alpha"); } try { shortUserLevelMessage = shortUserLevelMessage == null ? "" : shortUserLevelMessage; var fullDetailedMessage = shortUserLevelMessage; if (!string.IsNullOrEmpty(moreDetails)) { fullDetailedMessage = fullDetailedMessage + System.Environment.NewLine + moreDetails; } if (exception == null) { //the code below is simpler if we always have an exception, even this thing that gives //us the stacktrace we would otherwise be missing. Note, you might be tempted to throw //and then catch an exception instead, but for some reason the resulting stack trace //would contain only this method. exception = new ApplicationException(new StackTrace().ToString()); } if (Program.RunningUnitTests) { //It's not clear to me what we can do that works for all unit test scenarios... //We can imagine those for which throwing an exception at this point would be helpful, //but there are others in which say, not finding a file is expected. Either way, //the rest of the test should fail if the problem is real, so doing anything here //would just be a help, not really necessary for getting the test to fail. //So, for now I'm going to just go with doing nothing. return; } //if this isn't going modal even for devs, it's just background noise and we don't want the //thousands of exceptions we were getting as with BL-3280 if (modalThreshold != ModalIf.None) { Analytics.ReportException(exception); } Logger.WriteError("NonFatalProblem: " + fullDetailedMessage, exception); if (Matches(modalThreshold).Any(s => channel.Contains(s))) { try { SIL.Reporting.ErrorReport.ReportNonFatalExceptionWithMessage(exception, fullDetailedMessage); } catch (Exception) { //if we're running when the UI is already shut down, the above is going to throw. //At least if we're running in a debugger, we'll stop here: throw new ApplicationException(fullDetailedMessage + "Error trying to report normally."); } return; } //just convert from PassiveIf to ModalIf so that we don't have to duplicate code var passive = (ModalIf)ModalIf.Parse(typeof(ModalIf), passiveThreshold.ToString()); if (!string.IsNullOrEmpty(shortUserLevelMessage) && Matches(passive).Any(s => channel.Contains(s))) { ShowToast(shortUserLevelMessage, exception, fullDetailedMessage); } } catch (Exception errorWhileReporting) { // Don't annoy developers for expected error if the internet is not available. if (errorWhileReporting.Message.StartsWith("Bloom could not retrieve the URL") && Bloom.web.UrlLookup.FastInternetAvailable) { Debug.Fail("error in nonfatalError reporting"); } if (channel.Contains("alpha")) { ErrorReport.NotifyUserOfProblem(errorWhileReporting, "Error while reporting non fatal error"); } } }
/// <summary> /// Always log, possibly inform the user, possibly throw the exception /// </summary> /// <param name="modalThreshold">Will show a modal dialog if the channel is this or lower</param> /// <param name="passiveThreshold">Will toast if channel is this or lower (and didn't modal) and shortUserLevelMessage is defined.</param> /// <param name="shortUserLevelMessage">Simple message that fits in small toast notification</param> /// <param name="moreDetails">Info adds information about the problem, which we get if they report the problem</param> /// <param name="exception"></param> /// <param name="showSendReport">Set to 'false' to eliminate yellow screens and "Report" links on toasts</param> public static void Report(ModalIf modalThreshold, PassiveIf passiveThreshold, string shortUserLevelMessage = null, string moreDetails = null, Exception exception = null, bool showSendReport = true) { s_expectedByUnitTest?.ProblemWasReported(); var channel = ApplicationUpdateSupport.ChannelName.ToLowerInvariant(); try { shortUserLevelMessage = shortUserLevelMessage ?? ""; var fullDetailedMessage = shortUserLevelMessage; if (!string.IsNullOrEmpty(moreDetails)) { fullDetailedMessage = fullDetailedMessage + System.Environment.NewLine + moreDetails; } if (exception == null) { //the code below is simpler if we always have an exception, even this thing that gives //us the stacktrace we would otherwise be missing. Note, you might be tempted to throw //and then catch an exception instead, but for some reason the resulting stack trace //would contain only this method. exception = new ApplicationException(new StackTrace().ToString()); } if (Program.RunningUnitTests) { //It's not clear to me what we can do that works for all unit test scenarios... //We can imagine those for which throwing an exception at this point would be helpful, //but there are others in which say, not finding a file is expected. Either way, //the rest of the test should fail if the problem is real, so doing anything here //would just be a help, not really necessary for getting the test to fail. //So, for now I'm going to just go with doing nothing. return; } //if this isn't going modal even for devs, it's just background noise and we don't want the //thousands of exceptions we were getting as with BL-3280 if (modalThreshold != ModalIf.None) { Analytics.ReportException(exception); } Logger.WriteError("NonFatalProblem: " + fullDetailedMessage, exception); if (Program.RunningInConsoleMode) { // This is "nonfatal", so report as best we can (standard error) and keep going... Console.Error.WriteLine($"Nonfatal problem: {fullDetailedMessage}"); return; } //just convert from PassiveIf to ModalIf so that we don't have to duplicate code var passive = (ModalIf)ModalIf.Parse(typeof(ModalIf), passiveThreshold.ToString()); var formForSynchronizing = Application.OpenForms.Cast <Form>().LastOrDefault(); if (formForSynchronizing is ProgressDialog) { // Targetting ProgressDialog doesn't work so well for toasts, since the dialog tends // to disappear immediately and the user never sees the toast. modalThreshold = passive; } if (Matches(modalThreshold).Any(s => channel.Contains(s))) { try { if (showSendReport) { // N.B.: We should be more careful than ever about when we want 'showSendReport' to be 'true', // since this new "nonfatal" UI doesn't have a "Cancel" button. ProblemReportApi.ShowProblemDialog(Form.ActiveForm, exception, fullDetailedMessage, "nonfatal"); } else { // We don't want any notification (MessageBox or toast) targetting a ProgressDialog, // since the dialog seems to disappear quickly and leave us hanging... and not able to show. // We'll keep the form if it's not a ProgressDialog in order to center our message properly. if (formForSynchronizing is ProgressDialog) { MessageBox.Show(fullDetailedMessage, string.Empty, MessageBoxButtons.OK); } else { MessageBox.Show(formForSynchronizing, fullDetailedMessage, string.Empty, MessageBoxButtons.OK); } } } catch (Exception) { //if we're running when the UI is already shut down, the above is going to throw. //At least if we're running in a debugger, we'll stop here: throw new ApplicationException(fullDetailedMessage + "Error trying to report normally."); } return; } if (!string.IsNullOrEmpty(shortUserLevelMessage) && Matches(passive).Any(s => channel.Contains(s))) { ShowToast(shortUserLevelMessage, exception, fullDetailedMessage, showSendReport); } } catch (Exception errorWhileReporting) { // Don't annoy developers for expected error if the internet is not available. if (errorWhileReporting.Message.StartsWith("Bloom could not retrieve the URL") && Bloom.web.UrlLookup.FastInternetAvailable) { Debug.Fail("error in nonfatalError reporting"); } if (channel.Contains("developer") || channel.Contains("alpha")) { ErrorReport.NotifyUserOfProblem(errorWhileReporting, "Error while reporting non fatal error"); } } }
/// <summary> /// Always log, possibly inform the user, possibly throw the exception /// </summary> /// <param name="modalThreshold">Will show a modal dialog if the channel is this or lower</param> /// <param name="passiveThreshold">Ignored for now</param> /// <param name="shortUserLevelMessage">Simple message that fits in small toast notification</param> /// <param name="moreDetails">Info adds information about the problem, which we get if they report the problem</param> /// <param name="exception"></param> public static void Report(ModalIf modalThreshold, PassiveIf passiveThreshold, string shortUserLevelMessage = null, string moreDetails = null, Exception exception = null) { // Simplify some checks below by tweaking the channel name on Linux. var channel = ApplicationUpdateSupport.ChannelName.ToLowerInvariant(); if (channel.EndsWith("-unstable")) channel = channel.Replace("unstable", "alpha"); try { shortUserLevelMessage = shortUserLevelMessage == null ? "" : shortUserLevelMessage; var fullDetailedMessage = shortUserLevelMessage; if(!string.IsNullOrEmpty(moreDetails)) fullDetailedMessage = fullDetailedMessage + System.Environment.NewLine + moreDetails; if(exception == null) { //the code below is simpler if we always have an exception, even this thing that gives //us the stacktrace we would otherwise be missing. Note, you might be tempted to throw //and then catch an exception instead, but for some reason the resulting stack trace //would contain only this method. exception = new ApplicationException(new StackTrace().ToString()); } if(Program.RunningUnitTests) { //It's not clear to me what we can do that works for all unit test scenarios... //We can imagine those for which throwing an exception at this point would be helpful, //but there are others in which say, not finding a file is expected. Either way, //the rest of the test should fail if the problem is real, so doing anything here //would just be a help, not really necessary for getting the test to fail. //So, for now I'm going to just go with doing nothing. return; } //if this isn't going modal even for devs, it's just background noise and we don't want the //thousands of exceptions we were getting as with BL-3280 if (modalThreshold != ModalIf.None) { Analytics.ReportException(exception); } Logger.WriteError("NonFatalProblem: " + fullDetailedMessage, exception); if(Matches(modalThreshold).Any(s => channel.Contains(s))) { try { SIL.Reporting.ErrorReport.ReportNonFatalExceptionWithMessage(exception, fullDetailedMessage); } catch(Exception) { //if we're running when the UI is already shut down, the above is going to throw. //At least if we're running in a debugger, we'll stop here: throw new ApplicationException(fullDetailedMessage + "Error trying to report normally."); } return; } //just convert from PassiveIf to ModalIf so that we don't have to duplicate code var passive = (ModalIf) ModalIf.Parse(typeof(ModalIf), passiveThreshold.ToString()); if(!string.IsNullOrEmpty(shortUserLevelMessage) && Matches(passive).Any(s => channel.Contains(s))) { ShowToast(shortUserLevelMessage, exception, fullDetailedMessage); } } catch(Exception errorWhileReporting) { // Don't annoy developers for expected error if the internet is not available. if (errorWhileReporting.Message.StartsWith("Bloom could not retrieve the URL") && Bloom.web.UrlLookup.FastInternetAvailable) { Debug.Fail("error in nonfatalError reporting"); } if (channel.Contains("alpha")) ErrorReport.NotifyUserOfProblem(errorWhileReporting,"Error while reporting non fatal error"); } }