From 8ae2ec7573297a9f9c11597bb95f25182589b2b4 Mon Sep 17 00:00:00 2001 From: Mark Nellemann Date: Mon, 5 Oct 2020 10:40:42 +0200 Subject: [PATCH] Improve UDP reception and add file output and ansi flag on/off. --- build.gradle | 2 +- gradle.properties | 2 +- .../java/biz/nellemann/syslogd/LogEvent.java | 4 +- .../biz/nellemann/syslogd/LogListener.java | 2 + .../biz/nellemann/syslogd/SyslogMessage.java | 20 +++++- .../biz/nellemann/syslogd/SyslogParser.java | 38 +++++------ .../biz/nellemann/syslogd/SyslogServer.java | 68 +++++++++++++++++-- .../java/biz/nellemann/syslogd/UdpServer.java | 4 +- .../nellemann/syslogd/VersionProvider.java | 22 ++++++ 9 files changed, 126 insertions(+), 36 deletions(-) create mode 100644 src/main/java/biz/nellemann/syslogd/VersionProvider.java diff --git a/build.gradle b/build.gradle index cd5d64e..d46a576 100644 --- a/build.gradle +++ b/build.gradle @@ -59,7 +59,7 @@ ospackage { buildRpm { dependsOn startShadowScripts - requires('java-1.8.0-openjdk-headless') + //requires('java-1.8.0-openjdk-headless') os = LINUX } diff --git a/gradle.properties b/gradle.properties index 039dbb9..d090558 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ id = syslogd group = biz.nellemann.syslogd -version = 1.0.0 +version = 1.0.1 diff --git a/src/main/java/biz/nellemann/syslogd/LogEvent.java b/src/main/java/biz/nellemann/syslogd/LogEvent.java index 715302a..2eb8eeb 100644 --- a/src/main/java/biz/nellemann/syslogd/LogEvent.java +++ b/src/main/java/biz/nellemann/syslogd/LogEvent.java @@ -20,9 +20,9 @@ import java.util.EventObject; public class LogEvent extends EventObject { - private String message; + private final String message; - public LogEvent(Object source, String message ) { + public LogEvent(final Object source, final String message ) { super( source ); this.message = message; } diff --git a/src/main/java/biz/nellemann/syslogd/LogListener.java b/src/main/java/biz/nellemann/syslogd/LogListener.java index a42b529..801dec8 100644 --- a/src/main/java/biz/nellemann/syslogd/LogListener.java +++ b/src/main/java/biz/nellemann/syslogd/LogListener.java @@ -15,6 +15,8 @@ */ package biz.nellemann.syslogd; +import java.io.IOException; + public interface LogListener { void onLogEvent(LogEvent event); } diff --git a/src/main/java/biz/nellemann/syslogd/SyslogMessage.java b/src/main/java/biz/nellemann/syslogd/SyslogMessage.java index f802a54..f703955 100644 --- a/src/main/java/biz/nellemann/syslogd/SyslogMessage.java +++ b/src/main/java/biz/nellemann/syslogd/SyslogMessage.java @@ -44,12 +44,26 @@ public class SyslogMessage { String structuredData; // The MSG part contains a free-form message that provides information about the event. - String message; + final private String message; + + SyslogMessage(final String message) { + this.message = message; + } + public String toString() { - //return String.format("%s %s %s: %s", timestamp.toString(), hostname, application, message); + StringBuilder sb = new StringBuilder(); + sb.append(timestamp.toString() + " "); + sb.append("[" + facility + "." + severity + "]"); + sb.append("\t" + hostname); + sb.append("\t" + application); + sb.append("\t" + message); + return sb.toString(); + } + + public String toAnsiString() { StringBuilder sb = new StringBuilder(); sb.append(timestamp.toString() + " "); @@ -65,7 +79,7 @@ public class SyslogMessage { sb.append(Ansi.BLUE); sb.append("\t" + hostname); sb.append(Ansi.RESET); sb.append(Ansi.CYAN); sb.append("\t" + application); sb.append(Ansi.RESET); - sb.append("\t" + message); sb.append(Ansi.CLEAR_LINE); + sb.append("\t" + message); return sb.toString(); } diff --git a/src/main/java/biz/nellemann/syslogd/SyslogParser.java b/src/main/java/biz/nellemann/syslogd/SyslogParser.java index 17b0cbb..7df56ff 100644 --- a/src/main/java/biz/nellemann/syslogd/SyslogParser.java +++ b/src/main/java/biz/nellemann/syslogd/SyslogParser.java @@ -35,7 +35,7 @@ public class SyslogParser { private final static Logger log = LoggerFactory.getLogger(SyslogParser.class); - public static SyslogMessage parseRfc3164(String input) throws NumberFormatException { + public static SyslogMessage parseRfc3164(final String input) throws NumberFormatException { Pattern pattern = Pattern.compile("^<(\\d{1,3})>(\\D{3} \\d{2} \\d{2}:\\d{2}:\\d{2})\\s+(?:Message forwarded from )?([^\\s:]+):?\\s+(\\S+): (.*)", Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(input); @@ -45,11 +45,11 @@ public class SyslogParser { return null; } - String pri = matcher.group(1); - String date = matcher.group(2); - String hostname = matcher.group(3); - String application = matcher.group(4); - String message = matcher.group(5); + final String pri = matcher.group(1); + final String date = matcher.group(2); + final String hostname = matcher.group(3); + final String application = matcher.group(4); + final String message = matcher.group(5); log.debug("PRI: " + pri); log.debug("DATE: " + date); @@ -62,19 +62,18 @@ public class SyslogParser { log.debug("facility: " + facility); log.debug("severity: " + severity); - SyslogMessage syslogMessage = new SyslogMessage(); + SyslogMessage syslogMessage = new SyslogMessage(message); syslogMessage.facility = Facility.getByNumber(facility); syslogMessage.severity = Severity.getByNumber(severity); syslogMessage.timestamp = parseRfc3164Timestamp(date); syslogMessage.hostname = hostname; syslogMessage.application = application; - syslogMessage.message = message; return syslogMessage; } - public static SyslogMessage parseRfc5424(String input) 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); @@ -84,15 +83,15 @@ public class SyslogParser { 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); + final String pri = matcher.group(1); + final String ver = matcher.group(2); + final String date = matcher.group(3); + final String host = matcher.group(4); + final String app = matcher.group(5); + final String procId = matcher.group(6); + final String msgId = matcher.group(7); + final String data = matcher.group(8); + final String msg = matcher.group(9); log.debug("PRI: " + pri); log.debug("VER: " + ver); @@ -109,7 +108,7 @@ public class SyslogParser { log.debug("facility: " + facility); log.debug("severity: " + severity); - SyslogMessage syslogMessage = new SyslogMessage(); + SyslogMessage syslogMessage = new SyslogMessage(msg); syslogMessage.facility = Facility.getByNumber(facility); syslogMessage.severity = Severity.getByNumber(severity); syslogMessage.version = Integer.parseInt(ver); @@ -122,7 +121,6 @@ public class SyslogParser { if(msgId != null && !msgId.equals("-")) syslogMessage.messageId = msgId; syslogMessage.structuredData = data; - syslogMessage.message = msg; return syslogMessage; } diff --git a/src/main/java/biz/nellemann/syslogd/SyslogServer.java b/src/main/java/biz/nellemann/syslogd/SyslogServer.java index 619e48d..0e19550 100644 --- a/src/main/java/biz/nellemann/syslogd/SyslogServer.java +++ b/src/main/java/biz/nellemann/syslogd/SyslogServer.java @@ -19,26 +19,37 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import picocli.CommandLine; import picocli.CommandLine.Command; + +import java.io.*; import java.util.concurrent.Callable; -@Command(name = "syslogd", mixinStandardHelpOptions = true, version = "syslogd 1.0", - description = "Simple syslog server that prints messages to stdout.") +@Command(name = "syslogd", + mixinStandardHelpOptions = true, + description = "Basic syslog server.", + versionProvider = biz.nellemann.syslogd.VersionProvider.class) public class SyslogServer implements Callable, LogListener { private final static Logger log = LoggerFactory.getLogger(SyslogServer.class); + private BufferedWriter bw; - @CommandLine.Option(names = {"-p", "--port"}, description = "Listening port, 514 (privileged) by default.") + @CommandLine.Option(names = {"-p", "--port"}, description = "Listening port [default: 514]") private int port = 514; - @CommandLine.Option(names = "--no-udp", negatable = true, description = "Listen on UDP, true by default.") + @CommandLine.Option(names = "--no-udp", negatable = true, description = "Listen on UDP [default: true]") boolean udpServer = true; - @CommandLine.Option(names = "--no-tcp", negatable = true, description = "Listen on TCP, true by default.") + @CommandLine.Option(names = "--no-tcp", negatable = true, description = "Listen on TCP [default: true]") boolean tcpServer = true; - @CommandLine.Option(names = "--rfc3164", negatable = false, description = "Parse RFC3164 syslog message, RFC5424 by default.") + @CommandLine.Option(names = "--rfc3164", negatable = false, description = "Parse RFC3164 messages [default: RFC5424]") boolean rfc3164 = false; + @CommandLine.Option(names = "--no-ansi", negatable = true, description = "ANSI in output [default: true]") + boolean ansiOutput = true; + + @CommandLine.Option(names = {"-f", "--file"}, description = "Write output to file [default: STDOUT]") + File outputFile; + public static void main(String... args) { int exitCode = new CommandLine(new SyslogServer()).execute(args); System.exit(exitCode); @@ -48,6 +59,15 @@ public class SyslogServer implements Callable, LogListener { @Override public Integer call() throws Exception { + FileOutputStream fos = null; + OutputStreamWriter w; + + if(outputFile != null) { + fos = new FileOutputStream(outputFile); + w = new OutputStreamWriter(fos, "UTF-8"); + bw = new BufferedWriter(w); + } + if(udpServer) { UdpServer udpServer = new UdpServer(port); udpServer.addEventListener(this); @@ -60,6 +80,11 @@ public class SyslogServer implements Callable, LogListener { tcpServer.start(); } + if(outputFile != null) { + bw.close(); + fos.close(); + } + return 0; } @@ -67,6 +92,7 @@ public class SyslogServer implements Callable, LogListener { @Override public void onLogEvent(LogEvent event) { + // Parse message String message = event.getMessage(); SyslogMessage msg = null; try { @@ -80,9 +106,37 @@ public class SyslogServer implements Callable, LogListener { } if(msg != null) { - System.out.println(msg); + if(bw != null) { + writeToFile(msg); + } else { + writeToStdout(msg); + } } } + + void writeToFile(SyslogMessage msg) { + try { + if(ansiOutput) { + bw.append(msg.toAnsiString()); + } else { + bw.append(msg.toString()); + } + bw.newLine(); + bw.flush(); + } catch (IOException e) { + log.error("file error", e); + } + } + + + void writeToStdout(SyslogMessage msg) { + if(ansiOutput) { + System.out.println(msg.toAnsiString()); + } else { + System.out.println(msg); + } + } + } diff --git a/src/main/java/biz/nellemann/syslogd/UdpServer.java b/src/main/java/biz/nellemann/syslogd/UdpServer.java index 4123930..0b78379 100644 --- a/src/main/java/biz/nellemann/syslogd/UdpServer.java +++ b/src/main/java/biz/nellemann/syslogd/UdpServer.java @@ -38,13 +38,13 @@ public class UdpServer extends Thread { public void run() { - byte[] buf = new byte[10000]; + byte[] buf = new byte[4096]; DatagramPacket packet = new DatagramPacket(buf, buf.length); while (listen) { try { socket.receive(packet); - String packetData = new String(packet.getData(), "UTF8"); + String packetData = new String(packet.getData(), packet.getOffset(), packet.getLength(), "UTF8"); sendEvent(packetData); } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/java/biz/nellemann/syslogd/VersionProvider.java b/src/main/java/biz/nellemann/syslogd/VersionProvider.java new file mode 100644 index 0000000..ee93d82 --- /dev/null +++ b/src/main/java/biz/nellemann/syslogd/VersionProvider.java @@ -0,0 +1,22 @@ +package biz.nellemann.syslogd; + +import picocli.CommandLine; + +import java.io.IOException; +import java.net.URL; +import java.util.Properties; + +class VersionProvider implements CommandLine.IVersionProvider { + + public String[] getVersion() throws IOException { + + URL url = getClass().getResource("/version.properties"); + if (url == null) { + return new String[] { "No version.txt file found in the classpath." }; + } + Properties properties = new Properties(); + properties.load(url.openStream()); + return new String[] { "${COMMAND-FULL-NAME} " + properties.getProperty("VERSION_GRADLE") + "-" + properties.getProperty("VERSION_BUILD") }; + } + +}