Correct parsing of PRI to facility and severity + ansi colors in output.

This commit is contained in:
Mark Nellemann 2020-09-23 10:52:52 +02:00
parent 7a019c5c18
commit 4106453a68
6 changed files with 311 additions and 51 deletions

View file

@ -0,0 +1,87 @@
package biz.nellemann.syslogd;
public enum Color {
//Color end string, color reset
RESET("\033[0m"),
// Regular Colors. Normal color, no bold, background color etc.
BLACK("\033[0;30m"), // BLACK
RED("\033[0;31m"), // RED
GREEN("\033[0;32m"), // GREEN
YELLOW("\033[0;33m"), // YELLOW
BLUE("\033[0;34m"), // BLUE
MAGENTA("\033[0;35m"), // MAGENTA
CYAN("\033[0;36m"), // CYAN
WHITE("\033[0;37m"), // WHITE
// Bold
BLACK_BOLD("\033[1;30m"), // BLACK
RED_BOLD("\033[1;31m"), // RED
GREEN_BOLD("\033[1;32m"), // GREEN
YELLOW_BOLD("\033[1;33m"), // YELLOW
BLUE_BOLD("\033[1;34m"), // BLUE
MAGENTA_BOLD("\033[1;35m"), // MAGENTA
CYAN_BOLD("\033[1;36m"), // CYAN
WHITE_BOLD("\033[1;37m"), // WHITE
// Underline
BLACK_UNDERLINED("\033[4;30m"), // BLACK
RED_UNDERLINED("\033[4;31m"), // RED
GREEN_UNDERLINED("\033[4;32m"), // GREEN
YELLOW_UNDERLINED("\033[4;33m"), // YELLOW
BLUE_UNDERLINED("\033[4;34m"), // BLUE
MAGENTA_UNDERLINED("\033[4;35m"), // MAGENTA
CYAN_UNDERLINED("\033[4;36m"), // CYAN
WHITE_UNDERLINED("\033[4;37m"), // WHITE
// Background
BLACK_BACKGROUND("\033[40m"), // BLACK
RED_BACKGROUND("\033[41m"), // RED
GREEN_BACKGROUND("\033[42m"), // GREEN
YELLOW_BACKGROUND("\033[43m"), // YELLOW
BLUE_BACKGROUND("\033[44m"), // BLUE
MAGENTA_BACKGROUND("\033[45m"), // MAGENTA
CYAN_BACKGROUND("\033[46m"), // CYAN
WHITE_BACKGROUND("\033[47m"), // WHITE
// High Intensity
BLACK_BRIGHT("\033[0;90m"), // BLACK
RED_BRIGHT("\033[0;91m"), // RED
GREEN_BRIGHT("\033[0;92m"), // GREEN
YELLOW_BRIGHT("\033[0;93m"), // YELLOW
BLUE_BRIGHT("\033[0;94m"), // BLUE
MAGENTA_BRIGHT("\033[0;95m"), // MAGENTA
CYAN_BRIGHT("\033[0;96m"), // CYAN
WHITE_BRIGHT("\033[0;97m"), // WHITE
// Bold High Intensity
BLACK_BOLD_BRIGHT("\033[1;90m"), // BLACK
RED_BOLD_BRIGHT("\033[1;91m"), // RED
GREEN_BOLD_BRIGHT("\033[1;92m"), // GREEN
YELLOW_BOLD_BRIGHT("\033[1;93m"), // YELLOW
BLUE_BOLD_BRIGHT("\033[1;94m"), // BLUE
MAGENTA_BOLD_BRIGHT("\033[1;95m"), // MAGENTA
CYAN_BOLD_BRIGHT("\033[1;96m"), // CYAN
WHITE_BOLD_BRIGHT("\033[1;97m"), // WHITE
// High Intensity backgrounds
BLACK_BACKGROUND_BRIGHT("\033[0;100m"), // BLACK
RED_BACKGROUND_BRIGHT("\033[0;101m"), // RED
GREEN_BACKGROUND_BRIGHT("\033[0;102m"), // GREEN
YELLOW_BACKGROUND_BRIGHT("\033[0;103m"), // YELLOW
BLUE_BACKGROUND_BRIGHT("\033[0;104m"), // BLUE
MAGENTA_BACKGROUND_BRIGHT("\033[0;105m"), // MAGENTA
CYAN_BACKGROUND_BRIGHT("\033[0;106m"), // CYAN
WHITE_BACKGROUND_BRIGHT("\033[0;107m"); // WHITE
private final String code;
Color(String code) {
this.code = code;
}
@Override
public String toString() {
return code;
}
}

