/// <summary> /// Renders the main contents of the page. /// </summary> /// <param name="sb">The <see cref="StringBuilder"/> to render to.</param> protected override void RenderInnerHtml(StringBuilder sb) { void RenderVariableTable(string title, string className, NameValueCollection vars) { if (vars == null || vars.Count == 0) { return; } var fetchError = vars[Constants.CollectionErrorKey]; var errored = fetchError.HasValue(); var keys = vars.AllKeys.Where(key => !HiddenHttpKeys.Contains(key) && key != Constants.CollectionErrorKey).OrderBy(k => k); sb.AppendFormat(" <div class=\"{0}\">", className).AppendLine(); if (errored) { sb.AppendFormat(" <h3 class=\"title-error\">{0} - Error while gathering data</h3>", title).AppendLine(); } else { sb.AppendFormat(" <h3>{0}</h3>", title).AppendLine(); } if (keys.Any()) { var hiddenRows = new StringBuilder(); sb.AppendLine(" <div class=\"side-scroll\">") .AppendLine(" <table class=\"alt-rows key-value\">") .AppendLine(" <tbody>"); foreach (var k in keys) { // If this has no value, skip it if (vars[k].HasValue()) { // If this is a hidden row, buffer it up, since CSS has no clean mechanism for :visible:nth-row(odd) type styling behavior (DefaultHttpKeys.Contains(k) ? hiddenRows : sb).AppendFormat(" <tr><td>{0}</td><td>{1}</td></tr>", k, Linkify(vars[k])).AppendLine(); } } if (vars["HTTP_HOST"].HasValue() && vars["URL"].HasValue()) { var ssl = vars["HTTP_X_FORWARDED_PROTO"] == "https" || vars["HTTP_X_SSL"].HasValue() || vars["HTTPS"] == "on"; var url = string.Format("http{3}://{0}{1}{2}", vars["HTTP_HOST"], vars["URL"], vars["QUERY_STRING"].HasValue() ? "?" + vars["QUERY_STRING"] : "", ssl ? "s" : ""); sb.AppendFormat(" <tr><td>URL and Query</td><td>{0}</td></tr>", vars["REQUEST_METHOD"] == "GET" ? Linkify(url) : url.HtmlEncode()).AppendLine(); } sb.AppendLine(" </tbody>"); if (hiddenRows.Length > 0) { sb.AppendLine(" <tbody class=\"hidden\">") .Append(hiddenRows) .AppendLine(" </tbody>"); } sb.AppendLine(" </table>") .AppendLine(" </div>"); } if (errored) { sb.AppendFormat("<span class=\"custom-error-label\">Get {0} threw an exception:</span>", title) .AppendFormat("<pre class=\"stack\"><code>{0}</code></pre>", fetchError.HtmlEncode()); } sb.AppendFormat(" </div>"); } void RenderKeyValueTable(IEnumerable <KeyValuePair <string, string> > kvPairs) { if (kvPairs?.Any() == true) { sb.AppendLine(" <div class=\"side-scroll\">") .AppendLine(" <table class=\"alt-rows key-value\">"); foreach (var kv in kvPairs) { sb.Append(" <tr>") .Append("<td>").AppendHtmlEncode(kv.Key).Append("</td>") .Append("<td>").Append(Linkify(kv.Value)).Append("</td>") .AppendLine("</tr>"); } sb.AppendLine(" </table>") .AppendLine(" </div>"); } } if (Error == null) { sb.AppendFormat(" <h1 class=\"not-found\">Oh no! Error {0} was not found!</h1>", _guid.ToString()).AppendLine(); } else { sb.Append(" <h1>").AppendHtmlEncode(Error.Message).AppendLine("</h1>") .Append(" <div class=\"subtitle\">").AppendHtmlEncode(Error.Type); if (Error.DuplicateCount > 1) { sb.Append(" <span class=\"duplicate-count\">(thrown ").Append(Error.DuplicateCount.Value).AppendLine(" times)</span>"); } sb.AppendLine("</div>") .Append(" <pre class=\"stack dark\"><code class=\"nohighlight\">").Append(Utils.StackTrace.HtmlPrettify(Error.Detail)).AppendLine().AppendLine("</code></pre>") // TODO: Controls for show/hide of async .stack.row.async in the block above // TODO: Remove - temporarily showing the raw while the user-friendlier display above gets tuned //.Append(" <pre class=\"stack\"><code>").AppendHtmlEncode(Error.Detail).AppendLine().AppendLine("</code></pre>") .Append(" <p class=\"sub-info\">occurred <b title=\"") .AppendHtmlEncode(Error.CreationDate.ToLongDateString()).Append(" at ").AppendHtmlEncode(Error.CreationDate.ToLongTimeString()) .Append("\">") .Append(Error.CreationDate.ToRelativeTime()) .Append("</b> on ") .AppendHtmlEncode(Error.MachineName); if (ShowActionLinks) { sb.Append(" <span>(<a href=\"delete?guid=").Append(Error.GUID.ToString()).AppendLine("\">delete</a>)</span>"); } sb.Append("</p>"); if (Error.Commands != null) { foreach (var cmd in Error.Commands) { var lang = cmd.GetHighlightLanguage(); sb.Append(" <h3>Command: ").AppendHtmlEncode(cmd.Type).AppendLine("</h3>") .Append(" <pre class=\"command\"><code"); if (lang.HasValue()) { sb.Append(" class=\"").Append(lang).Append("\""); } sb.Append(">") .AppendHtmlEncode(cmd.CommandString) .AppendLine("</code></pre>"); RenderKeyValueTable(cmd.Data); } } RenderVariableTable("Server Variables", "server-variables", Error.ServerVariables); if (Error.CustomData?.Count > 0) { var errored = Error.CustomData.ContainsKey(Constants.CustomDataErrorKey); var cdKVs = Error.CustomData.Where(kv => kv.Key != Constants.CustomDataErrorKey); sb.AppendLine(" <div class=\"custom-data\">"); if (errored) { sb.AppendLine(" <h3 class=\"title-error\">Custom - Error while gathering custom data</h3>"); } else { sb.AppendLine(" <h3>Custom</h3>"); } if (cdKVs.Any()) { RenderKeyValueTable(cdKVs); } if (errored) { sb.AppendLine(" <span class=\"custom-error-label\">GetCustomData threw an exception:</span>") .AppendLine(" <pre class=\"stack\"><code>").Append(Error.CustomData[Constants.CustomDataErrorKey]).AppendLine("</code></pre>"); } sb.AppendLine(" </div>"); } RenderVariableTable("Querystring", "querystring", Error.QueryString); RenderVariableTable("Form", "form", Error.Form); RenderVariableTable("Cookies", "cookies", Error.Cookies); RenderVariableTable("Request Headers", "headers", Error.RequestHeaders); } }
protected override void RenderHtml(StringBuilder sb) { void RenderVariableTable(string title, NameValueCollection vars, bool renderUrls = false) { if (vars == null || vars.Count == 0) { return; } var fetchError = vars[Constants.CollectionErrorKey]; var errored = fetchError.HasValue(); var keys = vars.AllKeys.Where(key => !HiddenHttpKeys.Contains(key) && key != Constants.CollectionErrorKey).OrderBy(k => k); sb.AppendFormat(" <div>").AppendLine(); sb.AppendFormat(" <h3 style=\"color: #224C00; font-family: Verdana, Tahoma, Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 14px; margin: 10px 0 5px 0;\">{0}{1}</h3>", title, errored ? " - Error while gathering data" : "").AppendLine(); if (keys.Any()) { sb.AppendFormat(" <table style=\"font-family: Verdana, Tahoma, Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 12px; width: 100%; border-collapse: collapse; border: 0;\">").AppendLine(); var i = 0; string getBackground() => i++ % 2 == 1 ? " style=\"background-color: #F2F2F2;\"" : ""; foreach (var k in keys) { // If this has no value, skip it if (vars[k].IsNullOrEmpty() || DefaultHttpKeys.Contains(k)) { continue; } sb.AppendFormat(" <tr{2}><td style=\"padding: 0.4em; width: 200px;\">{0}</td><td style=\"padding: 0.4em;\">{1}</td></tr>", k, Linkify(vars[k]), getBackground()).AppendLine(); } if (renderUrls && vars["Request Method"].IsNullOrEmpty()) // told to render and we don't have them elsewhere { var method = error.HTTPMethod; if (method.HasValue()) { sb.AppendFormat(" <tr{1}><td style=\"padding: 0.4em; width: 200px;\">Method</td><td style=\"padding: 0.4em;\">{0}</td></tr>", method, getBackground()).AppendLine(); var fullUrl = error.GetFullUrl(); if (fullUrl.HasValue()) { sb.AppendFormat(" <tr{1}><td style=\"padding: 0.4em; width: 200px;\">URL and Query</td><td style=\"padding: 0.4em;\">{0}</td></tr>", method == "GET" ? Linkify(fullUrl) : fullUrl.HtmlEncode(), getBackground()).AppendLine(); } } } sb.AppendFormat(" </table>").AppendLine(); } if (errored) { sb.AppendFormat(" <span style=\"color: maroon;\">Get {0} threw an exception:</span>", title).AppendLine(); sb.AppendFormat(" <pre style=\"background-color: #EEE; font-family: Consolas, Monaco, monospace; padding: 8px;\">{0}</pre>", fetchError.HtmlEncode()).AppendLine(); } sb.AppendFormat(" </div>").AppendLine(); } sb.AppendLine("<div style=\"font-family: Arial, \'Helvetica Neue\', Helvetica, sans-serif;\">"); if (error == null) { sb.AppendLine(" <h1 style=\"color: maroon; font-size: 16px;\">Error not found.</h1>"); } else { sb.Append(" <h1 style=\"color: maroon; font-size: 16px; padding: 0; margin: 0;\">") .AppendHtmlEncode(error.Message) .Append("</h1>").AppendLine() .Append(" <div style=\"font-size: 12px; color: #444; padding: 0; margin: 2px 0;\">") .AppendHtmlEncode(error.Type) .Append("</div>").AppendLine() .Append(" <pre style=\"background-color: #FFFFCC; font-family: Consolas, Monaco, monospace; font-size: 12px; margin: 2px 0; padding: 12px;\">") .AppendHtmlEncode(error.Detail).AppendLine() .Append(" </pre>").AppendLine() .Append(" <p class=\"error-time\" style=\"font-size: 13px; color: #555; margin: 5px 0;\">occurred at <b title=\"") .AppendHtmlEncode(error.CreationDate.ToLongDateString()) .Append(" at ") .AppendHtmlEncode(error.CreationDate.ToLongTimeString()) .Append("\">") .AppendHtmlEncode(error.CreationDate.ToUniversalTime().ToString()) .Append(" UTC</b> on ") .AppendHtmlEncode(error.MachineName) .Append("</p>") .AppendLine(); // TODO: Commands //if (!string.IsNullOrEmpty(error.SQL)) //{ // sb.Append(" <h3 style=\"color: #224C00; font-family: Verdana, Tahoma, Arial, \'Helvetica Neue\', Helvetica, sans-serif; font-size: 14px; margin: 10px 0 5px 0;\">SQL</h3>") // .AppendLine() // .Append(" <pre style=\"background-color: #EEE; font-family: Consolas, Monaco, monospace; padding: 8px 8px 8px 8px; margin: 2px 0;\">") // .AppendHtmlEncode(error.SQL) // .Append("</pre>").AppendLine() // .Append("<br/>").AppendLine(); //} RenderVariableTable("Server Variables", error.ServerVariables, renderUrls: true); if (error.CustomData?.Count > 0) { var errored = error.CustomData.ContainsKey(Constants.CustomDataErrorKey); var cdKeys = error.CustomData.Keys.Where(k => k != Constants.CustomDataErrorKey); sb.AppendLine(" <div class=\"custom-data\">"); if (errored) { sb.AppendLine(" <h3 style=\"color: maroon; font-family: Verdana, Tahoma, Arial, \'Helvetica Neue\', Helvetica, sans-serif; font-size: 14px; margin: 10px 0 5px 0;\">Custom - Error while gathering custom data</h3>"); } else { sb.AppendLine(" <h3 style=\"color: #224C00; font-family: Verdana, Tahoma, Arial, \'Helvetica Neue\', Helvetica, sans-serif; font-size: 14px; margin: 10px 0 5px 0;\">Custom</h3>\r\n"); } if (cdKeys.Any(k => k != Constants.CustomDataErrorKey)) { var i = -1; sb.AppendLine(" <table style=\"font-family: Verdana, Tahoma, Arial, \'Helvetica Neue\', Helvetica, sans-serif; font-size: 12px; width: 100%; border-collapse: collapse; border: 0;\">\r\n"); foreach (var cd in cdKeys) { i++; sb.Append(" <tr"); if (i % 2 == 0) { sb.Append(" style=\"background-color: #F2F2F2;\""); } sb.AppendLine(">") .AppendLine(" <td style=\"padding: 0.4em; width: 200px;\">") .AppendHtmlEncode(cd) .AppendLine("</td>") .Append(" <td style=\"padding: 0.4em;\">") .Append(Linkify(error.CustomData[cd])) .AppendLine("</td>") .AppendLine(" </tr>"); } sb.AppendLine(" </table>"); } if (errored) { sb.AppendLine(" <span style=\"color: maroon;\">GetCustomData threw an exception:</span>") .Append(" <pre style=\"background-color: #EEE; font-family: Consolas, Monaco, monospace; padding: 8px;\">") .AppendHtmlEncode(error.CustomData[Constants.CustomDataErrorKey]) .AppendLine("</pre>"); } sb.AppendLine(" </div>"); } RenderVariableTable("QueryString", error.QueryString); RenderVariableTable("Form", error.Form); RenderVariableTable("Cookies", error.Cookies); } sb.AppendLine("</div>"); }