From f2b64383cc93a7d6e2237ffe1cb52627022e4bc5 Mon Sep 17 00:00:00 2001 From: Mark Nellemann Date: Tue, 26 Jan 2021 15:24:23 +0100 Subject: [PATCH] Enable forwarding of received syslog messaged. --- build.gradle | 8 +- gradle.properties | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- .../biz/nellemann/syslogd/Application.java | 68 ++++++++++++----- .../biz/nellemann/syslogd/SyslogMessage.java | 34 --------- .../biz/nellemann/syslogd/SyslogParser.java | 1 + .../biz/nellemann/syslogd/SyslogPrinter.java | 74 +++++++++++++++++++ .../java/biz/nellemann/syslogd/UdpClient.java | 30 ++++++++ 8 files changed, 159 insertions(+), 60 deletions(-) create mode 100644 src/main/java/biz/nellemann/syslogd/SyslogPrinter.java create mode 100644 src/main/java/biz/nellemann/syslogd/UdpClient.java diff --git a/build.gradle b/build.gradle index 82fedbd..7a1ab15 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { id 'application' id "com.github.johnrengelman.shadow" version "6.1.0" id "net.nemerosa.versioning" version "2.14.0" - id "nebula.ospackage" version "8.4.1" + id "nebula.ospackage" version "8.4.2" } repositories { @@ -12,10 +12,10 @@ repositories { } dependencies { - annotationProcessor 'info.picocli:picocli-codegen:4.6.0' - implementation 'info.picocli:picocli:4.6.0' + annotationProcessor 'info.picocli:picocli-codegen:4.6.1' + implementation 'info.picocli:picocli:4.6.1' implementation 'org.slf4j:slf4j-api:1.7.30' - runtimeOnly 'org.slf4j:slf4j-simple:1.7.30' + 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.+") diff --git a/gradle.properties b/gradle.properties index 1df34fd..ac4b643 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ id = syslogd group = biz.nellemann.syslogd -version = 1.0.7 +version = 1.0.8 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 12d38de..da9702f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/biz/nellemann/syslogd/Application.java b/src/main/java/biz/nellemann/syslogd/Application.java index bad3e9a..28ea85d 100644 --- a/src/main/java/biz/nellemann/syslogd/Application.java +++ b/src/main/java/biz/nellemann/syslogd/Application.java @@ -25,37 +25,48 @@ import java.util.concurrent.Callable; @Command(name = "syslogd", mixinStandardHelpOptions = true, - description = "Basic syslog server.", + description = "Syslog Daemon.", versionProvider = biz.nellemann.syslogd.VersionProvider.class) public class Application implements Callable, LogListener { private final static Logger log = LoggerFactory.getLogger(Application.class); - @CommandLine.Option(names = {"-p", "--port"}, description = "Listening port [default: 514].") - private int port = 514; + @CommandLine.Option(names = {"-p", "--port"}, description = "Listening port [default: 514].", defaultValue = "514") + private int port; - @CommandLine.Option(names = "--no-udp", negatable = true, description = "Listen on UDP [default: true].") - boolean udpServer = true; + @CommandLine.Option(names = "--no-udp", negatable = true, description = "Listen on UDP [default: true].", defaultValue = "true") + private boolean udpServer; - @CommandLine.Option(names = "--no-tcp", negatable = true, description = "Listen on TCP [default: true].") - boolean tcpServer = true; + @CommandLine.Option(names = "--no-tcp", negatable = true, description = "Listen on TCP [default: true].", defaultValue = "true") + private boolean tcpServer; - @CommandLine.Option(names = "--no-ansi", negatable = true, description = "Output ANSI colors [default: true].") - boolean ansiOutput = true; + @CommandLine.Option(names = "--no-ansi", negatable = true, description = "Output ANSI colors [default: true].", defaultValue = "true") + private boolean ansiOutput; - @CommandLine.Option(names = "--rfc5424", description = "Parse RFC-5424 messages [default: RFC-3164].") - boolean rfc5424 = false; + @CommandLine.Option(names = "--no-stdout", negatable = true, description = "Output messages to stdout [default: true].", defaultValue = "true") + private boolean stdout; + @CommandLine.Option(names = "--rfc5424", description = "Parse RFC-5424 messages [default: RFC-3164].", defaultValue = "false") + private boolean rfc5424; - public static void main(String... args) { - int exitCode = new CommandLine(new Application()).execute(args); - System.exit(exitCode); - } + @CommandLine.Option(names = { "-f", "--forward"}, description = "Forward messages (UDP RFC-3164) [default: false].", defaultValue = "false") + private boolean forward; + @CommandLine.Option(names = "--forward-host", description = "Forward to host [default: localhost].", paramLabel = "", defaultValue = "localhost") + private String forwardHost; + + @CommandLine.Option(names = "--forward-port", description = "Forward to port [default: 1514].", paramLabel = "", defaultValue = "1514") + private int forwardPort; + + private UdpClient udpClient; @Override public Integer call() throws IOException { + if(forward) { + udpClient = new UdpClient(forwardHost, forwardPort); + } + if(udpServer) { UdpServer udpServer = new UdpServer(port); udpServer.addEventListener(this); @@ -85,17 +96,34 @@ public class Application implements Callable, LogListener { msg = SyslogParser.parseRfc3164(message); } } catch(Exception e) { - log.error("Problem parsing message: ", e); + log.error("onLogEvent() - Error parsing message: ", e); } if(msg != null) { - if(ansiOutput) { - System.out.println(msg.toAnsiString()); - } else { - System.out.println(msg); + + if(stdout) { + if(ansiOutput) { + System.out.println(SyslogPrinter.toAnsiString(msg)); + } else { + System.out.println(SyslogPrinter.toString(msg)); + } + } + + if(forward) { + try { + udpClient.send(SyslogPrinter.toRfc3164(msg)); + } catch (Exception e) { + log.error("onLogEvent()", e); + } } } } + + public static void main(String... args) { + int exitCode = new CommandLine(new Application()).execute(args); + System.exit(exitCode); + } + } diff --git a/src/main/java/biz/nellemann/syslogd/SyslogMessage.java b/src/main/java/biz/nellemann/syslogd/SyslogMessage.java index 23476a7..28c13f9 100644 --- a/src/main/java/biz/nellemann/syslogd/SyslogMessage.java +++ b/src/main/java/biz/nellemann/syslogd/SyslogMessage.java @@ -50,38 +50,4 @@ public class SyslogMessage { this.message = message; } - - - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(timestamp.toString()); - sb.append(String.format(" [%8.8s.%-6.6s] ", facility, severity)); - sb.append(String.format(" %-16.16s ", hostname)); - sb.append(String.format(" %-32.32s ", application)); - sb.append(message); - return sb.toString(); - } - - - public String toAnsiString() { - StringBuilder sb = new StringBuilder(); - - sb.append(timestamp.toString()); - - if(severity.toNumber() < 3 ) { - sb.append(Ansi.RED); - } else if(severity.toNumber() < 5) { - sb.append(Ansi.YELLOW); - } else { - sb.append(Ansi.GREEN); - } - - sb.append(String.format(" [%8.8s.%-6.6s] ", facility, severity)).append(Ansi.RESET); - sb.append(Ansi.BLUE).append(String.format(" %-16.16s ", hostname)).append(Ansi.RESET); - sb.append(Ansi.CYAN).append(String.format(" %-32.32s ", application)).append(Ansi.RESET); - sb.append(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 ef02b2a..c464f17 100644 --- a/src/main/java/biz/nellemann/syslogd/SyslogParser.java +++ b/src/main/java/biz/nellemann/syslogd/SyslogParser.java @@ -197,4 +197,5 @@ public class SyslogParser { return severity; } + } diff --git a/src/main/java/biz/nellemann/syslogd/SyslogPrinter.java b/src/main/java/biz/nellemann/syslogd/SyslogPrinter.java new file mode 100644 index 0000000..eb6aa48 --- /dev/null +++ b/src/main/java/biz/nellemann/syslogd/SyslogPrinter.java @@ -0,0 +1,74 @@ +package biz.nellemann.syslogd; + +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; + +public class SyslogPrinter { + + public static String toString(SyslogMessage msg) { + StringBuilder sb = new StringBuilder(); + sb.append(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)); + sb.append(msg.message); + return sb.toString(); + } + + + public static String toAnsiString(SyslogMessage msg) { + StringBuilder sb = new StringBuilder(); + + sb.append(msg.timestamp.toString()); + + if (msg.severity.toNumber() < 3) { + sb.append(Ansi.RED); + } else if (msg.severity.toNumber() < 5) { + sb.append(Ansi.YELLOW); + } else { + sb.append(Ansi.GREEN); + } + + sb.append(String.format(" [%8.8s.%-6.6s] ", msg.facility, msg.severity)).append(Ansi.RESET); + sb.append(Ansi.BLUE).append(String.format(" %-16.16s ", msg.hostname)).append(Ansi.RESET); + sb.append(Ansi.CYAN).append(String.format(" %-32.32s ", msg.application)).append(Ansi.RESET); + sb.append(msg.message); + + return sb.toString(); + } + + + // <13>Sep 23 08:53:28 xps13 mark: adfdfdf3432434 + public static String toRfc3164(SyslogMessage msg) { + 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); + + return sb.toString(); + } + + + // <13>1 2020-09-23T08:57:30.950699+02:00 xps13 mark - - [timeQuality tzKnown="1" isSynced="1" syncAccuracy="125500"] adfdfdf3432434565656 + 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); + + return sb.toString(); + } + + + // TODO: Facility+Severity to PRI field + static private String getPri(Facility facility, Severity severity) { + return "<13>"; + } + + +} diff --git a/src/main/java/biz/nellemann/syslogd/UdpClient.java b/src/main/java/biz/nellemann/syslogd/UdpClient.java new file mode 100644 index 0000000..296bc47 --- /dev/null +++ b/src/main/java/biz/nellemann/syslogd/UdpClient.java @@ -0,0 +1,30 @@ +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(); + } +}