View file

@ -0,0 +1,80 @@
package biz.nellemann.syslogd;
import java.util.HashMap;
import java.util.Map;
/*
0 kernel messages
1 user-level messages
2 mail system
3 system daemons
4 security/authorization messages
5 messages generated internally by syslogd
6 line printer subsystem
7 network news subsystem
8 UUCP subsystem
9 clock daemon
10 security/authorization messages
11 FTP daemon
12 NTP subsystem
13 log audit
14 log alert
15 clock daemon (note 2)
16 local use 0 (local0)
17 local use 1 (local1)
18 local use 2 (local2)
19 local use 3 (local3)
20 local use 4 (local4)
21 local use 5 (local5)
22 local use 6 (local6)
23 local use 7 (local7)
*/
public enum Facility {
KERNEL(0),
USER(1),
MAIL(2),
DAEMON(3),
AUTH(4),
SYSLOG(5),
PRINT(6),
NEWS(7),
UUCP(8),
CRON(9),
AUTHPRIV(10),
FTP(11),
NTP(12),
AUDIT(13),
ALERT(14),
TIME(15),
LOCAL0(16),
LOCAL1(17),
LOCAL2(18),
LOCAL3(19),
LOCAL4(20),
LOCAL5(21),
LOCAL6(22),
LOCAL7(23);
// Cache lookups
private static final Map<Integer, Facility> BY_NUMBER = new HashMap<>();
static {
for (Facility f: values()) {
BY_NUMBER.put(f.facilityNumber, f);
}
}
public static Facility getByNumber(Integer number) {
return BY_NUMBER.get(number);
}
public Integer toNumber() {
return this.facilityNumber;
}
private Integer facilityNumber;
private Facility(int facilityNumber) {
this.facilityNumber = facilityNumber;
}
}

View file

@ -0,0 +1,48 @@
package biz.nellemann.syslogd;
import java.util.HashMap;
import java.util.Map;
/*
0 Emergency: system is unusable
1 Alert: action must be taken immediately
2 Critical: critical conditions
3 Error: error conditions
4 Warning: warning conditions
5 Notice: normal but significant condition
6 Informational: informational messages
7 Debug: debug-level messages
*/
public enum Severity {
EMERG(0),
ALERT(1),
CRIT(2),
ERROR(3),
WARN(4),
NOTICE(5),
INFO(6),
DEBUG(7);
// Cache lookups
private static final Map<Integer, Severity> BY_NUMBER = new HashMap<>();
static {
for (Severity s: values()) {
BY_NUMBER.put(s.severityNumber, s);
}
}
public static Severity getByNumber(Integer number) {
return BY_NUMBER.get(number);
}
public Integer toNumber() {
return this.severityNumber;
}
private Integer severityNumber;
private Severity(int severityNumber) {
this.severityNumber = severityNumber;
}
}

View file

