Improve UDP reception and add file output and ansi flag on/off.

This commit is contained in:
Mark Nellemann 2020-10-05 10:40:42 +02:00
parent a1599d0862
commit 8ae2ec7573
9 changed files with 126 additions and 36 deletions

View file

@ -59,7 +59,7 @@ ospackage {
buildRpm { buildRpm {
dependsOn startShadowScripts dependsOn startShadowScripts
requires('java-1.8.0-openjdk-headless') //requires('java-1.8.0-openjdk-headless')
os = LINUX os = LINUX
} }

View file

@ -1,3 +1,3 @@
id = syslogd id = syslogd
group = biz.nellemann.syslogd group = biz.nellemann.syslogd
version = 1.0.0 version = 1.0.1

View file

@ -20,9 +20,9 @@ import java.util.EventObject;
public class LogEvent extends 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 ); super( source );
this.message = message; this.message = message;
} }

View file

@ -15,6 +15,8 @@
*/ */
package biz.nellemann.syslogd; package biz.nellemann.syslogd;
import java.io.IOException;
public interface LogListener { public interface LogListener {
void onLogEvent(LogEvent event); void onLogEvent(LogEvent event);
} }

View file

@ -44,12 +44,26 @@ public class SyslogMessage {
String structuredData; String structuredData;
// The MSG part contains a free-form message that provides information about the event. // The MSG part contains a free-form message that provides information about the event.
String message; final private String message;
SyslogMessage(final String message) {
this.message = message;
}
public String toString() { 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(); StringBuilder sb = new StringBuilder();
sb.append(timestamp.toString() + " "); 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.BLUE); sb.append("\t" + hostname); sb.append(Ansi.RESET);
sb.append(Ansi.CYAN); sb.append("\t" + application); 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(); return sb.toString();
} }

View file

@ -35,7 +35,7 @@ public class SyslogParser {
private final static Logger log = LoggerFactory.getLogger(SyslogParser.class); 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); 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); Matcher matcher = pattern.matcher(input);
@ -45,11 +45,11 @@ public class SyslogParser {
return null; return null;
} }
String pri = matcher.group(1); final String pri = matcher.group(1);
String date = matcher.group(2); final String date = matcher.group(2);
String hostname = matcher.group(3); final String hostname = matcher.group(3);
String application = matcher.group(4); final String application = matcher.group(4);
String message = matcher.group(5); final String message = matcher.group(5);
log.debug("PRI: " + pri); log.debug("PRI: " + pri);
log.debug("DATE: " + date); log.debug("DATE: " + date);
@ -62,19 +62,18 @@ public class SyslogParser {
log.debug("facility: " + facility); log.debug("facility: " + facility);
log.debug("severity: " + severity); log.debug("severity: " + severity);
SyslogMessage syslogMessage = new SyslogMessage(); SyslogMessage syslogMessage = new SyslogMessage(message);
syslogMessage.facility = Facility.getByNumber(facility); syslogMessage.facility = Facility.getByNumber(facility);
syslogMessage.severity = Severity.getByNumber(severity); syslogMessage.severity = Severity.getByNumber(severity);
syslogMessage.timestamp = parseRfc3164Timestamp(date); syslogMessage.timestamp = parseRfc3164Timestamp(date);
syslogMessage.hostname = hostname; syslogMessage.hostname = hostname;
syslogMessage.application = application; syslogMessage.application = application;
syslogMessage.message = message;
return syslogMessage; 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); 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); Matcher matcher = pattern.matcher(input);
@ -84,15 +83,15 @@ public class SyslogParser {
return null; return null;
} }
String pri = matcher.group(1); final String pri = matcher.group(1);
String ver = matcher.group(2); final String ver = matcher.group(2);
String date = matcher.group(3); final String date = matcher.group(3);
String host = matcher.group(4); final String host = matcher.group(4);
String app = matcher.group(5); final String app = matcher.group(5);
String procId = matcher.group(6); final String procId = matcher.group(6);
String msgId = matcher.group(7); final String msgId = matcher.group(7);
String data = matcher.group(8); final String data = matcher.group(8);
String msg = matcher.group(9); final String msg = matcher.group(9);
log.debug("PRI: " + pri); log.debug("PRI: " + pri);
log.debug("VER: " + ver); log.debug("VER: " + ver);
@ -109,7 +108,7 @@ public class SyslogParser {
log.debug("facility: " + facility); log.debug("facility: " + facility);
log.debug("severity: " + severity); log.debug("severity: " + severity);
SyslogMessage syslogMessage = new SyslogMessage(); SyslogMessage syslogMessage = new SyslogMessage(msg);
syslogMessage.facility = Facility.getByNumber(facility); syslogMessage.facility = Facility.getByNumber(facility);
syslogMessage.severity = Severity.getByNumber(severity); syslogMessage.severity = Severity.getByNumber(severity);
syslogMessage.version = Integer.parseInt(ver); syslogMessage.version = Integer.parseInt(ver);
@ -122,7 +121,6 @@ public class SyslogParser {
if(msgId != null && !msgId.equals("-")) if(msgId != null && !msgId.equals("-"))
syslogMessage.messageId = msgId; syslogMessage.messageId = msgId;
syslogMessage.structuredData = data; syslogMessage.structuredData = data;
syslogMessage.message = msg;
return syslogMessage; return syslogMessage;
} }

