介绍

在大型系统开发调试中,跨系统之间联调开始变得不好使了。莫名其妙一个错误爆出来了,日志虽然有记录,但到底是哪里出问题了呢?

是Ios端参数传的不对?还是A系统或B系统提供的接口导致?相信有不少人遇到这种情况,大多数问题往往不大,但排查起来比较费劲。

下面介绍下怎么通过上下文跟踪的方法,最快定位到其问题。

阅读目录:

  1. 概述
  2. web环境
  3. 多线程环境
  4. 异步环境
  5. 性能,大数据量,隐私安全
  6. 总结

概述

简单介绍就是,通过一个TraceId把整个业务请求逻辑相关联起来,根据时间顺序形成一个完整的调用链。

这样无论任何地方报错,只要拿TraceId去日志系统简查下,根据上下文的顺序就知道是哪一步、哪个函数、哪个参数出错了,能以最快速度定位处理BUG。

如图以博客园为例。当博客园收到一个请求后,自动为生产个唯一ID 1000,之后所有处理工作都是用这个1000。

每个处理模块都维持一个上下文ID自增,rpcid++。

其处理模块可以是函数级,逻辑层级,服务器级等都可以。

一旦发现有异常后,自动将TraceId发给博客园。这样程序员们,就能根据TraceId最快定位问题了。

关于各种环境下具体的代码实现:

web环境

定义跟踪日志需要的参数,进行上下文传递。

  1. public class LogBody
  2. {
  3. /// <summary>
  4. /// 跟踪ID
  5. /// </summary>
  6. public string TraceId { get; set; }
  7. /// <summary>
  8. /// 上下文ID
  9. /// </summary>
  10. public int RpcId { get; set; }
  11. /// <summary>
  12. /// 处理时间
  13. /// </summary>
  14. public DateTime LastTime { get; set; }
  15. }

在global.asax全局Application_BeginRequest函数中,使用HttpContext.Current上下文,开始进行埋点(跟踪),设置rpc 0。

  1. void Application_BeginRequest(object sender, EventArgs e)
  2. {
  3. var lb = new LogBody();
  4. lb.TraceId = Guid.NewGuid().ToString("N");
  5. lb.RpcId=0;
  6. lb.LastTime = DateTime.Now;
  7. HttpContext.Current.Response.AppendHeader("traceID", lb.TraceId);
  8. HttpContext.Current.Items.Add(lb.TraceId, lb);
  9. //记录日志,例:用户请求参数,userAgent等。
  10. }

在default页开始业务逻辑,设置rpc 1。

  1. protected void Page_Load(object sender, EventArgs e)
  2. {
  3. var traceID = HttpContext.Current.Response.Headers["traceID"];
  4. LogBody logbody = HttpContext.Current.Items[traceID] as LogBody;
  5. logbody.RpcId++;
  6. logbody.LastTime = DateTime.Now;
  7. //业务逻辑。
  8. //记录日志。。。
  9. }

如上就完成上下文的传递。

Application_BeginRequest  中在实际使用中,只需要对有用的页面(例:aspx,ashx)进行埋点。

日志记录的时候,可以把logbody都存储起来。

存储到Headers可以让前端通过JS也能拿到TraceId,方便去排查问题。

LastTime这个字段,可以与上一次的相减,这样就得出中间逻辑处理所花费的时间了。

多线程环境

在web程序中可以用httpcontext的上下文传递。

在单线程的程序中,按照线性顺序即可。

多线程中利用用threadlocal传递。

  1. public static ThreadLocal<LogBody> Body = new ThreadLocal<LogBody>();
  2. static void Main(string[] args)
  3. {
  4. var t1 = new Thread(() =>
  5. {
  6. Body.Value = new LogBody()
  7. {
  8. LastTime = DateTime.Now,
  9. RpcId = ,
  10. TraceId = Guid.NewGuid().ToString("N")
  11. };
  12. //业务1
  13. Console.WriteLine("Thread1 log record:" + Body.Value.TraceId + "-" + Body.Value.RpcId + "-" + Body.Value.LastTime);
  14.  
  15. Thread.Sleep();
  16.  
  17. Body.Value.RpcId++;
  18. Body.Value.LastTime = DateTime.Now;
  19. //业务2
  20. Console.WriteLine("Thread1 log record:" + Body.Value.TraceId + "-" + Body.Value.RpcId + "-" + Body.Value.LastTime);
  21. });
  22. t1.Start();
  23.  
  24. var t2 = new Thread(() =>
  25. {
  26. Body.Value = new LogBody()
  27. {
  28. LastTime = DateTime.Now,
  29. RpcId = ,
  30. TraceId = Guid.NewGuid().ToString("N")
  31. };
  32. //业务1
  33. Console.WriteLine("Thread2 log record:" + Body.Value.TraceId + "-" + Body.Value.RpcId + "-" + Body.Value.LastTime);
  34.  
  35. Thread.Sleep();
  36. Body.Value.RpcId++;
  37. Body.Value.LastTime = DateTime.Now;
  38. //业务2
  39. Console.WriteLine("Thread2 log record:" + Body.Value.TraceId + "-" + Body.Value.RpcId + "-" + Body.Value.LastTime);
  40. });
  41. t2.Start();
  42. }

