Как разобрать логи, написанные несколькими потоками? - PullRequest
0 голосов
/ 07 ноября 2008

У меня есть интересная проблема, и я был бы признателен за ваши мысли для лучшего решения. Мне нужно разобрать набор логов. Журналы создаются многопоточной программой, и один цикл обработки создает несколько строк журналов.

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

К счастью, каждая строка дает идентификатор процесса, поэтому я могу различить, какие журналы принадлежат какому процессу.

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

Итак, что я могу сделать с суперклассом, чтобы он мог управлять фрагментированными журналами и гарантировать минимальное изменение существующих реализованных синтаксических анализаторов?

Ответы [ 5 ]

2 голосов
/ 07 ноября 2008

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

Похоже, ваши классы могут выглядеть так:

abstract class Parser {
    public abstract void parse( ... );
    protected String readLine() { ... }
}

class SpecialPurposeParser extends Parser {
    public void parse( ... ) { 
        // ... special stuff
        readLine();
        // ... more stuff
    }
}

И я бы написал что-то вроде:

class SingleProcessReadingDecorator extends Parser {
    private Parser parser;
    private String processId;
    public SingleProcessReadingDecorator( Parser parser, String processId ) {
        this.parser = parser;
        this.processId = processId;
    }

    public void parse( ... ) { parser.parse( ... ); }

    public String readLine() {
        String text = super.readLine();
        if( /*text is for processId */ ) { 
            return text; 
        }
        else {
            //keep readLine'ing until you find the next line and then return it
            return this.readLine();
        }
    }

Тогда любое вхождение, которое вы хотите изменить, будет использоваться так:

//old way
Parser parser = new SpecialPurposeParser();
//changes to
Parser parser = new SingleProcessReadingDecorator( new SpecialPurposeParser(), "process1234" );

Этот фрагмент кода является простым и неполным, но дает вам представление о том, как здесь может работать шаблон декоратора.

1 голос
/ 08 ноября 2008

Я бы написал простой распространитель, который построчно считывает файл журнала и сохраняет их в разных объектах VirtualLog в памяти. VirtualLog - это своего рода виртуальный файл, фактически просто String или что-то, к чему могут применяться существующие анализаторы к. Виртуальные журналы хранятся на карте с идентификатором процесса (PID) в качестве ключа. Когда вы читаете строку из журнала, проверьте, есть ли уже PID. Если это так, добавьте строку в соответствующий VirtualLog PID. Если нет, создайте новый объект VirtualLog и добавьте его на карту. Парсеры работают как отдельные потоки, по одному на каждый VirtualLog. Каждый объект VirtualLog уничтожается, как только он был полностью проанализирован.

0 голосов
/ 07 ноября 2008

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

0 голосов
/ 07 ноября 2008

Может ли что-то подобное сделать? Он запускает новый поток для каждого идентификатора процесса в файле журнала.

class Parser {
   String currentLine;
   Parser() {
      //Construct parser
   }
   synchronized String readLine(String processID) {
      if (currentLine == null)
         currentLine = readLinefromLog();

      while (currentline != null && ! getProcessIdFromLine(currentLine).equals(processId)
        wait();

      String line = currentLine;
      currentLine = readLinefromLog();
      notify();
      return line;
   }
}

class ProcessParser extends Parser implements Runnable{
   String processId;
   ProcessParser(String processId) {
      super();
      this.processId = processId;
   }

   void startParser() {
       new Thread(this).start();
   }

   public void run() {
      String line = null;
      while ((line = readLine()) != null) {
          // process log line here
      }
   }

   String readLine() {
      String line = super.readLine(processId);
      return line;
   }      
0 голосов
/ 07 ноября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...