diff options
Diffstat (limited to 'contrib/pmdb2diag/pmdb2diag.c')
-rw-r--r-- | contrib/pmdb2diag/pmdb2diag.c | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/contrib/pmdb2diag/pmdb2diag.c b/contrib/pmdb2diag/pmdb2diag.c new file mode 100644 index 0000000..5810eb4 --- /dev/null +++ b/contrib/pmdb2diag/pmdb2diag.c @@ -0,0 +1,305 @@ +/* pmdb2diag.c + * + * This is a parser module specifically for DB2diag log file. + * It extracted program, pid and severity from the log. + * + * Copyright 2015 Philippe Duveau @ Pari Mutuel Urbain. + * + * This file is contribution 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 <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#endif +#include "rsyslog.h" +#include "conf.h" +#include "syslogd-types.h" +#include "template.h" +#include "msg.h" +#include "module-template.h" +#include "glbl.h" +#include "errmsg.h" +#include "parser.h" +#include "datetime.h" +#include "unicode-helper.h" + +MODULE_TYPE_PARSER +MODULE_TYPE_NOKEEP +PARSER_NAME("db2.diag") +MODULE_CNFNAME("pmdb2diag") + +/* internal structures + */ +DEF_PMOD_STATIC_DATA +DEFobjCurrIf(glbl) +DEFobjCurrIf(datetime) + +/* input instance parameters */ +static struct cnfparamdescr parserpdescr[] = { + { "levelpos", eCmdHdlrInt, 0 }, + { "timepos", eCmdHdlrInt, 0 }, + { "timeformat", eCmdHdlrString, 0 }, + { "pidstarttoprogstartshift", eCmdHdlrInt, 0 }, +}; +static struct cnfparamblk parserpblk = + { CNFPARAMBLK_VERSION, + sizeof(parserpdescr)/sizeof(struct cnfparamdescr), + parserpdescr + }; + +struct instanceConf_s { + int levelpos; /* expected severity position in read message */ + int timepos; /* expected time position in read message */ + int pidstarttoprogstartshift; /* position of prog related to pid */ + char *timeformat; /* format of timestamp in read message */ + char sepSec; /* decimal separator between second and milliseconds */ +}; + +BEGINisCompatibleWithFeature +CODESTARTisCompatibleWithFeature + if(eFeat == sFEATUREAutomaticPRIParsing) + iRet = RS_RET_OK; +ENDisCompatibleWithFeature + +BEGINparse2 + struct tm tm; + char *ms, *timepos, *pid, *prog, *eprog, *backslash, *end, *lvl; + int lprog, lpid, lvl_len; + char buffer[128]; +CODESTARTparse2 + assert(pMsg != NULL); + assert(pMsg->pszRawMsg != NULL); + + DBGPRINTF("Message will now be parsed by \"db2diag\" parser.\n"); + if(pMsg->iLenRawMsg - (int)pMsg->offAfterPRI < pInst->levelpos+4) + ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE); + + /* Instead of comparing strings which a waste of cpu cycles we take interpret the 4 first chars of + * level read it as int32 and compare it to same interpretation of our constant "levels" + * So this test is not sensitive to ENDIANESS. This is not a clean way but very efficient. + */ + lvl = (char*)(pMsg->pszRawMsg + pMsg->offAfterPRI + pInst->levelpos); + + switch (*lvl) { + case 'C': /* Critical */ + pMsg->iSeverity = LOG_EMERG; + lvl_len = 8; + break; + case 'A': /* Alert */ + pMsg->iSeverity = LOG_ALERT; + lvl_len = 5; + break; + case 'S': /* Severe */ + pMsg->iSeverity = LOG_CRIT; + lvl_len = 6; + break; + case 'E': /* Error / Event */ + pMsg->iSeverity = (lvl[1] == 'r') ? LOG_ERR : LOG_NOTICE; + lvl_len = 5; + break; + case 'W': /* Warning */ + pMsg->iSeverity = LOG_WARNING; + lvl_len = 7; + break; + case 'I': /* Info */ + pMsg->iSeverity = LOG_INFO; + lvl_len = 4; + break; + case 'D': /* Debug */ + pMsg->iSeverity = LOG_DEBUG; + lvl_len = 5; + break; + default: + /* perhaps the message does not contain a proper level if so don't parse the log */ + ABORT_FINALIZE(0); + } + + /* let recheck with the real level len */ + if(pMsg->iLenRawMsg - (int)pMsg->offAfterPRI < pInst->levelpos+lvl_len) + ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE); + + DBGPRINTF("db2parse Level %d\n", pMsg->iSeverity); + + end = (char*)pMsg->pszRawMsg + pMsg->iLenRawMsg ; + + timepos = (char*)pMsg->pszRawMsg + pMsg->offAfterPRI + pInst->timepos; + + DBGPRINTF("db2parse Time %.30s\n", timepos); + ms = strptime(timepos, pInst->timeformat, &tm); + + if (ms > timepos && *(ms-1) == pInst->sepSec) + { + /* the timestamp could be properly interpreted by strptime & our format then set proper timestamp */ + int secfrac = 0, tzoff = 0; + + char *tzpos = strchr(ms, '+'); + if (!tzpos) tzpos = strchr(ms, '-'); + if (!tzpos) tzpos = (char*)"+"; + + sscanf(ms, (*tzpos == '+') ? "%d+%d " : "%d-%d ", &secfrac, &tzoff); + + pMsg->tTIMESTAMP.year = tm.tm_year+1900; + pMsg->tTIMESTAMP.month = tm.tm_mon + 1; + pMsg->tTIMESTAMP.day = tm.tm_mday; + pMsg->tTIMESTAMP.hour = tm.tm_hour; + pMsg->tTIMESTAMP.minute = tm.tm_min; + pMsg->tTIMESTAMP.second = tm.tm_sec; + pMsg->tTIMESTAMP.secfrac = secfrac; + pMsg->tTIMESTAMP.secfracPrecision = tzpos-ms; + pMsg->tTIMESTAMP.OffsetMode = *tzpos; + pMsg->tTIMESTAMP.OffsetHour = tzoff / 60; + pMsg->tTIMESTAMP.OffsetMinute = tzoff % 60; + } + + pid = strchr((char*)pMsg->pszRawMsg + pInst->levelpos + lvl_len, ':'); + if (!pid || pid>=end) ABORT_FINALIZE(0); + pid += 2; + lpid = strchr(pid, ' ') - pid; + + DBGPRINTF("db2parse pid %.*s\n", lpid, pid); + + /* set the pid */ + snprintf(buffer, 128, "%.*s", lpid, pid); + MsgSetPROCID(pMsg, buffer); + + prog = pid + pInst->pidstarttoprogstartshift; /* this offset between start of pid to start of prog */ + if (prog>=end) ABORT_FINALIZE(0); + + eprog = strchr(prog, ' '); /* let find the end of the program */ + if (eprog && eprog>=end) ABORT_FINALIZE(0); + + backslash = strchr(prog, '\\'); /* perhaps program contain an backslash */ + if (!backslash || backslash>=end) backslash = end; + + /* Determine the final length of prog */ + lprog = (eprog && eprog<backslash) ? eprog-prog : backslash-prog; + + DBGPRINTF("db2parse prog %.*s lprog %d\n", lprog, prog, lprog); + +/* set the appname */ + snprintf(buffer, 128, "%.*s", lprog, prog); + MsgSetAPPNAME(pMsg, buffer); + + /* the original raw msg if not altered by the parser */ +finalize_it: +ENDparse2 + + +BEGINfreeParserInst +CODESTARTfreeParserInst + free(pInst->timeformat); +ENDfreeParserInst + +static rsRetVal +createInstance(instanceConf_t **ppInst) +{ + instanceConf_t *pInst; + DEFiRet; + CHKmalloc(pInst = (instanceConf_t *)malloc(sizeof(instanceConf_t))); + pInst->timeformat = NULL; + pInst->levelpos = 59; + pInst->timepos = 0; + pInst->pidstarttoprogstartshift = 49; + + *ppInst = pInst; +finalize_it: + RETiRet; +} + +BEGINnewParserInst + struct cnfparamvals *pvals = NULL; + int i; +CODESTARTnewParserInst + inst = NULL; + + DBGPRINTF("newParserInst (pmdb2diag)\n"); + CHKiRet(createInstance(&inst)); + + if (lst) + { + if((pvals = nvlstGetParams(lst, &parserpblk, NULL)) == NULL) { + ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); + } + + if(Debug) { + DBGPRINTF("parser param blk in pmdb2diag:\n"); + cnfparamsPrint(&parserpblk, pvals); + } + + for(i = 0 ; i < parserpblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(parserpblk.descr[i].name, "timeformat")) { + inst->timeformat = (char*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(parserpblk.descr[i].name, "timepos")) { + inst->timepos = (int)pvals[i].val.d.n; + } else if(!strcmp(parserpblk.descr[i].name, "levelpos")) { + inst->levelpos = (int) pvals[i].val.d.n; + } else if(!strcmp(parserpblk.descr[i].name, "pidstarttoprogstartshift")) { + inst->pidstarttoprogstartshift = (int) pvals[i].val.d.n; + } else { + DBGPRINTF("pmdb2diag: program error, non-handled " + "param '%s'\n", parserpblk.descr[i].name); + } + } + } + + if (inst->timeformat == NULL) + { + inst->timeformat = strdup("%Y-%m-%d-%H.%M.%S."); + inst->sepSec = '.'; + }else + inst->sepSec = inst->timeformat[strlen(inst->timeformat)-1]; + + DBGPRINTF("pmdb2diag: parsing date/time with '%s' at position %d and level at position %d.\n", + inst->timeformat, inst->timepos, inst->levelpos); + +finalize_it: +CODE_STD_FINALIZERnewParserInst + if(lst != NULL) + cnfparamvalsDestruct(pvals, &parserpblk); +ENDnewParserInst + +BEGINmodExit +CODESTARTmodExit + /* release what we no longer need */ + objRelease(glbl, CORE_COMPONENT); + objRelease(datetime, CORE_COMPONENT); +ENDmodExit + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_PMOD2_QUERIES +CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES +ENDqueryEtryPt + + + +BEGINmodInit() +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr + CHKiRet(objUse(glbl, CORE_COMPONENT)); + CHKiRet(objUse(datetime, CORE_COMPONENT)); + DBGPRINTF("pmdb2diag parser init called, compiled with version %s\n", VERSION); +ENDmodInit |