summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog4
-rw-r--r--doc/property_replacer.html6
-rw-r--r--runtime/msg.c112
-rw-r--r--template.c17
-rw-r--r--template.h3
5 files changed, 137 insertions, 5 deletions
diff --git a/ChangeLog b/ChangeLog
index 1c4b00f5..65040607 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,12 +1,14 @@
---------------------------------------------------------------------------
Version 6.3.8 [DEVEL] 2012-02-??
+- added "json" property replacer option to support JSON encoding on a
+ per-property basis
- added mmjsonparse to support recognizing and parsing JSON enhanced syslog
messages
- upgraded more plugins to support the new v6 config format:
- ommysql
- omlibdbi
- omsnmp
-. added message property parsesuccess to indicate if the last run
+- added message property parsesuccess to indicate if the last run
higher-level parser could successfully parse the message or not
(see property replacer html doc for details)
- bugfix: abort during startup when rsyslog.conf v6+ format was used in
diff --git a/doc/property_replacer.html b/doc/property_replacer.html
index 394011a1..f0ac3c94 100644
--- a/doc/property_replacer.html
+++ b/doc/property_replacer.html
@@ -342,6 +342,12 @@ case-insensitive. Currently, the following options are defined:
<td>convert property text to uppercase only</td>
</tr>
<tr>
+<td><b>json</b></td>
+<td>encode the value so that it can be used inside a JSON field. This means
+that several characters (according to the JSON spec) are being escaped, for
+example US-ASCII LF is replaced by "\n".</td>
+</tr>
+<tr>
<td valign="top"><b>csv</b></td>
<td>formats the resulting field (after all modifications) in CSV format
as specified in <a href="http://www.ietf.org/rfc/rfc4180.txt">RFC 4180</a>.
diff --git a/runtime/msg.c b/runtime/msg.c
index 8f5fb080..548afb15 100644
--- a/runtime/msg.c
+++ b/runtime/msg.c
@@ -2334,6 +2334,112 @@ finalize_it:
}
}
+
+/* encode a property in JSON escaped format. This is a helper
+ * to MsgGetProp. It needs to update all provided parameters.
+ * Note: Code is borrowed from libee (my own code, so ASL 2.0
+ * is fine with it); this function may later be replaced by
+ * some "better" and more complete implementation (maybe from
+ * libee or its helpers).
+ * For performance reasons, we begin to copy the string only
+ * when we recognice that we actually need to do some escaping.
+ * rgerhards, 2012-03-16
+ */
+static rsRetVal
+jsonEncode(uchar **ppRes, unsigned short *pbMustBeFreed, int *pBufLen)
+{
+ static char hexdigit[16] =
+ {'0', '1', '2', '3', '4', '5', '6', '7', '8',
+ '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+ unsigned char c;
+ es_size_t i;
+ char numbuf[4];
+ int j;
+ unsigned buflen;
+ uchar *pSrc;
+ es_str_t *dst = NULL;
+ DEFiRet;
+
+ pSrc = *ppRes;
+ buflen = (*pBufLen == -1) ? ustrlen(pSrc) : *pBufLen;
+ for(i = 0 ; i < buflen ; ++i) {
+ c = pSrc[i];
+ if( (c >= 0x23 && c <= 0x5b)
+ || (c >= 0x5d /* && c <= 0x10FFFF*/)
+ || c == 0x20 || c == 0x21) {
+ /* no need to escape */
+ if(dst != NULL)
+ es_addChar(&dst, c);
+ } else {
+ if(dst == NULL) {
+ if(i == 0) {
+ /* we hope we have only few escapes... */
+ dst = es_newStr(buflen+10);
+ } else {
+ dst = es_newStrFromBuf((char*)pSrc, i-1);
+ }
+ if(dst == NULL) {
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ }
+ }
+ /* we must escape, try RFC4627-defined special sequences first */
+ switch(c) {
+ case '\0':
+ es_addBuf(&dst, "\\u0000", 6);
+ break;
+ case '\"':
+ es_addBuf(&dst, "\\\"", 2);
+ break;
+ case '/':
+ es_addBuf(&dst, "\\/", 2);
+ break;
+ case '\\':
+ es_addBuf(&dst, "\\\\", 2);
+ break;
+ case '\010':
+ es_addBuf(&dst, "\\b", 2);
+ break;
+ case '\014':
+ es_addBuf(&dst, "\\f", 2);
+ break;
+ case '\n':
+ es_addBuf(&dst, "\\n", 2);
+ break;
+ case '\r':
+ es_addBuf(&dst, "\\r", 2);
+ break;
+ case '\t':
+ es_addBuf(&dst, "\\t", 2);
+ break;
+ default:
+ /* TODO : proper Unicode encoding (see header comment) */
+ for(j = 0 ; j < 4 ; ++j) {
+ numbuf[3-j] = hexdigit[c % 16];
+ c = c / 16;
+ }
+ es_addBuf(&dst, "\\u", 2);
+ es_addBuf(&dst, numbuf, 4);
+ break;
+ }
+ }
+ }
+
+ if(dst != NULL) {
+ /* we updated the string and need to replace the
+ * previous data.
+ */
+ if(*pbMustBeFreed)
+ free(*ppRes);
+ *ppRes = (uchar*)es_str2cstr(dst, NULL);
+ *pbMustBeFreed = 1;
+ es_deleteStr(dst);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
/* This function returns a string-representation of the
* requested message property. This is a generic function used
* to abstract properties so that these can be easier
@@ -3105,8 +3211,8 @@ dbgprintf("prop repl 4, pRes='%s', len %d\n", pRes, bufLen);
}
}
- /* finally, we need to check if the property should be formatted in CSV
- * format (we use RFC 4180, and always use double quotes). As of this writing,
+ /* finally, we need to check if the property should be formatted in CSV or JSON.
+ * For CSV we use RFC 4180, and always use double quotes. As of this writing,
* this should be the last action carried out on the property, but in the
* future there may be reasons to change that. -- rgerhards, 2009-04-02
*/
@@ -3140,6 +3246,8 @@ dbgprintf("prop repl 4, pRes='%s', len %d\n", pRes, bufLen);
pRes = pBStart;
bufLen = -1;
*pbMustBeFreed = 1;
+ } else if(pTpe->data.field.options.bJSON) {
+ jsonEncode(&pRes, pbMustBeFreed, &bufLen);
}
if(bufLen == -1)
diff --git a/template.c b/template.c
index ca1688f7..bcdcc367 100644
--- a/template.c
+++ b/template.c
@@ -542,7 +542,19 @@ static void doOptions(unsigned char **pp, struct templateEntry *pTpe)
} else if(!strcmp((char*)Buf, "secpath-replace")) {
pTpe->data.field.options.bSecPathReplace = 1;
} else if(!strcmp((char*)Buf, "csv")) {
- pTpe->data.field.options.bCSV = 1;
+ if(pTpe->data.field.options.bJSON) {
+ errmsg.LogError(0, NO_ERRCODE, "error: can not specify "
+ "both csv and json options - csv ignored");
+ } else {
+ pTpe->data.field.options.bCSV = 1;
+ }
+ } else if(!strcmp((char*)Buf, "json")) {
+ if(pTpe->data.field.options.bCSV) {
+ errmsg.LogError(0, NO_ERRCODE, "error: can not specify "
+ "both csv and json options - json ignored");
+ } else {
+ pTpe->data.field.options.bJSON = 1;
+ }
} else {
dbgprintf("Invalid field option '%s' specified - ignored.\n", Buf);
}
@@ -1280,6 +1292,9 @@ void tplPrintList(rsconf_t *conf)
if(pTpe->data.field.options.bCSV) {
dbgprintf("[format as CSV (RFC4180)]");
}
+ if(pTpe->data.field.options.bJSON) {
+ dbgprintf("[format as JSON");
+ }
if(pTpe->data.field.options.bDropLastLF) {
dbgprintf("[drop last LF in msg] ");
}
diff --git a/template.h b/template.h
index d394809b..b2cef271 100644
--- a/template.h
+++ b/template.h
@@ -2,7 +2,7 @@
* Please see syslogd.c for license information.
* begun 2004-11-17 rgerhards
*
- * Copyright (C) 2004 by Rainer Gerhards and Adiscon GmbH
+ * Copyright (C) 2004-2012 by Rainer Gerhards and Adiscon GmbH
*
* This file is part of rsyslog.
*
@@ -103,6 +103,7 @@ struct templateEntry {
unsigned bSecPathReplace: 1; /* replace slashes, replace dots, empty string */
unsigned bSPIffNo1stSP: 1; /* replace slashes, replace dots, empty string */
unsigned bCSV: 1; /* format field in CSV (RFC 4180) format */
+ unsigned bJSON: 1; /* format field JSON escaped */
} options; /* options as bit fields */
} field;
} data;