2008-11-07 50 views
0

我有一个有趣的问题,并会感谢您的想法的最佳解决方案。 我需要解析一组日志。日志由多线程程序生成,单个进程周期生成多行日志。如何解析由多个线程写入的日志?

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

幸运的是,每一行都给出了一个进程ID,所以我能够区分哪些日志属于哪个进程。

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

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

回答

2

这听起来像是有一些现有的解析器类已经在使用,你希望利用。在这种情况下,我会为分析器编写一个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"); 

此代码片段很简单且不完整,但可以让您了解装饰器pa ttern可以在这里工作。

0

您需要临时将线存储在单个线程使用它们的队列中,并在每个线程集完成后传递它们。如果无法通过行数或行内容知道集合是否完整,则可以考虑使用滑动窗口技术,在这种技巧中,您不会收集单个集合,直到某个时间过后已经过去了。

0

会这样吗?它为日志文件中的每个进程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; 
    }  
0

一个简单的解决方案可能是逐行读取文件并写入多个文件,每个文件对应一个进程ID。进程ID列表可以保存在内存中的散列映射中,以确定是否需要新文件,或者确定某个进程ID的行是否已经创建了文件。一旦所有(临时)文件都被写入,现有的解析器就可以在每个文件上完成这项工作。

1

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