package com.yirendai.infra.cicada.task; import com.yirendai.infra.cicada.entity.SpanStatisInfo; import com.yirendai.infra.cicada.entity.model.SpanModel; import org.joda.time.DateTime; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; /** * 用于统计trace和span的相关信息,并存储数据库. * @author Zecheng */ public class LogStatistician { private static final double RATE_95 = 0.95; private static final double RATE_999 = 0.999; private static final String KEY_SEPERATE = "_"; private final DateTime statisTime; private final List<SpanStatisInfo> spanStatisInfos; private final Map<String, List<SpanModel>> spanMap; public LogStatistician(final DateTime statisTime) { this.statisTime = statisTime; this.spanStatisInfos = new LinkedList<SpanStatisInfo>(); this.spanMap = new HashMap<String, List<SpanModel>>(); } public List<SpanStatisInfo> getSpanStatisInfos() { return spanStatisInfos; } public void statistic(final List<SpanModel> models) { for (final SpanModel spanModel : models) { addSpan(spanModel); } for (final Map.Entry<String, List<SpanModel>> entry : spanMap.entrySet()) { buildSpanStatisInfo(entry.getKey(), entry.getValue()); } } private void buildSpanStatisInfo(final String key, final List<SpanModel> spans) { Collections.sort(spans, new Comparator<SpanModel>() { public int compare(final SpanModel span1, final SpanModel span2) { return span1.getDurationServer() - span2.getDurationServer(); } }); final String[] ids = key.split("_"); final SpanStatisInfo info = new SpanStatisInfo(); info.setStatisTime(statisTime); info.setAppId(Integer.parseInt(ids[0])); info.setServiceId(Integer.parseInt(ids[1])); info.setMethodId(Integer.parseInt(ids[2])); info.setCount(spans.size()); info.setMinDuration(spans.get(0).getDurationServer()); info.setMaxDuration(spans.get(spans.size() - 1).getDurationServer()); int line95Pos = (int) (spans.size() * RATE_95) - 1; if (line95Pos < 0) { line95Pos = 0; } info.setLine95Duration(spans.get(line95Pos).getDurationServer()); int line999Pos = (int) (spans.size() * RATE_999) - 1; if (line999Pos < 0) { line999Pos = 0; } info.setLine999Duration(spans.get(line999Pos).getDurationServer()); traverseSpanStatistic(spans, info); spanStatisInfos.add(info); } private void traverseSpanStatistic(final List<SpanModel> spans, final SpanStatisInfo info) { if (spans.isEmpty()) { return; } double avg = 0.0; int index = 1; int exceptionCount = 0; for (final SpanModel span : spans) { avg = (index - 1) / (double) index * avg + (double) span.getDurationServer() / index; ++index; if (span.isHasException()) { ++exceptionCount; } } final double failureRate = (double) exceptionCount / (index - 1); info.setAvgDuration(avg); info.setFailureRate(failureRate); } private void addSpan(final SpanModel span) { final StringBuilder sb = new StringBuilder(); sb.append(span.getAppId()).append(KEY_SEPERATE) // .append(span.getServiceId()).append(KEY_SEPERATE) // .append(span.getMethodId()); final String key = sb.toString(); List<SpanModel> spans = spanMap.get(key); if (spans == null) { spans = new LinkedList<SpanModel>(); spanMap.put(key, spans); } spans.add(span); } }