避免使用 -XX : +UseGCLogFileRotation

開發者通過 JVM 參數 `-XX:+UseGCLogFileRotation` 實現 GC 日誌輪轉。


像下面這樣:


```shell
"-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/home/GCEASY/gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M"
```


指定上述參數,當日志文件大小增加到 20MB,JVM 會進行 GC 日誌輪轉生成最多5個文件,擴展名分別爲 `gc.log.0`、`gc.log.1`、`gc.log.2`、`gc.log.3` 和 `gc.log.4`。


這種做法會帶來以下問題:


1. 丟失舊的 GC 日誌


如果設置參數 `-XX:NumberOfGCLogFiles=5`,一段時間過後會創建5個 GC 日誌文件:


  • gc.log.0 ← `最老的 GC 日誌內容`

  • gc.log.1

  • gc.log.2

  • gc.log.3

  • gc.log.4 ← `最新的 GC 日誌內容`


最新的 GC 日誌寫入 `gc.log.4`,而過去的 GC 日誌內容存入 `gc.log.0`。


使用 `-XX:NumberOfGCLogFiles` 配置時,如果應用不斷產生更多的 GC 日誌,`gc.log.0` 中的舊日誌內容會被刪除,新產生的 GC 事件將寫入 `gc.log.0`。這意味着日誌內容完整性早到破壞,即無法看到所有 GC 事件。


2. GC 日誌混合


假設某個應用創建了5個 GC 日誌文件:


  • gc.log.0

  • gc.log.1

  • gc.log.2

  • gc.log.3

  • gc.log.4


接着,如果重啓這個應用,現在新 GC 日誌會寫入 `gc.log.0`。重啓前的日誌信息保存在 `gc.log.1`, `gc.log.2`, `gc.log.3`, `gc.log.4` 中。


  • gc.log.0 ← 重啓後的 GC 日誌文件內容

  • gc.log.1 ← 重啓前的 GC 日誌文件內容

  • gc.log.2 ← 重啓前的 GC 日誌文件內容

  • gc.log.3 ← 重啓前的 GC 日誌文件內容

  • gc.log.4 ← 重啓前的 GC 日誌文件內容


因此,重啓後的 GC 新日誌與舊日誌混在一起。要解決這個問題,可以在重啓應用前把所有舊日誌移動到一個獨立的文件夾中。


3. 將 GC 日誌轉發到中央存儲


採用這種方法,當前寫入的日誌文件會標記 `.current` 擴展,例如當前寫入 `gc.log.3`,會命名爲 `gc.log.3.current`。


如果要把 GC 日誌從單個服務器彙總到中央存儲,大多數 DevOps 工程師會使用 `rsyslog`。然而,正如[這篇博客][1]討論的那樣,這種命名方式給 `rsyslog` 帶來了巨大挑戰。


[1]:http://www.planetcobalt.net/sdb/forward_gc_logs.shtml


4. 工具


現在,有許多像 [GCeasy][2]、GCViewer 這樣的工具可以用來分析 GC 日誌,支持上傳多個日誌文件。


[2]:https://gceasy.io/


5. 推薦的解決方案


當 JVM 重啓後可以爲 GC 日誌加上時間戳作爲後綴,這樣 GC 日誌文件路徑能夠保持唯一。這樣,新日誌就不會覆蓋掉舊的 GC 日誌。通過爲 GC 日誌文件指定 `%t` 後綴可以實現,像下面這樣:


```shell
"-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/home/GCEASY/gc-%t.log"
```


`%t` 後綴格式生成的時間戳格式爲 `YYYY-MM-DD_HH-MM-SS`。因此,生成的日誌文件名看起來像這樣 `gc-2019-01-29_20-41-47.log`。


這種簡單的方法解決了 `-XX:+UseGCLogFileRotation` 參數的所有缺點。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章