Here is a new message modifier plugin (mmcount) attached as patch file which 
counts each log messages by severity or json property of given app-name.  Below 
is more information from commit log

This module provides the capability to count log messages by severity
or json property of given app-name.  The count value is added into the
log message as json property named 'mmcount'

Example usage of the module in the configuration file

 module(load="mmcount")

 # count each severity of appname gluster
 action(type="mmcount" appname="gluster")

 # count each value of gf_code of appname gluster
 action(type="mmcount" appname="glusterd" key="!gf_code")

 # count value 9999 of gf_code of appname gluster
 action(type="mmcount" appname="glusterfsd" key="!gf_code" value="9999")

 # send email for every 50th mmcount
 if $app-name == 'glusterfsd' and $!mmcount <> 0 and $!mmcount % 50 == 0 then {
    $ActionMailSMTPServer smtp.example.com
    $ActionMailFrom [email protected]
    $ActionMailTo [email protected]
    $template mailSubject,"50th message of gf_code=9999 on %hostname%"
    $template mailBody,"RSYSLOG Alert\r\nmsg='%msg%'"
    $ActionMailSubject mailSubject
    $ActionExecOnlyOnceEveryInterval 30
    :ommail:;RSYSLOG_SyslogProtocol23Format
 }

Please provide your feedback by reviewing the patch.

Thanks in advance,

Regards,
Bala
From 18af7b3d5a435ffdeec4009ee51c9775e3b44dbc Mon Sep 17 00:00:00 2001
From: "Bala.FA" <[email protected]>
Date: Thu, 2 May 2013 12:51:27 +0530
Subject: [PATCH 1/1] mmcount: message modification plugin which counts
 messages

This module provides the capability to count log messages by severity
or json property of given app-name.  The count value is added into the
log message as json property named 'mmcount'

Example usage of the module in the configuration file

 module(load="mmcount")

 # count each severity of appname gluster
 action(type="mmcount" appname="gluster")

 # count each value of gf_code of appname gluster
 action(type="mmcount" appname="glusterd" key="!gf_code")

 # count value 9999 of gf_code of appname gluster
 action(type="mmcount" appname="glusterfsd" key="!gf_code" value="9999")

 # send email for every 50th mmcount
 if $app-name == 'glusterfsd' and $!mmcount <> 0 and $!mmcount % 50 == 0 then {
    $ActionMailSMTPServer smtp.example.com
    $ActionMailFrom [email protected]
    $ActionMailTo [email protected]
    $template mailSubject,"50th message of gf_code=9999 on %hostname%"
    $template mailBody,"RSYSLOG Alert\r\nmsg='%msg%'"
    $ActionMailSubject mailSubject
    $ActionExecOnlyOnceEveryInterval 30
    :ommail:;RSYSLOG_SyslogProtocol23Format
 }

Signed-off-by: Bala.FA <[email protected]>
---
 Makefile.am                     |   4 +
 build/rhel/rsyslog/rsyslog.spec |  15 ++
 configure.ac                    |  15 ++
 plugins/mmcount/Makefile.am     |   8 +
 plugins/mmcount/mmcount.c       | 342 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 384 insertions(+)
 create mode 100644 plugins/mmcount/Makefile.am
 create mode 100644 plugins/mmcount/mmcount.c

diff --git a/Makefile.am b/Makefile.am
index ed3b54b..cc64b71 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -241,6 +241,10 @@ if ENABLE_MMANON
 SUBDIRS += plugins/mmanon
 endif
 
+if ENABLE_MMCOUNT
+SUBDIRS += plugins/mmcount
+endif
+
 if ENABLE_ORACLE
 SUBDIRS += plugins/omoracle
 endif
diff --git a/build/rhel/rsyslog/rsyslog.spec b/build/rhel/rsyslog/rsyslog.spec
index 4aa91df..7bb367d 100644
--- a/build/rhel/rsyslog/rsyslog.spec
+++ b/build/rhel/rsyslog/rsyslog.spec
@@ -47,6 +47,11 @@ Summary: ElasticSearch output module for rsyslog
 Group: System Environment/Daemons
 Requires: %name = %version-%release
 
+%package mmcount
+Summary: Message counting support for rsyslog
+Group: System Environment/Daemons
+Requires: %name = %version-%release
+
 %package mmjsonparse
 Summary: JSON enhanced logging support
 Group: System Environment/Daemons
@@ -154,6 +159,11 @@ relay chains.
 This module provides the capability for rsyslog to feed logs directly into
 Elasticsearch.
 
