调试方法总结

 

简介

可能是目前为止最全的调试指南

前言

在编程开发的过程中总会遇到一些迷人的BUG,最近几个人来找我帮忙DEBUG,都是很难通过搜索引擎解决的问题。最后发现都是很神奇的错误,于是决定写一篇文章好好来聊一聊如何DEBUG。

0 整体思路

总的来说,问题的本质是预期结果A与实际结果B不符。这个时候就要DEBUG了,下文将以本人DEBUG的经验说一说DEBUG的原则,以及从不同的平台来聊一聊如何DEBUG。

调试条件:
– 有预期结果
– 结果可观测
(注意一些薛定谔的结果,试图观测可能会破坏原有状态)
基本方法:
– 控制变量
– 二分法查找

所以只要让结果可观测,那么就可以借此定位出现问题的部分在哪。一般来说都是打印日志或者IDE断点

0.1 分解问题

当遇到BUG的时候,从结果出发分析结果出现的过程,追溯到输入。一般来说,都是输入经过n个处理流程后得到最后出现的结果。

0.2 定位问题

从结果开始追溯,观测每个阶段结果,从而确定从哪个流程开始结果开始不对的。

0.3 设计方案

设法观测中间结果,使结果可观测。
设法改变出问题阶段的输出恒为正确答案,观测最终结果是否正确。
如果正确,说明该流程后面的流程都没问题,继续向前定位。
如果不正确,说明后面的流程至少有一个有问题,继续向后定位。

1 Web端调试

常用工具为Chrome的F12的开发人员调试工具。
APP上的内嵌网页如果需要依赖APP则使用Remove device功能
一般网页的加载过程:浏览器接收HTTP报文,解析HTML并渲染,加载JS、CSS等静态资源,解析JS脚本并执行。

1.1 html

HTML最终结果的调试在elements标签中可以动态更改,查看效果。
前后端分离的项目大多是JS来渲染DOM并填充数据,所以DOM是动态生成的,需要查看原始的source,而不是elements

1.2 js

JS在source中可以寻找源码,添加断点,单步调试。

1.3 css

CSS的调试在style窗口可以动态修改样式。

2.移动端调试(Android/iOS)

移动端的调试大多依赖对应的IDE,以Android为例,Android Studio支持真机下断点进行调试,logcat可以输出调试日志。当然也有一些手机上的sqlite可视化工具可以使用。
也可以改写代码将中间结果图形化直接显示到界面上,或者弹出提示窗进行显示。

3 API调试

API大部分都是基于HTTP协议的,只要抓住HTTP报文,一般问题都可以解决。
API调试可以借助Chrome开发工具的Network标签,可以详细显示HTTP协议的header和body。自带json格式化。拦截对应的HTTP请求,分析请求过程。
如果Chrome开发工具搞不定,可以使用Postman模拟构造HTTP报文来发送请求,当然也可以使用curl生成自定义的HTTP报文。
如果上面的工具依然难以调试,则可以选择设置流量代理或者借助其他软件拦截所有网络流量包分析过程。

4 Server端调试

服务端调试大部分情况下依赖于日志
tail -f filename.log可以持续查看一个文件的变化,用于监控日志非常方便。

4.1 接入层调试

nginx apache有对应的请求日志和加载日志,直接对应查看即可。
如果转发有误则可以考虑放测试文件到对应的文件夹下来测试转发。

4.2 应用层调试

不同的应用服务器也有自己的启动日志和运行日志,可供调试参考。
如果在开发环境下,可以直接借助IDE进行断点调试,追踪问题。如果在特殊环境无法加载IDE,则最简单的办法依然是打印详细日志来追踪程序执行过程,缩小并确定问题范围。
注意未知因素的影响,例如有些框架取数据是自动拼接方法名并调用来取值,注意避开这些自动生成的方法名以免冲突。

5 Linux调试

Linux下的调试大部分都没有图形界面,只能看字符输出,所以多打印中间结果总能找到出问题的部分。

5.1 shell脚本调试

利用echo打印中间结果或使用sh -x参数进行调试。

5.2 shell命令调试

shell命令是在环境变量PATH中搜索的,一般使用which可以查看到命令的可执行文件所在位置,并继续跟踪执行过程即可。

