/// <summary> /// Recursively logs exception and any children. /// </summary> /// <param name="ex">The <see cref="System.Exception"/> to log.</param> /// <param name="log">The parent <see cref="Rock.Model.ExceptionLog"/> of the exception being logged. This value is nullable.</param> /// <param name="isParent">A <see cref="System.Boolean"/> flag indicating if this Exception is a parent exception. This value is /// <c>true</c> if the exception that is being logged is a parent exception, otherwise <c>false</c>. /// </param> private static void LogExceptions(Exception ex, ExceptionLog log, bool isParent) { // First, attempt to log exception to the database. try { ExceptionLog exceptionLog; // If this is a recursive call and not the originating exception being logged, // attempt to clone the initial one, and populate it with Exception Type and Message // from the inner exception, while retaining the contextual information from where // the exception originated. if (!isParent) { exceptionLog = log.Clone(false); if (exceptionLog != null) { // Populate with inner exception type, message and update whether or not there is another inner exception. exceptionLog.ExceptionType = ex.GetType().ToString(); exceptionLog.Description = ex.Message; exceptionLog.Source = ex.Source; exceptionLog.StackTrace = ex.StackTrace; exceptionLog.HasInnerException = ex.InnerException != null; // Ensure EF properly recognizes this as a new record. exceptionLog.Id = 0; exceptionLog.Guid = Guid.NewGuid(); exceptionLog.ParentId = log.Id; } } else { exceptionLog = log; } // The only reason this should happen is if the `log.Clone()` operation failed. Compiler sugar. if (exceptionLog == null) { return; } // Write ExceptionLog record to database. var rockContext = new Rock.Data.RockContext(); var exceptionLogService = new ExceptionLogService(rockContext); exceptionLogService.Add(exceptionLog); rockContext.SaveChanges(); // Recurse if inner exception is found if (exceptionLog.HasInnerException.GetValueOrDefault(false)) { LogExceptions(ex.InnerException, exceptionLog, false); } if (ex is AggregateException) { // if an AggregateException occurs, log the exceptions individually var aggregateException = (ex as AggregateException); foreach (var innerException in aggregateException.InnerExceptions) { LogExceptions(innerException, exceptionLog, false); } } } catch (Exception) { // If logging the exception fails, write the exceptions to a file try { string directory = AppDomain.CurrentDomain.BaseDirectory; directory = Path.Combine(directory, "App_Data", "Logs"); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } string filePath = Path.Combine(directory, "RockExceptions.csv"); string when = RockDateTime.Now.ToString(); while (ex != null) { File.AppendAllText(filePath, string.Format("{0},{1},\"{2}\"\r\n", when, ex.GetType(), ex.Message)); ex = ex.InnerException; } } catch { // failed to write to database and also failed to write to log file, so there is nowhere to log this error } } }
/// <summary> /// Populates the <see cref="Rock.Model.ExceptionLog" /> entity with the exception data. /// </summary> /// <param name="ex">The <see cref="System.Exception" /> to log.</param> /// <param name="context">The <see cref="System.Web.HttpContext" />.</param> /// <param name="pageId">An <see cref="System.Int32" /> containing the Id of the <see cref="Rock.Model.Page" /> where the exception occurred. /// This value is nullable.</param> /// <param name="siteId">An <see cref="System.Int32" /> containing the Id the <see cref="Rock.Model.Site" /> where the exception occurred. /// This value is nullable.</param> /// <param name="personAlias">The person alias.</param> /// <returns></returns> private static ExceptionLog PopulateExceptionLog(Exception ex, HttpContext context, int?pageId, int?siteId, PersonAlias personAlias) { int?personAliasId = null; if (personAlias != null) { personAliasId = personAlias.Id; } string exceptionMessage = ex.Message; if (ex is System.Data.SqlClient.SqlException) { var sqlEx = ex as System.Data.SqlClient.SqlException; var sqlErrorList = sqlEx.Errors.OfType <System.Data.SqlClient.SqlError>().ToList().Select(a => string.Format("{0}: Line {1}", a.Procedure, a.LineNumber)); if (sqlErrorList.Any()) { exceptionMessage += string.Format("[{0}]", sqlErrorList.ToList().AsDelimited(", ")); } } var exceptionLog = new ExceptionLog { SiteId = siteId, PageId = pageId, HasInnerException = ex.InnerException != null, ExceptionType = ex.GetType().ToString(), Description = exceptionMessage, Source = ex.Source, StackTrace = ex.StackTrace, Guid = Guid.NewGuid(), CreatedByPersonAliasId = personAliasId, ModifiedByPersonAliasId = personAliasId, CreatedDateTime = RockDateTime.Now, ModifiedDateTime = RockDateTime.Now, ModifiedAuditValuesAlreadyUpdated = true }; if (exceptionLog.StackTrace == null) { try { // if the Exception didn't include a StackTrace, manually grab it var stackTrace = new System.Diagnostics.StackTrace(2); exceptionLog.StackTrace = stackTrace.ToString(); } catch { // ignore } } try { ex.Data.Add("ExceptionLogGuid", exceptionLog.Guid); } catch { // ignore } try { // If current HttpContext is null, return early. if (context == null) { return(exceptionLog); } // If current HttpContext is available, populate its information as well. var request = context.Request; StringBuilder cookies = new StringBuilder(); var cookieList = request.Cookies; if (cookieList.Count > 0) { cookies.Append("<table class=\"cookies exception-table\">"); foreach (string cookie in cookieList) { var httpCookie = cookieList[cookie]; if (httpCookie != null) { cookies.Append("<tr><td><b>" + cookie + "</b></td><td>" + httpCookie.Value.EncodeHtml() + "</td></tr>"); } } cookies.Append("</table>"); } StringBuilder formItems = new StringBuilder(); var formList = request.Form; if (formList.Count > 0) { formItems.Append("<table class=\"form-items exception-table\">"); foreach (string formItem in formList) { formItems.Append("<tr><td><b>" + formItem + "</b></td><td>" + formList[formItem].EncodeHtml() + "</td></tr>"); } formItems.Append("</table>"); } StringBuilder serverVars = new StringBuilder(); var serverVarList = request.ServerVariables; if (serverVarList.Count > 0) { serverVars.Append("<table class=\"server-variables exception-table\">"); foreach (string serverVar in serverVarList) { serverVars.Append("<tr><td><b>" + serverVar + "</b></td><td>" + serverVarList[serverVar].EncodeHtml() + "</td></tr>"); } serverVars.Append("</table>"); } exceptionLog.Cookies = cookies.ToString(); exceptionLog.StatusCode = context.Response.StatusCode.ToString(); exceptionLog.PageUrl = request.Url.ToString(); exceptionLog.ServerVariables = serverVars.ToString(); exceptionLog.QueryString = request.Url.Query; exceptionLog.Form = formItems.ToString(); } catch { // Intentionally do nothing } return(exceptionLog); }
/// <summary> /// To the dto. /// </summary> /// <param name="value">The value.</param> /// <returns></returns> public static ExceptionLogDto ToDto(this ExceptionLog value) { return(new ExceptionLogDto(value)); }
/// <summary> /// Instantiates a new DTO object from the entity /// </summary> /// <param name="exceptionLog"></param> public ExceptionLogDto(ExceptionLog exceptionLog) { CopyFromModel(exceptionLog); }
/// <summary> /// Populates the <see cref="Rock.Model.ExceptionLog" /> entity with the exception data. /// </summary> /// <param name="ex">The <see cref="System.Exception" /> to log.</param> /// <param name="context">The <see cref="System.Web.HttpContext" />.</param> /// <param name="pageId">An <see cref="System.Int32" /> containing the Id of the <see cref="Rock.Model.Page" /> where the exception occurred. /// This value is nullable.</param> /// <param name="siteId">An <see cref="System.Int32" /> containing the Id the <see cref="Rock.Model.Site" /> where the exception occurred. /// This value is nullable.</param> /// <param name="personAlias">The person alias.</param> /// <returns></returns> private static ExceptionLog PopulateExceptionLog(Exception ex, HttpContext context, int?pageId, int?siteId, PersonAlias personAlias) { int?personAliasId = null; if (personAlias != null) { personAliasId = personAlias.Id; } string exceptionMessage = ex.Message; if (ex is System.Data.SqlClient.SqlException) { var sqlEx = ex as System.Data.SqlClient.SqlException; var sqlErrorList = sqlEx.Errors.OfType <System.Data.SqlClient.SqlError>().ToList().Select(a => string.Format("{0}: Line {1}", a.Procedure, a.LineNumber)); if (sqlErrorList.Any()) { exceptionMessage += string.Format("[{0}]", sqlErrorList.ToList().AsDelimited(", ")); } } var exceptionLog = new ExceptionLog { SiteId = siteId, PageId = pageId, HasInnerException = ex.InnerException != null, ExceptionType = ex.GetType().ToString(), Description = exceptionMessage, Source = ex.Source, StackTrace = ex.StackTrace, Guid = Guid.NewGuid(), CreatedByPersonAliasId = personAliasId, ModifiedByPersonAliasId = personAliasId, CreatedDateTime = RockDateTime.Now, ModifiedDateTime = RockDateTime.Now, ModifiedAuditValuesAlreadyUpdated = true }; if (exceptionLog.StackTrace == null) { try { // if the Exception didn't include a StackTrace, manually grab it var stackTrace = new System.Diagnostics.StackTrace(2); exceptionLog.StackTrace = stackTrace.ToString(); } catch { } } try { ex.Data.Add("ExceptionLogGuid", exceptionLog.Guid); } catch { } try { // If current HttpContext is null, return early. if (context == null) { return(exceptionLog); } // If current HttpContext is available, populate its information as well. var request = context.Request; StringBuilder cookies = new StringBuilder(); var cookieList = request.Cookies; if (cookieList.Count > 0) { cookies.Append("<table class=\"cookies exception-table\">"); foreach (string cookie in cookieList) { var httpCookie = cookieList[cookie]; if (httpCookie != null) { cookies.Append("<tr><td><b>" + cookie + "</b></td><td>" + httpCookie.Value.EncodeHtml() + "</td></tr>"); } } cookies.Append("</table>"); } StringBuilder serverVars = new StringBuilder(); // 'serverVarList[serverVar]' throws an exception if the value is empty, even if the key exists, // so make a copy of the request server variables to help avoid that error var serverVarList = new NameValueCollection(request.ServerVariables); var serverVarListString = serverVarList.ToString(); var serverVarKeys = request.ServerVariables.AllKeys; if (serverVarList.Count > 0) { serverVars.Append("<table class=\"server-variables exception-table\">"); foreach (string serverVar in serverVarList) { string val = string.Empty; try { val = serverVarList[serverVar].ToStringSafe().EncodeHtml(); } catch { // ignore } serverVars.Append($"<tr><td><b>{serverVar}</b></td><td>{val}</td></tr>"); } serverVars.Append("</table>"); } exceptionLog.Cookies = cookies.ToString(); exceptionLog.StatusCode = context.Response.StatusCode.ToString(); exceptionLog.PageUrl = request.UrlProxySafe().ToString(); exceptionLog.ServerVariables = serverVars.ToString(); exceptionLog.QueryString = request.UrlProxySafe().Query; /* * SK - 11/24/2021 * We are commenting out below line as we have decided not to store form data from now on as it may contain sensative data. * exceptionLog.Form = formItems.ToString(); */ } catch { } return(exceptionLog); }
/// <summary> /// Populates the <see cref="Rock.Model.ExceptionLog" /> entity with the exception data. /// </summary> /// <param name="ex">The <see cref="System.Exception" /> to log.</param> /// <param name="request">The <see cref="T:System.Net.HttpRequestMessage" />.</param> /// <param name="personAlias">The person alias.</param> /// <returns></returns> private static ExceptionLog PopulateExceptionLog(Exception ex, HttpRequestMessage request, PersonAlias personAlias) { string exceptionMessage = ex.Message; if (ex is System.Data.SqlClient.SqlException) { var sqlEx = ex as System.Data.SqlClient.SqlException; var sqlErrorList = sqlEx.Errors.OfType <System.Data.SqlClient.SqlError>().ToList().Select(a => string.Format("{0}: Line {1}", a.Procedure, a.LineNumber)); if (sqlErrorList.Any()) { exceptionMessage += string.Format("[{0}]", sqlErrorList.ToList().AsDelimited(", ")); } } var exceptionLog = new ExceptionLog { HasInnerException = ex.InnerException != null, ExceptionType = ex.GetType().ToString(), Description = exceptionMessage, Source = ex.Source, StackTrace = ex.StackTrace, Guid = Guid.NewGuid(), CreatedByPersonAliasId = personAlias?.Id, ModifiedByPersonAliasId = personAlias?.Id, CreatedDateTime = RockDateTime.Now, ModifiedDateTime = RockDateTime.Now, ModifiedAuditValuesAlreadyUpdated = true }; if (exceptionLog.StackTrace == null) { try { // if the Exception didn't include a StackTrace, manually grab it var stackTrace = new System.Diagnostics.StackTrace(2); exceptionLog.StackTrace = stackTrace.ToString(); } catch { } } try { ex.Data.Add("ExceptionLogGuid", exceptionLog.Guid); } catch { } try { // If current HttpRequestMessage is null, return early. if (request == null) { return(exceptionLog); } StringBuilder cookies = new StringBuilder(); var cookieList = request.Headers.GetCookies(); if (cookieList.Count > 0) { cookies.Append("<table class=\"cookies exception-table\">"); foreach (var cookieHeaderValue in cookieList) { foreach (var cookie in cookieHeaderValue.Cookies) { cookies.Append("<tr><td><b>" + cookie.Name + "</b></td><td>" + cookie.Value.EncodeHtml() + "</td></tr>"); } } cookies.Append("</table>"); } // // Check query string parameters for sensitive data. // string queryString = null; var queryCollection = request.RequestUri.ParseQueryString(); if (queryCollection.Count > 0) { var nvc = new NameValueCollection(); foreach (string qKey in queryCollection.Keys) { if (IsKeySensitive(qKey.ToLower())) { nvc.Add(qKey, "obfuscated"); } else { nvc.Add(qKey, queryCollection[qKey]); } } queryString = "?" + string.Join("&", nvc.AllKeys.Select(a => a.UrlEncode() + "=" + nvc[a].UrlEncode())); } exceptionLog.Cookies = cookies.ToString(); exceptionLog.PageUrl = request.RequestUri.GetLeftPart(UriPartial.Path); exceptionLog.QueryString = queryString; } catch { } return(exceptionLog); }