private Dictionary <string, object> TraceSummaryToMustache(string serviceName, IList <TraceSummary> ts) { var maxDuration = ts.Max(t => t.Duration / 1000); var traces = ts.Select(t => { var duration = t.Duration / 1000; var groupedSpanTimestamps = t.SpanTimestamps.GroupBy(s => s.Name); var serviceDurations = groupedSpanTimestamps.Select(g => new MustacheServiceDuration() { name = g.Key, count = g.Count(), max = g.Max(st => st.Duration / 1000) }).ToList(); long?serviceTime = null; if (!string.IsNullOrEmpty(serviceName)) { var timestamps = groupedSpanTimestamps.FirstOrDefault(g => g.Key == serviceName); if (timestamps != null) { serviceTime = TotalServiceTime(timestamps); } } return(new MustacheTraceSummary() { traceId = t.TraceId, startTs = Util.FromUnixTimeMilliseconds(t.Timestamp / 1000).ToString(), timestamp = t.Timestamp, duration = duration, durationStr = WebUtil.FormatDurtion(t.Duration), servicePercentage = serviceTime.HasValue ? (int)(100 * (float)serviceTime.Value / (float)t.Duration) : 0, //spanCount = groupedSpanTimestamps.Aggregate(0, (acc, sts) => acc + sts.Count()), spanCount = t.SpanTimestamps.Count(), serviceDurations = serviceDurations, width = (int)(100 * (float)duration / (float)maxDuration) }); }).OrderBy(t => t.duration).Reverse(); return(new Dictionary <string, object>() { { "traces", traces }, { "count", traces.Count() } }); }
public ActionResult Index(string id) { var traceId = Util.HexToLong(id); var client = new RestClient(Zipkin.UI.Web.Helpers.WebAppSettings.QueryHost); var response = client.Execute <List <JsonSpan> >(new RestRequest(string.Format("/api/v1/trace/{0}", traceId))); if (response.StatusCode != System.Net.HttpStatusCode.OK || response.Data == null || response.Data.Count == 0) { return(HttpNotFound()); } var trace = response.Data.Select(js => js.Invert()).ToList(); var traceTimestamp = trace.First().timestamp ?? 0L; var traceDuration = Trace.Duration(trace) ?? 0L; var spanDepths = TraceSummary.ToSpanDepths(trace); var spanMap = WebUtil.GetIdToSpanMap(trace); var rootSpans = WebUtil.GetRootSpans(trace); var spans = new List <Dictionary <string, object> >(); foreach (var rootSpan in rootSpans) { foreach (var span in SpanNode.Create(rootSpan, trace).ToSpans()) { var spanStartTs = span.timestamp ?? traceTimestamp; int depth; if (!spanDepths.TryGetValue(span.id, out depth)) { depth = 1; } var width = span.duration.HasValue ? 100 * (double)span.duration.Value / (double)traceDuration : 0.0; var binaryAnnotations = new List <JsonBinaryAnnotation>(); span.binaryAnnotations.ToList().ForEach(ba => { if (Constants.CoreAddress.Contains(ba.key)) { if (ba.endpoint != null) { binaryAnnotations.Add(ToHostAndPort(Constants.CoreAnnotationNames[ba.key], ba.endpoint)); } } else { var jsonAnno = new JsonBinaryAnnotation(ba); if (Constants.CoreAnnotationNames.ContainsKey(ba.key)) { jsonAnno.key = Constants.CoreAnnotationNames[ba.key]; } binaryAnnotations.Add(jsonAnno); } }); span.binaryAnnotations.Where(ba => ba.key == Constants.LocalComponent).ToList().ForEach(ba => { if (ba.endpoint != null) { binaryAnnotations.Add(ToHostAndPort("Local Address", ba.endpoint)); } }); spans.Add(new Dictionary <string, object>() { { "spanId", Util.LongToHex(span.id) }, { "parentId", span.parentId.HasValue && spanMap.ContainsKey(span.parentId.Value) ? Util.LongToHex(span.parentId.Value) : string.Empty }, { "spanName", span.name }, { "serviceNames", string.Join(",", span.ServiceNames) }, { "serviceName", span.ServiceName }, { "duration", span.duration }, { "durationStr", WebUtil.FormatDurtion(span.duration ?? 0) }, { "left", ((float)(spanStartTs - traceTimestamp) / (float)traceDuration) * 100 }, { "width", width < 0.1 ? 0.1 : width }, { "depth", (depth + 1) * 5 }, { "depthClass", (depth - 1) % 6 }, { "children", string.Join(",", trace.Where(s => s.parentId == span.id).Select(s => Util.LongToHex(s.id)).ToArray()) }, { "annotations", span.annotations.Select(a => new Dictionary <string, object>() { { "isCore", Constants.CoreAnnotations.Contains(a.value) }, { "left", span.duration.HasValue ? 100 * (float)(a.timestamp - spanStartTs) / (float)span.duration.Value : 0 }, { "endpoint", a.endpoint == null ? string.Empty : string.Format("{0}:{1}", a.endpoint.GetHostAddress(), a.endpoint.GetUnsignedPort()) }, { "value", WebUtil.GetAnnotationName(a.value) }, { "timestamp", a.timestamp }, { "relativeTime", WebUtil.FormatDurtion(a.timestamp - traceTimestamp) }, { "serviceName", a.endpoint == null ? string.Empty : a.endpoint.serviceName }, { "width", 8 } }).ToList() }, { "binaryAnnotations", binaryAnnotations } }); } } var serviceDurations = new List <MustacheServiceDuration>(); var summary = TraceSummary.Create(trace); if (summary != null) { serviceDurations = summary.SpanTimestamps.GroupBy(sts => sts.Name).Select(g => new MustacheServiceDuration() { name = g.Key, count = g.Count(), max = g.Max(st => st.Duration / 1000) }).ToList(); } var i = 0; var timeMarkers = new double[] { 0.0, 0.2, 0.4, 0.6, 0.8, 1.0 } .Select(p => new Dictionary <string, string>() { { "index", (i++).ToString() }, { "time", WebUtil.FormatDurtion((long)(traceDuration * p)) } }).ToList(); var timeMarkersBackup = timeMarkers.Select(m => m).ToList(); var spansBackup = spans.Select(s => s).ToList(); ViewBag.duration = WebUtil.FormatDurtion(traceDuration); ViewBag.services = serviceDurations.Count; ViewBag.depth = spanDepths.Values.Max(); ViewBag.totalSpans = spans.Count; ViewBag.serviceCounts = serviceDurations.OrderBy(sd => sd.name); ViewBag.timeMarkers = timeMarkers; ViewBag.timeMarkersBackup = timeMarkersBackup; ViewBag.spans = spans; ViewBag.spansBackup = spansBackup; return(View()); }