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
# 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.

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.
To add a remote logging destination:
To add a remote logging destination, run the following command. Use the filter to discard unwanted messaged.
```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.

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
# Log authentication messages to remote host
auth.info,authpriv.info @10.32.64.1
auth.*,authpriv.* @10.32.64.1
```
Restart the rsyslog service
```shell
systemctl restart rsyslogd
systemctl restart rsyslog
```

View file

@ -1,3 +1,3 @@
id = 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.Severity;
import biz.nellemann.syslogd.msg.SyslogMessage;
import biz.nellemann.syslogd.parser.JsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -89,7 +90,7 @@ public class SyslogPrinter {
public static String toGelf(SyslogMessage msg) {
StringBuilder sb = new StringBuilder("{ \"version\": \"1.1\",");
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("\"timestamp\": %d,", msg.timestamp.getEpochSecond()));
sb.append(String.format("\"level\": %d,", msg.severity.toNumber()));
@ -119,6 +120,7 @@ public class SyslogPrinter {
sb.append("}, \"values\": [ ");
sb.append(String.format("[ \"%d\", \"%s\" ]", msg.timestamp.getEpochSecond() * 1000000000l, getMessageLine(msg)));
sb.append(" ] } ] }");
log.debug(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 ", msg.hostname));
sb.append(String.format("%s ", msg.application));
sb.append(msg.message);
sb.append(JsonUtil.encode(msg.message));
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 {
void setup() {
}