From 0752c0b6a64a9a272dd26a1b37bef57dbaad4102 Mon Sep 17 00:00:00 2001 From: Mark Nellemann Date: Wed, 27 Jan 2021 01:29:38 +0100 Subject: [PATCH 1/2] Improve rfc5424 parsing. --- build.gradle | 3 +- doc/syslogd.service | 2 +- .../biz/nellemann/syslogd/Application.java | 4 +- .../biz/nellemann/syslogd/SyslogMessage.java | 8 +- .../biz/nellemann/syslogd/SyslogParser.java | 46 ++++++-- .../biz/nellemann/syslogd/SyslogPrinter.java | 20 +++- .../nellemann/syslogd/SyslogParserTest.groovy | 100 +++++++++++++++++- src/test/resources/simplelogger.properties | 6 ++ 8 files changed, 169 insertions(+), 20 deletions(-) create mode 100644 src/test/resources/simplelogger.properties diff --git a/build.gradle b/build.gradle index f68bf6c..3e86756 100644 --- a/build.gradle +++ b/build.gradle @@ -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 { diff --git a/doc/syslogd.service b/doc/syslogd.service index 2520742..6b4598d 100644 --- a/doc/syslogd.service +++ b/doc/syslogd.service @@ -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 diff --git a/src/main/java/biz/nellemann/syslogd/Application.java b/src/main/java/biz/nellemann/syslogd/Application.java index 1eaf712..d5f38fd 100644 --- a/src/main/java/biz/nellemann/syslogd/Application.java +++ b/src/main/java/biz/nellemann/syslogd/Application.java @@ -53,7 +53,7 @@ public class Application implements Callable, 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 = "") + @CommandLine.Option(names = { "-f", "--forward"}, description = "Forward to UDP host[:port] (RFC-5424).", paramLabel = "") private String forward; @CommandLine.Option(names = { "-d", "--debug" }, description = "Enable debugging [default: 'false'].") @@ -131,7 +131,7 @@ public class Application implements Callable, LogListener { if(doForward) { try { - udpClient.send(SyslogPrinter.toRfc3164(msg)); + udpClient.send(SyslogPrinter.toRfc5424(msg)); } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/biz/nellemann/syslogd/SyslogMessage.java b/src/main/java/biz/nellemann/syslogd/SyslogMessage.java index 28c13f9..5dfa80a 100644 --- a/src/main/java/biz/nellemann/syslogd/SyslogMessage.java +++ b/src/main/java/biz/nellemann/syslogd/SyslogMessage.java @@ -32,16 +32,16 @@ public class SyslogMessage { protected String hostname; // The APP-NAME field SHOULD identify the device or application that originated the message. - protected String application; + protected String application = "-"; // The PROCID field is often used to provide the process name or process ID associated with a syslog system. - protected String processId; + protected String processId = "-"; // The MSGID SHOULD identify the type of message. - protected String messageId; + protected String messageId = "-"; // STRUCTURED-DATA provides a mechanism to express information in a well defined, easily parseable and interpretable data format. - protected String structuredData; + protected String structuredData = "-"; // The MSG part contains a free-form message that provides information about the event. protected final String message; diff --git a/src/main/java/biz/nellemann/syslogd/SyslogParser.java b/src/main/java/biz/nellemann/syslogd/SyslogParser.java index 93b1cc3..52271a8 100644 --- a/src/main/java/biz/nellemann/syslogd/SyslogParser.java +++ b/src/main/java/biz/nellemann/syslogd/SyslogParser.java @@ -18,9 +18,13 @@ 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; @@ -82,11 +86,15 @@ public class SyslogParser { */ 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); + 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.warn("parseRfc5424() - Match not found in: " + input); + log.debug("parseRfc5424() - Match not found in: " + input); System.err.println("!" + input); return null; } @@ -116,7 +124,8 @@ public class SyslogParser { syslogMessage.processId = procId; if(msgId != null && !msgId.equals("-")) syslogMessage.messageId = msgId; - syslogMessage.structuredData = data; + if(data != null && !data.equals("-")) + syslogMessage.structuredData = data; return syslogMessage; } @@ -154,15 +163,40 @@ public class SyslogParser { */ static protected Instant parseRfc5424Timestamp(String dateString) { - Instant instant = null; + /* + 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 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 instant;*/ + return null; } diff --git a/src/main/java/biz/nellemann/syslogd/SyslogPrinter.java b/src/main/java/biz/nellemann/syslogd/SyslogPrinter.java index 6004afc..da6be63 100644 --- a/src/main/java/biz/nellemann/syslogd/SyslogPrinter.java +++ b/src/main/java/biz/nellemann/syslogd/SyslogPrinter.java @@ -1,7 +1,12 @@ package biz.nellemann.syslogd; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class SyslogPrinter { + private final static Logger log = LoggerFactory.getLogger(SyslogPrinter.class); + public static String toString(SyslogMessage msg) { StringBuilder sb = new StringBuilder(); sb.append(msg.timestamp.toString()); @@ -44,23 +49,28 @@ public class SyslogPrinter { sb.append(" " + msg.application); sb.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(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(" " + msg.hostname); sb.append(" " + msg.application); - sb.append(": " + msg.message); - + sb.append(" " + msg.processId); + sb.append(" " + msg.messageId); + sb.append(" " + msg.structuredData); + sb.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); diff --git a/src/test/groovy/biz/nellemann/syslogd/SyslogParserTest.groovy b/src/test/groovy/biz/nellemann/syslogd/SyslogParserTest.groovy index d1d2a0c..ff35567 100644 --- a/src/test/groovy/biz/nellemann/syslogd/SyslogParserTest.groovy +++ b/src/test/groovy/biz/nellemann/syslogd/SyslogParserTest.groovy @@ -1,5 +1,6 @@ package biz.nellemann.syslogd +import spock.lang.Ignore import spock.lang.Specification import java.time.Instant import java.time.OffsetDateTime; @@ -9,15 +10,49 @@ class SyslogParserTest extends Specification { void "test rfc5424 message"() { setup: - def input = "<13>1 2020-09-23T08:57:30.950699+02:00 xps13 mark - - [timeQuality tzKnown=\"1\" isSynced=\"1\" syncAccuracy=\"125500\"] adfdfdf3432434565656" + 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.parseRfc5424(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.parseRfc5424(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.parseRfc5424(input) + + then: + msg.hostname == "192.0.2.1" + msg.application == "myproc" + msg.processId == "8710" + msg.messageId == "-" + msg.structuredData == "-" + } + + void "test rfc3164 aix/vios message"() { setup: @@ -109,6 +144,69 @@ class SyslogParserTest extends Specification { 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: diff --git a/src/test/resources/simplelogger.properties b/src/test/resources/simplelogger.properties new file mode 100644 index 0000000..876e5b5 --- /dev/null +++ b/src/test/resources/simplelogger.properties @@ -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 From 633ec03baeaef3963212847ad6004120c62ee5c1 Mon Sep 17 00:00:00 2001 From: Mark Nellemann Date: Wed, 27 Jan 2021 15:18:46 +0100 Subject: [PATCH 2/2] Refactoring and more tests. --- gradle.properties | 2 +- .../biz/nellemann/syslogd/Application.java | 20 +- .../biz/nellemann/syslogd/LogListener.java | 2 +- .../biz/nellemann/syslogd/SyslogParser.java | 235 ------------------ .../biz/nellemann/syslogd/SyslogPrinter.java | 37 +-- .../java/biz/nellemann/syslogd/UdpClient.java | 30 --- .../nellemann/syslogd/{ => msg}/Facility.java | 2 +- .../nellemann/syslogd/{ => msg}/Severity.java | 2 +- .../syslogd/{ => msg}/SyslogMessage.java | 24 +- .../syslogd/{ => net}/TcpServer.java | 11 +- .../biz/nellemann/syslogd/net/UdpClient.java | 50 ++++ .../syslogd/{ => net}/UdpServer.java | 8 +- .../syslogd/parser/SyslogParser.java | 66 +++++ .../syslogd/parser/SyslogParserRfc3164.java | 103 ++++++++ .../syslogd/parser/SyslogParserRfc5424.java | 134 ++++++++++ .../syslogd/SyslogParserRfc3164Test.groovy | 112 +++++++++ .../syslogd/SyslogParserRfc5424Test.groovy | 125 ++++++++++ .../nellemann/syslogd/SyslogParserTest.groovy | 216 +--------------- 18 files changed, 666 insertions(+), 513 deletions(-) delete mode 100644 src/main/java/biz/nellemann/syslogd/SyslogParser.java delete mode 100644 src/main/java/biz/nellemann/syslogd/UdpClient.java rename src/main/java/biz/nellemann/syslogd/{ => msg}/Facility.java (98%) rename src/main/java/biz/nellemann/syslogd/{ => msg}/Severity.java (96%) rename src/main/java/biz/nellemann/syslogd/{ => msg}/SyslogMessage.java (77%) rename src/main/java/biz/nellemann/syslogd/{ => net}/TcpServer.java (94%) create mode 100644 src/main/java/biz/nellemann/syslogd/net/UdpClient.java rename src/main/java/biz/nellemann/syslogd/{ => net}/UdpServer.java (90%) create mode 100644 src/main/java/biz/nellemann/syslogd/parser/SyslogParser.java create mode 100644 src/main/java/biz/nellemann/syslogd/parser/SyslogParserRfc3164.java create mode 100644 src/main/java/biz/nellemann/syslogd/parser/SyslogParserRfc5424.java create mode 100644 src/test/groovy/biz/nellemann/syslogd/SyslogParserRfc3164Test.groovy create mode 100644 src/test/groovy/biz/nellemann/syslogd/SyslogParserRfc5424Test.groovy diff --git a/gradle.properties b/gradle.properties index bdcfd3b..e8f0478 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ id = syslogd group = biz.nellemann.syslogd -version = 1.0.9 +version = 1.0.10 diff --git a/src/main/java/biz/nellemann/syslogd/Application.java b/src/main/java/biz/nellemann/syslogd/Application.java index d5f38fd..c806e74 100644 --- a/src/main/java/biz/nellemann/syslogd/Application.java +++ b/src/main/java/biz/nellemann/syslogd/Application.java @@ -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, LogListener { private boolean doForward = false; + private SyslogParser syslogParser; private UdpClient udpClient; @@ -67,6 +75,12 @@ public class Application implements Callable, 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, 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(); } diff --git a/src/main/java/biz/nellemann/syslogd/LogListener.java b/src/main/java/biz/nellemann/syslogd/LogListener.java index a42b529..380bd8d 100644 --- a/src/main/java/biz/nellemann/syslogd/LogListener.java +++ b/src/main/java/biz/nellemann/syslogd/LogListener.java @@ -16,5 +16,5 @@ package biz.nellemann.syslogd; public interface LogListener { - void onLogEvent(LogEvent event); + public void onLogEvent(LogEvent event); } diff --git a/src/main/java/biz/nellemann/syslogd/SyslogParser.java b/src/main/java/biz/nellemann/syslogd/SyslogParser.java deleted file mode 100644 index 52271a8..0000000 --- a/src/main/java/biz/nellemann/syslogd/SyslogParser.java +++ /dev/null @@ -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 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; - } - - -} diff --git a/src/main/java/biz/nellemann/syslogd/SyslogPrinter.java b/src/main/java/biz/nellemann/syslogd/SyslogPrinter.java index da6be63..8f3dff7 100644 --- a/src/main/java/biz/nellemann/syslogd/SyslogPrinter.java +++ b/src/main/java/biz/nellemann/syslogd/SyslogPrinter.java @@ -1,5 +1,8 @@ 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; @@ -7,9 +10,10 @@ 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)); @@ -19,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); @@ -45,10 +47,9 @@ 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(); } @@ -59,21 +60,21 @@ public class SyslogPrinter { public static String toRfc5424(SyslogMessage msg) { StringBuilder sb = new StringBuilder(); 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(" " + msg.hostname); - sb.append(" " + msg.application); - sb.append(" " + msg.processId); - sb.append(" " + msg.messageId); - sb.append(" " + msg.structuredData); - sb.append(" " + msg.message); + 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, '>'); } } diff --git a/src/main/java/biz/nellemann/syslogd/UdpClient.java b/src/main/java/biz/nellemann/syslogd/UdpClient.java deleted file mode 100644 index 296bc47..0000000 --- a/src/main/java/biz/nellemann/syslogd/UdpClient.java +++ /dev/null @@ -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(); - } -} diff --git a/src/main/java/biz/nellemann/syslogd/Facility.java b/src/main/java/biz/nellemann/syslogd/msg/Facility.java similarity index 98% rename from src/main/java/biz/nellemann/syslogd/Facility.java rename to src/main/java/biz/nellemann/syslogd/msg/Facility.java index 001a7c4..79b675b 100644 --- a/src/main/java/biz/nellemann/syslogd/Facility.java +++ b/src/main/java/biz/nellemann/syslogd/msg/Facility.java @@ -1,4 +1,4 @@ -package biz.nellemann.syslogd; +package biz.nellemann.syslogd.msg; import java.util.HashMap; import java.util.Map; diff --git a/src/main/java/biz/nellemann/syslogd/Severity.java b/src/main/java/biz/nellemann/syslogd/msg/Severity.java similarity index 96% rename from src/main/java/biz/nellemann/syslogd/Severity.java rename to src/main/java/biz/nellemann/syslogd/msg/Severity.java index 4364afb..3f8a1df 100644 --- a/src/main/java/biz/nellemann/syslogd/Severity.java +++ b/src/main/java/biz/nellemann/syslogd/msg/Severity.java @@ -1,4 +1,4 @@ -package biz.nellemann.syslogd; +package biz.nellemann.syslogd.msg; import java.util.HashMap; import java.util.Map; diff --git a/src/main/java/biz/nellemann/syslogd/SyslogMessage.java b/src/main/java/biz/nellemann/syslogd/msg/SyslogMessage.java similarity index 77% rename from src/main/java/biz/nellemann/syslogd/SyslogMessage.java rename to src/main/java/biz/nellemann/syslogd/msg/SyslogMessage.java index 5dfa80a..7326f64 100644 --- a/src/main/java/biz/nellemann/syslogd/SyslogMessage.java +++ b/src/main/java/biz/nellemann/syslogd/msg/SyslogMessage.java @@ -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; } diff --git a/src/main/java/biz/nellemann/syslogd/TcpServer.java b/src/main/java/biz/nellemann/syslogd/net/TcpServer.java similarity index 94% rename from src/main/java/biz/nellemann/syslogd/TcpServer.java rename to src/main/java/biz/nellemann/syslogd/net/TcpServer.java index 65f5cf0..ed66153 100644 --- a/src/main/java/biz/nellemann/syslogd/TcpServer.java +++ b/src/main/java/biz/nellemann/syslogd/net/TcpServer.java @@ -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; } diff --git a/src/main/java/biz/nellemann/syslogd/net/UdpClient.java b/src/main/java/biz/nellemann/syslogd/net/UdpClient.java new file mode 100644 index 0000000..9b57cf2 --- /dev/null +++ b/src/main/java/biz/nellemann/syslogd/net/UdpClient.java @@ -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(); + } +} diff --git a/src/main/java/biz/nellemann/syslogd/UdpServer.java b/src/main/java/biz/nellemann/syslogd/net/UdpServer.java similarity index 90% rename from src/main/java/biz/nellemann/syslogd/UdpServer.java rename to src/main/java/biz/nellemann/syslogd/net/UdpServer.java index 8e34d71..850f23b 100644 --- a/src/main/java/biz/nellemann/syslogd/UdpServer.java +++ b/src/main/java/biz/nellemann/syslogd/net/UdpServer.java @@ -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 eventListeners = new ArrayList<>(); - public synchronized void addEventListener( LogListener l ) { + public synchronized void addEventListener(Application l ) { eventListeners.add( l ); } diff --git a/src/main/java/biz/nellemann/syslogd/parser/SyslogParser.java b/src/main/java/biz/nellemann/syslogd/parser/SyslogParser.java new file mode 100644 index 0000000..d7fc4fc --- /dev/null +++ b/src/main/java/biz/nellemann/syslogd/parser/SyslogParser.java @@ -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; + } + + +} diff --git a/src/main/java/biz/nellemann/syslogd/parser/SyslogParserRfc3164.java b/src/main/java/biz/nellemann/syslogd/parser/SyslogParserRfc3164.java new file mode 100644 index 0000000..37da9ca --- /dev/null +++ b/src/main/java/biz/nellemann/syslogd/parser/SyslogParserRfc3164.java @@ -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; + } + +} diff --git a/src/main/java/biz/nellemann/syslogd/parser/SyslogParserRfc5424.java b/src/main/java/biz/nellemann/syslogd/parser/SyslogParserRfc5424.java new file mode 100644 index 0000000..dc16626 --- /dev/null +++ b/src/main/java/biz/nellemann/syslogd/parser/SyslogParserRfc5424.java @@ -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 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; + } + +} diff --git a/src/test/groovy/biz/nellemann/syslogd/SyslogParserRfc3164Test.groovy b/src/test/groovy/biz/nellemann/syslogd/SyslogParserRfc3164Test.groovy new file mode 100644 index 0000000..221f938 --- /dev/null +++ b/src/test/groovy/biz/nellemann/syslogd/SyslogParserRfc3164Test.groovy @@ -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" + } + +} + + diff --git a/src/test/groovy/biz/nellemann/syslogd/SyslogParserRfc5424Test.groovy b/src/test/groovy/biz/nellemann/syslogd/SyslogParserRfc5424Test.groovy new file mode 100644 index 0000000..6eaa7a7 --- /dev/null +++ b/src/test/groovy/biz/nellemann/syslogd/SyslogParserRfc5424Test.groovy @@ -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 + } + + +} + + diff --git a/src/test/groovy/biz/nellemann/syslogd/SyslogParserTest.groovy b/src/test/groovy/biz/nellemann/syslogd/SyslogParserTest.groovy index ff35567..55e72eb 100644 --- a/src/test/groovy/biz/nellemann/syslogd/SyslogParserTest.groovy +++ b/src/test/groovy/biz/nellemann/syslogd/SyslogParserTest.groovy @@ -1,223 +1,33 @@ 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 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 - - [exampleSDID@32473 iut="3" eventSource="Application" eventID="1011"] adfdfdf3432434565656' - - when: - SyslogMessage msg = SyslogParser.parseRfc5424(input) - - then: - msg.message == "adfdfdf3432434565656" - msg.processId == "-" + void setup() { + syslogParser = new SyslogParserRfc5424(); } - 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" - + void "test facility LOCAL0"() { when: - SyslogMessage msg = SyslogParser.parseRfc5424(input) + int code = syslogParser.getFacility("132") then: - msg.hostname == "mymachine.example.com" - msg.application == "su" - msg.processId == "-" - msg.messageId == "ID47" - msg.structuredData == "-" + code == Facility.LOCAL0.toNumber() } - 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." - + void "test severity WARN"() { when: - SyslogMessage msg = SyslogParser.parseRfc5424(input) + int code = syslogParser.getSeverity("132") then: - msg.hostname == "192.0.2.1" - 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 + code == Severity.WARN.toNumber() } }