diff --git a/.gitignore b/.gitignore
index 9154f4c..c129e3f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,13 @@
hs_err_pid*
replay_pid*
+**/.DS_Store
+**/*.class
+/bin/
+/.project
+/.classpath
+/.factorypath
+/.settings/
+/target/
+/.apt_generated/
+/.apt_generated_tests/
diff --git a/README.md b/README.md
index e78b6a8..28fa698 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,30 @@
# Verteilte_Systeme
-Bearbeitung Prüfungsvorleistung für Verteilte Systeme
\ No newline at end of file
+Bearbeitung Prüfungsvorleistung für Verteilte Systeme
+
+# Syslog-Logging-System (RFC 5424)
+
+Dieses Projekt implementiert einen Syslog-Server und einen Test-Client auf Basis von UDP. Es beinhaltet eine automatische Service-Discovery.
+
+## Projektstruktur & Klassen
+
+* **`pom.xml`**: Die Maven-Konfigurationsdatei. Sie verwaltet Abhängigkeiten und die Java-Version.
+* **`justfile`**: Automatisierungsskript für Windows (PowerShell) und Linux, um Befehle wie `just clean` oder `just exec` zu nutzen.
+* **`vs.SyslogServer`**: Der Hauptdienst. Er startet zwei parallele Threads:
+ * **Syslog-Service**: Empfängt Log-Nachrichten auf Port 514.
+ * **Discovery-Service**: Antwortet auf Broadcast-Anfragen auf Port 8888.
+* **`vs.SyslogClient`**: Test-Client, der den Server via Broadcast sucht und eine standardkonforme Nachricht sendet.
+* **`vs.SyslogMessage`**: Repräsentiert eine Nachricht gemäß RFC 5424.
+* **`vs.AsciiChars` / `vs.StructuredData`**: Hilfsklassen zur Validierung und Formatierung von Zeichenketten gemäß Spezifikation.
+
+## Funktionsablauf (Service Discovery)
+
+1. **Broadcast**: Der Client sendet ein UDP-Paket an `255.255.255.255:8888`.
+2. **Antwort**: Der Server empfängt den Broadcast und sendet ein leeres UDP-Paket an den Client zurück.
+3. **Identifikation**: Der Client liest die IP-Adresse des Absenders aus dem Antwort-Paket aus.
+4. **Logging**: Der Client sendet die eigentliche Syslog-Nachricht direkt an die ermittelte IP auf Port 514.
+
+## Ausführung
+* Server starten: `just exec SyslogServer`
+* Client starten: `just exec SyslogClient`
+* Aufräumen: `just clean`
\ No newline at end of file
diff --git a/justfile b/justfile
new file mode 100644
index 0000000..f5abb79
--- /dev/null
+++ b/justfile
@@ -0,0 +1,16 @@
+# Konfiguration für Windows (PowerShell).
+# Falls Sie Linux/macOS nutzen, bitte diese Zeile in ["sh", "-c"] ändern:
+set shell := ["powershell.exe", "-c"]
+
+default:
+ mvn clean compile test
+exec class +args="": compile
+ mvn exec:java "-Dexec.mainClass={{class}}" "-Dexec.args={{args}}"
+clean:
+ mvn clean
+compile:
+ mvn compile
+test: compile
+ mvn test
+javadoc:
+ mvn javadoc:javadoc
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..47d6394
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,60 @@
+
+ 4.0.0
+ vs
+ vs.pu01
+ 1.0-SNAPSHOT
+ jar
+
+
+ 10
+ UTF-8
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ 5.10.0
+ test
+
+
+ org.projectlombok
+ lombok
+ 1.18.30
+ provided
+
+
+ net.jcip
+ jcip-annotations
+ 1.0
+ provided
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.0.0
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.9.0
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 3.1.0
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 3.5.0
+
+ private
+ en_US
+
+
+
+
+
diff --git a/src/helloworld.java b/src/helloworld.java
deleted file mode 100644
index 6994211..0000000
--- a/src/helloworld.java
+++ /dev/null
@@ -1,5 +0,0 @@
-public class helloworld {
- public static void main(String[] args) {
- System.out.println("Hello World!");
- }
-}
diff --git a/src/main/java/vs/AsciiChars.java b/src/main/java/vs/AsciiChars.java
new file mode 100644
index 0000000..bdd005b
--- /dev/null
+++ b/src/main/java/vs/AsciiChars.java
@@ -0,0 +1,87 @@
+package vs;
+
+/**
+ * helper class for RFC 5424 (https://datatracker.ietf.org/doc/html/rfc5424)
+ * compliant log messages as immutable Java objects - representation of a subset
+ * of printable strings of specific length
+ *
+ * @author Sandro Leuchter
+ *
+ */
+public abstract class AsciiChars {
+
+ private final String value;
+
+ public String value() {
+ return this.value;
+ }
+
+ protected AsciiChars(int length, String value) {
+ if (value != null) {
+ if (value.length() > length) {
+ throw new IllegalArgumentException(
+ "Stringlänge = " + value.length() + " > " + length
+ );
+ }
+ for (int c : value.getBytes()) {
+ if (c < 33 || c > 126) {
+ throw new IllegalArgumentException(
+ "Stringinhalt nicht printable US-ASCII ohne Space"
+ );
+ }
+ }
+ }
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ if (value() == null || value().length() == 0) {
+ return "-";
+ } else {
+ return value();
+ }
+ }
+
+ public static final class L004 extends AsciiChars {
+
+ public L004(String value) {
+ super(4, value);
+ }
+ }
+
+ public static final class L012 extends AsciiChars {
+
+ public L012(String value) {
+ super(12, value);
+ }
+ }
+
+ public static final class L032 extends AsciiChars {
+
+ public L032(String value) {
+ super(32, value);
+ }
+ }
+
+ public static final class L048 extends AsciiChars {
+
+ public L048(String value) {
+ super(48, value);
+ }
+ }
+
+ public static final class L128 extends AsciiChars {
+
+ public L128(String value) {
+ super(128, value);
+ }
+ }
+
+ public static final class L255 extends AsciiChars {
+
+ public L255(String value) {
+ super(255, value);
+ }
+ }
+}
diff --git a/src/main/java/vs/StructuredData.java b/src/main/java/vs/StructuredData.java
new file mode 100644
index 0000000..2c54682
--- /dev/null
+++ b/src/main/java/vs/StructuredData.java
@@ -0,0 +1,180 @@
+package vs;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * helper class for RFC 5424 (https://datatracker.ietf.org/doc/html/rfc5424)
+ * compliant log messages as immutable Java objects - structured data (set of
+ * key/value-pairs) with some predefined sets according to the standard
+ *
+ * @author Sandro Leuchter
+ *
+ */
+public class StructuredData {
+
+ public static class Element {
+
+ private final String name;
+ private List parameters;
+
+ public static Element newTimeQuality(
+ boolean tzKnown,
+ boolean isSynced
+ ) {
+ return newTimeQuality(tzKnown, isSynced, null);
+ }
+
+ public static Element newTimeQuality(
+ boolean tzKnown,
+ boolean isSynced,
+ Integer syncAccuracy
+ ) {
+ var e = new Element("timeQuality");
+ e.add(new Param("tzKnown", (tzKnown) ? "1" : "0"));
+ e.add(new Param("isSynced", (isSynced) ? "1" : "0"));
+ if (syncAccuracy != null && !isSynced) {
+ e.add(new Param("syncAccuracy", String.valueOf(syncAccuracy)));
+ }
+ return e;
+ }
+
+ public static Element newOrigin(
+ String enterpriseId,
+ String software,
+ String swVersion
+ ) {
+ return newOrigin(
+ new String[] {},
+ enterpriseId,
+ software,
+ swVersion
+ );
+ }
+
+ public static Element newOrigin(
+ String ip,
+ String enterpriseId,
+ String software,
+ String swVersion
+ ) {
+ return newOrigin(
+ new String[] { ip },
+ enterpriseId,
+ software,
+ swVersion
+ );
+ }
+
+ public static Element newOrigin(
+ String[] ip,
+ String enterpriseId,
+ String software,
+ String swVersion
+ ) {
+ var e = new Element("origin");
+ for (var p : ip) {
+ e = e.add(new Param("ip", p));
+ }
+ if (enterpriseId != null && !enterpriseId.equals("")) {
+ e = e.add(new Param("enterpriseId", enterpriseId));
+ }
+ if (software != null && !software.equals("")) {
+ e = e.add(new Param("software", software));
+ }
+ if (swVersion != null && !swVersion.equals("")) {
+ e = e.add(new Param("swVersion", swVersion));
+ }
+ return e;
+ }
+
+ public static Element newMeta(
+ Integer sequenceId,
+ Integer sysUpTime,
+ String language
+ ) {
+ var e = new Element("meta");
+ if (sequenceId != null && sequenceId > 0) {
+ e = e.add(
+ new Param(
+ "sequenceId",
+ String.valueOf(sequenceId % 2147483647)
+ )
+ );
+ }
+ if (sysUpTime != null && sysUpTime >= 0) {
+ e = e.add(new Param("sysUpTime", String.valueOf(sysUpTime)));
+ }
+ if (language != null && !language.equals("")) {
+ e = e.add(new Param("language", language));
+ }
+ return e;
+ }
+
+ public Element(String name) {
+ this.name = name;
+ this.parameters = new ArrayList<>();
+ }
+
+ public Element add(Param parameter) {
+ var e = new Element(this.name);
+ e.parameters = this.parameters;
+ e.parameters.add(parameter);
+ return e;
+ }
+
+ @Override
+ public String toString() {
+ var str = "[" + this.name;
+ for (var p : this.parameters) {
+ str = str + " " + p.toString();
+ }
+ return str + "]";
+ }
+ }
+
+ public static class Param {
+
+ private final String name;
+ // name: printable US-ASCII string ^[@=\]\"\s]+
+ // "@" + private enterpise number "@\d+(\.\d+)*"
+ private final String value;
+
+ public Param(String name, String value) {
+ this.name = name; // 7-Bit ASCII
+ this.value = value; // UTF-8
+ }
+
+ @Override
+ public String toString() {
+ return this.name + "=\"" + this.value + "\"";
+ }
+ }
+
+ private List params;
+
+ public StructuredData() {
+ this.params = new ArrayList<>();
+ }
+
+ public StructuredData(List params) {
+ this.params = params;
+ }
+
+ public String toString() {
+ if (this.params.size() == 0) {
+ return "-";
+ }
+ var str = "";
+ for (var p : this.params) {
+ str = str + p.toString();
+ }
+ return str;
+ }
+
+ public StructuredData add(Element e) {
+ var p = this.params;
+ p.add(e);
+ return new StructuredData(p);
+ }
+}
diff --git a/src/main/java/vs/SyslogClient.java b/src/main/java/vs/SyslogClient.java
new file mode 100644
index 0000000..4a3af1d
--- /dev/null
+++ b/src/main/java/vs/SyslogClient.java
@@ -0,0 +1,56 @@
+package vs;
+
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.nio.charset.StandardCharsets;
+
+public class SyslogClient {
+
+ public static void main(String[] args) throws Exception {
+ DatagramSocket socket = new DatagramSocket();
+ socket.setBroadcast(true);
+
+ // 🔍 Discovery (Unverändert)
+ byte[] discoverMsg = "DISCOVER".getBytes();
+ DatagramPacket packet = new DatagramPacket(
+ discoverMsg, discoverMsg.length,
+ InetAddress.getByName("255.255.255.255"), 8888
+ );
+ socket.send(packet);
+
+ // 📥 Antwort empfangen (Unverändert)
+ byte[] buffer = new byte[256];
+ DatagramPacket response = new DatagramPacket(buffer, buffer.length);
+ socket.receive(response);
+ System.out.println("Server gefunden: " + response.getAddress());
+
+ // 📨 ECHTE RFC 5424 Syslog-Nachricht aus DEINEN Klassen bauen!
+ SyslogMessage msg = new SyslogMessage(
+ SyslogMessage.Facility.USER,
+ SyslogMessage.Severity.INFORMATIONAL,
+ new AsciiChars.L255("mein-laptop"),
+ new AsciiChars.L048("SyslogClient"),
+ new AsciiChars.L128("PID1234"),
+ new AsciiChars.L032("MSG-01"),
+ new StructuredData().add(StructuredData.Element.newTimeQuality(true, true)),
+ new SyslogMessage.TextMessage("Das ist eine standardkonforme Nachricht!")
+ );
+
+ // Die Klasse wandelt alles in den perfekten String um (inklusive Zeitstempel)
+ String logString = msg.toString();
+ System.out.println("Sende generierten String: " + logString);
+
+ // Nachricht als UTF-8 Bytes senden
+ byte[] data = logString.getBytes(StandardCharsets.UTF_8);
+
+ DatagramPacket syslogPacket = new DatagramPacket(
+ data, data.length, response.getAddress(), 514
+ );
+
+ socket.send(syslogPacket);
+ System.out.println("Log gesendet!");
+
+ socket.close();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/vs/SyslogMessage.java b/src/main/java/vs/SyslogMessage.java
new file mode 100644
index 0000000..156810c
--- /dev/null
+++ b/src/main/java/vs/SyslogMessage.java
@@ -0,0 +1,213 @@
+package vs;
+
+import java.io.Serializable;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * RFC 5424 (https://datatracker.ietf.org/doc/html/rfc5424) compliant log
+ * messages as immutable Java objects
+ *
+ * @author Sandro Leuchter
+ *
+ */
+public class SyslogMessage implements Serializable {
+
+ private static final long serialVersionUID = -5895573029109990861L;
+ private final Facility fac;
+ private final Severity sev;
+ private final AsciiChars.L255 host;
+ private final AsciiChars.L048 appName;
+ private final AsciiChars.L128 procId;
+ private final AsciiChars.L032 msgId;
+ private final StructuredData data;
+ private final Message message;
+
+ public SyslogMessage(
+ Facility fac,
+ Severity sev,
+ AsciiChars.L255 host,
+ AsciiChars.L048 appName,
+ AsciiChars.L128 procId,
+ AsciiChars.L032 msgId,
+ StructuredData data,
+ Message message
+ ) {
+ this.fac = fac;
+ this.sev = sev;
+ this.host = host;
+ this.appName = appName;
+ this.procId = procId;
+ this.msgId = msgId;
+ this.data = data;
+ this.message = message;
+ }
+
+ public Facility fac() {
+ return this.fac;
+ }
+
+ public Severity sev() {
+ return sev;
+ }
+
+ public AsciiChars.L255 host() {
+ return host;
+ }
+
+ public AsciiChars.L048 appName() {
+ return appName;
+ }
+
+ public AsciiChars.L128 procId() {
+ return procId;
+ }
+
+ public AsciiChars.L032 msgId() {
+ return msgId;
+ }
+
+ public StructuredData data() {
+ return data;
+ }
+
+ public Message message() {
+ return message;
+ }
+
+ public static int version() {
+ return VERSION;
+ }
+
+ public static enum Facility {
+ KERNEL,
+ USER,
+ MAIL_SYSTEM,
+ SYS_DAEMON,
+ SECURITY1,
+ INTERNAL,
+ PRINTER,
+ NEWS,
+ UUCP,
+ CLOCK1,
+ SECURITY2,
+ FTP,
+ NTP,
+ AUDIT,
+ ALERT,
+ CLOCK2,
+ LOCAL0,
+ LOCAL1,
+ LOCAL2,
+ LOCAL3,
+ LOCAL4,
+ LOCAL5,
+ LOCAL6,
+ LOCAL7,
+ }
+
+ public static enum Severity {
+ EMERGENCY,
+ ALERT,
+ CRITICAL,
+ ERROR,
+ WARNING,
+ NOTICE,
+ INFORMATIONAL,
+ DEBUG,
+ }
+
+ public static interface Message {
+ public Object message();
+
+ public int length();
+ }
+
+ public static class BinaryMessage implements Message {
+
+ private Byte[] message;
+
+ public BinaryMessage(Byte[] message) {
+ this.message = message;
+ }
+
+ @Override
+ public String toString() {
+ return message.toString();
+ }
+
+ @Override
+ public Object message() {
+ return this.message;
+ }
+
+ @Override
+ public int length() {
+ return this.message.length;
+ }
+ }
+
+ public static class TextMessage implements Message {
+
+ private String message; // UTF8
+
+ public TextMessage(String message) {
+ this.message = message;
+ }
+
+ @Override
+ public String toString() {
+ return "\u00EF\u00BB\u00BF" + message.toString();
+ }
+
+ @Override
+ public Object message() {
+ return this.message;
+ }
+
+ @Override
+ public int length() {
+ return this.message.length();
+ }
+ }
+
+ static final int VERSION = 1; // RFC 5424, Mar 2009
+
+ @Override
+ public String toString() {
+ var prival = String.valueOf(fac().ordinal() * 8 + sev().ordinal());
+ var d = "";
+ if (data() != null) {
+ d = " " + data();
+ }
+ var m = "";
+ if (
+ message() != null &&
+ message().message() != null &&
+ message().length() > 0
+ ) {
+ m = " " + message();
+ }
+ var timestamp = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").format(
+ new Date()
+ );
+ return (
+ "<" +
+ prival +
+ ">" +
+ VERSION +
+ " " +
+ timestamp +
+ " " +
+ host().toString() +
+ " " +
+ appName().toString() +
+ " " +
+ procId().toString() +
+ " " +
+ msgId().toString() +
+ d +
+ m
+ );
+ }
+}
diff --git a/src/main/java/vs/SyslogServer.java b/src/main/java/vs/SyslogServer.java
new file mode 100644
index 0000000..42b6c69
--- /dev/null
+++ b/src/main/java/vs/SyslogServer.java
@@ -0,0 +1,102 @@
+package vs;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.nio.charset.StandardCharsets;
+
+public class SyslogServer {
+
+ private static final int SYSLOG_PORT = 514;
+ private static final int DISCOVERY_PORT = 8888;
+ private static final int MAX_MESSAGE_LENGTH = 1024; // konservativ (UDP safe)
+
+ public static void main(String[] args) {
+ System.out.println("Starting Syslog Server...");
+
+ // Thread für Syslog
+ new Thread(() -> runSyslogServer()).start();
+
+ // Thread für Discovery
+ new Thread(() -> runDiscoveryServer()).start();
+ }
+
+ private static void runSyslogServer() {
+ try (DatagramSocket socket = new DatagramSocket(SYSLOG_PORT)) {
+ // Puffergröße 2048 gemäß RFC 5424 Empfehlung.
+ // Ermöglicht den Empfang kompletter Netzwerkpakete (MTU), um zu lange
+ // Nachrichten aktiv zu erkennen und abzulehnen, statt sie nur abzuschneiden.
+ byte[] buffer = new byte[2048];
+
+ System.out.println("Syslog Server läuft auf Port " + SYSLOG_PORT);
+
+ while (true) {
+ DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
+ socket.receive(packet);
+
+ int length = packet.getLength();
+
+ // Länge prüfen
+ if (length > MAX_MESSAGE_LENGTH) {
+ System.out.println("Nachricht zu lang von " + packet.getAddress());
+ continue;
+ }
+
+ String message = new String(packet.getData(), 0, length, StandardCharsets.UTF_8);
+
+ // 📡 Ausgabe mit Client-IP
+ System.out.println(
+ "[SYSLOG] Von " +
+ packet.getAddress().getHostAddress() +
+ ":" +
+ packet.getPort() +
+ " -> " +
+ message
+ );
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void runDiscoveryServer() {
+ try (DatagramSocket socket = new DatagramSocket(DISCOVERY_PORT)) {
+ byte[] buffer = new byte[256];
+
+ System.out.println("Discovery Service läuft auf Port " + DISCOVERY_PORT);
+
+ while (true) {
+ DatagramPacket request = new DatagramPacket(buffer, buffer.length);
+ socket.receive(request);
+
+ InetAddress clientAddress = request.getAddress();
+ int clientPort = request.getPort();
+
+ System.out.println(
+ "[DISCOVERY] Anfrage von " +
+ clientAddress.getHostAddress()
+ );
+
+ // Antwort (leer reicht laut Aufgabe)
+ byte[] responseData = new byte[0];
+
+ DatagramPacket response = new DatagramPacket(
+ responseData,
+ responseData.length,
+ clientAddress,
+ clientPort
+ );
+
+ socket.send(response);
+
+ System.out.println(
+ "[DISCOVERY] Antwort gesendet an " +
+ clientAddress.getHostAddress()
+ );
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/vs/AsciiCharsTest.java b/src/test/java/vs/AsciiCharsTest.java
new file mode 100644
index 0000000..8aab640
--- /dev/null
+++ b/src/test/java/vs/AsciiCharsTest.java
@@ -0,0 +1,56 @@
+package vs;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+class AsciiCharsTest {
+
+ @Test
+ void nullValue() {
+ var ac = new AsciiChars.L004(null);
+ assertEquals(ac.toString(), "-");
+ }
+
+ @Test
+ void emptyValue() {
+ var ac = new AsciiChars.L004("");
+ assertEquals(ac.toString(), "-");
+ }
+
+ @Test
+ void longValue() {
+ var ac = new AsciiChars.L004("1234");
+ assertEquals(ac.toString(), "1234");
+ }
+
+ @Test
+ void longerValue() {
+ var thrown = assertThrows(IllegalArgumentException.class, () -> {
+ new AsciiChars.L004("12345");
+ });
+ assertEquals("Stringlänge = 5 > 4", thrown.getMessage());
+ }
+
+ @Test
+ void space() {
+ var thrown = assertThrows(IllegalArgumentException.class, () -> {
+ new AsciiChars.L004("1 1");
+ });
+ assertEquals(
+ "Stringinhalt nicht printable US-ASCII ohne Space",
+ thrown.getMessage()
+ );
+ }
+
+ @Test
+ void special() {
+ var thrown = assertThrows(IllegalArgumentException.class, () -> {
+ new AsciiChars.L004("ä");
+ });
+ assertEquals(
+ "Stringinhalt nicht printable US-ASCII ohne Space",
+ thrown.getMessage()
+ );
+ }
+}
diff --git a/src/test/java/vs/SyslogMessageTest.java b/src/test/java/vs/SyslogMessageTest.java
new file mode 100644
index 0000000..b60ff0c
--- /dev/null
+++ b/src/test/java/vs/SyslogMessageTest.java
@@ -0,0 +1,93 @@
+package vs;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+import vs.StructuredData.*;
+import vs.SyslogMessage.*;
+
+class SyslogMessageTest {
+
+ @Test
+ void testToString() {
+ var m1 = new SyslogMessage(
+ //
+ Facility.SECURITY1, //
+ Severity.CRITICAL, //
+ new AsciiChars.L255("mymachine.example.com"), //
+ new AsciiChars.L048("su"), //
+ new AsciiChars.L128(""), //
+ new AsciiChars.L032("ID47"), //
+ new StructuredData() //
+ .add(Element.newTimeQuality(true, true))
+ .add(
+ new Element("exampleSDID@32473")
+ .add(new Param("iut", "3"))
+ .add(new Param("eventSource", "Application"))
+ .add(new Param("eventID", "1011"))
+ )
+ .add(
+ new Element("examplePriority@32473").add(
+ new Param("class", "high")
+ )
+ ),
+ new TextMessage("'su root' failed for lonvick on /dev/pts/8")
+ );
+ var s = m1.toString();
+ assertEquals(s.substring(0, 6), "<34>1 ");
+ assertEquals(
+ s.substring(s.length() - 221, s.length()),
+ " mymachine.example.com su - ID47 [timeQuality tzKnown=\"1\" isSynced=\"1\"][exampleSDID@32473 iut=\"3\" eventSource=\"Application\" eventID=\"1011\"][examplePriority@32473 class=\"high\"] 'su root' failed for lonvick on /dev/pts/8"
+ );
+ }
+
+ @Test
+ void test2() {
+ var m1 = new SyslogMessage(
+ //
+ Facility.SECURITY1, //
+ Severity.CRITICAL, //
+ new AsciiChars.L255("mymachine.example.com"), //
+ new AsciiChars.L048("su"), //
+ new AsciiChars.L128(""), //
+ new AsciiChars.L032("ID47"), //
+ null, //
+ new TextMessage("'su root' failed for lonvick on /dev/pts/8")
+ );
+ var s = m1.toString();
+ assertEquals(s.substring(0, 6), "<34>1 ");
+ assertEquals(
+ s.substring(s.length() - 78, s.length()),
+ " mymachine.example.com su - ID47 'su root' failed for lonvick on /dev/pts/8"
+ );
+ }
+
+ @Test
+ void test3() {
+ var m1 = new SyslogMessage(
+ //
+ Facility.SECURITY1, //
+ Severity.CRITICAL, //
+ new AsciiChars.L255("mymachine.example.com"), //
+ new AsciiChars.L048("su"), //
+ new AsciiChars.L128(""), //
+ new AsciiChars.L032("ID47"), //
+ new StructuredData() //
+ .add(Element.newTimeQuality(true, true))
+ .add(
+ Element.newOrigin(
+ new String[] { "0.0.8.8", "8.8.8.8" },
+ null,
+ null,
+ null
+ )
+ )
+ .add(Element.newMeta(null, 32, "de")),
+ new BinaryMessage(null)
+ );
+ assertEquals(
+ m1.data().toString(),
+ "[timeQuality tzKnown=\"1\" isSynced=\"1\"][origin ip=\"0.0.8.8\" ip=\"8.8.8.8\"][meta sysUpTime=\"32\" language=\"de\"]"
+ );
+ }
+}