Java 当使用hadoop从原始日志捕获异常时,一个完整的异常将被拆分为两个映射

Java 当使用hadoop从原始日志捕获异常时,一个完整的异常将被拆分为两个映射,java,exception,logging,hadoop,mapreduce,Java,Exception,Logging,Hadoop,Mapreduce,我想使用hadoop从原始日志中获取和解析异常。 我遇到了一个问题,一些异常(跨越多条线)将是2个不同拆分的一部分,因此是2个不同的映射器 我有办法避免这个问题。我可以重写getSplits()方法,使每个分割都有少量冗余数据。我认为这个解决方案对我来说成本太高了 那么,有没有人有更好的解决这个问题的方法呢?这些类可能会有所帮助。TextInputFormat将按行分割文件,因此如果异常以换行符结束(且其中不包含任何换行符),则应该可以执行此操作。如果异常包含固定数量的行,那么NLINEIPUT

我想使用hadoop从原始日志中获取和解析异常。 我遇到了一个问题,一些异常(跨越多条线)将是2个不同拆分的一部分,因此是2个不同的映射器

我有办法避免这个问题。我可以重写
getSplits()
方法,使每个分割都有少量冗余数据。我认为这个解决方案对我来说成本太高了

那么,有没有人有更好的解决这个问题的方法呢?

这些类可能会有所帮助。TextInputFormat将按行分割文件,因此如果异常以换行符结束(且其中不包含任何换行符),则应该可以执行此操作。如果异常包含固定数量的行,那么NLINEIPUTFORMAT类应该是您想要的,因为您可以设置要采用的行数

不幸的是,如果异常中可能包含可变数量的换行符,那么这将不起作用

如果是那样的话,我建议你找一个收银员。它跨越了分裂的界限,所以对大多数东西都有效。只需运行预处理器,将异常放入
标记中,并将其指定为开始/结束标记

示例预处理器,使用正则表达式识别异常

String input; //code this to the input string
String regex; //make this equal to the exception regex
BufferedWriter bw; //make this go to file where output will be stored

String toProcess = input;
boolean continueLoop = true;

while(continueLoop){
    Pattern p = Pattern.compile(regex);
    Matcher m = p.matcher(toProcess);
    if(m.find()){
        bw.write("<exception>"+toProcess.substring(m.start(),m.end())+"</exception>");
        toProcess = toProcess.substring(m.end());
    }else{
        continueLoop = false;
    }

}
字符串输入//将其编码为输入字符串
字符串正则表达式//使其等于异常正则表达式
缓冲写入器bw//将此文件转到存储输出的文件
字符串toProcess=输入;
布尔连续运算=真;
while(continueLoop){
Pattern p=Pattern.compile(regex);
Matcher m=p.Matcher(toProcess);
if(m.find()){
write(“+toProcess.substring(m.start(),m.end())+”);
toProcess=toProcess.substring(m.end());
}否则{
continueLoop=false;
}
}

我会去做一个预处理工作,在那里你用XML标签标记异常。接下来,您可以使用
XMLInputformat
来处理文件。(这只是解决方案的开始,根据您的反馈,我们可能会让事情变得更具体)

提供编写自己的XMLinputformat的教程,您可以自定义该教程以查找“异常”特征。本教程的要点是这句话:

如果记录跨越InputSplit边界,则记录 读者会注意到这一点,所以我们不必担心这一点

我将复制粘贴该网站的信息,因为它可能会在将来离线,这可能会让人们在将来查看该网站时感到非常沮丧:

输入格式:

package org.undercloud.mapreduce.example3;

import java.io.IOException;

import org.apache.hadoop.io.*;
import org.apache.hadoop.mapred.*;

public class XmlInputFormat extends FileInputFormat {

 public RecordReader getRecordReader(InputSplit input, JobConf job, Reporter reporter)
 throws IOException {

reporter.setStatus(input.toString());
 return new XmlRecordReader(job, (FileSplit)input);
 }
记录阅读器: 注意:读取拆分结束后的逻辑在
readUntilMatch
函数中,如果存在打开的标记,则该函数读取拆分结束后的数据。我想这才是你真正想要的

package org.undercloud.mapreduce.example3;

import java.io.IOException;

import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapred.*;

public class XmlRecordReader implements RecordReader {

    private String startTagS = "";
    private String endTagS = "";
    private byte[] startTag;
    private byte[] endTag;
    private long start;
    private long end;
    private FSDataInputStream fsin;
    private DataOutputBuffer buffer = new DataOutputBuffer();
    private LineRecordReader lineReader;
    private LongWritable lineKey;
    private Text lineValue;

    public XmlRecordReader(JobConf job, FileSplit split) throws IOException {
      lineReader = new LineRecordReader(job, split);
      lineKey = lineReader.createKey();
      lineValue = lineReader.createValue();
      startTag = startTagS.getBytes();
      endTag = endTagS.getBytes();

      // Open the file and seek to the start of the split
      start = split.getStart();
      end = start + split.getLength();
      Path file = split.getPath();
      FileSystem fs = file.getFileSystem(job);
      fsin = fs.open(split.getPath());
      fsin.seek(start);
   }

    public boolean next(Text key, XmlContent value) throws IOException {
    // Get the next line
        if (fsin.getPos() < end) { 
            if (readUntilMatch(startTag, false)) { 
                try { 
                    buffer.write(startTag); 
                    if (readUntilMatch(endTag, true)) { 
                        key.set(Long.toString(fsin.getPos())); 
                        value.bufferData = buffer.getData(); 
                        value.offsetData = 0; 
                        value.lenghtData = buffer.getLength(); 
                        return true; 
                    } 
                } 
                finally { 
                    buffer.reset(); 
                } 
            } 
        } 
        return false; 
    } 

    private boolean readUntilMatch(byte[] match, boolean withinBlock) throws IOException { 
        int i = 0; 
        while (true) { 
            int b = fsin.read(); // End of file -> T
            if (b == -1) return false;
            // F-> Save to buffer:
            if (withinBlock) buffer.write(b);
            if (b == match[i]) {
                i++;
                if (i >= match.length) return true;
            } else i = 0;
            // see if we’ve passed the stop point:
            if(!withinBlock && i == 0 && fsin.getPos() >= end) return false;
        }
    }

  public Text createKey() {
     return new Text("");
  }

  public XmlContent createValue() {
    return new XmlContent();
  }

  public long getPos() throws IOException {
    return lineReader.getPos();
  }

  public void close() throws IOException {
    lineReader.close();
  }

  public float getProgress() throws IOException {
    return lineReader.getProgress();
  }
}

这看起来是一个非常有用的教程,解决了跨多个拆分的记录问题。请告诉我您是否能够根据您的问题调整此示例。

感谢您提供的所有解决方案。我认为这对我很有用

特别注意上面的评论

如果记录跨越InputSplit边界,则记录 读者会注意到这一点,所以我们不必担心 这个。”

然后我研究了LineRecordReader如何读取数据表单拆分的源代码。然后我发现实际上LineRecordReader已经有了一些逻辑来读取跨越InputSplit边界的记录,因为由于块的大小限制,分割底部的行记录总是被分割成两个不同的分割。 因此,我认为我需要做的是添加LineRecordReader在分割边界上读取的数据大小

现在我的解决方案是:重写LineRecordReader中的方法“nextKeyValue()

public boolean nextKeyValue() throws IOException {
if (key == null) {
  key = new LongWritable();
}
key.set(pos);
if (value == null) {
  value = new Text();
}
int newSize = 0;
while (pos < end) {
  newSize = in.readLine(value, maxLineLength,
                        Math.max((int)Math.min(Integer.MAX_VALUE, end-pos),
                                 maxLineLength));
public boolean nextKeyValue()引发IOException{
if(key==null){
key=新的LongWritable();
}
按键设置(pos);
如果(值==null){
值=新文本();
}
int newSize=0;
while(pos
将行“while(pos
{param}表示readRecorder跨拆分边界读取的冗余数据的大小。

假设异常具有固定行数可能不起作用。我们是否可以使用“exception”关键字作为记录分隔符?@PraveenSripati我知道该限制,并且该限制在回答中已说明。我将以更高的级别进行编辑当/如果我找到一个通用的解决方案。@SSaikia_JtheRocker:不,那不行。我发现使用记录分隔符的输入格式似乎都扩展了TextInputFormat或NLineInputFormat,它们具有相同的限制。如果你说每行有3条记录,它们就很好了。如果你可以每行有多条记录,就没那么多了。Dear Ejay,您的数据和集群的维度是什么?预处理mapreduce作业是一种选择吗?您的数据是一个大文件还是多个小文件也是一个相关的问题。如果是小文件(由于不清楚异常来自哪种语言:您能给出它们的外观示例吗?mahout有一个已经存在的XMLInputFormat。这将节省您自己编写一个的时间。将它们放在标记中,并指定为开始标记和结束标记。这将不必编写您自己的代码。输出为TextWri表,与文本InputFormat相同。只需使用JDOM或其他方法提取标记文本,并从中进行处理。是的,这是正确的,但如果他不想使用标记预处理文件,他可以修改此代码,这就是我将其放在此处的原因,因为它消除了预处理步骤。它还显示了如何读取经过分割边界的内容,而我思考是高度相关的。
public boolean nextKeyValue() throws IOException {
if (key == null) {
  key = new LongWritable();
}
key.set(pos);
if (value == null) {
  value = new Text();
}
int newSize = 0;
while (pos < end) {
  newSize = in.readLine(value, maxLineLength,
                        Math.max((int)Math.min(Integer.MAX_VALUE, end-pos),
                                 maxLineLength));