added check and match counters in log parser rules
[public/netxms.git] / src / libnxlp / rule.cpp
CommitLineData
659bf814 1/*
5039dede
AK
2** NetXMS - Network Management System
3** Log Parsing Library
d930bfc9 4** Copyright (C) 2003-2016 Raden Solutions
5039dede
AK
5**
6** This program is free software; you can redistribute it and/or modify
65d2c384
VK
7** it under the terms of the GNU Lesser General Public License as published by
8** the Free Software Foundation; either version 3 of the License, or
5039dede
AK
9** (at your option) any later version.
10**
11** This program is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14** GNU General Public License for more details.
15**
65d2c384 16** You should have received a copy of the GNU Lesser General Public License
5039dede
AK
17** along with this program; if not, write to the Free Software
18** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19**
20** File: rule.cpp
21**
22**/
23
24#include "libnxlp.h"
25
66ee0102
VK
26/**
27 * Constructor
28 */
da623f8b 29LogParserRule::LogParserRule(LogParser *parser, const TCHAR *regexp, UINT32 eventCode, const TCHAR *eventName,
659bf814 30 int numParams, int repeatInterval, int repeatCount, bool resetRepeat, const TCHAR *source,
31 UINT32 level, UINT32 idStart, UINT32 idEnd)
5039dede
AK
32{
33 String expandedRegexp;
34
35 m_parser = parser;
6d738067 36 expandMacros(regexp, expandedRegexp);
a0c761dd 37 m_regexp = _tcsdup(expandedRegexp);
08b214c6 38 m_isValid = (_tregcomp(&m_preg, expandedRegexp, REG_EXTENDED | REG_ICASE) == 0);
2dd24569 39 m_eventCode = eventCode;
6646dca4 40 m_eventName = (eventName != NULL) ? _tcsdup(eventName) : NULL;
5039dede
AK
41 m_numParams = numParams;
42 m_pmatch = (numParams > 0) ? (regmatch_t *)malloc(sizeof(regmatch_t) * (numParams + 1)) : NULL;
43 m_source = (source != NULL) ? _tcsdup(source) : NULL;
44 m_level = level;
45 m_idStart = idStart;
46 m_idEnd = idEnd;
47 m_context = NULL;
48 m_contextAction = 0;
49 m_contextToChange = NULL;
50 m_isInverted = FALSE;
51 m_breakOnMatch = FALSE;
52 m_description = NULL;
659bf814 53 m_repeatInterval = repeatInterval;
54 m_repeatCount = repeatCount;
55 m_matchArray = new IntegerArray<time_t>();
56 m_resetRepeat = resetRepeat;
aa07a370
VK
57 m_checkCount = 0;
58 m_matchCount = 0;
5039dede
AK
59}
60
66ee0102
VK
61/**
62 * Copy constructor
63 */
64LogParserRule::LogParserRule(LogParserRule *src, LogParser *parser)
65{
66 m_parser = parser;
67 m_regexp = _tcsdup(src->m_regexp);
68 m_isValid = (_tregcomp(&m_preg, m_regexp, REG_EXTENDED | REG_ICASE) == 0);
69 m_eventCode = src->m_eventCode;
70 m_eventName = (src->m_eventName != NULL) ? _tcsdup(src->m_eventName) : NULL;
71 m_numParams = src->m_numParams;
72 m_pmatch = (m_numParams > 0) ? (regmatch_t *)malloc(sizeof(regmatch_t) * (m_numParams + 1)) : NULL;
73 m_source = (src->m_source != NULL) ? _tcsdup(src->m_source) : NULL;
74 m_level = src->m_level;
75 m_idStart = src->m_idStart;
76 m_idEnd = src->m_idEnd;
77 m_context = (src->m_context != NULL) ? _tcsdup(src->m_context) : NULL;
78 m_contextAction = src->m_contextAction;
659bf814 79 m_contextToChange = (src->m_contextToChange != NULL) ? _tcsdup(src->m_contextToChange) : NULL;
66ee0102
VK
80 m_isInverted = src->m_isInverted;
81 m_breakOnMatch = src->m_breakOnMatch;
659bf814 82 m_description = (src->m_description != NULL) ? _tcsdup(src->m_description) : NULL;
83 m_repeatInterval = src->m_repeatInterval;
84 m_repeatCount = src->m_repeatCount;
85 m_resetRepeat = src->m_resetRepeat;
86 if (src->m_matchArray != NULL)
87 {
88 m_matchArray = new IntegerArray<time_t>(src->m_matchArray->size(), 16);
89 for(int i = 0; i < src->m_matchArray->size(); i++)
90 m_matchArray->add(src->m_matchArray->get(i));
91 }
92 else
93 {
94 m_matchArray = new IntegerArray<time_t>();
95 }
aa07a370
VK
96 m_checkCount = src->m_checkCount;
97 m_matchCount = src->m_matchCount;
66ee0102 98}
5039dede 99
66ee0102
VK
100/**
101 * Destructor
102 */
5039dede
AK
103LogParserRule::~LogParserRule()
104{
105 if (m_isValid)
106 regfree(&m_preg);
107 safe_free(m_pmatch);
108 safe_free(m_description);
a0c761dd
VK
109 safe_free(m_source);
110 safe_free(m_regexp);
2dd24569 111 safe_free(m_eventName);
66ee0102
VK
112 safe_free(m_context);
113 safe_free(m_contextToChange);
659bf814 114 delete m_matchArray;
5039dede
AK
115}
116
66ee0102
VK
117/**
118 * Match line
119 */
da623f8b
VK
120bool LogParserRule::matchInternal(bool extMode, const TCHAR *source, UINT32 eventId, UINT32 level,
121 const TCHAR *line, LogParserCallback cb, UINT32 objectId, void *userArg)
5039dede 122{
aa07a370 123 m_checkCount++;
da623f8b
VK
124 if (extMode)
125 {
126 if (m_source != NULL)
127 {
128 m_parser->trace(6, _T(" matching source \"%s\" against pattern \"%s\""), source, m_source);
129 if (!MatchString(m_source, source, FALSE))
130 {
131 m_parser->trace(6, _T(" source: no match"));
132 return false;
133 }
134 }
135
136 if ((eventId < m_idStart) || (eventId > m_idEnd))
137 {
138 m_parser->trace(6, _T(" event id 0x%08x not in range 0x%08x - 0x%08x"), eventId, m_idStart, m_idEnd);
139 return false;
140 }
141
142 if (!(m_level & level))
143 {
144 m_parser->trace(6, _T(" severity level 0x%04x not match mask 0x%04x"), level, m_level);
145 return false;
146 }
147 }
148
5039dede 149 if (!m_isValid)
4be31662
VK
150 {
151 m_parser->trace(6, _T(" regexp is invalid: %s"), m_regexp);
6d738067 152 return false;
4be31662 153 }
5039dede
AK
154
155 if (m_isInverted)
156 {
6d738067 157 m_parser->trace(6, _T(" negated matching against regexp %s"), m_regexp);
d930bfc9 158 if ((_tregexec(&m_preg, line, 0, NULL, 0) != 0) && matchRepeatCount())
5039dede 159 {
6d738067 160 m_parser->trace(6, _T(" matched"));
2dd24569 161 if ((cb != NULL) && ((m_eventCode != 0) || (m_eventName != NULL)))
d930bfc9
VK
162 cb(m_eventCode, m_eventName, line, source, eventId, level, 0, NULL, objectId,
163 ((m_repeatCount > 0) && (m_repeatInterval > 0)) ? m_matchArray->size() : 1, userArg);
aa07a370 164 m_matchCount++;
6d738067 165 return true;
5039dede
AK
166 }
167 }
168 else
169 {
6d738067 170 m_parser->trace(6, _T(" matching against regexp %s"), m_regexp);
d930bfc9 171 if ((_tregexec(&m_preg, line, (m_numParams > 0) ? m_numParams + 1 : 0, m_pmatch, 0) == 0) && matchRepeatCount())
5039dede 172 {
6d738067 173 m_parser->trace(6, _T(" matched"));
2dd24569 174 if ((cb != NULL) && ((m_eventCode != 0) || (m_eventName != NULL)))
5039dede 175 {
6646dca4 176 TCHAR **params = NULL;
5039dede
AK
177 int i, len;
178
179 if (m_numParams > 0)
180 {
8d4bb054 181#if HAVE_ALLOCA
6646dca4 182 params = (TCHAR **)alloca(sizeof(TCHAR *) * m_numParams);
8d4bb054
VK
183#else
184 params = (TCHAR **)malloc(sizeof(TCHAR *) * m_numParams);
185#endif
5039dede
AK
186 for(i = 0; i < m_numParams; i++)
187 {
188 if (m_pmatch[i + 1].rm_so != -1)
189 {
190 len = m_pmatch[i + 1].rm_eo - m_pmatch[i + 1].rm_so;
6646dca4
VK
191 params[i] = (TCHAR *)malloc((len + 1) * sizeof(TCHAR));
192 memcpy(params[i], &line[m_pmatch[i + 1].rm_so], len * sizeof(TCHAR));
5039dede
AK
193 params[i][len] = 0;
194 }
195 else
196 {
6646dca4 197 params[i] = _tcsdup(_T(""));
5039dede
AK
198 }
199 }
200 }
201
d930bfc9
VK
202 cb(m_eventCode, m_eventName, line, source, eventId, level, m_numParams, params, objectId,
203 ((m_repeatCount > 0) && (m_repeatInterval > 0)) ? m_matchArray->size() : 1, userArg);
659bf814 204
5039dede 205 for(i = 0; i < m_numParams; i++)
8d4bb054
VK
206 free(params[i]);
207#if !HAVE_ALLOCA
208 free(params);
209#endif
aa07a370
VK
210
211 m_matchCount++;
659bf814 212 }
6d738067 213 return true;
5039dede
AK
214 }
215 }
216
6d738067
VK
217 m_parser->trace(6, _T(" no match"));
218 return false; // no match
219}
220
66ee0102 221/**
da623f8b 222 * Match line
66ee0102 223 */
da623f8b 224bool LogParserRule::match(const TCHAR *line, LogParserCallback cb, UINT32 objectId, void *userArg)
6d738067 225{
da623f8b
VK
226 return matchInternal(false, NULL, 0, 0, line, cb, objectId, userArg);
227}
6d738067 228
da623f8b
VK
229/**
230 * Match event
231 */
232bool LogParserRule::matchEx(const TCHAR *source, UINT32 eventId, UINT32 level,
233 const TCHAR *line, LogParserCallback cb, UINT32 objectId, void *userArg)
234{
235 return matchInternal(true, source, eventId, level, line, cb, objectId, userArg);
5039dede
AK
236}
237
66ee0102
VK
238/**
239 * Expand macros in regexp
240 */
6646dca4 241void LogParserRule::expandMacros(const TCHAR *regexp, String &out)
5039dede 242{
6646dca4
VK
243 const TCHAR *curr, *prev;
244 TCHAR name[256];
5039dede
AK
245
246 for(curr = prev = regexp; *curr != 0; curr++)
247 {
6646dca4 248 if (*curr == _T('@'))
5039dede
AK
249 {
250 // Check for escape
6646dca4 251 if ((curr != regexp) && (*(curr - 1) == _T('\\')))
5039dede 252 {
4e0e77e6 253 out.append(prev, (size_t)(curr - prev - 1));
5039dede
AK
254 out += _T("@");
255 }
256 else
257 {
258 // { should follow @
6646dca4 259 if (*(curr + 1) == _T('{'))
5039dede
AK
260 {
261 int i;
262
4e0e77e6 263 out.append(prev, (size_t)(curr - prev));
5039dede
AK
264 curr += 2;
265 for(i = 0; (*curr != 0) && (*curr != '}'); i++)
266 name[i] = *curr++;
267 name[i] = 0;
6d738067 268 out += m_parser->getMacro(name);
5039dede
AK
269 }
270 else
271 {
4e0e77e6 272 out.append(prev, (size_t)(curr - prev + 1));
5039dede
AK
273 }
274 }
275 prev = curr + 1;
276 }
277 }
4e0e77e6 278 out.append(prev, (size_t)(curr - prev));
5039dede 279}
659bf814 280
d930bfc9
VK
281/**
282 * Match repeat count
283 */
284bool LogParserRule::matchRepeatCount()
659bf814 285{
d930bfc9
VK
286 if ((m_repeatCount == 0) || (m_repeatInterval == 0))
287 return true;
659bf814 288
d930bfc9 289 // remove expired matches
659bf814 290 time_t now = time(NULL);
291 for(int i = 0; i < m_matchArray->size(); i++)
292 {
d930bfc9 293 if (m_matchArray->get(i) >= (now - m_repeatInterval))
659bf814 294 break;
d930bfc9
VK
295 m_matchArray->remove(i);
296 i--;
659bf814 297 }
659bf814 298
d930bfc9 299 m_matchArray->add(now);
659bf814 300 bool match = m_matchArray->size() >= m_repeatCount;
d930bfc9
VK
301 if (m_resetRepeat && match)
302 m_matchArray->clear();
659bf814 303 return match;
304}