5.3 网络调试

网络出现问题时候可以使用ping和traceroute来追中出问题的网络节点,使用netstat -ano查看端口占用情况。

6 特定语言调试

其实也没什么特殊的,除了借助IDE的断点调试之外,各个语言都有自带的打印函数可以打印中间结果。

6.1 C/C++

printf cout

6.2 Java

System.out.println
Log4j框架

6.3 Python

print
logging和traceback模块

7 常见错误

7.1 环境错误

开发环境本身配置有误,没有完成测试就继续开发了,避免方法是提前做好环境测试。运行测试的DEMO

7.2 语法错误

IDE一般会提示的,不解释

7.3 拼写错误

手残不解释

7.4 逻辑错误

这类错误是最常见的,也最难调试。也就是所有语法都对,但是结果不对,说明代码逻辑有问题,解决方法就是上面所说的逐步缩小范围,直到一行代码。

7.5 调试的错误

调试本身的错误,由于某些情况下的薛定谔效应无法直接观测结果,只能间接观察的时候容易引起人的误判。想办法避免直接观测即可。

7.6 其他错误

程序都对,但是某配置文件有错,比如makefile文件只能用tab。yaml中只能用空格。python中tab和空格的混用。某些print不支持中文等。

 

spring jmstemplate的坑

jmstemplate 发送文本消息的写法
第一种手动构造msg的话不需要做特殊处理,但是很麻烦

调用方写法

第二种自动构造msg需要在注入bean的时候指定msgConverter,写法简洁但是需要注意不要做第手动转换

调用方写法

MongoDB基础

https://www.mongodb.com/presentations/webinar-back-to-basics-thinking-in-documents?utm_campaign=T5_V3_DEV_ZH_E1_Schema_Design_A&utm_medium=email&utm_source=Eloqua

https://www.slideshare.net/mongodb/webinar-back-to-basics-thinking-in-documents

[slideshare id=52598563&doc=thinkingindocuments1-150909190439-lva1-app6891]

apache反向代理tomcat获取用户真实IP

校内CAS单点登录的审计日志中,用户的IP地址一直是服务器地址。这个问题拖了好久,今天终于解决了。
通过追查日志的来源,发现是自己实现了一个appender配置在了log4j中。
从这个类入手开始追查代码,最终发现IP的来源是通过request.getRemoteAddr这个方法来获得的。
而这个方法是来源于tomcat自身的servlet-api提供,返回的向tomcat发起请求的客户端IP。

但是一般情况下tomcat都会用nginx或apache代理tomcat,所以这个方法得到的IP就是服务器自身的IP而不是真实的远程IP了
而我又不想更改CAS的源码重新部署,所以只能想办法更改tomcat,让tomcat得到真实的IP。
根据apache文档所述,proxy模块会自动添加这三个header。
但是还差两个header没有添加,于是引入header模块使用下面的指令设置剩下的信息。
RequestHeader set x-forwarded-by “IP”
RequestHeader set x-forwarded-proto “https”
由于我的服务器是https协议,所以写了https。

根据tomcat文档所述,在tomcat端的server.xml中
Engine级别下添加Value节点。

文档写的很清楚internalProxies指定的正则所匹配的地址不会在x-forwarded-by中出现,而trustedProxied正则匹配的地址会出现在x-forwarded-by中。
我想要的结果是真实IP为用户IP,同时添加校内服务器的ip地址进入x-forwarded-by中,所以理论上我值需要把服务器IP地址段的正则写入trustedProxied即可。但是实际测试结果依然不对。
getRemoteAddr方法返回的依然是服务器的IP而不是真实IP。

而如果我在internalProxies服务器IP地址段正则的话,getRemoteAddr返回的结果就是正常的,但是x-forwarded-by字段就看不见代理服务器的IP了。

同时更改tomcat日志的格式如下。

解决IDEA控制台乱码问题

