package org.weiboad.ragnar.server.controller.web;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TermQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.weiboad.ragnar.server.config.FieryConfig;
import org.weiboad.ragnar.server.data.MetaLog;
import org.weiboad.ragnar.server.data.ResponseJson;
import org.weiboad.ragnar.server.search.IndexService;
import org.weiboad.ragnar.server.storage.DBManage;
import org.weiboad.ragnar.server.storage.DBSharder;
import org.weiboad.ragnar.server.util.RPCIDKeySortComparator;
import org.weiboad.ragnar.server.util.TraceIdDecoder;
import java.text.DecimalFormat;
import java.util.*;
@Controller
public class ShowTrace {
Logger log = LoggerFactory.getLogger(ShowTrace.class);
@Autowired
DBManage dbmanager;
@Autowired
IndexService indexHelper;
@Autowired
FieryConfig fieryConfig;
private Map<String, Map<String, String>> _tracelist = new TreeMap<String, Map<String, String>>(new RPCIDKeySortComparator());
private int _renderIndex = 0;
@RequestMapping(value = "/showtrace", method = RequestMethod.GET)
public String ShowTracePage(Model model,
@RequestParam(value = "traceid", required = false) String traceid,
@RequestParam(value = "rpcid", required = false, defaultValue = "0") String selectedrpcid,
@RequestParam(value = "oldstyle", required = false, defaultValue = "0") String oldstylebool) {
DecimalFormat df = new DecimalFormat("######0.0000");
boolean oldstyle = false;
if (oldstylebool.trim().equals("1")) {
oldstyle = true;
model.addAttribute("oldstyle", 1);
} else {
model.addAttribute("oldstyle", 0);
}
//clean up the tracelist
_tracelist.clear();
//if render the trace
if (traceid != null && traceid.length() > 6) {
traceid = traceid.trim();
//set page parameter
model.addAttribute("selectedrpcid", selectedrpcid);
model.addAttribute("traceid", traceid);
//set the traceid info
Map<String, String> traceIdInfo = TraceIdDecoder.decodev10(traceid);
model.addAttribute("idc", traceIdInfo.get("idc"));
model.addAttribute("ip", traceIdInfo.get("ip"));
model.addAttribute("timestamp", traceIdInfo.get("timestamp"));
model.addAttribute("starttime", traceIdInfo.get("time"));
Date startdate = new Date(Long.parseLong(traceIdInfo.get("time")) * 1000L);
model.addAttribute("starttimedate", startdate);
//rpcid and logs data map
Map<String, String> loglist = new TreeMap<String, String>(new RPCIDKeySortComparator());
//rpcid and ragnarlog data map
Map<String, MetaLog> indexlist = new TreeMap<String, MetaLog>(new RPCIDKeySortComparator());
//get the log from kvDB
try {
Long timestampLong = Long.parseLong(traceIdInfo.get("time"));
DBSharder dbhelper = dbmanager.getDB(timestampLong);
//prevent expire db
if (dbhelper == null) {
model.addAttribute("tips", "日志信息库未找到!" + timestampLong);
return "showtrace_empty";
}
String rpcidlist = dbhelper.get(traceid + "_index");
if (rpcidlist != null) {
String[] rpcidArray = rpcidlist.split(",");
// remove the repeat
for (int rpcidIndex = 0; rpcidIndex < rpcidArray.length; rpcidIndex++) {
//log.info("add ragnarlog:" + rpcidIndex + " rpcid:" + rpcidArray[rpcidIndex]);
loglist.put(rpcidArray[rpcidIndex], "");
}
//get the logs by rpcid list
for (Map.Entry<String, String> entry : loglist.entrySet()) {
String logString = dbhelper.get(traceid + "_" + entry.getKey());
loglist.put(entry.getKey(), logString);
//log.info(entry.getKey() + " val:" + entry.getValue());
}
}
//log.debug("traceid:" + traceid + " rpcidlist:" + rpcidlist);
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage());
}
//查询traceid对应的metalog
try {
Sort sort;
sort = new Sort(new SortField("time", SortField.Type.DOUBLE, true));
Query query;
query = new TermQuery(new Term("traceid", traceid));
ResponseJson searchResult = indexHelper.searchByQuery(Long.parseLong(traceIdInfo.get("time")), query, 0, 200, sort);
List<MetaLog> searchResultMetaList = searchResult.getResult();
for (MetaLog metalogitem : searchResultMetaList) {
//get the parameter to select
if (selectedrpcid.equals(metalogitem.getRpcid())) {
model.addAttribute("parameter", metalogitem.getParam());
}
indexlist.put(metalogitem.getRpcid(), metalogitem);
}
//log.info("indexlist Count:" + searchResultMetaList.size());
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage());
}
renderTrace("0", traceid, selectedrpcid, loglist, indexlist, oldstyle);
model.addAttribute("tracelist", _tracelist);
//select api log list prepare
//相关日志渲染
//todo:网络间隔和实际时间比例展示
TreeMap<String, HashMap<String, String>> selectLogList = new TreeMap<String, HashMap<String, String>>();
if (loglist.containsKey(selectedrpcid)) {
try {
//log.debug(loglist.get(selectedrpcid));
JsonParser jsonparserHelper = new JsonParser();
JsonArray logListRootObj = (JsonArray) jsonparserHelper.parse(loglist.get(selectedrpcid));
JsonArray logListArray = (JsonArray) logListRootObj.get(0).getAsJsonObject().get("val");
Double beforLogTime = 0d;
for (int logindex = 0; logindex < logListArray.size(); logindex++) {
JsonObject logItemObj = (JsonObject) logListArray.get(logindex);
HashMap<String, String> logitem = new HashMap<>();
//inter time warning
if (logindex == 0) {
beforLogTime = logItemObj.get("e").getAsDouble();
}
logitem.put("logindex", logindex + "");
logitem.put("logintertimewarning", df.format(logItemObj.get("e").getAsDouble() - beforLogTime) + "");
beforLogTime = logItemObj.get("e").getAsDouble();
//other basic field
logitem.put("r", logItemObj.get("r").getAsString());
logitem.put("t", logItemObj.get("t").getAsString());
logitem.put("e", logItemObj.get("e").getAsString());
logitem.put("g", logItemObj.get("g").getAsString());
logitem.put("p", logItemObj.get("p").getAsString());
logitem.put("l", logItemObj.get("l").getAsString());
//make sure the string and obj will decode
if (logItemObj.has("m")) {
if (logItemObj.get("m").isJsonArray() || logItemObj.get("m").isJsonNull() || logItemObj.get("m").isJsonObject()) {
logitem.put("m", logItemObj.get("m").toString());
} else {
logitem.put("m", logItemObj.get("m").getAsString());
}
} else {
logitem.put("m", "");
}
if (logItemObj.has("c")) {
logitem.put("c", logItemObj.get("c").getAsString()); //?
} else {
logitem.put("c", "");
}
selectLogList.put(logindex + "", logitem);
}
} catch (Exception e) {
e.printStackTrace();
}
}
model.addAttribute("loglist", selectLogList);
return "showtrace_render";
} else {
//waiting the input the traceid
model.addAttribute("tips", "请填写请求标识 TraceId!");
return "showtrace_empty";
}
}
//循环方式渲染,用于应对数据不全情况
public void renderByLog(String renderrRPCid, String renderTraceid, String selectKey,
Map<String, String> loglist, Map<String, MetaLog> indexlist) {
//渲染顺序计数器,用来做日志排序的
int renderIndex = 0;
//根据bizlog进行渲染
for (Map.Entry<String, String> entry : loglist.entrySet()) {
String itemrpcid = entry.getKey();
Map<String, String> traceinfo = new HashMap<String, String>();
//rpcid
traceinfo.put("rpcid", itemrpcid);
traceinfo.put("r", itemrpcid);
//entrance was no tag
traceinfo.put("tag", "");
//type 日志类型
traceinfo.put("type", "");
//if the metaloginfo existed
MetaLog metaloginfo;
if (indexlist.containsKey(itemrpcid)) {
metaloginfo = indexlist.get(itemrpcid);
traceinfo.put("url", metaloginfo.getUrl());
} else {
metaloginfo = new MetaLog();
traceinfo.put("url", "====== Meta Info Not Found ===");
}
//selected
if (itemrpcid.equals(selectKey)) {
traceinfo.put("selected", "selected");
} else {
traceinfo.put("selected", "");
}
//indent
traceinfo.put("indent", StringUtils.repeat(" ", itemrpcid.split("\\.").length));
//type
traceinfo.put("type", metaloginfo.getHttpcode());
//cost time
traceinfo.put("elapsed", metaloginfo.getElapsed_ms() + "");
//server
traceinfo.put("ip", metaloginfo.getIp());
//procject
traceinfo.put("project", metaloginfo.getProject());
_tracelist.put(renderIndex + "", traceinfo);
renderIndex++;
//todo:xhprof日志
//render the biz log
//now check
if (loglist.containsKey(itemrpcid)) {
String logString = loglist.get(itemrpcid);
try {
JsonParser jsonparserHelper = new JsonParser();
JsonArray logArray = (JsonArray) jsonparserHelper.parse(logString);
//walk the val log array
for (int i = 0; i < logArray.size(); i++) {
JsonObject logRoot = logArray.get(i).getAsJsonObject();
String logTraceid = logRoot.get("key").getAsString();
String logTimestamp = logRoot.get("timestamp").getAsString();
JsonArray logItemArray = logRoot.getAsJsonArray("val");
//walk the biz log array
for (int logItemIndex = 0; logItemIndex < logItemArray.size(); logItemIndex++) {
Map<String, String> traceLogInfo = new HashMap<String, String>();
JsonObject logItemObj = logItemArray.get(logItemIndex).getAsJsonObject();
String logRpcid = logItemObj.get("r").getAsString();
String type = logItemObj.get("t").getAsString();
String tag = logItemObj.get("g").getAsString();
//only show the 9
if (!type.equals("9")) {
//continue;
}
traceLogInfo.put("rpcid", itemrpcid);
traceLogInfo.put("r", logRpcid);
traceLogInfo.put("indent", StringUtils.repeat(" -> -", logRpcid.split("\\.").length));
traceLogInfo.put("selected", "");
traceLogInfo.put("type", type);
if (logItemObj.has("c")) {
traceLogInfo.put("elapsed", logItemObj.get("c").getAsString());
} else {
traceLogInfo.put("elapsed", "");
}
traceLogInfo.put("ip", "--");
traceLogInfo.put("project", "--");
traceLogInfo.put("tag", tag);
//log.info("tag:" + tag);
//render the tag log
if (tag.equals("curl")) {
//curl log render
JsonObject logItemMsgObj = logItemObj.get("m").getAsJsonObject();
String[] curlUrlString = logItemMsgObj.get("url").getAsString().split("\\?");
if (curlUrlString.length > 0) {
traceLogInfo.put("url", curlUrlString[0]);
} else {
traceLogInfo.put("url", logItemMsgObj.getAsString());
}
} else if (tag.equals("mysql")) {
//mysql log render
JsonObject logItemMsgObj = logItemObj.get("m").getAsJsonObject();
if (logItemMsgObj != null) {
String sqlString = logItemMsgObj.get("sql").getAsString();
traceLogInfo.put("url", sqlString);
} else {
traceLogInfo.put("url", logItemMsgObj.getAsString());
}
} else {
//mysql log render
if (logItemObj != null) {
traceLogInfo.put("url", logItemObj.toString());
}
}
_tracelist.put(renderIndex + "", traceLogInfo);
renderIndex++;
}
//walk the log content
}
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage());
}
}
}
}
//主流方式:递归方式渲染
public void renderTrace(String renderrRPCid, String renderTraceid, String selectKey,
Map<String, String> loglist, Map<String, MetaLog> indexlist, boolean oldstyle
) {
//todo: 标题过长隐藏功能
//metalog current render
MetaLog metaloginfo;
//entrance api metalog info
MetaLog entranceMetaLogInfo;
//render the metalog trace info
Map<String, String> traceinfo = new HashMap<String, String>();
//recorde the befor trace info timestamp
Double beforEndTimeStamp = 0d;
//total cost time of full request
Double totalCostTime = 0d;
DecimalFormat df = new DecimalFormat("######0.0000");
///////////////////////
//API trace info render
///////////////////////
//_tracelist
//rpcid
traceinfo.put("rpcid", renderrRPCid);
traceinfo.put("r", renderrRPCid);
//entrance was no tag
traceinfo.put("tag", "api");
//type 日志类型
traceinfo.put("type", "");
if (indexlist.containsKey(renderrRPCid) && !oldstyle) {
//if the metaloginfo existed
metaloginfo = indexlist.get(renderrRPCid);
traceinfo.put("url", metaloginfo.getUrl());
beforEndTimeStamp = metaloginfo.getTime_raw();
} else {
//metaloginfo = new metaLog();
//traceinfo.put("url", "====== Meta Info Not Found ===");
if (renderrRPCid.equals("0")) {
renderByLog(renderrRPCid, renderTraceid, selectKey, loglist, indexlist);
}
return;
}
//if found the entrance metalog info
//in fact this sure..
if (indexlist.containsKey("0")) {
entranceMetaLogInfo = indexlist.get("0");
totalCostTime = new Double(entranceMetaLogInfo.getElapsed_ms());
} else {
entranceMetaLogInfo = new MetaLog();
}
//selected
if (renderrRPCid.equals(selectKey)) {
traceinfo.put("selected", "selected");
} else {
traceinfo.put("selected", "");
}
//indent
traceinfo.put("indent", StringUtils.repeat(" ", renderrRPCid.split("\\.").length));
//type
traceinfo.put("type", metaloginfo.getHttpcode());
//cost time
traceinfo.put("elapsed", metaloginfo.getElapsed_ms() + "");
//server
traceinfo.put("ip", metaloginfo.getIp());
//procject
traceinfo.put("project", metaloginfo.getProject());
_tracelist.put(_renderIndex + "", traceinfo);
_renderIndex++;
///////////////////////
//now render the log on trace
///////////////////////
String logString = loglist.get(renderrRPCid);
try {
JsonParser jsonparserHelper = new JsonParser();
JsonArray logArray = (JsonArray) jsonparserHelper.parse(logString);
//walk the val log array shell
for (int i = 0; i < logArray.size(); i++) {
JsonObject logRoot = logArray.get(i).getAsJsonObject();
String logTraceid = logRoot.get("key").getAsString();
String logTimestamp = logRoot.get("timestamp").getAsString();
JsonArray logItemArray = logRoot.getAsJsonArray("val");
//walk the biz log array
for (int logItemIndex = 0; logItemIndex < logItemArray.size(); logItemIndex++) {
Map<String, String> traceLogInfo = new HashMap<String, String>();
JsonObject logItemObj = logItemArray.get(logItemIndex).getAsJsonObject();
String logRpcid = logItemObj.get("r").getAsString();
String type = logItemObj.get("t").getAsString();
String tag = logItemObj.get("g").getAsString();
//only show the 9
if (!type.equals("9")) {
continue;
}
//两个埋点时间间隔
Double logInterTime = logItemObj.get("e").getAsDouble() - beforEndTimeStamp;
traceLogInfo.put("logintertimewarning", df.format(logInterTime));
traceLogInfo.put("logindex", logItemIndex + "");
traceLogInfo.put("rpcid", renderrRPCid);
traceLogInfo.put("r", logRpcid);
traceLogInfo.put("indent", StringUtils.repeat(" ", logRpcid.split("\\.").length));
traceLogInfo.put("selected", "");
traceLogInfo.put("type", type);
if (logItemObj.has("c")) {
traceLogInfo.put("elapsed", logItemObj.get("c").getAsString());
} else {
traceLogInfo.put("elapsed", "");
}
traceLogInfo.put("ip", "--");
traceLogInfo.put("project", "--");
traceLogInfo.put("tag", tag);
//log.info("tag:" + tag);
//render the tag log
if (tag.equals("curl")) {
//curl log render
JsonObject logItemMsgObj = logItemObj.get("m").getAsJsonObject();
String[] curlUrlStr = logItemMsgObj.get("url").getAsString().split("\\?");
if (curlUrlStr.length > 0) {
traceLogInfo.put("url", curlUrlStr[0]);
} else {
traceLogInfo.put("url", logItemMsgObj.getAsString());
}
//timeout lable show
try {
JsonObject curlinfoRoot = logItemObj.get("m").getAsJsonObject();
JsonObject curlinfoobj = (JsonObject) curlinfoRoot.get("info");
JsonObject curlinfoErrorObj = (JsonObject) curlinfoobj.get("error");
String errorno = curlinfoErrorObj.get("errorno").getAsString();
if (errorno.equals("28")) {
traceLogInfo.put("istimeout", "1");
}
} catch (Exception e) {
//ignore the errorno field not set
//e.printStackTrace();
//log.error(e.getMessage());
}
} else if (tag.equals("mysql")) {
//mysql log render
JsonObject logItemMsgObj = logItemObj.get("m").getAsJsonObject();
if (logItemMsgObj != null) {
String sqlStr = logItemMsgObj.get("sql").getAsString();
traceLogInfo.put("url", sqlStr);
} else {
traceLogInfo.put("url", logItemMsgObj.getAsString());
}
} else {
//other log render direct
if (logItemObj != null) {
traceLogInfo.put("url", logItemObj.toString());
}
}
///////////////////////
//TimeLine Parameter
///////////////////////
try {
if (beforEndTimeStamp > 0 && totalCostTime > 0) {
//start bar
Double startBarPercent = ((beforEndTimeStamp - entranceMetaLogInfo.getTime_raw()) / totalCostTime);
Double interBarPercent = ((logItemObj.get("e").getAsDouble() - logItemObj.get("c").getAsDouble()) - beforEndTimeStamp) / totalCostTime;
Double costBarPercent = logItemObj.get("c").getAsDouble() / totalCostTime;
//log.debug("logcost:" + logItemObj.get("c").getAsDouble() + " total:" + totalCostTime);
//log.debug("totaltimestamp:" + df.format(entranceMetaLogInfo.getTime_raw()) + "befortime:" + df.format(beforEndTimeStamp) + " total:" + totalCostTime + " start:" + startBarPercent + " inter:" + interBarPercent + " cost:" + costBarPercent);
//timeline bar length
traceLogInfo.put("timeline_startbar", (int) (startBarPercent * 470) + "");
traceLogInfo.put("timeline_interbar", (int) (interBarPercent * 470) + "");
traceLogInfo.put("timeline_costbar", (int) (costBarPercent * 470) + "");
//if the bar biger than 50px show the time
if ((int) (interBarPercent * 470) > 50) {
traceLogInfo.put("timeline_interbar_title", df.format(beforEndTimeStamp - entranceMetaLogInfo.getTime_raw()));
} else {
traceLogInfo.put("timeline_interbar_title", "");
}
if ((int) (costBarPercent * 470) > 50) {
traceLogInfo.put("timeline_costbar_title", logItemObj.get("c").getAsString());
} else {
traceLogInfo.put("timeline_costbar_title", "");
}
} else {
traceLogInfo.put("timeline_startbar", "0");
traceLogInfo.put("timeline_interbar", "0");
traceLogInfo.put("timeline_costbar", "0");
traceLogInfo.put("timeline_interbar_title", "");
traceLogInfo.put("timeline_costbar_title", "");
}
} catch (Exception e) {
//ignore the exception
traceLogInfo.put("timeline_startbar", "-1");
traceLogInfo.put("timeline_interbar", "-1");
traceLogInfo.put("timeline_costbar", "-1");
traceLogInfo.put("timeline_interbar_title", "");
traceLogInfo.put("timeline_costbar_title", "");
}
beforEndTimeStamp = logItemObj.get("e").getAsDouble();
_tracelist.put(_renderIndex + "", traceLogInfo);
_renderIndex++;
renderTrace(logRpcid, renderTraceid, selectKey, loglist, indexlist, oldstyle);
}
//walk the log content
}
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage());
}
}
}