開發者經過 JVM 參數 `-XX:+UseGCLogFileRotation` 實現 GC 日誌輪轉。html
像下面這樣:shell
```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`。服務器
這種作法會帶來如下問題:app
1. 丟失舊的 GC 日誌ide
若是設置參數 `-XX:NumberOfGCLogFiles=5`,一段時間事後會建立5個 GC 日誌文件:工具
gc.log.0 ← `最老的 GC 日誌內容`spa
gc.log.1.net
gc.log.2日誌
gc.log.3code
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` 參數的全部缺點。