Python實戰社羣
Java實戰社羣
長按識別下方二維碼,按需求添加
掃碼關注添加客服
進Python社羣▲
掃碼關注添加客服
進Java社羣▲
來源:小姐姐味道(微信公衆號ID:xjjdog)
tail命令能夠看到日誌的滾動,非常方便。於是xjjdog
想,既然我們能夠用這個命令,看到所有的日誌,那能不能使用tail命令,做日誌收集呢?
想象歸想象,如果你想要一個快速的實時
日誌收集工具,那tail確實是個非常棒的工具。它比什麼flume、logstatsh,比什麼filebeat之類的,快捷的多。事實上,在工具缺乏的舊年代,我就曾經這麼幹過,而且它工作的很好。
下面是一段使用Java語言書寫的代碼。我們可以按行讀取日誌,然後使用自己喜歡的語言,做任何事情。
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class TailReader {
public static void main(String[] args) throws Exception {
ProcessBuilder ps = new ProcessBuilder("tail", "-f", "/tmp/tail0");
//把錯誤輸出也打印
ps.redirectErrorStream(true);
Process process = ps.start();
//持續讀取tail的輸出
try (BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = in.readLine()) != null) {
setLogToKafka(line);
//注意這裏不要產生異常,否則會打斷while循環
}
}
}
//模擬發送到kafka,我們這裏只簡單的打印出來
static void setLogToKafka(String line) {
System.out.println(line);
}
}
主要的思想,就是使用Java的Process啓動一個子tail進程,一直監控着文件的輸出。然後把標準輸出和標準錯誤流,全部定向到BufferedReader中。接下來,你能做你想要做的任何事。
這有一定的風險,假如tail命令被殺掉了,我們的Java程序就失去了作用。
程序很簡單,但xjjdog
在這裏討論的卻不是這個簡單的收集程序,而是tail
命令的一些有趣的特性,你可以從中一窺一些日誌收集工具對文件的特殊處理。
你知道tail -f和tail -F的區別麼?
在回答這個問題之前,我們先回憶一下,Java常用的日誌框架,對日誌的處理。
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- Support multiple-JVM writing to the same log file -->
<prudent>true</prudent>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
上面的配置,將在每晚凌晨的時候,滾動形成一個新的文件。
那這個滾動,是如何做的呢?我們可以收工模擬這個過程。
mv run.log run.2020-11-02.log
touch run.log
測試一下
文件滾動,會生成新的文件,那tail命令還能跟蹤到麼?
我們來測試一下。
第一步,創建要監控的文件
touch /tmp/tail0
第二步,啓動我們的Java代碼
第三步,生成一個不間斷的流
watch -n 1 'echo `date` >> /tmp/tail0 '
上面的命令每隔1秒鐘,往我們的文件中打印一下當前的日期,可以看到Java端已經收到了這些數據。
第四步,模擬文件滾動
mv /tmp/tail0 /tmp/tail.bak
touch /tmp/tail0
此時,我們可以看到,Java端此時已經接受不到數據了。
Why?
爲了看到這是爲什麼,我們使用兩個命令來看一下進程的一些狀態。
首先,使用ps命令,查看當前的tail進程。
ps -ef|grep tail
501 21374 21373 0 1:51PM ?? 0:00.01 tail -f /tmp/tail0
這正是我們的命令。
我們使用lsof命令去查看這個進程所關聯的文件。
lsof -p 21374 | awk '{print $4 "\t" $9}'
FD NAME
cwd /tmp/
txt /usr/bin/tail
txt /usr/lib/dyld
3r /private/tmp/tail.bak
我們看到tail進程所監控的文件,其實是tail.bak文件,已經和tail命令沒什麼關係了。
我們嘗試像tail.bak輸入一點內容。
echo "haha: xjjdog, i am from tail.bak" >> /tmp/tail.bak
此時如我們所願,Java進程有反應了,正常輸出了這句話。
怎麼辦?
就如同我們問題中問的一樣,把tail -f
換成tail -F
就可以了。
tail -f
的意思是,根據文件描述符進行追蹤。
tail -F
的意思是,根據文件名進行追蹤,它會有重試的動作。
所以,我們的日誌收集程序,毫無疑問是根據日誌名稱追蹤的,應該把f
改成F
。
End
既然知道了這些小區別,我們就對日常工作中遇到的一些靈異問題有了解釋。
大家都知道rm命令,能夠刪除一個文件。如果有這個文件,正在被其他進程所使用,那這些文件你看起來像是刪掉了,但它的內容卻不釋放。
lsof | grep deleted
上面這個命令,能夠看到這些失控的文件。一般你kill掉相應的進程,這些句柄也就釋放了。但你刪除這些文件的本意,就是爲了避免重啓應用,這可真讓人糾結。
cat /dev/null > logpath
所以我們在刪除文件的時候,一般不會使用rm,而應該使用重定向符號。將萬物皆空的/dev/null
,發向它們。
作者簡介:小姐姐味道 (xjjdog),一個不允許程序員走彎路的公衆號。聚焦基礎架構和Linux。十年架構,日百億流量,與你探討高併發世界,給你不一樣的味道。
程序員專欄 掃碼關注填加客服 長按識別下方二維碼進羣
近期精彩內容推薦:
在看點這裏好文分享給更多人↓↓