public static void SendEmail(CrashReport report, String attachment) { if (!Settings.CrashReportEmailEnabled) { return; } if (String.IsNullOrEmpty(Settings.CrashReportEmailAddress)) { return; } var email = new MailMessage { Sender = new MailAddress(CreateSenderEmailAddress()) }; email.To.Add(Settings.CrashReportEmailAddress); email.Subject = report.Title; email.Body = report.Body; email.IsBodyHtml = true; if (attachment != null) { email.Attachments.Add(new Attachment(attachment)); } var smtp = new SmtpClient(); smtp.Send(email); }
protected override void OnLoad(EventArgs e) { // This is the base page from which my custom error pages inherit. If an unhandled exception occurs here then // my custom error page is not displayed to the user. Instead, the user sees the default ASP.NET Server Error // page (the "Yellow Screen of Death"). In effect, if an unhandled exception occurs in the error page, then all // my custom error handling is overridden, and the original exception details are lost. // throw new DemoException("What happens to an unhandled exception in the custom error page?"); base.OnLoad(e); _httpStatusCode = Int32.Parse(StatusCode.Value); Response.StatusCode = _httpStatusCode; if (!IsPostBack) { Form.Action = Request.Url.PathAndQuery; if (_httpStatusCode == 404) { CrashReportTitle.Text = "Page Not Found"; CrashReportBody.Text = String.Empty; ReportTitle.Value = "Page Not Found"; ReportBody.Value = String.Empty; } else { ReportTitle.Value = "Unhandled Exception"; ReportBody.Value = String.Empty; var report = HttpContext.Current.Cache[ApplicationErrorModule.Settings.Names.CrashReportKey] as CrashReport; if (report == null) { Exception ex = HttpContext.Current.Server.GetLastError() ?? new HttpUnhandledException("Server Error"); report = new CrashReport(ex); } CrashReportTitle.Text = report.Title; CrashReportBody.Text = report.Body; ReportTitle.Value = report.Title; ReportBody.Value = report.Body; NextSteps.EnableTechnicalInformation = true; } BrokenUrl.Text = GetBrokenUrl(); } else { // If this is a postback then we're OK -- the user is submitting a quick question from the error page. Response.StatusCode = 200; } }
static private String SaveCrashReport(CrashReport report, HttpRequest request, String action) { String path = CreateFilePath(request.Url.Host, action); if (String.IsNullOrEmpty(path)) { return(null); } String directory = Path.GetDirectoryName(path); if (directory != null && !Directory.Exists(directory)) { Directory.CreateDirectory(directory); } File.WriteAllText(path, report.Body); return(path); }
private static String FormatException(Exception ex, CrashReport report, Int32 depth) { report.Title = ex.Message; var sb = new StringBuilder(); if (ex.InnerException != null) { sb.Append(FormatException(ex.InnerException, report, depth - 1)); } sb.AppendFormat("<tr><th colspan='2'><h3>Exception #{0}</h3></th></tr>", Math.Abs(depth) + 1); if (!String.IsNullOrEmpty(ex.Source)) { sb.AppendFormat("<tr><td class='label'>Source</td><td>{0}</td></tr>", ex.Source); } sb.AppendFormat("<tr><td class='label'>Message</td><td>{0}</td></tr>", FormatExceptionMessage(ex)); sb.AppendFormat("<tr><td class='label'>Stack Trace</td><td>{0}</td></tr>", FormatExceptionStackTrace(ex)); return(sb.ToString()); }
private static void ExceptionOccurred(Exception ex) { // If an unhandled exception is thrown here then this Application_Error event handler will re-catch it, // wiping out the original exception, and it will not re-throw the exception -- so this line of code does // not create an infinite loop (although it might appear to do so). // throw new DemoException("What happens to an unhandled exception in ApplicationErrorModule.ExceptionOccurred?"); // If the current request is itself an error page then we need to allow the exception to pass through. HttpRequest request = HttpContext.Current.Request; if (Regex.IsMatch(request.Url.AbsolutePath, ErrorPagePattern)) { return; } // Otherwise, we should handle the exception here HttpResponse response = HttpContext.Current.Response; CrashReport report = new CrashReport(ex); // Save the crash report in the current cache so it is accessible to my custom error pages. The key used to // index the cache includes the session ID, so it is session-safe. A session variable set here does not seem // to be available in the error page's session state - although the session ID is available there. if (HttpContext.Current.Cache != null) { HttpContext.Current.Cache[Settings.Names.CrashReportKey] = report; } // Save the crash report on the file system String path = SaveCrashReport(report, request, null); // Send the crash report to the programmers try { SendEmail(report, path); } catch (Exception sendEmailException) { SaveCrashReport(new CrashReport(sendEmailException), request, "SendEmail"); } // Write the crash report to the browser if there is no replacement defined for the HTTP response if (!ReplaceResponse) { HttpContext.Current.Server.ClearError(); if (!String.IsNullOrEmpty(Settings.CrashReportUrl)) { HttpContext.Current.Server.Transfer(Settings.CrashReportUrl); } else { try { response.Clear(); response.StatusCode = 500; response.StatusDescription = "Server Error"; response.TrySkipIisCustomErrors = true; response.Write(report.Body); response.End(); } catch { } } } }