Java项目调试技巧及版本控制

开发项目中,调试是必不可少的。

本篇博客的代码举例均为该系列博客涉及的项目:社交网站后端项目开发日记(一)

本篇博客从以下4个方面介绍项目调试技巧:

  • 响应状态码的含义
  • 服务端断点调试技巧
  • 客户端断点调试技巧
  • 设置日志级别,并将日志输出到不同的终端

以及,最后简单的介绍了一下git的使用。

1. 项目调试技巧

项目调试过程中,web项目首先看HTTP状态响应码,找是客户端还是服务端的错误,看看日志信息有没有错误信息,如果没有找到,进行断点调试,这是一个大概的流程。

1.1 响应状态码的含义

接下来介绍几个最常见的响应状态码,参考网址://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status

HTTP 响应状态代码指示特定 HTTP 请求是否已成功完成。响应分为五类:信息响应(100199),成功响应(200299),重定向(300399),客户端错误(400499)和服务器错误 (500599)。状态代码由 section 10 of RFC 2616定义

打开任意一个网址,都会有如下信息:

image-20210720002539703

200 OK (成功响应)

请求成功。成功的含义取决于HTTP方法:

  • GET:资源已被提取并在消息正文中传输。
  • HEAD:实体标头位于消息正文中。
  • POST:描述动作结果的资源在消息体中传输。
  • TRACE:消息正文包含服务器收到的请求消息

302 Found (重定向)

请求的资源现在临时从不同的 URI 响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。

介绍一下重定向:假如说网页上进行删除功能,这个时候删除完毕,是不需要返回一个html的,但是生活中常见的会发生什么?一般删除之后,网页会回到一个地址,比如说首页或者说回到查询页面。这个就是重新进行定位。另外一个例子:我们注册之后,一般直接会跳转到登录页面。

image-20210720001717638

服务器进行删除功能之后,返回302状态码,以及一个新的路径。至于为什么删除之后没有直接进行查询功能呢?项目中不同功能之间要保持松耦合,不能形成依赖。

404 Not Found (客户端响应)

请求失败,请求所希望得到的资源未被在服务器上发现。没有信息能够告诉用户这个状况到底是暂时的还是永久的。假如服务器知道情况的话,应当使用410状态码来告知旧资源因为某些内部的配置机制问题,已经永久的不可用,而且没有任何可以跳转的地址。404这个状态码被广泛应用于当服务器不想揭示到底为何请求被拒绝或者没有其他适合的响应可用的情况下。

500 Internal Server Error (服务端响应)

服务器遇到了不知道如何处理的情况。这时候应该去检查服务端的程序。

1.2 服务端断点调试技巧

以本人博客的后端项目开发代码为例,介绍IDEA的调试技巧。

image-20210720005218639

    1. 设置断点,进入debug模式

    2. 访问URL,浏览器会一直处理,因为服务端程序停留在断点那一步image-20210720005310828

    3. 调试界面如下(step over只直接跳转到程序的下一行,不进入方法,step into则是进入方法进行跳转下一行):

      image-20210720005441254

      如果想跳过循环条件,毕竟循环可能会循环很多次。可以进行Resume Program恢复程序,比如,你在第20行和25行有两个断点,当前运行至第20行,按F9,则运行到下一个断点(即第25行),再按F9,则运行完整个流程,因为后面已经没有断点了。

其中,debug部分,View Breakpoints可以进行断点管理,可以看到所有断点,进行是否使用的设置。

image-20210720010356448

1.3 客户端断点调试技巧

image-20210720010639855

浏览器F12进入开发者模式,其中

Elements调试的是页面,前端部分会用到。Console可以看到JS输出的结果。Source可以看客户端执行的代码。

image-20210720010900931

其实这里设置断点和IDEA相同,点击代码行左侧设置断点,当使用该部分功能时,会呈现如下效果:

image-20210720010951887

在Source右侧,和IDEA相同,还是有step over和step into等操作。逻辑都是相同的。

image-20210720011143468

1.4 设置日志级别

SpringBoot的日志工具是logback,官网为://logback.qos.ch/

image-20210720011830431

logger中不同的日志级别:

  • trace跟踪级别
  • debug调试级别
  • info普通级别
  • warn警告级别
  • error错误级别

这五个级别从低到高顺序排列。例:开启Info级别,则trace和debug不会打印出来,只有更高级别的日志会打印出来。级别设置是为了提高性能。

接下来举个例子:(SpringBoot中在application.properties设置Logger级别即可)

logging.level.com.nowcoder.community=debug

举测试类:

public class LoggerTests {
    //为了便于所有的方法去调用,一般设置为静态,不可改变的,注意使用org.slf4j包下的
    //传入的类即logger的名字,一般传入当前类,这样便于区别不同的Logger
    private static final Logger logger = LoggerFactory.getLogger(LoggerTests.class);

    @Test
    public void testLogger() {
        System.out.println(logger.getName());
        logger.debug("debug log");
        logger.info("info log");
        logger.warn("warn log");
        logger.error("error log");
    }
}

这是在logger为debug级别下进行的测试:

