最近做了一个日志监控系统,要求动态的监控后台日志的变化并展示到其客户端(第一次要求读取文件末尾的500行数据,后面日志变化要追加到监控页面);经过分析,难点在于两个地方:
1.后台日志文件过大时,读取效率;
2.前台的展现方式,采用一种合适的控件动态的展现日志。
对于以上两个问题的解决:
1.采用java中的RandomAccessFile可以进行快速读取,其中seek方法可以快速定位到读取位置;
2.采用textarea来显示后台日志,并且日志变化时自动将滚动条显示到最后,这样可以看到最新的日志。
系统已经实现,经过测试,读取300M左右的文件效率很快,没有问题,核心代码如下:
1.后台读取文件模块
/**
*第一次读取文件时采用seek到文件的后500行位置,在逐行读取,html textarea在ie8下的换行符为"\r\n"
*/
private static String getFirstReadInfo(File f) throws IOException {
String lastModifyTime = FileQueryDao.SDF_DETAIL.format(new Date(f
.lastModified()));
RandomAccessFile rf = new RandomAccessFile(f, "r");
int totalReadRows = FIRST_READ_ROW_NUM;
long len = f.length();
long pos = len - 1;
while (pos > 0) {
pos--;
rf.seek(pos);
if (rf.readByte() == '\n') {
totalReadRows--;
}
if (totalReadRows == 0) {
break;
}
}
if (pos == 0) {
rf.seek(0);
} else {
rf.seek(pos);
}
StringBuffer contentBuffer = new StringBuffer();
String content = null;
while ((content = rf.readLine()) != null) {
contentBuffer.append(new String(content.getBytes("iso8859-1"),
"GBK"));
contentBuffer.append("\r\n");
}
rf.close();
return lastModifyTime + "," + len + ";" + contentBuffer.toString();
}
/**
*以后每次读取从上次的记录文职读取,读取后将位置返回
*/
private static String readFileFromPos(File f, long pos) throws IOException {
RandomAccessFile rf = new RandomAccessFile(f, "r");
rf.seek(pos);
StringBuffer contentBuffer = new StringBuffer();
String content = null;
while ((content = rf.readLine()) != null) {
contentBuffer.append(new String(content.getBytes("iso8859-1"),
"GBK"));
contentBuffer.append("\r\n");
}
long nowPos = rf.getFilePointer();
rf.close();
return nowPos + ";" + contentBuffer.toString();
}
2.前台ajax返回处理
function append(str){
var textArea=$('taid');
var child=document.createTextNode(str);
textArea.appendChild(child);
if(textArea.style.dispaly!='none'){
setFocusLast(textArea);
}
}
function setFocusLast(obj){
obj.focus();
var r = obj.createTextRange();
r.moveStart("character",obj.value.length);
r.collapse(true);
r.select();
}
关键的一点,每次读取文件后要记录下本机的seek位置,下次从这个位置重新读取来获取新的日志。