added check and match counters in log parser rules
[public/netxms.git] / src / libnxlp / rule.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Log Parsing Library
4 ** Copyright (C) 2003-2016 Raden Solutions
5 **
6 ** This program is free software; you can redistribute it and/or modify
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
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 **
16 ** You should have received a copy of the GNU Lesser General Public License
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
26 /**
27 * Constructor
28 */
29 LogParserRule::LogParserRule(LogParser *parser, const TCHAR *regexp, UINT32 eventCode, const TCHAR *eventName,
30 int numParams, int repeatInterval, int repeatCount, bool resetRepeat, const TCHAR *source,
31 UINT32 level, UINT32 idStart, UINT32 idEnd)
32 {
33 String expandedRegexp;
34
35 m_parser = parser;
36 expandMacros(regexp, expandedRegexp);
37 m_regexp = _tcsdup(expandedRegexp);
38 m_isValid = (_tregcomp(&m_preg, expandedRegexp, REG_EXTENDED | REG_ICASE) == 0);
39 m_eventCode = eventCode;
40 m_eventName = (eventName != NULL) ? _tcsdup(eventName) : NULL;
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;
53 m_repeatInterval = repeatInterval;
54 m_repeatCount = repeatCount;
55 m_matchArray = new IntegerArray<time_t>();
56 m_resetRepeat = resetRepeat;
57 m_checkCount = 0;
58 m_matchCount = 0;
59 }
60
61 /**
62 * Copy constructor
63 */
64 LogParserRule::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;
79 m_contextToChange = (src->m_contextToChange != NULL) ? _tcsdup(src->m_contextToChange) : NULL;
80 m_isInverted = src->m_isInverted;
81 m_breakOnMatch = src->m_breakOnMatch;
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 }
96 m_checkCount = src->m_checkCount;
97 m_matchCount = src->m_matchCount;
98 }
99
100 /**
101 * Destructor
102 */
103 LogParserRule::~LogParserRule()
104 {
105 if (m_isValid)
106 regfree(&m_preg);
107 safe_free(m_pmatch);
108 safe_free(m_description);
109 safe_free(m_source);
110 safe_free(m_regexp);
111 safe_free(m_eventName);
112 safe_free(m_context);
113 safe_free(m_contextToChange);
114 delete m_matchArray;
115 }
116
117 /**
118 * Match line
119 */
120 bool LogParserRule::matchInternal(bool extMode, const TCHAR *source, UINT32 eventId, UINT32 level,
121 const TCHAR *line, LogParserCallback cb, UINT32 objectId, void *userArg)
122 {
123 m_checkCount++;
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
149 if (!m_isValid)
150 {
151 m_parser->trace(6, _T(" regexp is invalid: %s"), m_regexp);
152 return false;
153 }
154
155 if (m_isInverted)
156 {
157 m_parser->trace(6, _T(" negated matching against regexp %s"), m_regexp);
158 if ((_tregexec(&m_preg, line, 0, NULL, 0) != 0) && matchRepeatCount())
159 {
160 m_parser->trace(6, _T(" matched"));
161 if ((cb != NULL) && ((m_eventCode != 0) || (m_eventName != NULL)))
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);
164 m_matchCount++;
165 return true;
166 }
167 }
168 else
169 {
170 m_parser->trace(6, _T(" matching against regexp %s"), m_regexp);
171 if ((_tregexec(&m_preg, line, (m_numParams > 0) ? m_numParams + 1 : 0, m_pmatch, 0) == 0) && matchRepeatCount())
172 {
173 m_parser->trace(6, _T(" matched"));
174 if ((cb != NULL) && ((m_eventCode != 0) || (m_eventName != NULL)))
175 {
176 TCHAR **params = NULL;
177 int i, len;
178
179 if (m_numParams > 0)
180 {
181 #if HAVE_ALLOCA
182 params = (TCHAR **)alloca(sizeof(TCHAR *) * m_numParams);
183 #else
184 params = (TCHAR **)malloc(sizeof(TCHAR *) * m_numParams);
185 #endif
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;
191 params[i] = (TCHAR *)malloc((len + 1) * sizeof(TCHAR));
192 memcpy(params[i], &line[m_pmatch[i + 1].rm_so], len * sizeof(TCHAR));
193 params[i][len] = 0;
194 }
195 else
196 {
197 params[i] = _tcsdup(_T(""));
198 }
199 }
200 }
201
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);
204
205 for(i = 0; i < m_numParams; i++)
206 free(params[i]);
207 #if !HAVE_ALLOCA
208 free(params);
209 #endif
210
211 m_matchCount++;
212 }
213 return true;
214 }
215 }
216
217 m_parser->trace(6, _T(" no match"));
218 return false; // no match
219 }
220
221 /**
222 * Match line
223 */
224 bool LogParserRule::match(const TCHAR *line, LogParserCallback cb, UINT32 objectId, void *userArg)
225 {
226 return matchInternal(false, NULL, 0, 0, line, cb, objectId, userArg);
227 }
228
229 /**
230 * Match event
231 */
232 bool 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);
236 }
237
238 /**
239 * Expand macros in regexp
240 */
241 void LogParserRule::expandMacros(const TCHAR *regexp, String &out)
242 {
243 const TCHAR *curr, *prev;
244 TCHAR name[256];
245
246 for(curr = prev = regexp; *curr != 0; curr++)
247 {
248 if (*curr == _T('@'))
249 {
250 // Check for escape
251 if ((curr != regexp) && (*(curr - 1) == _T('\\')))
252 {
253 out.append(prev, (size_t)(curr - prev - 1));
254 out += _T("@");
255 }
256 else
257 {
258 // { should follow @
259 if (*(curr + 1) == _T('{'))
260 {
261 int i;
262
263 out.append(prev, (size_t)(curr - prev));
264 curr += 2;
265 for(i = 0; (*curr != 0) && (*curr != '}'); i++)
266 name[i] = *curr++;
267 name[i] = 0;
268 out += m_parser->getMacro(name);
269 }
270 else
271 {
272 out.append(prev, (size_t)(curr - prev + 1));
273 }
274 }
275 prev = curr + 1;
276 }
277 }
278 out.append(prev, (size_t)(curr - prev));
279 }
280
281 /**
282 * Match repeat count
283 */
284 bool LogParserRule::matchRepeatCount()
285 {
286 if ((m_repeatCount == 0) || (m_repeatInterval == 0))
287 return true;
288
289 // remove expired matches
290 time_t now = time(NULL);
291 for(int i = 0; i < m_matchArray->size(); i++)
292 {
293 if (m_matchArray->get(i) >= (now - m_repeatInterval))
294 break;
295 m_matchArray->remove(i);
296 i--;
297 }
298
299 m_matchArray->add(now);
300 bool match = m_matchArray->size() >= m_repeatCount;
301 if (m_resetRepeat && match)
302 m_matchArray->clear();
303 return match;
304 }