View file

@ -19,26 +19,37 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import picocli.CommandLine; import picocli.CommandLine;
import picocli.CommandLine.Command; import picocli.CommandLine.Command;
import java.io.*;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
@Command(name = "syslogd", mixinStandardHelpOptions = true, version = "syslogd 1.0", @Command(name = "syslogd",
description = "Simple syslog server that prints messages to stdout.") mixinStandardHelpOptions = true,
description = "Basic syslog server.",
versionProvider = biz.nellemann.syslogd.VersionProvider.class)
public class SyslogServer implements Callable<Integer>, LogListener { public class SyslogServer implements Callable<Integer>, LogListener {
private final static Logger log = LoggerFactory.getLogger(SyslogServer.class); 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; 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; 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; 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; 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) { public static void main(String... args) {
int exitCode = new CommandLine(new SyslogServer()).execute(args); int exitCode = new CommandLine(new SyslogServer()).execute(args);
System.exit(exitCode); System.exit(exitCode);
@ -48,6 +59,15 @@ public class SyslogServer implements Callable<Integer>, LogListener {
@Override @Override
public Integer call() throws Exception { 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) { if(udpServer) {
UdpServer udpServer = new UdpServer(port); UdpServer udpServer = new UdpServer(port);
udpServer.addEventListener(this); udpServer.addEventListener(this);
@ -60,6 +80,11 @@ public class SyslogServer implements Callable<Integer>, LogListener {
tcpServer.start(); tcpServer.start();
} }
if(outputFile != null) {
bw.close();
fos.close();
}
return 0; return 0;
} }
@ -67,6 +92,7 @@ public class SyslogServer implements Callable<Integer>, LogListener {
@Override @Override
public void onLogEvent(LogEvent event) { public void onLogEvent(LogEvent event) {
// Parse message
String message = event.getMessage(); String message = event.getMessage();
SyslogMessage msg = null; SyslogMessage msg = null;
try { try {
@ -80,9 +106,37 @@ public class SyslogServer implements Callable<Integer>, LogListener {
} }
if(msg != null) { if(msg != null) {
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); System.out.println(msg);
} }
} }
} }

View file

@ -38,13 +38,13 @@ public class UdpServer extends Thread {
public void run() { public void run() {
byte[] buf = new byte[10000]; byte[] buf = new byte[4096];
DatagramPacket packet = new DatagramPacket(buf, buf.length); DatagramPacket packet = new DatagramPacket(buf, buf.length);
while (listen) { while (listen) {
try { try {
socket.receive(packet); socket.receive(packet);
String packetData = new String(packet.getData(), "UTF8"); String packetData = new String(packet.getData(), packet.getOffset(), packet.getLength(), "UTF8");
sendEvent(packetData); sendEvent(packetData);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();

View file

@ -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") };
}
}