Java專案除錯技巧及版本控制

GaoYuan206 發表於 2021-07-23
Java

開發專案中,除錯是必不可少的。

本篇部落格的程式碼舉例均為該系列部落格涉及的專案:社交網站後端專案開發日記(一)

本篇部落格從以下4個方面介紹專案除錯技巧:

  • 響應狀態碼的含義
  • 服務端斷點除錯技巧
  • 客戶端斷點除錯技巧
  • 設定日誌級別,並將日誌輸出到不同的終端

以及,最後簡單的介紹了一下git的使用。

1. 專案除錯技巧

專案除錯過程中,web專案首先看HTTP狀態響應碼,找是客戶端還是服務端的錯誤,看看日誌資訊有沒有錯誤資訊,如果沒有找到,進行斷點除錯,這是一個大概的流程。

1.1 響應狀態碼的含義

接下來介紹幾個最常見的響應狀態碼,參考網址:https://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,官網為:http://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知識,建議閱讀(中文版):https://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以及登入倉庫即可。