image-20210720013318371

更高级别的log都有显示。

级别改为warn后:

image-20210720013548956

一般来说,项目开发过程中采用debug级日志,方便调试。上线后一般采用更高级别。并且上线后都不会有控制台了,这就需要我们把日志文件给打印出来。

1.4.1. 直接在application.properties中配置

过程:在properties进行配置

logging.file.path=sp.log

该配置会在同级目录下生成sp.log文件夹,里面存储spring.log

(不知为何,logging.file这句配置在我这里失效了,暂未找到原因,SpringBoot2.5.1)

这种方式会混杂各种类型的日志,而且可能文件比较大,建议将各种级别的日志放在不同的文件中,介绍第二种配置方式。

1.4.2 logback-spring.xml配置

在resources目录下,SpringBoot会自动识别该命名的.xml文件并进行配置,注意如果命名不同则不会识别。

以error级别log文件配置为例:(已加注释,请阅读源码)

<contextName>community</contextName>
<!-- log文件存放地址,这里相当于一个string -->
<property name="LOG_PATH" value="D:/javawork/data"/>
<property name="APPDIR" value="community"/>

<!-- error file -->
<appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!-- 声明地址 -->
    <file>${LOG_PATH}/${APPDIR}/log_error.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!-- 命名格式,%d为日期, %i是一个变量,如0,1,2等 -->
        <fileNamePattern>${LOG_PATH}/${APPDIR}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
        <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
            <!-- 一个Log最大为5MB,如果存不下再存新的 -->
            <maxFileSize>5MB</maxFileSize>
        </timeBasedFileNamingAndTriggeringPolicy>
        <!-- 最长存储时间30天 -->
        <maxHistory>30</maxHistory>
    </rollingPolicy>
    <!-- 以追加的方式存储而不是覆盖 -->
    <append>true</append>
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <!-- 定义日志输出格式%d日期,%level级别, [%thread]哪个线程执行,%logger{10}logger所处的类,[%file:%line] 所处哪个文件多少行 -->
        <!-- %msg%n消息内容 -->
        <pattern>%d %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
        <charset>utf-8</charset>
    </encoder>
    <!-- 过滤器,过滤error级日志 -->
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>error</level>
        <!-- 这里的意思是匹配到就接收,不匹配就拒绝 -->
        <onMatch>ACCEPT</onMatch>
        <onMismatch>DENY</onMismatch>
    </filter>
</appender>

其他级别的Log文件配置类似,可自行配置。

其中,也可以配置如何打印控制台Log信息,例:

<!-- console -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>%d %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
        <charset>utf-8</charset>
    </encoder>
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>debug</level>
    </filter>
</appender>

在.xml文件末尾需要声明:

<logger name="com.nowcoder.community" level="debug"/>
<root level="info">
    <appender-ref ref="FILE_ERROR"/>
    <appender-ref ref="FILE_WARN"/>
    <appender-ref ref="FILE_INFO"/>
    <appender-ref ref="STDOUT"/>
</root>

root代表项目根目录,因为项目中包含着非常多的包,所以级别为info即可,debug级将会多出许多不必要的log。其中,logger会将日志信息传给root,root再根据appender进行打印。具体信息可参考官方网站,没有细说。

中间4行代表启用上述的error等各个级别的文件配置。

项目中的日志配置参考该模板即可。

2. 版本控制

分布式版本控制系统,在这类系统中,像 Git、Mercurial、Bazaar 以及 Darcs 等,客户端并不只提取最新版本的文件快照, 而是把代码仓库完整地镜像下来,包括完整的历史记录。 这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。 因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份。

image-20210723005512820

这部分介绍git相关。便于备份代码,或者在开发流程中共享代码。是团队开发中非常重要的工具。

首先介绍相关命令(windows命令):个人建议使用git.bash,使用linux命令,毕竟linux在将来的开发中还会有应用。

image-20210723004647485

参考网址:Git官网

如果想详细了解git知识,建议阅读(中文版)://git-scm.com/book/zh/v2

具体原理及应用可见:B站尚硅谷教学视频

我个人的操作一般是:

    1. 在github上创建仓库,因为一般仓库中都有readme这些信息文件,所以将仓库克隆到本地(你需要上传的仓库地址)命令:git clone url
    2. 将需要上传的文件复制进克隆的文件夹里
    3. cd 文件地址,输入命令
    git add .  //添加所有文件
    git commit -m "需要提交的信息" //提交到本地仓库
    git push -u origin main //将本地仓库push到远程的main分支,也可以push到master分支
    

    因为我个人使用了两个github,所以一个采用ssh密钥,上述方式介绍的是http协议上传方式,使用账号密码即可。

    除此之外的方式还有利用fetch和push等等,我介绍的方式比较适用于初学者。

IDEA配置git:

image-20210723015439977

改为自己的git路径。

IDEA的VCS中有git的各个操作,首先如图Create Git Repository,

image-20210723015504904

然后Git中有commit,选中要提交的文件,不用选中全部,比如maven那些包其实没必要提交。

commit之后,Git中有push,填上远程仓库的url以及登录仓库即可。