今天做实验,迷之乱码。项目结构:Spring+Mybaits,同时用了log4j。但是tomcat控制台总是乱码。
项目编码、文件本身编码都是UTF8,我之前使用IDEA也没出现过类似问题,这次却耽误了我好长时间= =
把IDEA编译的class文件反编译,发现已经是乱码了,所以给javac附加UTF-8参数,之后依然乱码。
网上有人在运行tomcat时候传入UTF-8我也试了,依然不行。log4j也设置了UTF-8。
经过实验,发现有一次控制台是乱码但是输出的日志文件不是乱码,才把注意力转向了IDEA自身
最后发现居然是IDEA自身编码没改,解决方案如下
打开IDEA安装目录找到idea64.exe.vmoptions加入一行-Dfile.encoding=UTF-8即可
总结下,有这么几个位置
源文件自身编码
项目编码
javac编译时候的编码参数
tomcat运行时jvm的编码参数
IDEA运行时jvm的编码参数
log4j配置的输出编码
均统一为UTF-8后问题消失

遗留的玄学问题:为什么反编译class文件时候,出现过一次部分文件编码异常而部分文件编码正常的情况=-=

Linux使用代理服务器的异常

今天在ZFZ的CAS测试环境下配置代理,配置环境变量https_proxy和http_proxy=’xxx.xxx.xxx.xxx:port’之后发现还是上不了网
但是我在windows上测试是正常的,说明代理服务器没问题。
而curl可以正常使用,只有sudo git clone 不行
我怀疑是root下没有这个环境变量,去root下改.bashrc后再测试还是不行
于是尝试把这两个环境变量写到系统级别,/etc/profile的最后,source /etc/profile了还是不行
查度娘后发现sudo之后环境变量需要配置,于是在/etc/sudoers
加了这么一行,成功
Defaults env_keep += “http_proxy https_proxy no_proxy”

HTTPS证书制作和配置示例

今天在校内尝试从git.bistu.edu.cn克隆代码,使用https协议时候发现clone失败,于是开始实验,使用curl连接cas.bistu.edu.cn、x.bistu.edu.cn、chat.bistu.edu.cn、community.bistu.edu.cn、site.bistu.edu.cn
发现只有cas和site正常,其他的都报错了,但是浏览器访问并未发现错误。
联想之前ZFZ的safari访问git报错而我电脑的浏览器却没事,瞬间意识到之前配置的HTTPS可能很多都有问题。
经过一翻折腾,发现之前的补链不全导致有些浏览器无法识别完整的信任链,最终通过补链解决。
当Apache、IIS、Nginx都不报错之后,只有Tomcat还有问题。
Tomcat之前的jks使用的是http://www.agentbob.info/agentbob/79-AB.html这篇文章的工具生成的,说明这个工具有问题。
继续搜索发现可以使用
openssl s_client -msg -connect cas.bistu.edu.cn:8443
这个命令来调试详细的ssl连接过程
和正常的过程对比发现Tomcat的信任链依然不一样。遂放弃这个工具。
最终在文末的文章中找到了解决方案。
废话说完了,下面是完整操作记录。
—————————————————————–
拿到手的有bistu.crt证书一张、bistu.key私钥一个。
实际需要开启HTTPS的服务器若干,服务器种类为Tomcat、Nginx、Apache、IIS。
到证书签发机构官网下载root根证书和中间证书,在windows上另存为Base64PEM格式
分别为root.crt和intermediate.crt
拷贝到Linux下开始制作

补链后证书为bistu.chained.crt

生成bistu.jks为tomcat所需格式

openssl pkcs12 -export -out bistu.pfx -inkey bistu.key -in bistu.crt

生成bistu.pfx为IIS所需格式

至此所有文件如下
1.bistu.crt #原始证书
2.bistu.key #私钥
3.root.crt #根证书
4.intermediate.crt #中间证书
5.bistu.chained.crt #补链后证书
6.combined.crt #中间链+根证书
7.bistu.p12 #p12格式的证书库
8.bistu.jks #jks格式的证书库
9.bistu.pfx #pfx格式的证书库

Apache需要的:1、2、5
Nginx需要的:2、5
Tomcat需要的:8
IIS需要的:2、9

配置示例

Apache

Nginx配置示例

Tomcat配置示例

参考:
http://blog.csdn.net/jun55xiu/article/details/8980812
http://www.fourproc.com/2010/06/23/create-a-ssl-keystore-for-a-tomcat-server-using-openssl-.html
https://blogs.oracle.com/blogbypuneeth/entry/steps_to_create_a_jks