【CAT魔改】为cat-home添加链路追踪查询

扩展cat-home,实现traces链路追踪。

0. 目录

      • 1. 前言
      • 2. 效果图
      • 3. 实现
        • 3.1 新建 `com.dianping.cat.report.page.traces` PACKAGE
        • 3.2 修改 `com.dianping.cat.report.ReportModule` 类
        • 3.3 新增前端页面traces.jsp
        • 3.4 修改`application.tag`文件
      • 4. 补充
      • 5. Links

1. 前言

在过去的四五年里对于监控的持续关注和研究中,每每涉及到诸如pinpoint,skywaling, zipkin,CAT等主流APM比较时候,必被提及的一点一定是对于trace链路追踪功能的支持,而这一点在CAT开源版中确实是没有的。

最近笔者再次开始尝试在部门内部推进CAT,跟进反馈之后得到第一个诉求就是这个链路追踪,果然是绕不过去的坎,只能开整了。

2. 效果图

正式开始介绍实现前,先让我们看看最终的效果,协助读者建立一个直观的印象:

  1. 我们在CAT首页左侧导航树中新增了一个名为"Traces链路"的根节点,其下有一个名为"链路追踪"的子节点。单击即可在右侧打开自定义的链路查询页面。
  2. 在自定义的链路查询页面下,输入traceid,点击查询即可查看对应的链路日志信息,效果和官方提供的logview视图完全一致。

【CAT魔改】为cat-home添加链路追踪查询_第1张图片

3. 实现

最终的效果看完了,愿意接着往下看的应该就是对此解决方案感兴趣的同学了,那么废话少说,我们直接开始。

3.1 新建 com.dianping.cat.report.page.traces PACKAGE

按照实现思路,我们先介绍相关的后端实现代码。

  1. 本PACKAGE是需要手动新建的,相关内容参见下方。

    # 有几个类根本没必要, 但为了方便读者理解, 就让它们先活着吧
    #	将cat源码导入IDE后,新增如下目录和文件(文件内容请继续往下看)
    cat-home\src\main\java\com\dianping\cat\report\page\traces\
    	Handler.java
    	JspFile.java
    	JspViewer.java
    	Model.java
    
    1. 上述四个文件的具体内容:
    // 注意: 以下涉及到的类, 优先从 com.dianping.cat.report.page.logview PACKAGE 中导入
    
    public class Handler implements PageHandler<Context> {
           
    	@Inject
    	private JspViewer m_jspViewer;
    
    	@Override
    	@PayloadMeta(Payload.class)
    	@InboundActionMeta(name = "traces")
    	public void handleInbound(Context ctx) throws ServletException, IOException {
           
    		// display only, no action here
    	}
    
    	@Override
    	@OutboundActionMeta(name = "traces")
    	public void handleOutbound(Context ctx) throws ServletException, IOException {
           
    		Model model = new Model(ctx);
    		Payload payload = ctx.getPayload();
    
    		model.setAction(payload.getAction());
    		model.setPage(ReportPage.LOGVIEW);
    		model.setDomain(payload.getDomain());
    		model.setDate(payload.getDate());
    
    		m_jspViewer.view(ctx, model);
    	}
    }
    
    public enum JspFile {
           
    	LOGVIEW("/jsp/report/traces/traces.jsp");
    
    	private String m_path;
    
    	private JspFile(String path) {
           
    		m_path = path;
    	}
    
    	public String getPath() {
           
    		return m_path;
    	}
    }
    
    public class JspViewer extends BaseJspViewer<ReportPage, Action, Context, Model> {
           
    	@Override
    	protected String getJspFilePath(Context ctx, Model model) {
           
    		Action action = model.getAction();
    		Payload payload = ctx.getPayload();
    
    		switch (action) {
           
    		case VIEW:
    			return JspFile.LOGVIEW.getPath();
    		}
    
    		throw new RuntimeException("Unknown action: " + action);
    	}
    }
    
    @ModelMeta("traces")
    public class Model extends com.dianping.cat.report.page.logview.Model {
           
    
    	private String traceId;
    
    	public Model(Context ctx) {
           
    		super(ctx);
    	}
    
    	public String getTraceId() {
           
    		return traceId;
    	}
    
    	public void setTraceId(String traceId) {
           
    		this.traceId = traceId;
    	}
    
    }
    
    

3.2 修改 com.dianping.cat.report.ReportModule

这一步的主要目的是为上面定义的Handler类提供注册,实现对外的RESTFUL服务提供。

@ModuleMeta(name = "r", defaultInboundAction = "top", defaultTransition = "default", defaultErrorAction = "default")
@ModulePagesMeta({
     
	.......
	com.dianping.cat.report.page.traces.Handler.class, // 在原有内容基础上新增这一行

})
public class ReportModule extends AbstractModule {
     

}

至此后端需要的操作全部完成,接下来让我们看看前端所需的修改。

