Improve escaping of chars in json output (Loki and GELF).
This commit is contained in:
parent
8e84b9ca5c
commit
7c2762dcff
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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
16
doc/powervc-auth.md
Normal 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
|
||||||
|
```
|
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
id = syslogd
|
id = syslogd
|
||||||
group = biz.nellemann.syslogd
|
group = biz.nellemann.syslogd
|
||||||
version = 1.2.2
|
version = 1.2.3
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
97
src/main/java/biz/nellemann/syslogd/parser/JsonUtil.java
Normal file
97
src/main/java/biz/nellemann/syslogd/parser/JsonUtil.java
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
42
src/test/groovy/biz/nellemann/syslogd/JsonUtilTest.groovy
Normal file
42
src/test/groovy/biz/nellemann/syslogd/JsonUtilTest.groovy
Normal 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...'
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -7,7 +7,6 @@ import spock.lang.Specification
|
||||||
|
|
||||||
class SyslogPrinterTest extends Specification {
|
class SyslogPrinterTest extends Specification {
|
||||||
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue