commonlib/src/main/java/com/streamsets/pipeline/lib/parser/log/LogCharDataParser.java
private int readAhead(Map<String, Field> fieldsFromLogLine, StringBuilder stackTrace)
    throws DataParserException, IOException {
    StringBuilder multilineLog = new StringBuilder();
    int read = readLine(multilineLog);
    int numberOfLinesRead = 0;
    while (read > -1) {
      try {
        Map<String, Field> stringFieldMap = parseLogLine(multilineLog);
        fieldsFromLogLine.putAll(stringFieldMap);
        currentLine.append(multilineLog);
        //If the line can be parsed successfully, do not read further
        //This line will be used in the current record if this is the first line being read
        //or stored for the next round if there is a line from the previous round.
        break;
      } catch (DataParserException e) {
        //is this the first line being read? Yes -> throw exception
        if (previousLine.length() == 0 || maxStackTraceLines == -1) {
          throw e;
        }
        //otherwise read until we get a line that matches pattern
        if(numberOfLinesRead < maxStackTraceLines) {
          if(numberOfLinesRead != 0) {
            stackTrace.append("\n");
          }
          stackTrace.append(multilineLog.toString());
        }
        numberOfLinesRead++;
        multilineLog.setLength(0);
        read = readLine(multilineLog);
      }
    }
    return read;
  }
private int readAhead(Map<String, Field> fieldsFromLogLine, StringBuilder stackTrace)
    throws DataParserException, IOException {
    StringBuilder multilineLog = new StringBuilder();
    int read = readLine(multilineLog);
    int numberOfLinesRead = 0;
    while (read > -1) {
      try {
        Map<String, Field> stringFieldMap = parseLogLine(multilineLog);
        fieldsFromLogLine.putAll(stringFieldMap);
        currentLine.append(multilineLog);
        //If the line can be parsed successfully, do not read further
        //This line will be used in the current record if this is the first line being read
        //or stored for the next round if there is a line from the previous round.
        break;
      } catch (DataParserException e) {
        //is this the first line being read? Yes -> throw exception
        if (previousLine.length() == 0 || maxStackTraceLines == -1) {
          throw e;
        }
        //otherwise read until we get a line that matches pattern
        if(numberOfLinesRead < maxStackTraceLines) {
          if(numberOfLinesRead != 0) {
            stackTrace.append("\n");
          }
          stackTrace.append(multilineLog.toString());
        }
        numberOfLinesRead++;
        multilineLog.setLength(0);
        read = readLine(multilineLog);
      }
    }
    return read;
  }
/*
Captures valid lines in "currentLine" and corresponding fields in "fieldsFromLogLine".
Captures stack traces if any in the argument "stackTrace"