class SyslogParser {

  public Event parseMessage(String msg, Charset charset, Set<String> keepFields) {
    Map<String, String> headers = Maps.newHashMap();

    int msgLen = msg.length();

    int curPos = 0;

    Preconditions.checkArgument(msg.charAt(curPos) == '<',
        "Bad format: invalid priority: cannot find open bracket '<' (%s)", msg);

    int endBracketPos = msg.indexOf('>');
    Preconditions.checkArgument(endBracketPos > 0 && endBracketPos <= 6,
        "Bad format: invalid priority: cannot find end bracket '>' (%s)", msg);

    String priority = msg.substring(1, endBracketPos);
    int pri = Integer.parseInt(priority);
    int facility = pri / 8;
    int severity = pri % 8;

    // Remember priority
    headers.put(SyslogUtils.SYSLOG_PRIORITY, priority);

    // put fac / sev into header
    headers.put(SyslogUtils.SYSLOG_FACILITY, String.valueOf(facility));
    headers.put(SyslogUtils.SYSLOG_SEVERITY, String.valueOf(severity));

    Preconditions.checkArgument(msgLen > endBracketPos + 1,
        "Bad format: no data except priority (%s)", msg);

    // update parsing position
    curPos = endBracketPos + 1;

    // remember version string
    String version = null;
    if (msgLen > curPos + 2 && "1 ".equals(msg.substring(curPos, curPos + 2))) {
      version = msg.substring(curPos, curPos + 1);
      headers.put(SyslogUtils.SYSLOG_VERSION, version);
      curPos += 2;
    }

    // now parse timestamp (handle different varieties)

    long ts;
    String tsString;
    char dateStartChar = msg.charAt(curPos);

    try {

      // no timestamp specified; use relay current time
      if (dateStartChar == '-') {
        tsString = Character.toString(dateStartChar);
        ts = System.currentTimeMillis();
        if (msgLen <= curPos + 2) {
          throw new IllegalArgumentException(
              "bad syslog format (missing hostname)");
        }
        curPos += 2; // assume we skip past a space to get to the hostname

      // rfc3164 timestamp
      } else if (dateStartChar >= 'A' && dateStartChar <= 'Z') {
        if (msgLen <= curPos + RFC3164_LEN) {
          throw new IllegalArgumentException("bad timestamp format");
        }
        tsString = msg.substring(curPos, curPos + RFC3164_LEN);
        ts = parseRfc3164Time(tsString);
        curPos += RFC3164_LEN + 1;

      // rfc 5424 timestamp
      } else {
        int nextSpace = msg.indexOf(' ', curPos);
        if (nextSpace == -1) {
          throw new IllegalArgumentException("bad timestamp format");
        }
        tsString = msg.substring(curPos, nextSpace);
        ts = parseRfc5424Date(tsString);
        curPos = nextSpace + 1;
      }

    } catch (IllegalArgumentException ex) {
      throw new IllegalArgumentException("Unable to parse message: " + msg, ex);
    }

    headers.put("timestamp", String.valueOf(ts));

    // parse out hostname
    int nextSpace = msg.indexOf(' ', curPos);
    if (nextSpace == -1) {
      throw new IllegalArgumentException(
          "bad syslog format (missing hostname)");
    }
    // copy the host string to avoid holding the message string in memory
    // if using a memory-based queue
    String hostname = new String(msg.substring(curPos, nextSpace));
    headers.put("host", hostname);

    // EventBuilder will do a copy of its own, so no defensive copy of the body
    String data = "";
    if (msgLen > nextSpace + 1 && !SyslogUtils.keepAllFields(keepFields)) {
      curPos = nextSpace + 1;
      data = msg.substring(curPos);
      data = SyslogUtils.addFieldsToBody(keepFields, data, priority, version, tsString, hostname);
    } else {
      data = msg;
    }

    Event event = EventBuilder.withBody(data, charset, headers);

    return event;
  }

  public  SyslogParser();

}