3.3 新增前端页面traces.jsp

相关地址如下: cat-home\src\main\webapp\jsp\report\traces\traces.jsp 。

<%@ page session="false" language="java" pageEncoding="UTF-8" %>
<%@ page contentType="text/html; charset=utf-8" trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="a" uri="/WEB-INF/app.tld"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="res" uri="http://www.unidal.org/webres"%>
<jsp:useBean id="ctx" type="com.dianping.cat.report.page.logview.Context" scope="request"/>
<jsp:useBean id="payload" type="com.dianping.cat.report.page.logview.Payload" scope="request"/>
<jsp:useBean id="model" type="com.dianping.cat.report.page.traces.Model" scope="request"/>

<a:application>

<res:useCss value="${res.css.local.logview_css}" target="head-css"/>
<res:useJs value="${res.js.local.logview_js}" target="head-js"/>

<script type="text/javascript">
	// copy from  logview.js
	function queryByTraceId(anchor, id) {
       
		var cell = document.getElementById(id);
		var text = anchor.innerHTML;
		
		$.ajax({
       
			type : "get",
			url :  "/cat/r/m/"+$("#traceId").val() + "?header=no&waterfall="+$("#waterfall").val() +"&map=true",
			success : function(data, textStatus) {
       
				cell.innerHTML = data;
			}
		});

		return false;
	}
script>

<table style="width:100%;">
	
	<tr>
	
	<th class="left" colspan="13"><input type="text" name="traceId" id="traceId" size="40" value="${model.traceId}">
    <input  class="btn btn-primary  btn-sm"  value="Filter" onclick="queryByTraceId(this,'container')" type="submit" placeholder="输入TraceId">
	<label class="label is-centered" style="margin-right:15px; ">图表展示:label>    
    <select id="waterfall" >
		<option value ="true">trueoption>
		<option value ="false">falseoption>
	select>		
	th>

	tr>
	
table>

<div id="container">div>

<br>

a:application>

本小节的内容算是完成了链路查询页面的编写,最后我们只需要将这个页面嵌入到现有cat-home系统中,这就涉及到application.tag文件的修改了。

3.4 修改application.tag文件

相关地址为: cat-home\src\main\webapp\WEB-INF\tags\application.tag

将以下内容加在application.tag文件中。(不要问加在哪里,页面打开之后你要还是不知道加在哪,你就和领导建议换个人来做这事)

<li id="System_report" class="hsub"><a href="#" class="dropdown-toggle"> <i class="menu-icon fa fa-gavel">i> <span class="menu-text">Traces链路span>
	<b class="arrow fa fa-angle-down">b>
	a> <b class="arrow">b>
	<ul class="submenu">
	<% 
		<li id="system_alteration"><a href="/cat/r/traces?domain=${model.domain}&ip=${model.ipAddress}&date=${model.date}&reportType=${payload.reportType}&op=${payload.action.name}">
		<i class="menu-icon fa fa-caret-right">i>链路追踪a>
		<b class="arrow">b>li> %>
	
	ul>
li>

综上可以看到,CAT的设计相当优秀,除了必要的注册之外,其功能的添加都是以新增来完成的,完美符合了开闭原则。

4. 补充

  1. 其实CAT开源版还提供了更多report报表,只是在进行代码开源时候注释掉了其前端展示(但后端实现依然是完整保持了的,而且均正常工作。这一点可以从上一小节介绍的application.tag中进行验证),这也是算是CAT给予充满好奇心,乐于刨根问底的同学的一点福利吧。

  2. 前面一直在反复提及的traceId,其值不要使用自定义的UUID,而应该直接复用CAT官方的MessageId,究其原因是CAT为了最小化外部依赖,默认将收集到的日志存储到了本地磁盘中,并按小时进行分开存储,所以CAT的每一个MessageId除了唯一性之外,还内涵了其对应的日志存储在哪个位置的功能。具体怎么将这个MessageId回传给前端,读者还请根据自身业务场景进行实现。

  3. 主要参考源码有:PACKAGE - com.dianping.cat.report.page.logview

  4. CAT不是一个标准的全链路系统。

    Cat 主要是一个实时监控系统,并不是一个标准的全链路系统,主要是 Cat 的 logview 在异步线程等等一些场景下,不太合适,Cat 本身模型并不适合这个。Cat 的 Github 上有说明:在美团点评内部,有 mtrace 专门做全链路分析。

    但是如果在 Mvc,远程调用等这些框架中做好了数据的无缝传输,Cat 也可以充当一个链路跟踪的系统,基本的场景足够了。

5. Links

  1. 爱奇艺内容中台基于CAT的服务监控实践
  2. 【CAT魔改】独立化cat-client
  3. 【CAT魔改】扩展cat-consumer实现自定义服务端收集日志
  4. APM 介绍与实现

你可能感兴趣的:(DevOps,CAT,监控,devops,CAT,trace链路追踪)