运行如下:

异步环境

往往在生产环境中,会有大量的异步操作。如果有异步行为的话,打乱上下文怎么办?这时候需要引入另外一个概念,父节点Id。

这样异步操作的行为就父节点之下,最终在日志后台展示的是一个倒着的树形结构。

如图可以看到业务2异步派生出来的子节点。

把上下文rpcid修改成double类型。

  1. static void Main(string[] args)
  2. {
  3. var t2 = new Thread(() =>
  4. {
  5. Body.Value = new LogBody()
  6. {
  7. LastTime = DateTime.Now,
  8. RpcId = 1,
  9. TraceId = Guid.NewGuid().ToString("N")
  10. };
  11. var t1 = new Thread((lb) =>
  12. {
  13. var temp = lb as LogBody;
  14. Body.Value = new LogBody()
  15. {
  16. LastTime = DateTime.Now,
  17. RpcId = temp.RpcId,
  18. TraceId = temp.TraceId
  19. };
  20. Body.Value.RpcId += 0.1;
  21. //业务x
  22. Console.WriteLine("async Thread:" + Body.Value.TraceId + "-" + Body.Value.RpcId + "-" + Body.Value.LastTime );
  23.  
  24. Thread.Sleep(5000);
  25.  
  26. Body.Value.RpcId+=0.1;
  27. Body.Value.LastTime = DateTime.Now;
  28. //业务y
  29. Console.WriteLine("async Thread:" + Body.Value.TraceId + "-" + Body.Value.RpcId + "-" + Body.Value.LastTime);
  30. });
  31. t1.Start(Body.Value);
  32.  
  33. //业务1
  34. Console.WriteLine("sync Thread:" + Body.Value.TraceId + "-" + Body.Value.RpcId + "-" + Body.Value.LastTime);
  35.  
  36. Thread.Sleep(2000);
  37. Body.Value.RpcId+=1;
  38. Body.Value.LastTime = DateTime.Now;
  39. //业务2
  40. Console.WriteLine("sync Thread:" + Body.Value.TraceId + "-" + Body.Value.RpcId + "-" + Body.Value.LastTime);
  41. });
  42. t2.Start();
  43. }

代码中用参数传递给了异步线程中,运行如下:

性能,大数据量,隐私安全

关于性能

从代码中可以看出,这种方式对程序性能影响可以忽略不计。

需要注意是:如果在生产环境跑的话,不论是写文件,还是数据库,或写统一日志平台。都会导致大量IO读写,网络资源消耗。

如果服务器都消耗这上面,就得不偿失了。

可以用内存队列+队列+批量push或pull的方式,并且注意设置阀值。

关于大数据量

大量的请求,其实多数是无效的。这里引入采样率的概念。 例如按求余取,或者按地区,时间等。也可以单独写采样规则。

日志可以只记录error以上的级别,只有在排查生产环境的时候才开启debug,info级别信息。

存储这块,可以根据实际需要选择sql server,mongodb,hbase hdfs。

关于隐私安全

如果有敏感数据,可根据安全级别进行加密。

总结

本文是基于Google dapper论文的思路展开,基于此进行很多扩展。

示例中采用的是手动记录,在实际使用中,可以简化调用,封装成自动构建的,有兴趣的可以看前2篇自动注入的相关介绍。

参考资源

Google dapper论文

淘宝EagleEye系统

