Проблема RandomAccessFile - PullRequest
       28

Проблема RandomAccessFile

3 голосов
/ 20 декабря 2010

Мне нужно прослушать файл, когда его содержимое будет добавлено, я прочитаю новую строку и поработаю с содержимым новой строки. Длина файла никогда не уменьшится (фактически это файл журнала tomcat).

Я использую следующие коды:


import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

import org.apache.log4j.Logger;

import com.zjswkj.analyser.ddao.LogEntryDao;
import com.zjswkj.analyser.model.LogEntry;
import com.zjswkj.analyser.parser.LogParser;

public class ListenTest {
    private RandomAccessFile    raf;
    private long                lastPosition;
    private String              logEntryPattern = "^([\\d.]+) (\\S+) (\\S+) \\[([\\w:/]+\\s[+\\-]\\d{4})\\] \"(.+?)\" (\\d{3}) (\\S+) \"([^\"]+)\" \"([^\"]+)\"";
    private static Logger       log             = Logger.getLogger(ListenTest.class);

    public void startListenLogOfCurrentDay() {

        try {
            if (raf == null)
                raf = new RandomAccessFile(
                        "/tmp/logs/localhost_access_log.2010-12-20.txt",
                        "r");
            String line;
            while (true) {
                raf.seek(lastPosition);
                while ((line = raf.readLine()) != null) {
                    if (!line.matches(logEntryPattern)) {
                        // not a complete line,roll back
                        lastPosition = raf.getFilePointer() - line.getBytes().length;
                        log.debug("roll back:" + line.getBytes().length + " bytes");
                        if (line.equals(""))
                            continue;
                        log.warn("broken line:[" + line + "]");
                        Thread.sleep(2000);
                    } else {
                        // save it
                        LogEntry le = LogParser.parseLog(line);
                        LogEntryDao.saveLogEntry(le);
                        lastPosition = raf.getFilePointer();
                    }
                }
            }
        } catch (FileNotFoundException e) {
            log.error("can not find log file of today");
        } catch (IOException e) {
            log.error("IO Exception:" + e.getMessage());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new ListenTest().startListenLogOfCurrentDay();
    }
}

Теперь, моя проблема в том, что, если строка, которая записывается в новую строку файла, не завершена, возникнет мертвая петля.

Например, если кот пытается записать в файл новую строку:

10.33.2.45 - - [08/Dec/2010:08:44:43 +0800] "GET /poi.txt HTTP/1.1" 200 672 "-" "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8"

И когда пишется только одна часть строки (например: <<strong> 10.33.2.45 - - [08 / Dec / 2010: 08: 44: 43 +0800] "GET /poi.txt HTTP / 1.1 «200 672 >), теперь, поскольку он не может соответствовать определенному мной шаблону, то есть tomcat не завершает свою работу по записи, поэтому я попытаюсь откатить указатель файла и перевести 2 секунды в спящий режим, а затем снова прочитать .

Во время сна последняя часть строки может быть еще написана (на самом деле я пишу их, а не tomcat для теста), по моему мнению, randomaccessfile будет читать новую строку, которая может соответствовать шаблону, однако, похоже, что нет .

Кто-нибудь может проверить коды?

ПРИМЕЧАНИЕ : формат файла журнала «комбинируется» следующим образом:

10.33.2.45 - - [08/Dec/2010:08:44:43 +0800] "GET /poi.txt HTTP/1.1" 200 672 "-" "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8"

Ответы [ 4 ]

3 голосов
/ 24 декабря 2010

Я вижу (из вашего кода), что ваша основная задача - отфильтровать записи / события журнала и затем записать отфильтрованные журналы в базу данных. У вас есть 2 варианта

Вариант 1: Лучший и правильный способ сделать. Но вы должны быть в состоянии изменить файл конфигурации log4j, который поставляется с tomcat

Если это так, то лучший способ сделать это - использовать предопределенные точки расширения log4j. В вашем случае точка подключения - это Appender

Log4j уже поставляется с DBAppender , который вы, возможно, захотите расширить, чтобы отфильтровать журналы с помощью вашего регулярного выражения, а затем делегировать остальное DBAppender , поскольку он хорошо протестирован. Ниже приведен пример того, как настраивать приложение appme для обычного пользователя

log4j.rootLogger = DEBUG, S

log4j.appender.S = com.gurock.smartinspect.log4j.MyCustomAppender

log4j.appender.S.layout = org.apache.log4j.SimpleLayout

Я предлагаю вам также взглянуть на использование AsyncAppender и DBAppender, если вы хотите улучшить производительность.

Опция 2: Резервная опция, если у вас нет доступа к файлу конфигурации log4j tomcat

Вместо того, чтобы писать свой собственный слушатель изменения файла, посмотрите этот пост в SO . Выберите тот, который наилучшим образом соответствует вашим потребностям. После этого вам остается только написать код для фильтрации и сохранения журнала в БД. Вы можете использовать эту ссылку в качестве примера для работы с RandomAccessFile.

0 голосов
/ 09 ноября 2013

readline RAF является методом блокировки и неэффективен (читает побайтово и делает так много системных вызовов). Также обратите внимание, что в вашем коде lines.getBytes (). Length не может быть точно использован, так как метод readLine пропускает возврат новой строки / возврата каретки.chars.

Чтобы использовать BufferedReader на RAF, проверьте мой ответ здесь https://stackoverflow.com/a/19867481/1282907

0 голосов
/ 27 декабря 2010

Первое, что я хотел бы сделать в этой ситуации, это отделить проблему чтения растущего файла от проблемы обработки строк.

Создать класс GrowingFileReader, чей метод readLine делает то, что выхочу.Тогда остальная часть кода становится проще.

В случае неудачного совпадения, зачем вообще обновлять lastPosition?Разве это не должно быть оставлено как есть?

0 голосов
/ 24 декабря 2010

Я думаю, что это не очень хороший способ проверки новых добавленных строк. Я рекомендую вам написать собственный appender для log4j. С помощью специального приложения вы можете получать все новые добавленные строки с событием. Здесь есть образец здесь

И Google для пользовательского приложения.

...