private async Task ImportSessionsFromUrl(string importUrl) { IEnumerable <ITimingSession> sessions = null; using (var httpClient = new HttpClient()) { var response = await httpClient.GetAsync(importUrl); if (response.StatusCode == HttpStatusCode.OK) { var content = await response.Content.ReadAsStringAsync(); sessions = ImportSerializer.DeserializeSessions(content); } } if (sessions == null) { return; } if (ProfilingSession.CircularBuffer == null) { return; } var existingIds = ProfilingSession.CircularBuffer.Select(session => session.Id).ToList(); foreach (var session in sessions) { if (!existingIds.Contains(session.Id)) { ProfilingSession.CircularBuffer.Add(session); } } }
public async Task Invoke(HttpContext context) { // disable view profiling if CircularBuffer is not enabled if (ProfilingSession.CircularBuffer == null) { await _next.Invoke(context); return; } ClearIfCurrentProfilingSessionStopped(); var url = UriHelper.GetDisplayUrl(context.Request); ProfilingSession.Start(url); // set correlationId if exists in header var correlationId = GetCorrelationIdFromHeaders(context); if (!string.IsNullOrWhiteSpace(correlationId)) { ProfilingSession.Current.AddField("correlationId", correlationId); } // only supports GET method for view results if (context.Request.Method != "GET") { await _next.Invoke(context); return; } var path = context.Request.Path.ToString().TrimEnd('/'); // generate baseViewPath string baseViewPath = null; var posStart = path.IndexOf(ViewUrl, StringComparison.OrdinalIgnoreCase); if (posStart < 0) { posStart = path.IndexOf(ViewUrlNano, StringComparison.OrdinalIgnoreCase); } if (posStart >= 0) { baseViewPath = path.Substring(0, posStart) + ViewUrl; } if (path.EndsWith("/coreprofiler-resources/icons")) { context.Response.ContentType = "image/png"; var iconsStream = GetType().GetTypeInfo().Assembly.GetManifestResourceStream("CoreProfiler.Web.icons.png"); using (var br = new BinaryReader(iconsStream)) { await context.Response.Body.WriteAsync(br.ReadBytes((int)iconsStream.Length), 0, (int)iconsStream.Length); } return; } if (path.EndsWith("/coreprofiler-resources/css")) { context.Response.ContentType = "text/css"; var cssStream = GetType().GetTypeInfo().Assembly.GetManifestResourceStream("CoreProfiler.Web.treeview_timeline.css"); using (var sr = new StreamReader(cssStream)) { await context.Response.WriteAsync(sr.ReadToEnd()); } return; } // view index of all latest results: ~/coreprofiler/view if (path.EndsWith(ViewUrl, StringComparison.OrdinalIgnoreCase) || path.EndsWith(ViewUrlNano, StringComparison.OrdinalIgnoreCase)) { // try to handle import/export first var import = context.Request.Query[Import]; if (Uri.IsWellFormedUriString(import, UriKind.Absolute)) { await ImportSessionsFromUrl(import); return; } if (context.Request.QueryString.ToString() == Export) { context.Response.ContentType = "application/json"; await context.Response.WriteAsync(ImportSerializer.SerializeSessions(ProfilingSession.CircularBuffer)); return; } var exportCorrelationId = context.Request.Query[CorrelationId]; if (!string.IsNullOrEmpty(exportCorrelationId)) { context.Response.ContentType = "application/json"; var result = ProfilingSession.CircularBuffer.FirstOrDefault( r => r.Data != null && r.Data.ContainsKey(CorrelationId) && r.Data[CorrelationId] == exportCorrelationId); if (result != null) { await context.Response.WriteAsync(ImportSerializer.SerializeSessions(new[] { result })); return; } } // render result list view context.Response.ContentType = "text/html"; var sb = new StringBuilder(); sb.Append("<head>"); sb.Append("<title>CoreProfiler Latest Profiling Results</title>"); sb.Append("<style>th { width: 200px; text-align: left; } .gray { background-color: #eee; } .nowrap { white-space: nowrap;padding-right: 20px; vertical-align:top; } </style>"); sb.Append("</head"); sb.Append("<body>"); sb.Append(ViewResultIndexHeaderHtml); var tagFilter = context.Request.Query["tag"]; if (!string.IsNullOrWhiteSpace(tagFilter)) { sb.Append("<div><strong>Filtered by tag:</strong> "); sb.Append(tagFilter); sb.Append("<br/><br /></div>"); } sb.Append("<table>"); sb.Append("<tr><th class=\"nowrap\">Time (UTC)</th><th class=\"nowrap\">Duration (ms)</th><th>Url</th></tr>"); var latestResults = ProfilingSession.CircularBuffer.OrderByDescending(r => r.Started); var i = 0; foreach (var result in latestResults) { if (!string.IsNullOrWhiteSpace(tagFilter) && (result.Tags == null || !result.Tags.Contains <string>(tagFilter, StringComparer.OrdinalIgnoreCase))) { continue; } sb.Append("<tr"); if ((i++) % 2 == 1) { sb.Append(" class=\"gray\""); } sb.Append("><td class=\"nowrap\">"); sb.Append(result.Started.ToString("yyyy-MM-ddTHH:mm:ss.FFF")); sb.Append("</td><td class=\"nowrap\">"); sb.Append(result.DurationMilliseconds); sb.Append("</td><td><a href=\""); sb.Append(baseViewPath); sb.Append("/"); sb.Append(result.Id.ToString()); sb.Append("\" target=\"_blank\">"); sb.Append(result.Name.Replace("\r\n", " ")); sb.Append("</a></td></tr>"); } sb.Append("</table>"); sb.Append("</body>"); await context.Response.WriteAsync(sb.ToString()); return; } // view specific result by uuid: ~/coreprofiler/view/{uuid} if (path.IndexOf(ViewUrl, StringComparison.OrdinalIgnoreCase) >= 0 || path.IndexOf(ViewUrlNano, StringComparison.OrdinalIgnoreCase) >= 0) { context.Response.ContentType = "text/html"; var sb = new StringBuilder(); sb.Append("<head>"); sb.Append("<meta charset=\"utf-8\" />"); sb.Append("<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />"); sb.Append("<title>CoreProfiler Profiling Result</title>"); sb.Append("<link rel=\"stylesheet\" href=\"./coreprofiler-resources/css\" />"); sb.Append("</head"); sb.Append("<body>"); sb.Append("<h1>CoreProfiler Profiling Result</h1>"); var uuid = path.Split('/').Last(); var result = ProfilingSession.CircularBuffer.FirstOrDefault( r => r.Id.ToString().ToLowerInvariant() == uuid.ToLowerInvariant()); if (result != null) { if (TryToImportDrillDownResult) { // try to import drill down results foreach (var timing in result.Timings) { if (timing.Data == null || !timing.Data.ContainsKey(CorrelationId)) { continue; } Guid parentResultId; if (!Guid.TryParse(timing.Data[CorrelationId], out parentResultId) || ProfilingSession.CircularBuffer.Any(r => r.Id == parentResultId)) { continue; } string remoteAddress; if (!timing.Data.TryGetValue("remoteAddress", out remoteAddress)) { remoteAddress = timing.Name; } if (!Uri.IsWellFormedUriString(remoteAddress, UriKind.Absolute)) { continue; } if (!remoteAddress.StartsWith("http", StringComparison.OrdinalIgnoreCase)) { continue; } var pos = remoteAddress.IndexOf("?"); if (pos > 0) { remoteAddress = remoteAddress.Substring(0, pos); } if (remoteAddress.Split('/').Last().Contains(".")) { remoteAddress = remoteAddress.Substring(0, remoteAddress.LastIndexOf("/")); } try { await ImportSessionsFromUrl(remoteAddress + "/coreprofiler/view?" + CorrelationId + "=" + parentResultId.ToString("N")); } catch (Exception ex) { System.Diagnostics.Debug.Write(ex.Message); //ignore exceptions } } } // render result tree sb.Append("<div class=\"css-treeview\">"); // print summary sb.Append("<ul>"); sb.Append("<li class=\"summary\">"); PrintDrillUpLink(sb, result, baseViewPath); sb.Append(result.Name.Replace("\r\n", " ")); sb.Append("</li>"); sb.Append("<li class=\"summary\">"); if (result.Data != null) { foreach (var keyValue in result.Data) { if (string.IsNullOrWhiteSpace(keyValue.Value)) { continue; } sb.Append("<b>"); sb.Append(keyValue.Key); sb.Append(": </b>"); var encodedValue = WebUtility.HtmlEncode(keyValue.Value); if (keyValue.Key.EndsWith("Count") || keyValue.Key.EndsWith("Duration")) { sb.Append("<span class=\""); sb.Append(keyValue.Key); sb.Append("\">"); sb.Append(encodedValue); sb.Append("</span>"); } else { sb.Append(encodedValue); } sb.Append(" "); } } sb.Append("<b>machine: </b>"); sb.Append(result.MachineName); sb.Append(" "); if (result.Tags != null && result.Tags.Any()) { sb.Append("<b>tags: </b>"); sb.Append(string.Join(", ", result.Tags.Select(t => string.Format("<a href=\"{2}?tag={0}\">{1}</a>", HttpUtility.UrlEncode(t), t, baseViewPath)))); sb.Append(" "); } sb.Append("</li>"); sb.Append("</ul>"); var totalLength = result.DurationMilliseconds; if (totalLength == 0) { totalLength = 1; } var factor = 300.0 / totalLength; // print ruler sb.Append("<ul>"); sb.Append("<li class=\"ruler\"><span style=\"width:300px\">0</span><span style=\"width:80px\">"); sb.Append(totalLength); sb.Append( " (ms)</span><span style=\"width:20px\"> </span><span style=\"width:60px\">Start</span><span style=\"width:60px\">Duration</span><span style=\"width:20px\"> </span><span>Timing Hierarchy</span></li>"); sb.Append("</ul>"); // print timings sb.Append("<ul class=\"timing\">"); PrintTimings(result, result.Id, sb, factor, baseViewPath); sb.Append(""); sb.Append("</ul>"); sb.Append("</div>"); // print timing data popups foreach (var timing in result.Timings) { if (timing.Data == null || !timing.Data.Any()) { continue; } sb.Append("<aside id=\"data_"); sb.Append(timing.Id.ToString()); sb.Append("\" style=\"display:none\" class=\"modal\">"); sb.Append("<div>"); sb.Append("<h4><code>"); sb.Append(timing.Name.Replace("\r\n", " ")); sb.Append("</code></h4>"); sb.Append("<textarea>"); foreach (var keyValue in timing.Data) { if (string.IsNullOrWhiteSpace(keyValue.Value)) { continue; } sb.Append(keyValue.Key); sb.Append(":\r\n"); var value = keyValue.Value.Trim(); if (value.StartsWith("<")) { // asuume it is XML // try to format XML with indent var doc = new XmlDocument(); try { doc.LoadXml(value); var ms = new MemoryStream(); var xwSettings = new XmlWriterSettings { Encoding = new UTF8Encoding(false), Indent = true, IndentChars = "\t" }; using (var writer = XmlWriter.Create(ms, xwSettings)) { doc.Save(writer); ms.Seek(0, SeekOrigin.Begin); using (var sr = new StreamReader(ms)) { value = sr.ReadToEnd(); } } } catch { //squash exception } } sb.Append(value); sb.Append("\r\n\r\n"); } if (timing.Tags != null && timing.Tags.Any()) { sb.Append("tags:\r\n"); sb.Append(timing.Tags); sb.Append("\r\n"); } sb.Append("</textarea>"); sb.Append( "<a href=\"#close\" title=\"Close\" onclick=\"this.parentNode.parentNode.style.display='none'\">Close</a>"); sb.Append("</div>"); sb.Append("</aside>"); } } else { sb.Append("Specified result does not exist!"); } sb.Append("</body>"); await context.Response.WriteAsync(sb.ToString()); return; } try { await _next.Invoke(context); } catch (System.Exception) { // stop and save profiling results on error using (ProfilingSession.Current.Step("Stop on Error")) { } throw; } finally{ ProfilingSession.Stop(); } }