Improve escaping of chars in json output (Loki and GELF).

This commit is contained in:
Mark Nellemann 2021-03-24 11:10:19 +01:00
parent 8e84b9ca5c
commit 7c2762dcff
9 changed files with 165 additions and 9 deletions

View file

@ -26,7 +26,7 @@ Add the following to the /etc/syslog.conf file:
*.warn /var/log/error.log rotate time 1d files 7 *.warn /var/log/error.log rotate time 1d files 7
# Optionally log authentication messages to remote host # Optionally log authentication messages to remote host
#auth.info,authpriv.info @10.32.64.29 #auth.info,authpriv.info @10.32.64.1
``` ```
We use *10.32.64.1* as our remote syslog server in the above example. We use *10.32.64.1* as our remote syslog server in the above example.

View file

@ -9,10 +9,10 @@ More information about HMC logging is available on the IBM [knowledge center](ht
Network / Firewall must allow UDP (and possible TCP) traffic on port 514 from HMC to the remote syslog server. We use *10.32.64.1* as our remote syslog server in the example below. Network / Firewall must allow UDP (and possible TCP) traffic on port 514 from HMC to the remote syslog server. We use *10.32.64.1* as our remote syslog server in the example below.
To add a remote logging destination: To add a remote logging destination, run the following command. Use the filter to discard unwanted messaged.
```shell ```shell
chhmc -c syslog -t udp -s add -h 10.32.64.1 --input "filter_msg_contains_discard_strings=run-parts,slice,session,leases,renewal,0anacron,Session,DHCPREQUEST,DHCPACK,CMD" chhmc -c syslog -t udp -s add -h 10.32.64.1 --input "filter_msg_contains_discard_strings=run-parts,slice,session,leases,renewal,0anacron,Session,DHCPREQUEST,DHCPACK,CMD,CRON"
``` ```
In the above example we filter away some messages that we are not interested in forwarding on remotely. In the above example we filter away some messages that we are not interested in forwarding on remotely.

16
doc/powervc-auth.md Normal file
View file

@ -0,0 +1,16 @@
# PowerVC Remote Loggging
Configure rsyslog on your PowerVC instance to forward authentication messages to a remote logging solution. We use *10.32.64.1* as our remote syslog server in this example.
Create a file new file in the **/etc/rsyslog.d** folder (eg. *remote.conf*) with the following content:
```text
# Log authentication messages to remote host
auth.info,authpriv.info @10.32.64.1
```
Restart the rsyslog service
```shell
systemctl restart rsyslog
```

View file

@ -6,11 +6,11 @@ Create a file new file in the **/etc/rsyslog.d** folder (eg. *90-auth.conf*) wit
```text ```text
# Log authentication messages to remote host # Log authentication messages to remote host
auth.info,authpriv.info @10.32.64.1 auth.*,authpriv.* @10.32.64.1
``` ```
Restart the rsyslog service Restart the rsyslog service
```shell ```shell
systemctl restart rsyslogd systemctl restart rsyslog
``` ```

View file

@ -1,3 +1,3 @@
id = syslogd id = syslogd
group = biz.nellemann.syslogd group = biz.nellemann.syslogd
version = 1.2.2 version = 1.2.3

View file

@ -3,6 +3,7 @@ package biz.nellemann.syslogd;
import biz.nellemann.syslogd.msg.Facility; import biz.nellemann.syslogd.msg.Facility;
import biz.nellemann.syslogd.msg.Severity; import biz.nellemann.syslogd.msg.Severity;
import biz.nellemann.syslogd.msg.SyslogMessage; import biz.nellemann.syslogd.msg.SyslogMessage;
import biz.nellemann.syslogd.parser.JsonUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -89,7 +90,7 @@ public class SyslogPrinter {
public static String toGelf(SyslogMessage msg) { public static String toGelf(SyslogMessage msg) {
StringBuilder sb = new StringBuilder("{ \"version\": \"1.1\","); StringBuilder sb = new StringBuilder("{ \"version\": \"1.1\",");
sb.append(String.format("\"host\": \"%s\",", msg.hostname)); sb.append(String.format("\"host\": \"%s\",", msg.hostname));
sb.append(String.format("\"short_message\": \"%s\",", msg.message)); sb.append(String.format("\"short_message\": \"%s\",", JsonUtil.encode(msg.message)));
//sb.append(String.format("\"full_message\": \"%s\",", msg.message)); //sb.append(String.format("\"full_message\": \"%s\",", msg.message));
sb.append(String.format("\"timestamp\": %d,", msg.timestamp.getEpochSecond())); sb.append(String.format("\"timestamp\": %d,", msg.timestamp.getEpochSecond()));
sb.append(String.format("\"level\": %d,", msg.severity.toNumber())); sb.append(String.format("\"level\": %d,", msg.severity.toNumber()));
@ -119,6 +120,7 @@ public class SyslogPrinter {
sb.append("}, \"values\": [ "); sb.append("}, \"values\": [ ");
sb.append(String.format("[ \"%d\", \"%s\" ]", msg.timestamp.getEpochSecond() * 1000000000l, getMessageLine(msg))); sb.append(String.format("[ \"%d\", \"%s\" ]", msg.timestamp.getEpochSecond() * 1000000000l, getMessageLine(msg)));
sb.append(" ] } ] }"); sb.append(" ] } ] }");
log.debug(sb.toString());
return sb.toString(); return sb.toString();
} }
@ -128,7 +130,7 @@ public class SyslogPrinter {
sb.append(String.format("[%s.%s] ", msg.facility, msg.severity)); sb.append(String.format("[%s.%s] ", msg.facility, msg.severity));
sb.append(String.format("%s ", msg.hostname)); sb.append(String.format("%s ", msg.hostname));
sb.append(String.format("%s ", msg.application)); sb.append(String.format("%s ", msg.application));
sb.append(msg.message); sb.append(JsonUtil.encode(msg.message));
return sb.toString(); return sb.toString();
} }

View file

@ -0,0 +1,97 @@
package biz.nellemann.syslogd.parser;
/*
Code from https://gist.github.com/jjfiv/2ac5c081e088779f49aa, which is BSD licensed: http://lemurproject.org/galago-license
*/
public class JsonUtil {
public static String encode(String input) {
StringBuilder output = new StringBuilder();
for(int i=0; i<input.length(); i++) {
char ch = input.charAt(i);
int chx = (int) ch;
// let's not put any nulls in our strings
if(chx == 0) {
continue;
}
if(ch == '\n') {
output.append("\\n");
} else if(ch == '\t') {
output.append("\\t");
} else if(ch == '\r') {
output.append("\\r");
} else if(ch == '\\') {
output.append("\\\\");
} else if(ch == '"') {
output.append("\\\"");
} else if(ch == '\b') {
output.append("\\b");
} else if(ch == '\f') {
output.append("\\f");
} else if(chx > 127) {
output.append(String.format("\\u%04x", chx));
} else {
output.append(ch);
}
}
return output.toString();
}
public static String decode(String input) {
StringBuilder builder = new StringBuilder();
int i = 0;
while (i < input.length()) {
char delimiter = input.charAt(i); i++; // consume letter or backslash
if(delimiter == '\\' && i < input.length()) {
// consume first after backslash
char ch = input.charAt(i); i++;
if(ch == '\\' || ch == '/' || ch == '"' || ch == '\'') {
builder.append(ch);
}
else if(ch == 'n') builder.append('\n');
else if(ch == 'r') builder.append('\r');
else if(ch == 't') builder.append('\t');
else if(ch == 'b') builder.append('\b');
else if(ch == 'f') builder.append('\f');
else if(ch == 'u') {
StringBuilder hex = new StringBuilder();
// expect 4 digits
if (i+4 > input.length()) {
throw new RuntimeException("Not enough unicode digits! ");
}
for (char x : input.substring(i, i + 4).toCharArray()) {
if(!Character.isLetterOrDigit(x)) {
throw new RuntimeException("Bad character in unicode escape.");
}
hex.append(Character.toLowerCase(x));
}
i+=4; // consume those four digits.
int code = Integer.parseInt(hex.toString(), 16);
builder.append((char) code);
} else {
throw new RuntimeException("Illegal escape sequence: \\"+ch);
}
} else { // it's not a backslash, or it's the last character.
builder.append(delimiter);
}
}
return builder.toString();
}
}

View file

@ -0,0 +1,42 @@
package biz.nellemann.syslogd
import biz.nellemann.syslogd.parser.JsonUtil
import spock.lang.Specification
class JsonUtilTest extends Specification {
def "test short decode"() {
setup:
def testShort = 'Eating a piece of \u03c0 (pi)'
when:
def result = JsonUtil.decode(testShort)
then:
result == 'Eating a piece of π (pi)'
}
def "test long decode"() {
setup:
def testLong = 'I stole this guy from wikipedia: \ud83d\ude02' // emoji "face with tears of joy"
when:
def result = JsonUtil.decode(testLong)
then:
result == 'I stole this guy from wikipedia: 😂'
}
def "test quotes decode"() {
setup:
def testQuote = 'here it comes \" to wreck the day...'
when:
def result = JsonUtil.decode(testQuote)
then:
result == 'here it comes " to wreck the day...'
}
}

View file

@ -7,7 +7,6 @@ import spock.lang.Specification
class SyslogPrinterTest extends Specification { class SyslogPrinterTest extends Specification {
void setup() { void setup() {
} }