Refactoring and more tests.

This commit is contained in:
Mark Nellemann 2021-01-27 15:18:46 +01:00
parent 0752c0b6a6
commit 633ec03bae
18 changed files with 666 additions and 513 deletions

View file

@ -1,3 +1,3 @@
id = syslogd id = syslogd
group = biz.nellemann.syslogd group = biz.nellemann.syslogd
version = 1.0.9 version = 1.0.10

View file

@ -15,6 +15,13 @@
*/ */
package biz.nellemann.syslogd; package biz.nellemann.syslogd;
import biz.nellemann.syslogd.msg.SyslogMessage;
import biz.nellemann.syslogd.net.TcpServer;
import biz.nellemann.syslogd.net.UdpClient;
import biz.nellemann.syslogd.net.UdpServer;
import biz.nellemann.syslogd.parser.SyslogParser;
import biz.nellemann.syslogd.parser.SyslogParserRfc3164;
import biz.nellemann.syslogd.parser.SyslogParserRfc5424;
import org.slf4j.impl.SimpleLogger; import org.slf4j.impl.SimpleLogger;
import picocli.CommandLine; import picocli.CommandLine;
@ -32,6 +39,7 @@ import java.util.regex.Pattern;
public class Application implements Callable<Integer>, LogListener { public class Application implements Callable<Integer>, LogListener {
private boolean doForward = false; private boolean doForward = false;
private SyslogParser syslogParser;
private UdpClient udpClient; private UdpClient udpClient;
@ -67,6 +75,12 @@ public class Application implements Callable<Integer>, LogListener {
System.setProperty(SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "DEBUG"); System.setProperty(SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "DEBUG");
} }
if(rfc5424) {
syslogParser = new SyslogParserRfc5424();
} else {
syslogParser = new SyslogParserRfc3164();
}
if(forward != null && !forward.isEmpty()) { if(forward != null && !forward.isEmpty()) {
String fHost, fPort; String fHost, fPort;
Pattern pattern = Pattern.compile("^([^:]+)(?::([0-9]+))?$", Pattern.CASE_INSENSITIVE); Pattern pattern = Pattern.compile("^([^:]+)(?::([0-9]+))?$", Pattern.CASE_INSENSITIVE);
@ -110,11 +124,7 @@ public class Application implements Callable<Integer>, LogListener {
String message = event.getMessage(); String message = event.getMessage();
SyslogMessage msg = null; SyslogMessage msg = null;
try { try {
if(rfc5424) { msg = syslogParser.parse(message);
msg = SyslogParser.parseRfc5424(message);
} else {
msg = SyslogParser.parseRfc3164(message);
}
} catch(Exception e) { } catch(Exception e) {
e.printStackTrace(); e.printStackTrace();
} }

View file

@ -16,5 +16,5 @@
package biz.nellemann.syslogd; package biz.nellemann.syslogd;
public interface LogListener { public interface LogListener {
void onLogEvent(LogEvent event); public void onLogEvent(LogEvent event);
} }

View file

@ -1,235 +0,0 @@
/*
Copyright 2020 mark.nellemann@gmail.com
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package biz.nellemann.syslogd;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SyslogParser {
private final static Logger log = LoggerFactory.getLogger(SyslogParser.class);
/**
* Parses [rfc3164](https://tools.ietf.org/html/rfc3164) syslog messages.
*
* @param input
* @return
* @throws NumberFormatException
*/
public static SyslogMessage parseRfc3164(final String input) throws NumberFormatException {
Pattern pattern = Pattern.compile("^<(\\d{1,3})>(\\D{3}\\s+\\d{1,2} \\d{2}:\\d{2}:\\d{2})\\s+(Message forwarded from \\S+:|\\S+)\\s+([^\\s:]+):?\\s+(.*)", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(input);
boolean matchFound = matcher.find();
if(!matchFound) {
//log.warn("parseRfc3164() - Match not found in: ");
System.err.println("!" + input);
return null;
}
String pri = matcher.group(1);
String date = matcher.group(2);
String hostname = matcher.group(3);
String application = matcher.group(4);
String msg = matcher.group(5);
if(hostname.endsWith(":")) {
String[] tmp = hostname.split(" ");
hostname = tmp[tmp.length-1];
hostname = hostname.substring(0, hostname.length()-1);
}
Integer facility = getFacility(pri);
Integer severity = getSeverity(pri);
SyslogMessage syslogMessage = new SyslogMessage(msg.trim());
syslogMessage.facility = Facility.getByNumber(facility);
syslogMessage.severity = Severity.getByNumber(severity);
syslogMessage.timestamp = parseRfc3164Timestamp(date);
syslogMessage.hostname = hostname;
syslogMessage.application = application;
return syslogMessage;
}
/**
* Parses [rfc5424](https://tools.ietf.org/html/rfc5424) syslog messages.
*
* @param input
* @return
* @throws NumberFormatException
*/
public static SyslogMessage parseRfc5424(final String input) throws NumberFormatException {
log.warn("parseRfc5424() " + input);
// "<165>1 2003-08-24T05:14:15.000003-07:00 192.0.2.1 myproc 8710 - - %% It's time to make the do-nuts."
// '<13>1 2020-09-23T08:57:30.950699+02:00 xps13 mark - - [exampleSDID@32473 iut="3" eventSource="Application" eventID="1011"] adfdfdf3432434565656'
Pattern pattern = Pattern.compile("^<(\\d{1,3})>(\\d+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\[.*\\]|-)\\s+(\\S+)", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(input);
boolean matchFound = matcher.find();
if(!matchFound) {
log.debug("parseRfc5424() - Match not found in: " + input);
System.err.println("!" + input);
return null;
}
String pri = matcher.group(1);
String ver = matcher.group(2);
String date = matcher.group(3);
String host = matcher.group(4);
String app = matcher.group(5);
String procId = matcher.group(6);
String msgId = matcher.group(7);
String data = matcher.group(8);
String msg = matcher.group(9);
Integer facility = getFacility(pri);
Integer severity = getSeverity(pri);
SyslogMessage syslogMessage = new SyslogMessage(msg.trim());
syslogMessage.facility = Facility.getByNumber(facility);
syslogMessage.severity = Severity.getByNumber(severity);
syslogMessage.version = Integer.parseInt(ver);
syslogMessage.timestamp = parseRfc5424Timestamp(date);
syslogMessage.hostname = host;
if(app != null && !app.equals("-"))
syslogMessage.application = app;
if(procId != null && !procId.equals("-"))
syslogMessage.processId = procId;
if(msgId != null && !msgId.equals("-"))
syslogMessage.messageId = msgId;
if(data != null && !data.equals("-"))
syslogMessage.structuredData = data;
return syslogMessage;
}
/**
* Parse rfc3164 TIMESTAMP field into Instant.
*
* @param dateString
* @return
*/
static protected Instant parseRfc3164Timestamp(String dateString) {
// We need to add year to parse date correctly
OffsetDateTime odt = OffsetDateTime.now();
// Date: Mmm dd hh:mm:ss
Instant instant = null;
try {
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy MMM [ ]d HH:mm:ss").withZone(ZoneOffset.UTC);
instant = Instant.from(dateTimeFormatter.parse(odt.getYear() + " " + dateString));
} catch(DateTimeParseException e) {
log.error("parseDate()", e);
}
return instant;
}
/**
* Parse rfc5424 TIMESTAMP field into Instant.
*
* @param dateString
* @return
*/
static protected Instant parseRfc5424Timestamp(String dateString) {
/*
https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html
ex1: 1985-04-12T23:20:50.52Z
ex2: 1985-04-12T19:20:50.52-04:00
ex3: 2003-10-11T22:14:15.003Z
ex4: 2003-08-24T05:14:15.000003-07:00
ex5: 2003-08-24T05:14:15.000000003-07:00
*/
List<String> formatStrings = Arrays.asList(
"yyyy-MM-dd'T'HH:mm:ss.SS'Z'",
"yyyy-MM-dd'T'HH:mm:ss.SSXXX",
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
"yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXX",
"yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSXXX"
);
for(String formatString : formatStrings)
{
try {
return new SimpleDateFormat(formatString).parse(dateString).toInstant();
}
catch (ParseException e) {}
}
/*
try {
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
instant = Instant.from(dateTimeFormatter.parse(dateString));
} catch(DateTimeParseException e) {
log.error("parseTimestamp()", e);
}
return instant;*/
return null;
}
/**
* Converts syslog PRI field into Facility.
*
* @param pri
* @return
*/
static protected int getFacility(String pri) {
int priority = Integer.parseInt(pri);
int facility = priority >> 3;
//log.debug("getFacility() - " + pri + " => " + facility);
return facility;
}
/**
* Converts syslog PRI field into Severity.
*
* @param pri
* @return
*/
static protected int getSeverity(String pri) {
int priority = Integer.parseInt(pri);
int severity = priority & 0x07;
//log.debug("getSeverity() - " + pri + " => " + severity);
return severity;
}
}

View file

@ -1,5 +1,8 @@
package biz.nellemann.syslogd; package biz.nellemann.syslogd;
import biz.nellemann.syslogd.msg.Facility;
import biz.nellemann.syslogd.msg.Severity;
import biz.nellemann.syslogd.msg.SyslogMessage;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -7,9 +10,10 @@ public class SyslogPrinter {
private final static Logger log = LoggerFactory.getLogger(SyslogPrinter.class); private final static Logger log = LoggerFactory.getLogger(SyslogPrinter.class);
private final static char SPACE = ' ';
public static String toString(SyslogMessage msg) { public static String toString(SyslogMessage msg) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder(msg.timestamp.toString());
sb.append(msg.timestamp.toString());
sb.append(String.format(" [%8.8s.%-6.6s] ", msg.facility, msg.severity)); sb.append(String.format(" [%8.8s.%-6.6s] ", msg.facility, msg.severity));
sb.append(String.format(" %-16.16s ", msg.hostname)); sb.append(String.format(" %-16.16s ", msg.hostname));
sb.append(String.format(" %-32.32s ", msg.application)); sb.append(String.format(" %-32.32s ", msg.application));
@ -19,9 +23,7 @@ public class SyslogPrinter {
public static String toAnsiString(SyslogMessage msg) { public static String toAnsiString(SyslogMessage msg) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder(msg.timestamp.toString());
sb.append(msg.timestamp.toString());
if (msg.severity.toNumber() < 3) { if (msg.severity.toNumber() < 3) {
sb.append(Ansi.RED); sb.append(Ansi.RED);
@ -45,10 +47,9 @@ public class SyslogPrinter {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(getPri(msg.facility, msg.severity)); sb.append(getPri(msg.facility, msg.severity));
sb.append(new java.text.SimpleDateFormat("MMM dd HH:mm:ss").format(new java.util.Date(msg.timestamp.toEpochMilli()))); sb.append(new java.text.SimpleDateFormat("MMM dd HH:mm:ss").format(new java.util.Date(msg.timestamp.toEpochMilli())));
sb.append(" " + msg.hostname); sb.append(SPACE).append(msg.hostname);
sb.append(" " + msg.application); sb.append(SPACE).append(msg.application);
sb.append(": " + msg.message); sb.append(":").append(SPACE).append(msg.message);
log.debug(sb.toString()); log.debug(sb.toString());
return sb.toString(); return sb.toString();
} }
@ -59,21 +60,21 @@ public class SyslogPrinter {
public static String toRfc5424(SyslogMessage msg) { public static String toRfc5424(SyslogMessage msg) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(getPri(msg.facility, msg.severity)).append("1"); sb.append(getPri(msg.facility, msg.severity)).append("1");
sb.append(" " + new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(new java.util.Date(msg.timestamp.toEpochMilli()))); sb.append(SPACE).append(new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(new java.util.Date(msg.timestamp.toEpochMilli())));
sb.append(" " + msg.hostname); sb.append(SPACE).append(msg.hostname);
sb.append(" " + msg.application); sb.append(SPACE).append(msg.application);
sb.append(" " + msg.processId); sb.append(SPACE).append(msg.processId);
sb.append(" " + msg.messageId); sb.append(SPACE).append(msg.messageId);
sb.append(" " + msg.structuredData); sb.append(SPACE).append(msg.structuredData);
sb.append(" " + msg.message); sb.append(SPACE).append(msg.message);
log.debug(sb.toString()); log.debug(sb.toString());
return sb.toString(); return sb.toString();
} }
static private String getPri(Facility facility, Severity severity) { static private String getPri(Facility facility, Severity severity) {
int prival = (facility.toNumber() * 8) + severity.toNumber(); int pri = (facility.toNumber() * 8) + severity.toNumber();
return String.format("<%d>", prival); return String.format("%c%d%c", '<', pri, '>');
} }
} }

View file

@ -1,30 +0,0 @@
package biz.nellemann.syslogd;
import java.io.IOException;
import java.net.*;
public class UdpClient {
private DatagramSocket socket;
private InetAddress address;
private Integer port;
private byte[] buf;
public UdpClient(String host, Integer port) throws UnknownHostException, SocketException {
socket = new DatagramSocket();
address = InetAddress.getByName(host);
this.port = port;
}
public void send(String msg) throws IOException {
buf = msg.getBytes();
DatagramPacket packet
= new DatagramPacket(buf, buf.length, address, port);
socket.send(packet);
}
public void close() {
socket.close();
}
}

View file

@ -1,4 +1,4 @@
package biz.nellemann.syslogd; package biz.nellemann.syslogd.msg;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;

View file

@ -1,4 +1,4 @@
package biz.nellemann.syslogd; package biz.nellemann.syslogd.msg;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;

View file

@ -13,40 +13,40 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package biz.nellemann.syslogd; package biz.nellemann.syslogd.msg;
import java.time.Instant; import java.time.Instant;
public class SyslogMessage { public class SyslogMessage {
protected Facility facility; public Facility facility;
protected Severity severity; public Severity severity;
// The VERSION field denotes the version of the syslog protocol specification. // The VERSION field denotes the version of the syslog protocol specification.
protected Integer version; public Integer version;
// The TIMESTAMP field is a formalized timestamp derived from [RFC3339]. // The TIMESTAMP field is a formalized timestamp derived from [RFC3339].
protected Instant timestamp; public Instant timestamp;
// The HOSTNAME field identifies the machine that originally sent the syslog message. // The HOSTNAME field identifies the machine that originally sent the syslog message.
protected String hostname; public String hostname;
// The APP-NAME field SHOULD identify the device or application that originated the message. // The APP-NAME field SHOULD identify the device or application that originated the message.
protected String application = "-"; public String application = "-";
// The PROCID field is often used to provide the process name or process ID associated with a syslog system. // The PROCID field is often used to provide the process name or process ID associated with a syslog system.
protected String processId = "-"; public String processId = "-";
// The MSGID SHOULD identify the type of message. // The MSGID SHOULD identify the type of message.
protected String messageId = "-"; public String messageId = "-";
// STRUCTURED-DATA provides a mechanism to express information in a well defined, easily parseable and interpretable data format. // STRUCTURED-DATA provides a mechanism to express information in a well defined, easily parseable and interpretable data format.
protected String structuredData = "-"; public String structuredData = "-";
// The MSG part contains a free-form message that provides information about the event. // The MSG part contains a free-form message that provides information about the event.
protected final String message; public final String message;
SyslogMessage(final String message) { public SyslogMessage(final String message) {
this.message = message; this.message = message;
} }

View file

@ -13,26 +13,29 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package biz.nellemann.syslogd; package biz.nellemann.syslogd.net;
import biz.nellemann.syslogd.LogEvent;
import biz.nellemann.syslogd.LogListener;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.ArrayList;
public class TcpServer { public class TcpServer {
private final int port; private final int port;
private ServerSocket serverSocket; private ServerSocket serverSocket;
TcpServer() { public TcpServer() {
this(514); this(514);
} }
TcpServer(int port) { public TcpServer(int port) {
this.port = port; this.port = port;
} }

View file

@ -0,0 +1,50 @@
package biz.nellemann.syslogd.net;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;
public class UdpClient {
private final static Logger log = LoggerFactory.getLogger(UdpClient.class);
private DatagramSocket socket;
private InetAddress address;
private final Integer port;
public UdpClient(String host, Integer port) throws UnknownHostException, SocketException {
try {
this.address = InetAddress.getByName(host);
} catch (UnknownHostException e) {
log.error("UdpClient() - UnknownHostException: " + e.getMessage());
}
try {
this.socket = new DatagramSocket();
} catch (SocketException e) {
log.error("UdpClient() - Could not instantiate DatagramSocket: " + e.getMessage());
}
this.port = port;
}
public void send(String msg) {
byte[] buf = msg.getBytes(StandardCharsets.US_ASCII);
DatagramPacket packet = new DatagramPacket(buf, buf.length, address, port);
if(this.socket != null) {
try {
socket.send(packet);
} catch (IOException e) {
log.error("send() - Could not send packet: " + e.getMessage());
}
}
}
public void close() {
socket.close();
}
}

View file

@ -13,7 +13,11 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package biz.nellemann.syslogd; package biz.nellemann.syslogd.net;
import biz.nellemann.syslogd.Application;
import biz.nellemann.syslogd.LogEvent;
import biz.nellemann.syslogd.LogListener;
import java.io.IOException; import java.io.IOException;
import java.net.DatagramPacket; import java.net.DatagramPacket;
@ -68,7 +72,7 @@ public class UdpServer extends Thread {
protected List<LogListener> eventListeners = new ArrayList<>(); protected List<LogListener> eventListeners = new ArrayList<>();
public synchronized void addEventListener( LogListener l ) { public synchronized void addEventListener(Application l ) {
eventListeners.add( l ); eventListeners.add( l );
} }

View file

@ -0,0 +1,66 @@
/*
Copyright 2020 mark.nellemann@gmail.com
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package biz.nellemann.syslogd.parser;
import biz.nellemann.syslogd.msg.SyslogMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.Instant;
public abstract class SyslogParser {
private final static Logger log = LoggerFactory.getLogger(SyslogParser.class);
public abstract SyslogMessage parse(final String input);
abstract Instant parseTimestamp(final String dateString);
/**
* Converts syslog PRI field into Facility.
*
* @param pri
* @return
*/
public int getFacility(String pri) {
int priority = Integer.parseInt(pri);
int facility = priority >> 3;
//log.debug("getFacility() - " + pri + " => " + facility);
return facility;
}
/**
* Converts syslog PRI field into Severity.
*
* @param pri
* @return
*/
public int getSeverity(String pri) {
int priority = Integer.parseInt(pri);
int severity = priority & 0x07;
//log.debug("getSeverity() - " + pri + " => " + severity);
return severity;
}
}

View file

@ -0,0 +1,103 @@
/*
Copyright 2020 mark.nellemann@gmail.com
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package biz.nellemann.syslogd.parser;
import biz.nellemann.syslogd.msg.Facility;
import biz.nellemann.syslogd.msg.Severity;
import biz.nellemann.syslogd.msg.SyslogMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SyslogParserRfc3164 extends SyslogParser {
private final static Logger log = LoggerFactory.getLogger(SyslogParserRfc3164.class);
private final Pattern pattern = Pattern.compile("^<(\\d{1,3})>(\\D{3}\\s+\\d{1,2} \\d{2}:\\d{2}:\\d{2})\\s+(Message forwarded from \\S+:|\\S+)\\s+([^\\s:]+):?\\s+(.*)", Pattern.CASE_INSENSITIVE);
private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy MMM [ ]d HH:mm:ss").withZone(ZoneId.systemDefault()); //.withZone(ZoneOffset.UTC);
/**
* Parses [rfc3164](https://tools.ietf.org/html/rfc3164) syslog messages.
*
* @param input
* @return
* @throws NumberFormatException
*/
@Override
public SyslogMessage parse(final String input) throws NumberFormatException {
log.debug("parseRfc3164() " + input);
Matcher matcher = pattern.matcher(input);
if(!matcher.find()) {
System.err.println("!" + input);
return null;
}
String pri = matcher.group(1);
String date = matcher.group(2);
String hostname = matcher.group(3);
String application = matcher.group(4);
String msg = matcher.group(5);
if(hostname.endsWith(":")) {
String[] tmp = hostname.split(" ");
hostname = tmp[tmp.length-1];
hostname = hostname.substring(0, hostname.length()-1);
}
Integer facility = getFacility(pri);
Integer severity = getSeverity(pri);
SyslogMessage syslogMessage = new SyslogMessage(msg.trim());
syslogMessage.facility = Facility.getByNumber(facility);
syslogMessage.severity = Severity.getByNumber(severity);
syslogMessage.timestamp = parseTimestamp(date);
syslogMessage.hostname = hostname;
syslogMessage.application = application;
return syslogMessage;
}
/**
* Parse rfc3164 TIMESTAMP field into Instant.
*
* @param dateString
* @return
*/
protected Instant parseTimestamp(String dateString) {
// We need to add year to parse date correctly
OffsetDateTime odt = OffsetDateTime.now();
// Date: Mmm dd hh:mm:ss
Instant instant = null;
try {
instant = Instant.from(dateTimeFormatter.parse(odt.getYear() + " " + dateString));
} catch(DateTimeParseException e) {
log.error("parseDate()", e);
}
return instant;
}
}

View file

@ -0,0 +1,134 @@
/*
Copyright 2020 mark.nellemann@gmail.com
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package biz.nellemann.syslogd.parser;
import biz.nellemann.syslogd.msg.Severity;
import biz.nellemann.syslogd.msg.Facility;
import biz.nellemann.syslogd.msg.SyslogMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SyslogParserRfc5424 extends SyslogParser {
private final static Logger log = LoggerFactory.getLogger(SyslogParserRfc5424.class);
private final Pattern pattern = Pattern.compile("^<(\\d{1,3})>(\\d+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\[.*\\]|-)\\s+(\\S+)", Pattern.CASE_INSENSITIVE);
/**
* Parses [rfc5424](https://tools.ietf.org/html/rfc5424) syslog messages.
*
* @param input
* @return
* @throws NumberFormatException
*/
@Override
public SyslogMessage parse(final String input) throws NumberFormatException {
log.debug("parseRfc5424() " + input);
Matcher matcher = pattern.matcher(input);
boolean matchFound = matcher.find();
if(!matchFound) {
log.debug("parseRfc5424() - Match not found in: " + input);
System.err.println("!" + input);
return null;
}
String pri = matcher.group(1);
String ver = matcher.group(2);
String date = matcher.group(3);
String host = matcher.group(4);
String app = matcher.group(5);
String procId = matcher.group(6);
String msgId = matcher.group(7);
String data = matcher.group(8);
String msg = matcher.group(9);
Integer facility = getFacility(pri);
Integer severity = getSeverity(pri);
SyslogMessage syslogMessage = new SyslogMessage(msg.trim());
syslogMessage.facility = Facility.getByNumber(facility);
syslogMessage.severity = Severity.getByNumber(severity);
syslogMessage.version = Integer.parseInt(ver);
syslogMessage.timestamp = parseTimestamp(date);
syslogMessage.hostname = host;
if(app != null && !app.equals("-"))
syslogMessage.application = app;
if(procId != null && !procId.equals("-"))
syslogMessage.processId = procId;
if(msgId != null && !msgId.equals("-"))
syslogMessage.messageId = msgId;
if(data != null && !data.equals("-"))
syslogMessage.structuredData = data;
return syslogMessage;
}
/**
* Parse rfc5424 TIMESTAMP field into Instant.
*
* @param dateString
* @return
*/
protected Instant parseTimestamp(String dateString) {
/*
https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html
ex1: 1985-04-12T23:20:50.52Z
ex2: 1985-04-12T19:20:50.52-04:00
ex3: 2003-10-11T22:14:15.003Z
ex4: 2003-08-24T05:14:15.000003-07:00
ex5: 2003-08-24T05:14:15.000000003-07:00
*/
List<String> formatStrings = Arrays.asList(
"yyyy-MM-dd'T'HH:mm:ss.SS'Z'",
"yyyy-MM-dd'T'HH:mm:ss.SSXXX",
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
"yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXX",
"yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSXXX"
);
for(String formatString : formatStrings)
{
try {
return new SimpleDateFormat(formatString).parse(dateString).toInstant();
}
catch (ParseException e) {}
}
/*
try {
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
instant = Instant.from(dateTimeFormatter.parse(dateString));
} catch(DateTimeParseException e) {
log.error("parseTimestamp()", e);
}
return instant;*/
return null;
}
}

View file

@ -0,0 +1,112 @@
package biz.nellemann.syslogd
import biz.nellemann.syslogd.msg.SyslogMessage
import biz.nellemann.syslogd.parser.SyslogParser
import biz.nellemann.syslogd.parser.SyslogParserRfc3164
import spock.lang.Specification
import java.time.Instant
import java.time.OffsetDateTime;
class SyslogParserRfc3164Test extends Specification {
SyslogParser syslogParser;
void setup() {
syslogParser = new SyslogParserRfc3164();
}
void "test rfc3164 aix/vios message"() {
setup:
def input = "<13>Sep 23 08:37:09 Message forwarded from p924vio1: padmin: test"
when:
SyslogMessage msg = syslogParser.parse(input)
then:
msg.message == "test"
msg.hostname == "p924vio1"
msg.application == "padmin"
}
void "test another rfc3164 aix/vios message"() {
setup:
def input = "<13>Dec 18 10:09:22 Message forwarded from p924vio1: root: [errnotify] seq: 24266 - AA8AB241 1218100920 T O OPERATOR OPERATOR NOTIFICATION"
when:
SyslogMessage msg = syslogParser.parse(input)
then:
msg.message == "[errnotify] seq: 24266 - AA8AB241 1218100920 T O OPERATOR OPERATOR NOTIFICATION"
msg.hostname == "p924vio1"
msg.application == "root"
}
void "test rfc3164 normal message"() {
setup:
def input = "<13>Sep 23 08:53:28 xps13 mark: adfdfdf3432434"
when:
SyslogMessage msg = syslogParser.parse(input)
then:
msg.message == "adfdfdf3432434"
msg.hostname == "xps13"
msg.application == "mark"
}
void "test rsyslogd sudo message"() {
setup:
String input = "<85>Oct 5 17:13:41 xps13 sudo: mark : TTY=pts/1 ; PWD=/etc/rsyslog.d ; USER=root ; COMMAND=/usr/sbin/service rsyslog restart"
when:
SyslogMessage msg = syslogParser.parse(input)
then:
msg.application == "sudo"
msg.message == "mark : TTY=pts/1 ; PWD=/etc/rsyslog.d ; USER=root ; COMMAND=/usr/sbin/service rsyslog restart"
}
void "test gdm-session message"() {
setup:
String input = "<12>Oct 5 18:31:01 xps13 /usr/lib/gdm3/gdm-x-session[1921]: (EE) event5 - CUST0001:00 06CB:76AF Touchpad: kernel bug: Touch jump detected and discarded."
when:
SyslogMessage msg = syslogParser.parse(input)
then:
msg.application == "/usr/lib/gdm3/gdm-x-session[1921]"
msg.message == "(EE) event5 - CUST0001:00 06CB:76AF Touchpad: kernel bug: Touch jump detected and discarded."
}
void "test intellij message"() {
setup:
String input = "<14>Oct 6 05:10:26 xps13 com.jetbrains.IntelliJ-IDEA-Ulti git4idea.commands.GitStandardProgressAnalyzer\$1.onLineAvailable(GitStandardProgressAnalyzer.java:45)"
when:
SyslogMessage msg = syslogParser.parse(input)
then:
msg.application == "com.jetbrains.IntelliJ-IDEA-Ulti"
msg.message == "git4idea.commands.GitStandardProgressAnalyzer\$1.onLineAvailable(GitStandardProgressAnalyzer.java:45)"
}
void "test parseRfc3164Timestamp"() {
setup:
OffsetDateTime odt = OffsetDateTime.now()
String dateString = "Sep 12 20:50:13"
when:
Instant inst = syslogParser.parseTimestamp(dateString)
then:
inst.toString() == "${odt.getYear()}-09-12T18:50:13Z"
}
}

View file

@ -0,0 +1,125 @@
package biz.nellemann.syslogd
import biz.nellemann.syslogd.msg.SyslogMessage
import biz.nellemann.syslogd.parser.SyslogParser
import biz.nellemann.syslogd.parser.SyslogParserRfc5424
import spock.lang.Specification
import java.time.Instant
class SyslogParserRfc5424Test extends Specification {
SyslogParser syslogParser;
void setup() {
syslogParser = new SyslogParserRfc5424();
}
void "test rfc5424 message"() {
setup:
def input = '<13>1 2020-09-23T08:57:30.950699+02:00 xps13 mark - - [exampleSDID@32473 iut="3" eventSource="Application" eventID="1011"] adfdfdf3432434565656'
when:
SyslogMessage msg = syslogParser.parse(input)
then:
msg.message == "adfdfdf3432434565656"
msg.processId == "-"
}
void "test rfc5424 example message"() {
setup:
def input = "<34>1 2003-10-11T22:14:15.003Z mymachine.example.com su - ID47 - BOM'su root' failed for lonvick on /dev/pts/8"
when:
SyslogMessage msg = syslogParser.parse(input)
then:
msg.hostname == "mymachine.example.com"
msg.application == "su"
msg.processId == "-"
msg.messageId == "ID47"
msg.structuredData == "-"
}
void "test rfc5424 example2 message"() {
setup:
def input = "<165>1 2003-08-24T05:14:15.000003-07:00 192.0.2.1 myproc 8710 - - %% It's time to make the do-nuts."
when:
SyslogMessage msg = syslogParser.parse(input)
then:
msg.hostname == "192.0.2.1"
msg.application == "myproc"
msg.processId == "8710"
msg.messageId == "-"
msg.structuredData == "-"
}
void "test parseRfc5424Timestamp ex1"() {
setup:
String dateString = "1985-04-12T23:20:50.52Z"
when:
Instant inst = syslogParser.parseTimestamp(dateString)
then:
inst.toString() == "1985-04-12T21:20:50.052Z"
inst.toEpochMilli() == 482188850052
}
void "test parseRfc5424Timestamp ex2"() {
setup:
String dateString = "1985-04-12T19:20:50.52-04:00"
when:
Instant inst = syslogParser.parseTimestamp(dateString)
then:
inst.toString() == "1985-04-12T23:20:50.052Z"
inst.toEpochMilli() == 482196050052
}
void "test parseRfc5424Timestamp ex3"() {
setup:
String dateString = "2003-10-11T22:14:15.003Z"
when:
Instant inst = syslogParser.parseTimestamp(dateString)
then:
inst.toString() == "2003-10-11T20:14:15.003Z"
inst.toEpochMilli() == 1065903255003
}
void "test parseRfc5424Timestamp ex4"() {
setup:
String dateString = "2003-08-24T05:14:15.000003-07:00"
when:
Instant inst = syslogParser.parseTimestamp(dateString)
then:
inst.toString() == "2003-08-24T12:14:15.003Z"
inst.toEpochMilli() == 1061727255003
}
void "test parseRfc5424Timestamp ex5"() {
setup:
String dateString = "2003-08-24T05:14:15.000000003-07:00"
when:
Instant inst = syslogParser.parseTimestamp(dateString)
then:
inst.toString() == "2003-08-24T12:14:15.003Z"
inst.toEpochMilli() == 1061727255003
}
}

View file

@ -1,223 +1,33 @@
package biz.nellemann.syslogd package biz.nellemann.syslogd
import spock.lang.Ignore import biz.nellemann.syslogd.msg.Facility
import biz.nellemann.syslogd.msg.Severity
import biz.nellemann.syslogd.parser.SyslogParser
import biz.nellemann.syslogd.parser.SyslogParserRfc5424
import spock.lang.Specification import spock.lang.Specification
import java.time.Instant
import java.time.OffsetDateTime;
class SyslogParserTest extends Specification { class SyslogParserTest extends Specification {
void "test rfc5424 message"() { SyslogParser syslogParser;
setup: void setup() {
def input = '<13>1 2020-09-23T08:57:30.950699+02:00 xps13 mark - - [exampleSDID@32473 iut="3" eventSource="Application" eventID="1011"] adfdfdf3432434565656' syslogParser = new SyslogParserRfc5424();
when:
SyslogMessage msg = SyslogParser.parseRfc5424(input)
then:
msg.message == "adfdfdf3432434565656"
msg.processId == "-"
} }
void "test rfc5424 example message"() { void "test facility LOCAL0"() {
setup:
def input = "<34>1 2003-10-11T22:14:15.003Z mymachine.example.com su - ID47 - BOM'su root' failed for lonvick on /dev/pts/8"
when: when:
SyslogMessage msg = SyslogParser.parseRfc5424(input) int code = syslogParser.getFacility("132")
then: then:
msg.hostname == "mymachine.example.com" code == Facility.LOCAL0.toNumber()
msg.application == "su"
msg.processId == "-"
msg.messageId == "ID47"
msg.structuredData == "-"
} }
void "test rfc5424 example2 message"() { void "test severity WARN"() {
setup:
def input = "<165>1 2003-08-24T05:14:15.000003-07:00 192.0.2.1 myproc 8710 - - %% It's time to make the do-nuts."
when: when:
SyslogMessage msg = SyslogParser.parseRfc5424(input) int code = syslogParser.getSeverity("132")
then: then:
msg.hostname == "192.0.2.1" code == Severity.WARN.toNumber()
msg.application == "myproc"
msg.processId == "8710"
msg.messageId == "-"
msg.structuredData == "-"
}
void "test rfc3164 aix/vios message"() {
setup:
def input = "<13>Sep 23 08:37:09 Message forwarded from p924vio1: padmin: test"
when:
SyslogMessage msg = SyslogParser.parseRfc3164(input)
then:
msg.message == "test"
msg.hostname == "p924vio1"
msg.application == "padmin"
}
void "test another rfc3164 aix/vios message"() {
setup:
def input = "<13>Dec 18 10:09:22 Message forwarded from p924vio1: root: [errnotify] seq: 24266 - AA8AB241 1218100920 T O OPERATOR OPERATOR NOTIFICATION"
when:
SyslogMessage msg = SyslogParser.parseRfc3164(input)
then:
msg.message == "[errnotify] seq: 24266 - AA8AB241 1218100920 T O OPERATOR OPERATOR NOTIFICATION"
msg.hostname == "p924vio1"
msg.application == "root"
}
void "test rfc3164 normal message"() {
setup:
def input = "<13>Sep 23 08:53:28 xps13 mark: adfdfdf3432434"
when:
SyslogMessage msg = SyslogParser.parseRfc3164(input)
then:
msg.message == "adfdfdf3432434"
msg.hostname == "xps13"
msg.application == "mark"
}
void "test rsyslogd sudo message"() {
setup:
String input = "<85>Oct 5 17:13:41 xps13 sudo: mark : TTY=pts/1 ; PWD=/etc/rsyslog.d ; USER=root ; COMMAND=/usr/sbin/service rsyslog restart"
when:
SyslogMessage msg = SyslogParser.parseRfc3164(input)
then:
msg.application == "sudo"
msg.message == "mark : TTY=pts/1 ; PWD=/etc/rsyslog.d ; USER=root ; COMMAND=/usr/sbin/service rsyslog restart"
}
void "test gdm-session message"() {
setup:
String input = "<12>Oct 5 18:31:01 xps13 /usr/lib/gdm3/gdm-x-session[1921]: (EE) event5 - CUST0001:00 06CB:76AF Touchpad: kernel bug: Touch jump detected and discarded."
when:
SyslogMessage msg = SyslogParser.parseRfc3164(input)
then:
msg.application == "/usr/lib/gdm3/gdm-x-session[1921]"
msg.message == "(EE) event5 - CUST0001:00 06CB:76AF Touchpad: kernel bug: Touch jump detected and discarded."
}
void "test intellij message"() {
setup:
String input = "<14>Oct 6 05:10:26 xps13 com.jetbrains.IntelliJ-IDEA-Ulti git4idea.commands.GitStandardProgressAnalyzer\$1.onLineAvailable(GitStandardProgressAnalyzer.java:45)"
when:
SyslogMessage msg = SyslogParser.parseRfc3164(input)
then:
msg.application == "com.jetbrains.IntelliJ-IDEA-Ulti"
msg.message == "git4idea.commands.GitStandardProgressAnalyzer\$1.onLineAvailable(GitStandardProgressAnalyzer.java:45)"
}
void "test parseRfc3164Timestamp"() {
setup:
OffsetDateTime odt = OffsetDateTime.now()
String dateString = "Sep 12 20:50:13"
when:
Instant inst = SyslogParser.parseRfc3164Timestamp(dateString)
then:
inst.toString() == "${odt.getYear()}-09-12T20:50:13Z"
}
void "test parseRfc5424Timestamp ex1"() {
setup:
String dateString = "1985-04-12T23:20:50.52Z"
when:
Instant inst = SyslogParser.parseRfc5424Timestamp(dateString)
then:
inst.toString() == "1985-04-12T21:20:50.052Z"
inst.toEpochMilli() == 482188850052
}
void "test parseRfc5424Timestamp ex2"() {
setup:
String dateString = "1985-04-12T19:20:50.52-04:00"
when:
Instant inst = SyslogParser.parseRfc5424Timestamp(dateString)
then:
inst.toString() == "1985-04-12T23:20:50.052Z"
inst.toEpochMilli() == 482196050052
}
void "test parseRfc5424Timestamp ex3"() {
setup:
String dateString = "2003-10-11T22:14:15.003Z"
when:
Instant inst = SyslogParser.parseRfc5424Timestamp(dateString)
then:
inst.toString() == "2003-10-11T20:14:15.003Z"
inst.toEpochMilli() == 1065903255003
}
void "test parseRfc5424Timestamp ex4"() {
setup:
String dateString = "2003-08-24T05:14:15.000003-07:00"
when:
Instant inst = SyslogParser.parseRfc5424Timestamp(dateString)
then:
inst.toString() == "2003-08-24T12:14:15.003Z"
inst.toEpochMilli() == 1061727255003
}
void "test parseRfc5424Timestamp ex5"() {
setup:
String dateString = "2003-08-24T05:14:15.000000003-07:00"
when:
Instant inst = SyslogParser.parseRfc5424Timestamp(dateString)
then:
inst.toString() == "2003-08-24T12:14:15.003Z"
inst.toEpochMilli() == 1061727255003
}
@Ignore
void "test parseRfc5424Timestamp"() {
setup:
String dateString = "2020-09-22T20:10:30.925438+02:00"
when:
Instant inst = SyslogParser.parseRfc5424Timestamp(dateString)
then:
inst.toString() == "2020-09-22T18:10:30.925438Z"
inst.toEpochMilli() == 1600798230925l
} }
} }