日志系统实战(三)-分布式跟踪的Net实现的更多相关文章

  1. 布式实时日志系统(三) 环境搭建之centos 6.4下hadoop 2.5.2完全分布式集群搭建最全资料

    最近公司业务数据量越来越大,以前的基于消息队列的日志系统越来越难以满足目前的业务量,表现为消息积压,日志延迟,日志存储日期过短,所以,我们开始着手要重新设计这块,业界已经有了比较成熟的流程,即基于流式 ...

  2. 日志系统实战(一)—AOP静态注入

    背景 近期在写日志系统,需要在运行时在函数内注入日志记录,并附带函数信息,这时就想到用Aop注入的方式. AOP分动态注入和静态注入两种注入的方式. 动态注入方式 利用Remoting的Context ...

  3. 日志系统实战(二)-AOP动态获取运行时数据

    介绍 这篇距上一篇已经拖3个月之久了,批评自己下. 通过上篇介绍了解如何利用mono反射代码,可以拿出编译好的静态数据.例如方法参数信息之类的. 但实际情况是往往需要的是运行时的数据,就是用户输入等外 ...

  4. 日志系统实战 AOP静态注入

    http://www.cnblogs.com/mushroom/p/3932698.html http://www.cnblogs.com/mushroom/p/4124878.html http:/ ...

  5. atitit. 日志系统的原则and设计and最佳实践(1)-----原理理论总结.

    atitit. 日志系统的原则and设计and最佳实践总结. 1. 日志系统是一种不可或缺的单元测试,跟踪调试工具 1 2. 日志系统框架通常应当包括如下基本特性 1 1. 所输出的日志拥有自己的分类 ...

  6. Android日志系统Logcat源代码简要分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6606957 在前面两篇文章Android日志系 ...

  7. 【转载】CentOS日志系统组成详解

    日志系统有三部分组成:一.使用什么工具记录系统产生的日志信息?      syslog服务脚本管理的两个进程: syslogd.klogd 来记录系统产生的日志信息:      klogd     进 ...

  8. 使用日志系统graylog获取Ceph集群状态

    前言 在看集群的配置文件的时候看到ceph里面有一个graylog的输出选择,目前看到的是可以收集mon日志和clog,osd单个的日志没有看到,Elasticsearch有整套的日志收集系统,可以很 ...

  9. 搭建Loki、Promtail、Grafana轻量级日志系统(centos7)

    搭建Loki.Promtail.Grafana轻量级日志系统(centos7)--简称PLG 需求 公司项目采用微服务的架构,服务很多,每个服务都有自己的日志,分别存放在不同的服务器上.当查找日志时需 ...

随机推荐

  1. Hadoop中的问题排查思路

    一.概述: 在实际使用hadoop的过程中,由于涉及到多台服务器.每台机器上可能还有多个服务等.所以当集群环境出现问题时,快速定位到错误出现的地方尤为重要. 在排查错误的过程中,基本上就是通过既有的工 ...

  2. HEAP CORRUPTION DETECTED :after Normal block 错误

    http://blog.csdn.net/zhccl/article/details/7889590

  3. CozyRSS开发记录19-窗口标题栏交互

    CozyRSS开发记录19-窗口标题栏交互 1.谈谈对mvvm解耦的看法 在使用mvvm时,如何操作窗口,这是一个问题.这个问题的关键点是:mvvm是把view和viewmodel解耦了的,很多写法一 ...

  4. iOS中的生命周期

    对于一个iOS app来讲,生命周期是一个十分至关重要的东西.对于一个app来讲控制着app的开启.睡眠.关闭等状态:对于一个页面的来讲,控制页面的加载.显示.消失:对于一个View或者一个普通的类来 ...

  5. redhat6下安装Lighttpd1.4.43

    学完了C语言,自信满满地冲着开源软件去了,首选了Lighttpd,这个软件代码量不多,适合初入开源的朋友 redhat下安装Lighttpd,一定要先安装依赖库,pcre和bzip2,这两个自行下载, ...

  6. 解决autolt上传图片报错cannot open system clipboard

    今天调试代码,发现本地可以上传图片,但是集成环境无法上传报错cannot open system clipboard: 百度查了下,我的系统没有剪切板程序,才报错. 验证方法如下: win+r,输入c ...

  7. 【转】TCP协议

    TCP是什么? TCP(Transmission Control Protocol 传输控制协议)是一种面向连接(连接导向)的.可靠的. 基于IP的传输层协议.TCP在IP报文的协议号是6.TCP是一 ...

  8. MongoDB数据库安装与连接

  9. 将excel数据读入matlab

    1.[NUM,TXT,RAW]=xlsread('example'),其中example是你的excel名,假设所有的数据都在example.xls中. 2.NUM返回的是excel中的数据,TXT输 ...

  10. spring hibernate4 c3p0连接池配置

    c3p0-0.9.1.2.jar,c3p0-oracle-thin-extras-0.9.1.2.jar,点此下载 <bean id="dataSource" class=& ...