Java - RandomAccessFile (эмуляция хвостовой функции Linux)

В Java IO реализация unix/linux "tail -f" имеет похожую проблему; но решение не подходит для файлов журналов, которые генерируют около 50-100 строк в секунду.

У меня есть алгоритм, который имитирует функциональность хвоста в Linux. Например,

File _logFile = new File("/tmp/myFile.txt");
long _filePtr = _logFile.length();

while (true)
{
     long length = _logFile.length();
     if (length < _filePtr)
     {
            // means file was truncated
     }
     else if (length > _filePtr)
     {
            // means something was added to the file
     } 
// we ignore len = _filePtr ... nothing was written to file
}

Моя проблема, когда: "что-то было добавлено в файл" (ссылаясь на оператор else if ()).

else if (length > _filePtr)
{
     RandomAccessFile _raf = new RandomAccessFile(_logFile, "r");
     raf.seek(_filePtr);

     while ((curLine = raf.readLine()) != null)
           myTextPane.append(curLine);

        _filePtr = raf.getFilePointer();
        raf.close();
}

Программа блокируется в то время как ((curLine = raf.readLine()).... после 15 секунд работы! (Примечание: программа работает в течение первых 15 секунд).

Похоже, что raf.readLine() никогда не достигает NULL, потому что я считаю, что этот файл журнала пишется так быстро, что мы заходим в цикл "бесконечный кот и мышь".

Какой лучший способ подражать хвосту Linux?

3 ответа

Решение

Я думаю, что вам лучше всего получить блок байтов в зависимости от длины файла, а затем освободить файл и проанализировать ByteArrayInputStream (вместо попытки чтения непосредственно из файла).

Поэтому используйте RandomAccessFile#read(byte[]) и размер буфера, используя длину возвращаемого файла. Вы не всегда будете показывать точный конец файла, но этого следует ожидать с помощью такого алгоритма опроса.

Кроме того, этот алгоритм ужасен - вы выполняете операции ввода-вывода в сумасшедшем узком цикле - вызовы File#length() будут блокироваться, но не очень сильно. Ожидайте, что эта рутина поставит ваше приложение на колени с точки зрения процессора. У меня не обязательно есть лучшее решение для вас (ну, собственно, да, исходное приложение записывает в поток, а не в файл, но я понимаю, что это не всегда возможно).

В дополнение к вышесказанному, вы можете захотеть ввести задержку опроса (спите поток на 100 мс в каждом цикле - мне кажется, что вы выводите на графический интерфейс - задержка в 100 мс никому не повредит, и значительно улучшит производительность качели).

хорошо - финальная фраза: вы настраиваете компонент Swing, исходя из того, что (я надеюсь) код не работает на EDT. Используйте SwingWorker#invokeLater() для обновления вашей текстовой панели.

Похоже, я нашел проблему и создал решение.

Под утверждением else if:

while ((curLine = raf.readLine()) != null)
    myTextPane.append(curLine);

Это была проблема. метод append(String) в myTextPane (который является производным классом JTextPane) вызывал setCaretPosition() в каждой строке append, которая ПЛОХАЯ!!

Это означало, что setCaretPosition() вызывался 50-100 Гц, пытаясь "прокрутить вниз". Это вызвало блокировку интерфейса.

Простым решением было создать класс StringBuffer и добавить "curLine", пока raf.readLine() не прочитает ноль.

Затем добавьте StringBuffer и вуаля... больше не нужно блокировать функцию setCaretPosition()!

Спасибо Кевину за то, что он направил меня в правильном направлении.

Вы всегда можете выполнить программу tail:

    BufferedReader in = new BufferedReader(new InputStreamReader(
            Runtime.getRuntime().exec("tail -F /tmp/myFile.txt").getInputStream()));
    String line;
    while ((line = in.readLine()) != null) {
        // process line
    }
Другие вопросы по тегам