看了这篇文章,比同事更快找到bug!

简介: 你以为程序员只是闷着头疯狂写bug,写好了发布到服务器就完了?不,你还要修bug!但在那之前,你还要找bug!

你以为程序员只是闷着头疯狂写bug,写好了发布到服务器就完了?

不,你还要修bug!但在那之前,你还要找bug!

那么问题来了,程序都部署都服务器了,怎么找bug呢?下面给大家介绍几种方法,帮助你轻松找到程序的bug。


日志篇


在上篇文章中,笔者介绍了《在Java代码中打日志需要注意什么?》。打日志是非常重要的,因为日志能够提供一些运行时的信息,非常有助于我们快速定位问题。

那么怎么通过日志去分析问题呢?我们首先从单机服务器说起,因为有时候可能bug是测试发现的,而测试环境一般机器都比较少,可能不会专门搭建ELK等日志收集器,所以可能是要到服务器上去看日志。

统一日志配置标准

这个其实是非常重要的,因为我们首先要找到日志文件的路径,才能找到日志进行分析。尤其在一个大点的团队,服务非常多,比较忌讳每个服务的日志文件路径乱放,找半天找不到日志放在哪。

所以建议团队有一个统一的日志配置标准。不论是格式、路径、保存日期和清理策略等,最好都保持一致,并写进团队的建设文档里,这样开发人员才能够快速地找到自己想要的日志。

分析日志常用命令

现在服务端大多部署在Linux服务器上,所以这里介绍一些在Linux上常用的分析日志的命令。

tail

tail命令是笔者最常用的命令。我们知道,日志文件大多都是追加的方式进行的。所以最新发生的问题一般都是在日志文件的末尾。tail命令可以打印出日志倒数第n行的日志,还可以通过-f参数来实时追踪。

# 打印x.log的末尾1000行日志,默认是10
tail x.log -n1000;
# 打印x.log末尾10行日志,并继续打印出后面新增的日志
tail x.log -f
# 这个跟上面是等价的
tailf x.log

cat + grep

上面介绍的tail命令,可以输出文件的末尾n行。但有时候我们发现问题时已经有点晚了,这个时候可以在整个日志文件里面找。

通过cat命令,可以打印出整个日志文件的内容。但整个日志文件内容太多了,所以我们通常需要通过管道加上grep来结合,只提取我们需要的信息。

cat x.log | grep 'xxx'
复制代码

这里grep的内容“xxx”,通常建议以下值:

  • 日志中的数据的id或者uid,比如43432413;
  • 日志中的traceId;
  • 日志中的问题描述,比如“call userService failed”;
  • 日志中的文件,比如“UserServiceImpl”

当然了,这里的管道和grep,也可以和上面的tail命令相结合。另外,如果grep出来的内容比较多,后续还想分析,管道还可以将输出流输出到一个文件里,比如:

cat x.log | grep 'xxx' > temp.log

输出到一个单独的文件里了,我们就能够对它进行下一步分析了,比如发生这个异常的数据有多少条,提取里面的关键信息。还可以通过sed命令来做。

比如:

# 打印temp.log
$ cat temp.log
error data: 124
error data: 121j
error data: 124fdsfsd
# 用正则提取里面的id
$ cat temp.log | sed -r "s/error data: (.*)/\1/g"
124
121j
124fdsfsd

vi

cat + grep虽然好,但是不是很方便查看异常周围的日志。比如我们想查看完整的日常堆栈,可以通过vi的搜索功能来做。

vi是在Linux平台上常用的文本编辑器,功能非常强大。当然,很多朋友更习惯用功能更强的vim,用法是类似的。今天要介绍的是它的一些常用的搜索功能和跳转功能。

# 从前到后搜索
/searchKey
# 从后到前搜索
?SearchKey
# 定位光标到下一个搜索的地方
快捷键n
# 定位到光标上一个搜索的地方
快捷键N
# 设置显示行号
:set nu
复制代码

以上差不多能够在服务器上定位和分析日志了。如果你的服务器是分布式的,部署在很多台机器上,比如UAT环境和生产环境,那其实不建议再去服务器上手动分析日志,而是用成熟的日志框架去收集和分析日志。

比较常用的开源日志收集框架是ELK、Splunk等,它们都提供了web界面和丰富的查询语句支持。


远程Debug篇


现在JVM支持远程调试。其原理是因为Java是跨平台的,只要远程的字节码文件和本地的字节码文件相同,两个JVM可以通过调试协议进行通信。

所以这里需要保证本地和服务端的代码要一致,不然可能导致某个端点无法进入。尤其使用分支开发的同学需要注意分支是否一致

IDEA上有一个非常好用的远程Debug工具,下面简单介绍一下如何在IDEA上如何Debug。首先通过Run -> EditConfigurations进去,然后添加一个Remote。

然后配置一下host和端口就行了,这里需要注意的是端口是服务器上面的新端口,不要与服务本身的web端口冲突了,可以在应用启动时传入下图中的JVM参数指定端口和远程debug配置。

remote debug

创建好以后,在IDEA打开你的远程debug,就可以愉快地Debug了。

不推荐在生产环境使用远程Debug


神器Arthas

Arthas是阿里开源的一款Java诊断工具,功能非常强大。官网上有一个在线的交互式教程,几分钟入门,非常方便。支持命令的自动补全、历史命令补全,管道等。这里介绍它的一些常用的功能。

