【问题标题】:How to parse logs written by multiple threads?如何解析多线程写入的日志?
【发布时间】:2008-11-07 17:18:26
【问题描述】:

我有一个有趣的问题,希望您能提出最佳解决方案。 我需要解析一组日志。日志由多线程程序产生,单个进程循环产生多行日志。

在解析这些日志时,我需要从每个进程中提取特定的信息 - 自然,这些信息跨越多行(我想将这些数据压缩成一行)。由于应用程序是多线程的,属于一个进程的行块可以像其他进程一样被碎片化,同时写入同一个日志文件。

幸运的是,每一行都提供了一个进程 ID,因此我能够区分哪些日志属于哪个进程。

现在,已经有几个解析器都扩展了同一个类,但被设计为从单线程应用程序读取日志(无碎片 - 来自原始系统)并在超类中使用 readLine() 方法。这些解析器将继续读取行,直到所有正则表达式都匹配到一行行(即在单个进程周期中写入的行)。

那么,我可以对超类做些什么,以便它可以管理碎片日志,并确保对现有实现的解析器的更改最小?

【问题讨论】:

    标签: java regex multithreading inheritance parsing


    【解决方案1】:

    听起来您希望利用一些现有的解析器类。在这种情况下,我会为解析器编写一个decorator,它会删除与您正在监视的进程无关的行。

    听起来您的课程可能如下所示:

    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" );
    

    这段代码 sn-p 简单且不完整,但让您了解装饰器模式如何在这里工作。

    【讨论】:

      【解决方案2】:

      我会编写一个简单的分发器,它逐行读取日志文件并将它们存储在内存中的不同 VirtualLog 对象中——VirtualLog 是一种虚拟文件,实际上只是一个字符串或现有解析器可以应用的东西到。 VirtualLogs 以进程 ID (PID) 作为键存储在 Map 中。当您从日志中读取一行时,请检查 PID 是否已经存在。如果是这样,请将该行添加到 PID 的相应 VirtualLog。如果没有,则创建一个新的 VirtualLog 对象并将其添加到 Map。解析器作为单独的线程运行,每个 VirtualLog 上都有一个。每个 VirtualLog 对象在被完全解析后立即被销毁。

      【讨论】:

        【解决方案3】:

        您需要将行临时存储在一个队列中,其中单个线程消耗它们并在每组完成后传递它们。如果您无法通过行数或行的内容来了解​​集合是否完整,您可以考虑使用滑动窗口技术,在该技术直到特定时间之后才收集各个集合已经过去了。

        【讨论】:

          【解决方案4】:

          一个简单的解决方案是逐行读取文件并写入多个文件,每个进程 id 一个。进程 ID 的列表可以保存在内存中的哈希映射中,以确定是否需要新文件或某个进程 ID 的行将进入哪个已创建的文件。写入所有(临时)文件后,现有的解析器就可以完成每个文件的工作。

          【讨论】:

            【解决方案5】:

            这样的事情会做吗?它为日志文件中的每个进程 ID 运行一个新线程。

            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;
               }      
            

            【讨论】:

              猜你喜欢
              • 2018-07-29
              • 1970-01-01
              • 2012-01-05
              • 2021-02-11
              • 1970-01-01
              • 2017-01-27
              • 2013-06-10
              • 2012-08-23
              • 2018-05-20
              相关资源
              最近更新 更多