Java 如何解析由多个线程编写的日志?
我有一个有趣的问题,希望你能想出最好的解决办法。 我需要解析一组日志。日志由多线程程序生成,单个进程周期生成多行日志 在解析这些日志时,我需要从每个进程中提取特定的信息片段——当然,这些信息跨越多行(我想将这些数据片段压缩到一行)。由于应用程序是多线程的,因此属于某个进程的行块可以像其他进程一样在同一时间写入同一日志文件 幸运的是,每一行都给出了一个进程ID,因此我能够区分哪些日志属于哪个进程 现在,已经有几个解析器,它们都扩展了同一个类,但设计用于从单线程应用程序读取日志(无碎片-来自原始系统),并在超类中使用readLine()方法。这些解析器将一直读取行,直到所有正则表达式都匹配一个行块(即在单个进程周期中写入的行)Java 如何解析由多个线程编写的日志?,java,regex,multithreading,inheritance,parsing,Java,Regex,Multithreading,Inheritance,Parsing,我有一个有趣的问题,希望你能想出最好的解决办法。 我需要解析一组日志。日志由多线程程序生成,单个进程周期生成多行日志 在解析这些日志时,我需要从每个进程中提取特定的信息片段——当然,这些信息跨越多行(我想将这些数据片段压缩到一行)。由于应用程序是多线程的,因此属于某个进程的行块可以像其他进程一样在同一时间写入同一日志文件 幸运的是,每一行都给出了一个进程ID,因此我能够区分哪些日志属于哪个进程 现在,已经有几个解析器,它们都扩展了同一个类,但设计用于从单线程应用程序读取日志(无碎片-来自原始系统
那么,我该如何处理super类,以便它能够管理分段日志,并确保对现有已实现的解析器的更改最小?您需要将行临时存储在队列中,在队列中,单个线程使用这些行,并在完成每一组后将其传递。如果无法通过行数或行内容来了解集合是否完整,你可以考虑使用滑动窗口技术,在某个时间过去之前,你不收集单独的集合。< P>你需要在一个队列中临时存储一行,其中一个线程消耗它们并在每一个集合完成后将它们传递给一个队列。如果你无法知道一个集合是由线的数量还是线的内容来完成的,你可以考虑使用滑动窗口技术,在一个特定的时间过去之前,你不收集单独的集合。它为日志文件中的每个进程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;
}
像这样的东西行吗?它为日志文件中的每个进程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;
}
听起来好像有一些现有的解析器类已经在使用,您希望利用它们。在这个场景中,我将为解析器编写一个脚本,它将删除与您正在监视的进程无关的行 听起来您的类可能是这样的:
abstract class Parser {
public abstract void parse( ... );
protected String readLine() { ... }
}
class SpecialPurposeParser extends Parser {
public void parse( ... ) {
// ... special stuff
readLine();
// ... more stuff
}
}
//old way
Parser parser = new SpecialPurposeParser();
//changes to
Parser parser = new SingleProcessReadingDecorator( new SpecialPurposeParser(), "process1234" );
我会这样写:
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();
}
}
然后,您要修改的任何引用都将按如下方式使用:
abstract class Parser {
public abstract void parse( ... );
protected String readLine() { ... }
}
class SpecialPurposeParser extends Parser {
public void parse( ... ) {
// ... special stuff
readLine();
// ... more stuff
}
}
//old way
Parser parser = new SpecialPurposeParser();
//changes to
Parser parser = new SingleProcessReadingDecorator( new SpecialPurposeParser(), "process1234" );
此代码片段简单且不完整,但让您了解了decorator模式在这里的工作方式。听起来您希望利用一些现有的解析器类。在这个场景中,我将为解析器编写一个脚本,它将删除与您正在监视的进程无关的行 听起来您的类可能是这样的:
abstract class Parser {
public abstract void parse( ... );
protected String readLine() { ... }
}
class SpecialPurposeParser extends Parser {
public void parse( ... ) {
// ... special stuff
readLine();
// ... more stuff
}
}
//old way
Parser parser = new SpecialPurposeParser();
//changes to
Parser parser = new SingleProcessReadingDecorator( new SpecialPurposeParser(), "process1234" );
我会这样写:
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();
}
}
然后,您要修改的任何引用都将按如下方式使用:
abstract class Parser {
public abstract void parse( ... );
protected String readLine() { ... }
}
class SpecialPurposeParser extends Parser {
public void parse( ... ) {
// ... special stuff
readLine();
// ... more stuff
}
}
//old way
Parser parser = new SpecialPurposeParser();
//changes to
Parser parser = new SingleProcessReadingDecorator( new SpecialPurposeParser(), "process1234" );
此代码片段简单且不完整,但让您了解了decorator模式在这里的工作原理。一个简单的解决方案是逐行读取文件并编写多个文件,每个进程id一个。进程id的列表可以保存在内存中的哈希映射中,以确定是否需要新文件,或者某个进程id的行将放在哪个已创建的文件中。一旦所有(临时)文件都被写入,现有的解析器就可以在每个文件上完成这项工作。一个简单的解决方案是逐行读取文件并写入多个文件,每个进程id一个。进程id的列表可以保存在内存中的哈希映射中,以确定是否需要新文件,或者某个进程id的行将放在哪个已创建的文件中。一旦所有(临时)文件都被写入,现有的解析器就可以对每个文件进行解析。我将编写一个简单的分发服务器,逐行读取日志文件,并将它们存储在内存中不同的VirtualLog对象中——VirtualLog是一种虚拟文件,实际上只是一个字符串或现有解析器可以应用的东西。VirtualLogs存储在以进程ID(PID)为键的映射中。当您从日志中读取一行时,检查PID是否已经存在。如果是这样,则将该行添加到PID的相应VirtualLog中。如果没有,请创建一个新的VirtualLog对象并将其添加到地图中。解析器作为单独的线程运行,每个VirtualLog上都有一个线程。每一个VirtualLog对象在被完全解析后都会被销毁 我将编写一个简单的分发程序,逐行读取日志文件,并将它们存储在内存中的不同VirtualLog对象中——VirtualLog是一种虚拟文件,实际上只是一个字符串或现有解析器可以应用的东西。VirtualLogs存储在以进程ID(PID)为键的映射中。当您从日志中读取一行时,检查PID是否已经存在。如果是这样,则将该行添加到PID的相应VirtualLog中。如果没有,请创建一个新的VirtualLog对象并将其添加到地图中。解析器作为单独的线程运行,每个VirtualLog上都有一个线程。每一个VirtualLog对象在被完全解析后都会被销毁