+%description mmcount
+This module provides the capability to count log messages by severity
+or json property of given app-name.  The count value is added into the
+log message in json property named 'mmcount'
+
 %description mmjsonparse
 This module provides the capability to recognize and parse JSON enhanced
 syslog messages.
@@ -226,6 +236,7 @@ export PKG_CONFIG=/usr/bin/pkg-config
 %configure --disable-static \
         --disable-testbench \
         --enable-elasticsearch \
+        --enable-mmcount \
         --enable-mmjsonparse \
         --enable-mmnormalize \
         --enable-imzmq3 \
@@ -339,6 +350,10 @@ fi
 %defattr(-,root,root)
 %{_libdir}/rsyslog/omelasticsearch.so
 
+%files mmcount
+%defattr(-,root,root)
+%{_libdir}/rsyslog/mmcount.so
+
 %files mmjsonparse
 %defattr(-,root,root)
 %{_libdir}/rsyslog/mmjsonparse.so
diff --git a/configure.ac b/configure.ac
index fd9cea8..56483e6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -968,6 +968,19 @@ AC_ARG_ENABLE(mmanon,
 AM_CONDITIONAL(ENABLE_MMANON, test x$enable_mmanon = xyes)
 
 
+# mmcount
+AC_ARG_ENABLE(mmcount,
+        [AS_HELP_STRING([--enable-mmcount],[Enable message counting @<:@default=no@:>@])],
+        [case "${enableval}" in
+         yes) enable_xmpp="yes" ;;
+          no) enable_xmpp="no" ;;
+           *) AC_MSG_ERROR(bad value ${enableval} for --enable-mmcount) ;;
+         esac],
+        [enable_mmcount=no]
+)
+AM_CONDITIONAL(ENABLE_MMCOUNT, test x$enable_mmcount = xyes)
+
+
 # RELP support
 AC_ARG_ENABLE(relp,
         [AS_HELP_STRING([--enable-relp],[Enable RELP support @<:@default=no@:>@])],
@@ -1470,6 +1483,7 @@ AC_CONFIG_FILES([Makefile \
 		plugins/mmjsonparse/Makefile \
 		plugins/mmaudit/Makefile \
 		plugins/mmanon/Makefile \
+		plugins/mmcount/Makefile \
 		plugins/omelasticsearch/Makefile \
 		plugins/sm_cust_bindcdr/Makefile \
 		plugins/mmsnmptrapd/Makefile \
@@ -1492,6 +1506,7 @@ echo "    uuid support enabled:                     $enable_uuid"
 echo "    Log file signing support:                 $enable_guardtime"
 echo "    Log file encryption support:              $enable_libgcrypt"
 echo "    anonymization support enabled:            $enable_mmanon"
+echo "    message counting support enabled:         $enable_mmcount"
 echo
 echo "---{ input plugins }---"
 echo "    Klog functionality enabled:               $enable_klog ($os_type)"
diff --git a/plugins/mmcount/Makefile.am b/plugins/mmcount/Makefile.am
new file mode 100644
index 0000000..9c8c99d
--- /dev/null
+++ b/plugins/mmcount/Makefile.am
@@ -0,0 +1,8 @@
+pkglib_LTLIBRARIES = mmcount.la
+
+mmcount_la_SOURCES = mmcount.c
+mmcount_la_CPPFLAGS =  $(RSRT_CFLAGS) $(PTHREADS_CFLAGS)
+mmcount_la_LDFLAGS = -module -avoid-version
+mmcount_la_LIBADD = 
+
+EXTRA_DIST = 
diff --git a/plugins/mmcount/mmcount.c b/plugins/mmcount/mmcount.c
new file mode 100644
index 0000000..56a4de5
--- /dev/null
+++ b/plugins/mmcount/mmcount.c
@@ -0,0 +1,342 @@
+/* mmcount.c
+ * count messages by priority or json property of given app-name.
+ *
+ * Copyright 2013 Red Hat Inc.
+ *
+ * This file is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *       -or-
+ *       see COPYING.ASL20 in the source distribution
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <json/json.h>
+#include "conf.h"
+#include "syslogd-types.h"
+#include "srUtils.h"
+#include "template.h"
+#include "module-template.h"
+#include "errmsg.h"
+#include "hashtable.h"
+
+#define JSON_COUNT_NAME "!mmcount"
+#define SEVERITY_COUNT 8
+
+MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("mmcount")
+
+
+DEFobjCurrIf(errmsg);
+DEF_OMOD_STATIC_DATA
+
+/* config variables */
+
+typedef struct _instanceData {
+	char *pszAppName;
+	int severity[SEVERITY_COUNT];
+	char *pszKey;
+	char *pszValue;
+	int valueCounter;
+	struct hashtable *ht;
+} instanceData;
+
+struct modConfData_s {
+	rsconf_t *pConf;	/* our overall config object */
+};
+static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
+static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current exec process */
+
+
+/* tables for interfacing with the v6 config system */
+/* action (instance) parameters */
+static struct cnfparamdescr actpdescr[] = {
+	{ "appname", eCmdHdlrGetWord, 0 },
+	{ "key", eCmdHdlrGetWord, 0 },
+	{ "value", eCmdHdlrGetWord, 0 },
+};
+static struct cnfparamblk actpblk =
+	{ CNFPARAMBLK_VERSION,
+	  sizeof(actpdescr)/sizeof(struct cnfparamdescr),
+	  actpdescr
+	};
+
+BEGINbeginCnfLoad
+CODESTARTbeginCnfLoad
+	loadModConf = pModConf;
+	pModConf->pConf = pConf;
+ENDbeginCnfLoad
+
+BEGINendCnfLoad
+CODESTARTendCnfLoad
+ENDendCnfLoad
+
+BEGINcheckCnf
+CODESTARTcheckCnf
+ENDcheckCnf
+
+BEGINactivateCnf
+CODESTARTactivateCnf
+	runModConf = pModConf;
+ENDactivateCnf
+
+BEGINfreeCnf
+CODESTARTfreeCnf
+ENDfreeCnf
+
+
+BEGINcreateInstance
+CODESTARTcreateInstance
+ENDcreateInstance
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ENDisCompatibleWithFeature
+
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ENDfreeInstance
+
+
+static inline void
+setInstParamDefaults(instanceData *pData)
+{
+	int i;
+
+	pData->pszAppName = NULL;
+	for (i = 0; i < SEVERITY_COUNT; i++)
+	        pData->severity[i] = 0;
+	pData->pszKey = NULL;
+	pData->pszValue = NULL;
+	pData->valueCounter = 0;
+	pData->ht = NULL;
+}
+
+static unsigned int
+hash_from_key_fn(void *k)
+{
+	return *(unsigned int *)k;
+}
+
+static int
+key_equals_fn(void *k1, void *k2)
+{
+	return (*(unsigned int *)k1 == *(unsigned int *)k2);
+}
+
+BEGINnewActInst
+	struct cnfparamvals *pvals;
+	int i;
+CODESTARTnewActInst
+	DBGPRINTF("newActInst (mmcount)\n");
+	if((pvals = nvlstGetParams(lst, &actpblk, NULL)) == NULL) {
+		ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
+	}
+
+	CODE_STD_STRING_REQUESTnewActInst(1)
+	CHKiRet(OMSRsetEntry(*ppOMSR, 0, NULL, OMSR_TPL_AS_MSG));
+	CHKiRet(createInstance(&pData));
+	setInstParamDefaults(pData);
+
+	for(i = 0 ; i < actpblk.nParams ; ++i) {
+		if(!pvals[i].bUsed)
+			continue;
+		if(!strcmp(actpblk.descr[i].name, "appname")) {
+			pData->pszAppName = es_str2cstr(pvals[i].val.d.estr, NULL);
+			continue;
+		}
+		if(!strcmp(actpblk.descr[i].name, "key")) {
+			pData->pszKey = es_str2cstr(pvals[i].val.d.estr, NULL);
+			continue;
+		}
+		if(!strcmp(actpblk.descr[i].name, "value")) {
+			pData->pszValue = es_str2cstr(pvals[i].val.d.estr, NULL);
+			continue;
+		}
+		dbgprintf("mmcount: program error, non-handled "
+			  "param '%s'\n", actpblk.descr[i].name);
+	}
+
+	if(pData->pszAppName == NULL) {
+		dbgprintf("mmcount: action requires a appname");
+		ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
+	}
+
+	if(pData->pszKey != NULL && pData->pszValue == NULL) {
+		if(NULL == (pData->ht = create_hashtable(100, hash_from_key_fn, key_equals_fn, NULL))) {
+			DBGPRINTF("mmcount: error creating hash table!\n");
+			ABORT_FINALIZE(RS_RET_ERR);
+		}
+	}
+CODE_STD_FINALIZERnewActInst
+	cnfparamvalsDestruct(pvals, &actpblk);
+ENDnewActInst
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ENDdbgPrintInstInfo
+
+
+BEGINtryResume
+CODESTARTtryResume
+ENDtryResume
+
+static int *
+getCounter(struct hashtable *ht, char *str) {
+	unsigned int key;
+	int *pCounter;
+	unsigned int *pKey;
+
+	/* we dont store str as key, instead we store hash of the str
+	   as key to reduce memory usage */
+	key = hash_from_string(str);
+	pCounter = hashtable_search(ht, &key);
+	if(pCounter) {
+		return pCounter;
+	}
+
+	/* counter is not found for the str, so add new entry and
+	   return the counter */
+	if(NULL == (pKey = (unsigned int*)malloc(sizeof(unsigned int)))) {
+		DBGPRINTF("mmcount: memory allocation for key failed\n");
+		return NULL;
+	}
+	*pKey = key;
+
+	if(NULL == (pCounter = (int*)malloc(sizeof(int)))) {
+		DBGPRINTF("mmcount: memory allocation for value failed\n");
+		free(pKey);
+		return NULL;
+	}
+	*pCounter = 0;
+
+	if(!hashtable_insert(ht, pKey, pCounter)) {
+		DBGPRINTF("mmcount: inserting element into hashtable failed\n");
+		free(pKey);
+		free(pCounter);
+		return NULL;
+	}
+	return pCounter;
+}
+
+BEGINdoAction
+	msg_t *pMsg;
+	char *appname;
+	struct json_object *json = NULL;
+	es_str_t *estr = NULL;
+	struct json_object *keyjson = NULL;
+	char *pszValue;
+	int *pCounter;
+CODESTARTdoAction
+	pMsg = (msg_t*) ppString[0];
+	appname = getAPPNAME(pMsg, LOCK_MUTEX);
+
+	if(0 != strcmp(appname, pData->pszAppName)) {
+		/* we are not working for this appname. nothing to do */
+		ABORT_FINALIZE(RS_RET_OK);
+	}
+
+	if(!pData->pszKey) {
+		/* no key given for count, so we count severity */
+		if(pMsg->iSeverity <= SEVERITY_COUNT) {
+			pData->severity[pMsg->iSeverity]++;
+			json = json_object_new_int(pData->severity[pMsg->iSeverity]);
+		}
+		ABORT_FINALIZE(RS_RET_OK);
+	}
+
+	/* key is given, so get the property json */
+	estr = es_newStrFromBuf(pData->pszKey, strlen(pData->pszKey));
+	if(msgGetCEEPropJSON(pMsg, estr, &keyjson) != RS_RET_OK) {
+		/* key not found in the message. nothing to do */
+		ABORT_FINALIZE(RS_RET_OK);
+	}
+
+	/* key found, so get the value */
+	pszValue = (char*)json_object_get_string(keyjson);
+
+	if(pData->pszValue) {
+		/* value also given for count */
+		if(!strcmp(pszValue, pData->pszValue)) {
+			/* count for (value and key and appname) matched */
+			pData->valueCounter++;
+			json = json_object_new_int(pData->valueCounter);
+		}
+		ABORT_FINALIZE(RS_RET_OK);
+	}
+
+	/* value is not given, so we count for each value of given key */
+	pCounter = getCounter(pData->ht, pszValue);
+	if(pCounter) {
+		(*pCounter)++;
+		json = json_object_new_int(*pCounter);
+	}
+finalize_it:
+	if(estr) {
+		es_deleteStr(estr);
+	}
+
+	if(json) {
+		msgAddJSON(pMsg, (uchar *)JSON_COUNT_NAME, json);
+	}
+ENDdoAction
+
+
+BEGINparseSelectorAct
+CODESTARTparseSelectorAct
+CODE_STD_STRING_REQUESTparseSelectorAct(1)
+	if(strncmp((char*) p, ":mmcount:", sizeof(":mmcount:") - 1)) {
+		errmsg.LogError(0, RS_RET_LEGA_ACT_NOT_SUPPORTED,
+			"mmcount supports only v6+ config format, use: "
+			"action(type=\"mmcount\" ...)");
+	}
+	ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+
+BEGINmodExit
+CODESTARTmodExit
+	objRelease(errmsg, CORE_COMPONENT);
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_OMOD_QUERIES
+CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES
+CODEqueryEtryPt_STD_CONF2_QUERIES
+ENDqueryEtryPt
+
+
+
+BEGINmodInit()
+CODESTARTmodInit
+	*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+	DBGPRINTF("mmcount: module compiled with rsyslog version %s.\n", VERSION);
+	CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ENDmodInit
-- 
1.8.1.4

_______________________________________________
rsyslog mailing list
http://lists.adiscon.net/mailman/listinfo/rsyslog
http://www.rsyslog.com/professional-services/
What's up with rsyslog? Follow https://twitter.com/rgerhards
NOTE WELL: This is a PUBLIC mailing list, posts are ARCHIVED by a myriad of 
sites beyond our control. PLEASE UNSUBSCRIBE and DO NOT POST if you DON'T LIKE 
THAT.

Reply via email to