@ -19,46 +19,8 @@ import java.time.Instant;
public class SyslogMessage { public class SyslogMessage {
/* Facility facility;
0 kernel messages Severity severity;
1 user-level messages
2 mail system
3 system daemons
4 security/authorization messages
5 messages generated internally by syslogd
6 line printer subsystem
7 network news subsystem
8 UUCP subsystem
9 clock daemon
10 security/authorization messages
11 FTP daemon
12 NTP subsystem
13 log audit
14 log alert
15 clock daemon (note 2)
16 local use 0 (local0)
17 local use 1 (local1)
18 local use 2 (local2)
19 local use 3 (local3)
20 local use 4 (local4)
21 local use 5 (local5)
22 local use 6 (local6)
23 local use 7 (local7)
*/
protected Integer facility;
/*
0 Emergency: system is unusable
1 Alert: action must be taken immediately
2 Critical: critical conditions
3 Error: error conditions
4 Warning: warning conditions
5 Notice: normal but significant condition
6 Informational: informational messages
7 Debug: debug-level messages
*/
Integer severity;
// The VERSION field denotes the version of the syslog protocol specification. // The VERSION field denotes the version of the syslog protocol specification.
Integer version; Integer version;
@ -86,7 +48,26 @@ public class SyslogMessage {
public String toString() { public String toString() {
return String.format("%s %s %s: %s", timestamp.toString(), hostname, application, message); //return String.format("%s %s %s: %s", timestamp.toString(), hostname, application, message);
StringBuilder sb = new StringBuilder();
sb.append(Color.WHITE); sb.append(timestamp.toString() + " "); sb.append(Color.RESET);
if(severity.toNumber() < 3 ) {
sb.append(Color.RED);
} else if(severity.toNumber() < 5) {
sb.append(Color.YELLOW);
} else {
sb.append(Color.GREEN);
}
sb.append("[" + facility + "." + severity + "]"); sb.append(Color.RESET);
sb.append(Color.BLUE); sb.append("\t" + hostname); sb.append(Color.RESET);
sb.append(Color.CYAN); sb.append("\t" + application); sb.append(Color.RESET);
sb.append("\t" + message);
return sb.toString();
} }
} }

View file

@ -21,7 +21,6 @@ import org.slf4j.LoggerFactory;
import java.time.*; import java.time.*;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException; import java.time.format.DateTimeParseException;
import java.util.Locale;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -38,7 +37,7 @@ public class SyslogParser {
public static SyslogMessage parseRfc3164(String input) throws NumberFormatException { public static SyslogMessage parseRfc3164(String input) throws NumberFormatException {
Pattern pattern = Pattern.compile("^<(\\d{1,3})>(\\D{3} \\d{2} \\d{2}:\\d{2}:\\d{2})\\s+(\\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);
boolean matchFound = matcher.find(); boolean matchFound = matcher.find();
if(!matchFound) { if(!matchFound) {
@ -58,14 +57,14 @@ public class SyslogParser {
log.debug("APP: " + application); log.debug("APP: " + application);
log.debug("MSG: " + message); log.debug("MSG: " + message);
Integer facility = Integer.parseInt(pri.substring(0, pri.length()-1)); Integer facility = getFacility(pri);
Integer severity = Integer.parseInt(pri.substring(pri.length()-1)); Integer severity = getSeverity(pri);
log.debug("facility: " + facility); log.debug("facility: " + facility);
log.debug("severity: " + severity); log.debug("severity: " + severity);
SyslogMessage syslogMessage = new SyslogMessage(); SyslogMessage syslogMessage = new SyslogMessage();
syslogMessage.facility = facility; syslogMessage.facility = Facility.getByNumber(facility);
syslogMessage.severity = 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;
@ -105,14 +104,14 @@ public class SyslogParser {
log.debug("DATA: " + data); log.debug("DATA: " + data);
log.debug("MSG: " + msg); log.debug("MSG: " + msg);
Integer facility = Integer.parseInt(pri.substring(0, pri.length()-1)); Integer facility = getFacility(pri);
Integer severity = Integer.parseInt(pri.substring(pri.length()-1)); Integer severity = getSeverity(pri);
log.debug("facility: " + facility); log.debug("facility: " + facility);
log.debug("severity: " + severity); log.debug("severity: " + severity);
SyslogMessage syslogMessage = new SyslogMessage(); SyslogMessage syslogMessage = new SyslogMessage();
syslogMessage.facility = facility; syslogMessage.facility = Facility.getByNumber(facility);
syslogMessage.severity = severity; syslogMessage.severity = Severity.getByNumber(severity);
syslogMessage.version = Integer.parseInt(ver); syslogMessage.version = Integer.parseInt(ver);
syslogMessage.timestamp = parseRfc5424Timestamp(date); syslogMessage.timestamp = parseRfc5424Timestamp(date);
syslogMessage.hostname = host; syslogMessage.hostname = host;
@ -160,4 +159,28 @@ public class SyslogParser {
return instant; return instant;
} }
/*
The priority value is calculated using the formula (Priority = Facility * 8 + Level).
For example, a kernel message (Facility=0) with a Severity of Emergency (Severity=0) would have a Priority value of 0.
Also, a "local use 4" message (Facility=20) with a Severity of Notice (Severity=5) would have a Priority value of 165.
*/
static protected int getFacility(String prio) {
int priority = Integer.parseInt(prio);
int facility = priority >> 3;
log.debug("getFacility() - " + prio + " => " + facility);
return facility;
}
static protected int getSeverity(String prio) {
int priority = Integer.parseInt(prio);
int severity = priority & 0x07;
log.debug("getSeverity() - " + prio + " => " + severity);
return severity;
}
} }

View file

@ -6,6 +6,47 @@ import java.time.OffsetDateTime;
class SyslogParserTest extends Specification { 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"
when:
SyslogMessage msg = SyslogParser.parseRfc5424(input)
then:
msg.message == "adfdfdf3432434565656"
}
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 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 parseRfc3164Timestamp"() { void "test parseRfc3164Timestamp"() {
setup: setup: