我有一个有趣的问题,非常感谢您对最佳解决方案的看法。 我需要解析一组日志。日志由多线程程序生成,单个进程循环生成多行日志。
在解析这些日志时,我需要从每个进程中提取特定的信息 - 当然这些信息是跨多行的(我想将这些数据压缩成一行)。由于应用程序是多线程的,属于进程的行块可以像其他进程一样被分段,同时写入同一个日志文件。
幸运的是,每一行都提供了一个进程ID,因此我能够区分哪些日志属于哪个进程。
现在,已经有几个解析器都扩展了同一个类但设计用于从单线程应用程序读取日志(没有碎片 - 来自原始系统)并在超类中使用readLine()方法。这些解析器将继续读取行,直到所有正则表达式与一个行块匹配(即在单个处理周期中写入的行)。
那么,我可以对超类做什么,以便它可以管理碎片日志,并确保对现有实现的解析器的更改是最小的?
答案 0 :(得分: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" );
此代码段简单且不完整,但可让您了解装饰器模式在此处的工作原理。
答案 1 :(得分:1)
我会编写一个简单的分发器,逐行读取日志文件并将它们存储在内存中的不同VirtualLog对象中 - VirtualLog是一种虚拟文件,实际上只是一个字符串或现有解析器可以应用的东西至。 VirtualLog存储在Map中,进程ID(PID)作为密钥。从日志中读取一行时,检查PID是否已存在。如果是,请将该行添加到PID的相应VirtualLog中。如果没有,请创建一个新的VirtualLog对象并将其添加到Map。解析器作为单独的线程运行,每个VirtualLog上都有一个。一旦完全解析了每个VirtualLog对象,它们就会被销毁。
答案 2 :(得分:0)
您需要临时将行存储在单个线程使用它们的队列中,并在每个集合完成后传递它们。如果您无法通过行数或行内容知道某个集合是否完整,您可以考虑使用滑动窗口技术,直到某个时间之后才收集单个集合已经过去了。
答案 3 :(得分: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;
}
答案 4 :(得分:0)
一个简单的解决方案可能是逐行读取文件并写入几个文件,每个文件对应一个进程ID。进程ID列表可以保存在内存中的哈希映射中,以确定是否需要新文件,或者在哪个已创建的文件中确定某个进程ID的行。一旦编写了所有(临时)文件,现有的解析器就可以在每个文件上完成工作。