該工具可實現對日志文件的分析,可以方便地對日志文件進行類似於數據庫查詢統計一樣的操作。主要功能包括:
- 條件篩選:包含、不包含、相等、不等、大於、大於等於、小於、小於等於、正則匹配
- 不同條件的“或”與“並”關系可靈活控制
- 函數:可以通過自定義
AttPreProcessor
對字段進行預處理,以此來實現“函數”能力 - 分組求值:最大值、最小值、平均值、求和
- 支持對統計結果的排序
- 使用Limit控制輸出數量
- 可同時處理多個文件,支持文件名通配符
按空格split
分析器認為每一行由空格分隔的每一段為一個列,例如:
120.244.106.255 - - 15/04/2019:01:38:49 +0800 "GET / HTTP/1.1" 302 -
120.244.106.255 - - 15/04/2019:01:38:49 +0800 "GET /auth.do HTTP/1.1" 302 -
120.244.106.255 - - 15/04/2019:01:38:49 +0800 "GET /loginUI.do HTTP/1.1" 302 -
120.244.106.255 - - 15/04/2019:01:39:04 +0800 "GET /loginUI.do HTTP/1.1" 200 13881
120.244.106.255 - - 15/04/2019:01:39:04 +0800 "GET /admin/filee/1ec45f5bfbd718a0e5b93a9 HTTP/1.1" 200 39735
120.244.106.255 - - 15/04/2019:01:39:05 +0800 "GET /img/favicon/favicon.ico HTTP/1.1" 404 1040
120.244.106.255 - - 15/04/2019:01:39:33 +0800 "GET /admin/filee/5f5bfbd718a0e5b93a9 HTTP/1.1" 200 39735
222.137.114.157 - - 15/04/2019:04:27:01 +0800 "GET / HTTP/1.1" 302 -
222.137.114.157 - - 15/04/2019:04:27:02 +0800 "GET / HTTP/1.1" 302 -
222.137.114.157 - - 15/04/2019:04:27:04 +0800 "GET / HTTP/1.1" 302 -
222.137.114.157 - - 15/04/2019:04:27:07 +0800 "GET / HTTP/1.1" 302 -
222.137.114.157 - - 15/04/2019:04:27:14 +0800 "GET / HTTP/1.1" 302 -
205.205.150.10 - - 15/04/2019:07:19:15 +0800 "GET / HTTP/1.1" 302 -
205.205.150.10 - - 15/04/2019:07:19:15 +0800 "GET /auth.do HTTP/1.1" 302 -
205.205.150.10 - - 15/04/2019:07:19:15 +0800 "GET /loginUI.do HTTP/1.1" 302 -
177.93.97.240 - - 15/04/2019:08:22:23 +0800 "GET / HTTP/1.1" 302 -
...
比如第一行,從0開始,各列的值為:
- 0:120.244.106.255
- 1:-
- 2:-
- 3:15/04/2019:01:38:49
- 4:+0800
- 5:"GET
- 6:HTTP/1.1"
- 7:302
- 8:-
使用分析器進行列選的時候,給出的是行的index。如果某一行按空格split之后,長度不夠給定的index,那么這一行就會被忽略。
初始化
主要工具類FileScaner
,可以同時指定由逗號分隔的多個路徑,並且支持通配符:
scaner = new FileScaner("/usr/local/tomcat/logs/localhost_access_log*.txt,/usr/local/tomcat2/logs/localhost_access_log*.txt");
字符匹配
用where查詢狀態碼為404的訪問記錄:
@Test
public void testWhere()throws Exception{
scaner.select("0,3,6,8") //選擇的列
.where(Conditions.contains("8", "404")) //指定列的篩選條件
.list();
scaner.print();
}
組合條件
多個查詢條件的情況,例如查詢狀態碼為404或者500的訪問記錄:
@Test
public void testMoreCondition()throws Exception{
scaner.select("0,3,6,8")
.where(Conditions.or(Conditions.eq("8", "404"),Conditions.eq("8", "500")))
.list();
scaner.print();
}
計數統計
統計404總共出現的次數:
@Test
public void testWhere()throws Exception{
scaner.select("8,count(1)")
.where(Conditions.contains("8", "404"))
.list();
scaner.print();
}
除了count,還支持sum、max、min和avg。這里不再舉例。
count中的1其實是沒意義的,既不表示列的index,並且寫成
count(0)
並不會少計數。其他統計函數則是有意義的,表示的是列的索引。
函數:自定義AttPreProcessor預處理行為
例如,我們想將日志文件中的時間,去掉秒,只精確到分鍾,只需要實現AttPreProcessor接口即可:
@Test
public void testAttPreProcessor()throws Exception{
AttPreProcessor processor = new AttPreProcessor() {
@Override
public String process(String att) {
Date date = DateUtil.parse(att);
if(date==null){
return "";
}
return DateUtil.format(date, "yyyy-MM-dd HH:mm");
}
};
scaner.select("0,3,6")
.processor(3, processor)
.where(Conditions.contains("8", "404"))
.list();
scaner.print();
}
結果類似如下,可以看到秒已經被去掉了:
[120.244.106.255, 2019-03-15 01:39, /img/favicon/favicon.ico]
[216.245.197.254, 2019-03-15 08:32, /robots.txt]
[223.72.82.114, 2019-03-15 13:52, /img/favicon/favicon.ico]
[223.72.82.114, 2019-03-15 15:03, /img/favicon/favicon.ico]
[5.8.55.40, 2019-03-15 16:32, /index.php?x=HelloThinkPHP]
...
分組
使用groupBy來進行分組統計。
注意: groupBy中的索引值是select中的列的的索引值。比如下面的代碼中,select的值是“3,count(1)",一個選擇了2列,需要以第1列排序,所以需要groupBy("0")
。
@Test
public void testGroupBy()throws Exception{
AttPreProcessor processor = new AttPreProcessor() {
@Override
public String process(String att) {
Date date = DateUtil.parse(att);
if(date==null){
return "";
}
return DateUtil.format(date, "yyyy-MM-dd HH:mm");
}
};
scaner.select("3,count(1)")
.processor(3, processor)
.groupBy("0")
.list();
scaner.print();
}
輸出效果:
[2019-03-15 01:38, 14.0]
[2019-03-15 04:27, 10.0]
[2019-03-15 07:19, 6.0]
[2019-03-15 08:22, 2.0]
[2019-03-15 08:32, 2.0]
[2019-03-15 09:28, 34342.0]
[2019-03-15 10:34, 4.0]
[2019-03-15 12:42, 2.0]
...
[2019-03-15 17:16, 20.0]
[2019-03-18 03:29, 1.0]
[2019-03-18 07:45, 1.0]
[2019-03-18 07:55, 1.0]
[2019-03-18 10:25, 47.0]
[2019-03-18 10:25, 2.0]
[2019-03-18 11:04, 1.0]
[2019-03-18 11:34, 376.0]
[2019-03-18 11:44, 6.0]
[2019-03-18 12:47, 2.0]
...
[2019-03-18 17:07, 41503.0]
[2019-03-18 19:09, 1.0]
[2019-03-18 21:08, 2.0]
[2019-03-20 13:42, 1.0]
[2019-03-20 13:42, 1.0]
排序
orderBy中可以有多個排序,排序的index和groupBy同理,是只選擇結果的列index。下面的代碼按每分鍾訪問量降序排列:
@Test
public void testOrderBy()throws Exception{
AttPreProcessor processor = new AttPreProcessor() {
@Override
public String process(String att) {
Date date = DateUtil.parse(att);
if(date==null){
return "";
}
return DateUtil.format(date, "yyyy-MM-dd HH:mm");
}
};
scaner.select("3,count(1)")
.processor(3, processor)
.groupBy("0")
.orderBy(OrderBy.desc(1))
.list();
scaner.print();
}
排序結果類似:
[2019-03-18 17:07, 41503.0]
[2019-03-15 09:28, 34342.0]
[2019-03-15 13:52, 582.0]
[2019-03-18 11:34, 376.0]
[2019-03-18 13:48, 267.0]
[2019-03-18 10:25, 47.0]
[2019-03-18 14:16, 33.0]
...
[2019-03-18 14:29, 9.0]
[2019-03-18 14:29, 8.0]
[2019-03-15 07:19, 6.0]
[2019-03-18 11:44, 6.0]
[2019-03-15 10:34, 4.0]
[2019-03-15 14:00, 3.0]
...
limit:限制輸出數量
如果只關心訪問量最高的10個記錄,可以這樣寫:
@Test
public void testLimit()throws Exception{
AttPreProcessor processor = new AttPreProcessor() {
@Override
public String process(String att) {
Date date = DateUtil.parse(att);
if(date==null){
return "";
}
return DateUtil.format(date, "yyyy-MM-dd HH:mm");
}
};
scaner.select("3,count(1)")
.processor(3, processor)
.groupBy("0")
.orderBy(OrderBy.desc(1))
.limit(10)
.list();
scaner.print();
}
輸出結果類似:
[2019-03-18 17:07, 41503.0]
[2019-03-15 09:28, 34342.0]
[2019-03-15 13:52, 582.0]
[2019-03-18 11:34, 376.0]
[2019-03-18 13:48, 267.0]
[2019-03-18 10:25, 47.0]
[2019-03-18 14:16, 33.0]
[2019-03-15 17:16, 20.0]
[2019-03-15 01:38, 14.0]
[2019-03-15 16:32, 14.0]
希望對你有用,歡迎討論!