Dashboard

Arthas的dashboard命令可以显示当前系统和JVM的实时状态。包括线程的状态、JVM的状态。

dashboard

查看线程

通过thread 1可以打印线程id为1的栈。而且Arthas也支持管道,可以对输出通过管道进行grep:

[arthas@37]$ thread 1
"main" Id=1 TIMED_WAITING
    at java.lang.Thread.sleep(Native Method)
    at java.lang.Thread.sleep(Thread.java:340)
    at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
    at demo.MathGame.main(MathGame.java:17)
Affect(row-cnt:0) cost in 9 ms.
[arthas@37]$ thread 1 | grep main(
    at demo.MathGame.main(MathGame.java:17)

查看方法的运行时信息

通过watch命令可以查看方法的运行时参数/返回值/异常信息。

[arthas@37]$ watch demo.MathGame primeFactors returnObj
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 40 ms.
ts=2020-05-10 02:53:46; [cost=1.129697ms] result=null
ts=2020-05-10 02:53:47; [cost=0.056254ms] result=null
ts=2020-05-10 02:53:48; [cost=0.056249ms] result=null
ts=2020-05-10 02:53:49; [cost=0.210126ms] result=@ArrayList[
    @Integer[2],
    @Integer[3],
    @Integer[5],
    @Integer[1039],
]
ts=2020-05-10 02:53:50; [cost=0.204834ms] result=@ArrayList[
    @Integer[3],
    @Integer[3],
    @Integer[3],
    @Integer[7],
    @Integer[839],
]

动态执行代码

这是我觉得Arthas里最强大的一个功能了!使用ognl命令可以让你的应用代码执行执行任意方法,用你制定的任何参数!比如:

[arthas@37]$ ognl '@java.lang.System@out.println("hello ognl")'

这个时候,你的应用代码会输出“hello ognl”。

Arthas还有很多非常强大的功能,它甚至可以热更新代码,就是动态替换你想替换的某个类!

当然,笔者觉得Arthas的命令强大归强大,使用起来还是有一些复杂的,毕竟是一个基于命令行的程序。IDEA有一个arthas idea插件,可以生成arthas命令,有兴趣的同学可以试试~

arthas idea

以上就是关于如何更快更方便地找bug的介绍。早一秒找到bug,就早一秒挽救损失,说不定就能升职加薪哦~

相关实践学习
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
目录
相关文章
|
XML 存储 数据可视化
Flowable学习笔记(一、入门)
Flowable学习笔记(一、入门)
3083 1
Flowable学习笔记(一、入门)
|
Java 数据库连接 Apache
Correct the classpath of your application so that it contains compatible versions of the classes com
Correct the classpath of your application so that it contains compatible versions of the classes com
|
机器学习/深度学习 人工智能 自然语言处理
【活动】人工智能:前沿科技中的创业机遇与挑战
本文探讨了人工智能领域的创业机遇与挑战。AI技术的快速发展,如深度学习、自然语言处理等,已广泛应用于医疗、金融、制造等行业。未来创业机会包括AI基础设施、垂直行业解决方案、伦理安全领域及AI与其他技术的融合创新。然而,创业者需面对技术壁垒、数据获取、市场接受度、商业模式创新及政策伦理挑战。要在AI领域成功创业,需紧跟技术趋势,深挖行业需求,创新商业模式,并妥善应对各种挑战。
1073 6
|
10月前
|
存储 缓存 安全
阿里云服务器计算型c7/c8y/c8i,通用型g7/g8y/g8i,内存型r7/r8y/r8i区别及选择参考
为了满足不同企业级用户的多样化需求,阿里云在当下的活动中推出了多款计算型、通用型和内存型的云服务器实例,包括计算型c7/c8y/c8i、通用型g7/g8y/g8i以及内存型r7/r8y/r8i等。这些实例各具特色,适用于不同的应用场景和业务需求。本文将为您详细解析这些实例的区别,以及选择参考,帮助您根据自己的需求选择合适的阿里云服务器实例。
|
JSON 前端开发 Java
Spring MVC——传递参数
本文介绍了在Spring框架中如何传递参数的方法,包括传递单个参数、多个参数、参数重命名、传递数组和集合以及JSON数据。对于单个参数,可以直接在方法中声明;多个参数无需关注传递顺序,只需确保参数名对应。使用`@RequestParam`注解可实现参数重命名,而传递数组和集合时需注意数据类型的转换。最后,通过`@RequestBody`注解可以处理JSON格式的数据,实现复杂对象的传递。
904 1
Spring MVC——传递参数
|
消息中间件 数据库
消息中间件系列教程(18) -RabbitMQ-基于RabbitMQ解决分布式事务(思想)
消息中间件系列教程(18) -RabbitMQ-基于RabbitMQ解决分布式事务(思想)
364 0
|
SQL 数据管理 数据库
|
前端开发
一张图搞懂盒子模型 margin padding
这篇文章通过一张图帮助读者理解CSS中的盒子模型,特别是`margin`和`padding`的概念,解决容易混淆这两个属性的问题。
|
负载均衡 NoSQL Java
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp微信小程序的社区垃圾回收管理系统的详细设计和实现
基于SpringBoot+Vue+uniapp微信小程序的社区垃圾回收管理系统的详细设计和实现
240 0
基于SpringBoot+Vue+uniapp微信小程序的社区垃圾回收管理系统的详细设计和实现