Merged in fixes (pull request #3)

Fixes
This commit is contained in:
Mark Nellemann 2021-01-27 14:23:00 +00:00
commit 0909298bbc
21 changed files with 687 additions and 385 deletions

View file

@ -18,7 +18,8 @@ dependencies {
implementation 'org.slf4j:slf4j-simple:1.7.30'
testImplementation('org.spockframework:spock-core:2.0-M4-groovy-3.0')
testImplementation("org.slf4j:slf4j-simple:1.7.30")
testImplementation 'org.slf4j:slf4j-api:1.7.30'
testRuntime("org.slf4j:slf4j-simple:1.7.30")
}
application {

View file

@ -4,7 +4,7 @@ Description=Simple Syslog Service
[Service]
TimeoutStartSec=0
Restart=always
ExecStart=/opt/syslogd/bin/syslogd --no-stdout --forward localhost:1514
ExecStart=/opt/syslogd/bin/syslogd --no-stdout --forward=localhost:1514
[Install]
WantedBy=default.target

View file

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

View file

@ -15,6 +15,13 @@
*/
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 picocli.CommandLine;
@ -32,6 +39,7 @@ import java.util.regex.Pattern;
public class Application implements Callable<Integer>, LogListener {
private boolean doForward = false;
private SyslogParser syslogParser;
private UdpClient udpClient;
@ -53,7 +61,7 @@ public class Application implements Callable<Integer>, LogListener {
@CommandLine.Option(names = "--rfc5424", description = "Parse RFC-5424 messages [default: RFC-3164].", defaultValue = "false")
private boolean rfc5424;
@CommandLine.Option(names = { "-f", "--forward"}, description = "Forward to UDP host[:port] (RFC-3164).", paramLabel = "<host>")
@CommandLine.Option(names = { "-f", "--forward"}, description = "Forward to UDP host[:port] (RFC-5424).", paramLabel = "<host>")
private String forward;
@CommandLine.Option(names = { "-d", "--debug" }, description = "Enable debugging [default: 'false'].")
@ -67,6 +75,12 @@ public class Application implements Callable<Integer>, LogListener {
System.setProperty(SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "DEBUG");
}
if(rfc5424) {
syslogParser = new SyslogParserRfc5424();
} else {
syslogParser = new SyslogParserRfc3164();
}
if(forward != null && !forward.isEmpty()) {
String fHost, fPort;
Pattern pattern = Pattern.compile("^([^:]+)(?::([0-9]+))?$", Pattern.CASE_INSENSITIVE);
@ -110,11 +124,7 @@ public class Application implements Callable<Integer>, LogListener {
String message = event.getMessage();
SyslogMessage msg = null;
try {
if(rfc5424) {
msg = SyslogParser.parseRfc5424(message);
} else {
msg = SyslogParser.parseRfc3164(message);
}
msg = syslogParser.parse(message);
} catch(Exception e) {
e.printStackTrace();
}
@ -131,7 +141,7 @@ public class Application implements Callable<Integer>, LogListener {
if(doForward) {
try {
udpClient.send(SyslogPrinter.toRfc3164(msg));
udpClient.send(SyslogPrinter.toRfc5424(msg));
} catch (Exception e) {
e.printStackTrace();
}

View file

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

View file

@ -1,201 +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.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
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 {
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.warn("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;
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) {
Instant instant = null;
try {
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
instant = Instant.from(dateTimeFormatter.parse(dateString));
} catch(DateTimeParseException e) {
log.error("parseTimestamp()", e);
}
return instant;
}
/**
* 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,10 +1,19 @@
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.LoggerFactory;
public class SyslogPrinter {
private final static Logger log = LoggerFactory.getLogger(SyslogPrinter.class);
private final static char SPACE = ' ';
public static String toString(SyslogMessage msg) {
StringBuilder sb = new StringBuilder();
sb.append(msg.timestamp.toString());
StringBuilder sb = new StringBuilder(msg.timestamp.toString());
sb.append(String.format(" [%8.8s.%-6.6s] ", msg.facility, msg.severity));
sb.append(String.format(" %-16.16s ", msg.hostname));
sb.append(String.format(" %-32.32s ", msg.application));
@ -14,9 +23,7 @@ public class SyslogPrinter {
public static String toAnsiString(SyslogMessage msg) {
StringBuilder sb = new StringBuilder();
sb.append(msg.timestamp.toString());
StringBuilder sb = new StringBuilder(msg.timestamp.toString());
if (msg.severity.toNumber() < 3) {
sb.append(Ansi.RED);
@ -40,30 +47,34 @@ public class SyslogPrinter {
StringBuilder sb = new StringBuilder();
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(" " + msg.hostname);
sb.append(" " + msg.application);
sb.append(": " + msg.message);
sb.append(SPACE).append(msg.hostname);
sb.append(SPACE).append(msg.application);
sb.append(":").append(SPACE).append(msg.message);
log.debug(sb.toString());
return sb.toString();
}
// <13>1 2020-09-23T08:57:30.950699+02:00 xps13 mark - - [timeQuality tzKnown="1" isSynced="1" syncAccuracy="125500"] adfdfdf3432434565656
// <34>1 2003-10-11T22:14:15.003Z mymachine.example.com su - ID47 - BOM'su root' failed for lonvick on /dev/pts/8
public static String toRfc5424(SyslogMessage msg) {
StringBuilder sb = new StringBuilder();
sb.append(getPri(msg.facility, msg.severity));
sb.append("1"); // Version
sb.append(" " + new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(new java.util.Date(msg.timestamp.toEpochMilli())));
sb.append(" " + msg.hostname);
sb.append(" " + msg.application);
sb.append(": " + msg.message);
sb.append(getPri(msg.facility, msg.severity)).append("1");
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(SPACE).append(msg.hostname);
sb.append(SPACE).append(msg.application);
sb.append(SPACE).append(msg.processId);
sb.append(SPACE).append(msg.messageId);
sb.append(SPACE).append(msg.structuredData);
sb.append(SPACE).append(msg.message);
log.debug(sb.toString());
return sb.toString();
}
static private String getPri(Facility facility, Severity severity) {
int prival = (facility.toNumber() * 8) + severity.toNumber();
return String.format("<%d>", prival);
int pri = (facility.toNumber() * 8) + severity.toNumber();
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.Map;

View file

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

View file

@ -13,40 +13,40 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
package biz.nellemann.syslogd;
package biz.nellemann.syslogd.msg;
import java.time.Instant;
public class SyslogMessage {
protected Facility facility;
protected Severity severity;
public Facility facility;
public Severity severity;
// 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].
protected Instant timestamp;
public Instant timestamp;
// 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.
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.
protected String processId;
public String processId = "-";
// 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.
protected String structuredData;
public String structuredData = "-";
// 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;
}

View file

@ -13,26 +13,29 @@
See the License for the specific language governing permissions and
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.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.ArrayList;
public class TcpServer {
private final int port;
private ServerSocket serverSocket;
TcpServer() {
public TcpServer() {
this(514);
}
TcpServer(int port) {
public TcpServer(int 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
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.net.DatagramPacket;
@ -68,7 +72,7 @@ public class UdpServer extends Thread {
protected List<LogListener> eventListeners = new ArrayList<>();
public synchronized void addEventListener( LogListener l ) {
public synchronized void addEventListener(Application 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,125 +1,33 @@
package biz.nellemann.syslogd
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 java.time.Instant
import java.time.OffsetDateTime;
class SyslogParserTest extends Specification {
void "test rfc5424 message"() {
SyslogParser syslogParser;
setup:
def input = "<13>1 2020-09-23T08:57:30.950699+02:00 xps13 mark - - [timeQuality tzKnown=\"1\" isSynced=\"1\" syncAccuracy=\"125500\"] adfdfdf3432434565656"
void setup() {
syslogParser = new SyslogParserRfc5424();
}
void "test facility LOCAL0"() {
when:
SyslogMessage msg = SyslogParser.parseRfc5424(input)
int code = syslogParser.getFacility("132")
then:
msg.message == "adfdfdf3432434565656"
code == Facility.LOCAL0.toNumber()
}
void "test rfc3164 aix/vios message"() {
setup:
def input = "<13>Sep 23 08:37:09 Message forwarded from p924vio1: padmin: test"
void "test severity WARN"() {
when:
SyslogMessage msg = SyslogParser.parseRfc3164(input)
int code = syslogParser.getSeverity("132")
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"() {
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
code == Severity.WARN.toNumber()
}
}

View file

@ -0,0 +1,6 @@
org.slf4j.simpleLogger.logFile=System.err
org.slf4j.simpleLogger.showDateTime=false
org.slf4j.simpleLogger.showShortLogName=true
org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss.SSS
org.slf4j.simpleLogger.levelInBrackets=true
org.slf4j.simpleLogger.defaultLogLevel=debug