Added option to to generate event from log parser if regexp matched more than X times...
authorzev <zev@radensolutions.com>
Wed, 1 Jun 2016 23:36:35 +0000 (02:36 +0300)
committerzev <zev@radensolutions.com>
Thu, 2 Jun 2016 07:44:03 +0000 (10:44 +0300)
include/nxlpapi.h
src/java/netxms-eclipse/ServerConfig/src/org/netxms/ui/eclipse/serverconfig/widgets/helpers/LogParserMatch.java
src/java/netxms-eclipse/ServerConfig/src/org/netxms/ui/eclipse/serverconfig/widgets/helpers/LogParserRuleEditor.java
src/libnxlp/parser.cpp
src/libnxlp/rule.cpp
webui/webapp/ServerConfig/src/org/netxms/ui/eclipse/serverconfig/widgets/helpers/LogParserMatch.java
webui/webapp/ServerConfig/src/org/netxms/ui/eclipse/serverconfig/widgets/helpers/LogParserRuleEditor.java

index 1ecc893..6ff0a88 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
 ** NetXMS - Network Management System
 ** Log Parsing Library
 ** Copyright (C) 2003-2012 Victor Kirhenshtein
@@ -73,7 +73,7 @@
 /**
  * Log parser callback
  * Parameters:
- *    NetXMS event code, NetXMS event name, original text, source, 
+ *    NetXMS event code, NetXMS event name, original text, source,
  *    original event ID (facility), original severity,
  *    number of capture groups, list of capture groups,
  *    object id, user arg
@@ -108,15 +108,20 @@ private:
        bool m_isInverted;
        bool m_breakOnMatch;
        TCHAR *m_description;
+       int m_repeatInterval;
+       int m_repeatCount;
+   IntegerArray<time_t> *m_matchArray;
+       bool m_resetRepeat;
 
        bool matchInternal(bool extMode, const TCHAR *source, UINT32 eventId, UINT32 level,
-                          const TCHAR *line, LogParserCallback cb, UINT32 objectId, void *userArg); 
+                          const TCHAR *line, LogParserCallback cb, UINT32 objectId, void *userArg);
    void expandMacros(const TCHAR *regexp, String &out);
 
 public:
        LogParserRule(LogParser *parser,
                      const TCHAR *regexp, UINT32 eventCode = 0, const TCHAR *eventName = NULL,
-                                         int numParams = 0, const TCHAR *source = NULL, UINT32 level = 0xFFFFFFFF,
+                                         int numParams = 0, int repeatInterval = 0, int repeatCount = 0,
+                                         bool resetRepeat = true, const TCHAR *source = NULL, UINT32 level = 0xFFFFFFFF,
                                          UINT32 idStart = 0, UINT32 idEnd = 0xFFFFFFFF);
        LogParserRule(LogParserRule *src, LogParser *parser);
        ~LogParserRule();
@@ -124,8 +129,8 @@ public:
        bool isValid() { return m_isValid; }
        bool match(const TCHAR *line, LogParserCallback cb, UINT32 objectId, void *userArg);
        bool matchEx(const TCHAR *source, UINT32 eventId, UINT32 level,
-                    const TCHAR *line, LogParserCallback cb, UINT32 objectId, void *userArg); 
-       
+                    const TCHAR *line, LogParserCallback cb, UINT32 objectId, void *userArg);
+
        void setContext(const TCHAR *context) { safe_free(m_context); m_context = (context != NULL) ? _tcsdup(context) : NULL; }
        void setContextToChange(const TCHAR *context) { safe_free(m_contextToChange); m_contextToChange = (context != NULL) ? _tcsdup(context) : NULL; }
        void setContextAction(int action) { m_contextAction = action; }
@@ -152,7 +157,19 @@ public:
        void setIdRange(UINT32 start, UINT32 end) { m_idStart = start; m_idEnd = end; }
        QWORD getIdRange() { return ((QWORD)m_idStart << 32) | (QWORD)m_idEnd; }
 
+   void setRepeatInterval(int repeatInterval) { m_repeatInterval = repeatInterval; }
+   int getRepeatInterval() { return m_repeatInterval; }
+
+   void setRepeatCount(int repeatCount) { m_repeatCount = repeatCount; }
+   int getRepeatCount() { return m_repeatCount; }
+
+   void setRepeatReset(bool resetRepeat) { m_resetRepeat = resetRepeat; }
+   bool isRepeatReset() { return m_resetRepeat; }
+
+   int getAppearanceCount() { return m_matchArray->size(); }
        const TCHAR *getRegexpSource() { return CHECK_NULL(m_regexp); }
+       void matchArrayHousekeeper();
+       bool processMatch();
 };
 
 /**
@@ -184,7 +201,7 @@ private:
 #ifdef _WIN32
    TCHAR *m_marker;
 #endif
-       
+
        const TCHAR *checkContext(LogParserRule *rule);
        void trace(int level, const TCHAR *format, ...);
        bool matchLogRecord(bool hasAttributes, const TCHAR *source, UINT32 eventId, UINT32 level, const TCHAR *line, UINT32 objectId);
@@ -202,8 +219,8 @@ public:
        LogParser();
        LogParser(LogParser *src);
        ~LogParser();
-       
-       static ObjectArray<LogParser> *createFromXml(const char *xml, int xmlLen = -1, 
+
+       static ObjectArray<LogParser> *createFromXml(const char *xml, int xmlLen = -1,
                TCHAR *errorText = NULL, int errBufSize = 0, bool (*eventResolver)(const TCHAR *, UINT32 *) = NULL);
 
        void setFileName(const TCHAR *name);
@@ -224,7 +241,7 @@ public:
        void setProcessAllFlag(bool flag) { m_processAllRules = flag; }
        bool getProcessAllFlag() { return m_processAllRules; }
 
-       bool addRule(const TCHAR *regexp, UINT32 eventCode = 0, const TCHAR *eventName = NULL, int numParams = 0);
+       bool addRule(const TCHAR *regexp, UINT32 eventCode = 0, const TCHAR *eventName = NULL, int numParams = 0, int repeatInterval = 0, int repeatCount = 0, bool resetRepeat = true);
        bool addRule(LogParserRule *rule);
        void setCallback(LogParserCallback cb) { m_cb = cb; }
        void setUserArg(void *arg) { m_userArg = arg; }
index c0f70f9..ece4c09 100644 (file)
@@ -33,6 +33,15 @@ public class LogParserMatch
 
        @Attribute(required=false)
        private String invert = null;
+
+   @Attribute(required=false)
+   private Integer repeatCount = null;
+
+   @Attribute(required=false)
+   private Integer repeatInterval = null;
+
+   @Attribute(required=false)
+   private String reset = null;
        
        /**
         * Protected constructor for XML parser
@@ -45,12 +54,63 @@ public class LogParserMatch
         * @param event
         * @param parameterCount
         */
-       public LogParserMatch(String match, boolean invert)
+       public LogParserMatch(String match, boolean invert, Integer repeatCount, Integer repeatInterval, boolean reset)
        {
                this.match = match;
                setInvert(invert);
+               this.repeatCount = repeatCount;
+               this.repeatInterval = repeatInterval;
+               setReset(reset);
        }
 
+   /**
+    * @return the repeatCount
+    */
+   public Integer getRepeatCount()
+   {
+      return repeatCount == null ? 0 : repeatCount;
+   }
+
+   /**
+    * @param repeatCount the repeatCount to set
+    */
+   public void setRepeatCount(Integer repeatCount)
+   {
+      this.repeatCount = repeatCount;
+   }
+
+   /**
+    * @return the repeatInterval
+    */
+   public Integer getRepeatInterval()
+   {
+      return repeatInterval == null ? 0 : repeatInterval;
+   }
+
+   /**
+    * @param repeatInterval the repeatInterval to set
+    */
+   public void setRepeatInterval(Integer repeatInterval)
+   {
+      this.repeatInterval = repeatInterval;
+   }
+
+   /**
+    * @return the reset
+    */
+   public boolean getReset()
+   {
+      return reset == null ? true : LogParser.stringToBoolean(reset);
+   }
+
+   /**
+    * @param reset the reset to set
+    */
+   public void setReset(boolean reset)
+   {
+      this.reset = reset ? null : "false";
+   }
+
    /**
     * @return the match
     */
@@ -82,4 +142,36 @@ public class LogParserMatch
    {
       this.invert = LogParser.booleanToString(invert);
    }
+   
+   public int getTimeRagne()
+   {
+      if(repeatInterval == null)
+         return 0;
+      int interval = repeatInterval;
+      if((interval % 60) == 0)
+      {
+         interval = interval/60;
+         if((interval % 60) == 0)
+         {
+            interval = interval/60;
+         }
+      }
+      return interval;
+   }
+   
+   public int getTimeUnit()
+   {
+      if (repeatInterval == null)
+         return 0;
+      int unit = 0;
+      if((repeatInterval % 60) == 0)
+      {
+         unit++;
+         if((repeatInterval % 3600) == 0)
+         {
+            unit++;
+         }
+      }      
+      return unit;
+   }   
 }
index f8039aa..7c72cf5 100644 (file)
@@ -39,6 +39,7 @@ import org.eclipse.ui.forms.events.HyperlinkEvent;
 import org.eclipse.ui.forms.widgets.FormToolkit;
 import org.eclipse.ui.forms.widgets.ImageHyperlink;
 import org.netxms.client.NXCSession;
+import org.netxms.client.TimePeriod;
 import org.netxms.client.events.EventTemplate;
 import org.netxms.ui.eclipse.console.resources.SharedIcons;
 import org.netxms.ui.eclipse.eventmanager.widgets.EventSelector;
@@ -65,6 +66,10 @@ public class LogParserRuleEditor extends DashboardComposite
        private LogParserEditor editor;
        private LabeledText regexp;
        private Button checkboxInvert;
+   private Button checkboxReset;
+   private Spinner repeatCount;
+   private Spinner timeRange;
+   private Combo timeUnits;
        private LabeledText severity;
        private LabeledText facility;
        private LabeledText tag;
@@ -224,6 +229,78 @@ public class LogParserRuleEditor extends DashboardComposite
          }
       });          
                checkboxInvert.setSelection(rule.getMatch().getInvert());
+               
+      Composite matcherRepeatConf = new Composite(matcher, SWT.NONE);
+      toolkit.adapt(matcherRepeatConf);
+      gd = new GridData();
+      gd.horizontalAlignment = SWT.FILL;
+      gd.grabExcessHorizontalSpace = true;
+      gd.horizontalSpan = 2;      
+      matcherRepeatConf.setLayoutData(gd);
+      
+      layout = new GridLayout();
+      layout.numColumns = 3;
+      layout.marginWidth = 0;
+      matcherRepeatConf.setLayout(layout);      
+
+      final WidgetFactory factory = new WidgetFactory() {
+         @Override
+         public Control createControl(Composite parent, int style)
+         {
+            return new Spinner(parent, style);
+         }
+      };
+      
+      repeatCount = (Spinner)WidgetHelper.createLabeledControl(matcherRepeatConf, SWT.BORDER, factory, "Repeat count", WidgetHelper.DEFAULT_LAYOUT_DATA);
+      toolkit.adapt(repeatCount);
+      repeatCount.setMinimum(0);
+      repeatCount.setSelection(rule.getMatch().getRepeatCount());
+      repeatCount.addModifyListener(new ModifyListener() {
+         @Override
+         public void modifyText(ModifyEvent e)
+         {
+            editor.fireModifyListeners();
+         }
+      });
+      
+      gd = new GridData();
+      gd.horizontalAlignment = SWT.FILL;
+      gd.grabExcessHorizontalSpace = true;
+      repeatCount.setLayoutData(gd); 
+      Composite timeBackGroup = new Composite(matcherRepeatConf, SWT.NONE);
+      toolkit.adapt(timeBackGroup);
+      layout = new GridLayout();
+      layout.marginWidth = 0;
+      layout.marginHeight = 0;
+      layout.horizontalSpacing = WidgetHelper.OUTER_SPACING;
+      layout.numColumns = 2;
+      timeBackGroup.setLayout(layout);
+      gd = new GridData();
+      gd.horizontalAlignment = SWT.FILL;
+      gd.grabExcessHorizontalSpace = true;
+      timeBackGroup.setLayoutData(gd);
+      
+      timeRange = WidgetHelper.createLabeledSpinner(timeBackGroup, SWT.BORDER, "Repeat interval", 1, 10000, WidgetHelper.DEFAULT_LAYOUT_DATA);
+      timeRange.setSelection(rule.getMatch().getTimeRagne()); 
+      toolkit.adapt(timeRange);
+      
+      timeUnits = WidgetHelper.createLabeledCombo(timeBackGroup, SWT.READ_ONLY, "", WidgetHelper.DEFAULT_LAYOUT_DATA);
+      timeUnits.add("Seconds");
+      timeUnits.add("Minutes");
+      timeUnits.add("Hours");
+      timeUnits.select(rule.getMatch().getTimeUnit()); 
+      toolkit.adapt(timeUnits);
+      //time range
+
+      checkboxReset = toolkit.createButton(matcherRepeatConf, "Reset repeat count", SWT.CHECK);
+      checkboxReset.addSelectionListener(new SelectionAdapter() {
+         @Override
+         public void widgetSelected(SelectionEvent e)
+         {
+            editor.fireModifyListeners();
+         }
+      });          
+      checkboxReset.setSelection(rule.getMatch().getReset());
 
                severity = new LabeledText(area, SWT.NONE);
                toolkit.adapt(severity);
@@ -483,7 +560,9 @@ public class LogParserRuleEditor extends DashboardComposite
         */
        public void save()
        {
-               rule.setMatch(new LogParserMatch(regexp.getText(), checkboxInvert.getSelection()));
+               rule.setMatch(new LogParserMatch(regexp.getText(), checkboxInvert.getSelection(), intOrNull(repeatCount.getText()), 
+                                                Integer.parseInt(timeRange.getText()) *(timeUnits.getSelectionIndex() * 60), 
+                                                checkboxReset.getSelection()));
       rule.setFacilityOrId(intOrNull(facility.getText()));
       rule.setSeverityOrLevel(intOrNull(severity.getText()));
       rule.setTagOrSource(tag.getText());
index edab35b..0422ae9 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
 ** NetXMS - Network Management System
 ** Log Parsing Library
 ** Copyright (C) 2003-2013 Victor Kirhenshtein
@@ -74,6 +74,9 @@ struct XML_PARSER_STATE
        String macro;
        bool invertedRule;
        bool breakFlag;
+       int repeatCount;
+       int repeatInterval;
+       bool resetRepeat;
 
        XML_PARSER_STATE() : encodings(1, 1, true)
        {
@@ -83,6 +86,9 @@ struct XML_PARSER_STATE
           breakFlag = false;
           contextAction = CONTEXT_SET_AUTOMATIC;
           numEventParams = 0;
+          repeatCount = 0;
+          repeatInterval = 0;
+          resetRepeat = true;
        }
 };
 
@@ -207,9 +213,9 @@ bool LogParser::addRule(LogParserRule *rule)
 /**
  * Create and add rule
  */
-bool LogParser::addRule(const TCHAR *regexp, UINT32 eventCode, const TCHAR *eventName, int numParams)
+bool LogParser::addRule(const TCHAR *regexp, UINT32 eventCode, const TCHAR *eventName, int numParams, int repeatInterval, int repeatCount, bool resetRepeat)
 {
-       return addRule(new LogParserRule(this, regexp, eventCode, eventName, numParams));
+       return addRule(new LogParserRule(this, regexp, eventCode, eventName, numParams, repeatInterval, repeatCount, resetRepeat));
 }
 
 /**
@@ -218,20 +224,20 @@ bool LogParser::addRule(const TCHAR *regexp, UINT32 eventCode, const TCHAR *even
 const TCHAR *LogParser::checkContext(LogParserRule *rule)
 {
        const TCHAR *state;
-       
+
        if (rule->getContext() == NULL)
        {
                trace(5, _T("  rule has no context"));
                return s_states[CONTEXT_SET_MANUAL];
        }
-               
+
        state = m_contexts.get(rule->getContext());
        if (state == NULL)
        {
                trace(5, _T("  context '%s' inactive, rule should be skipped"), rule->getContext());
-               return NULL;    // Context inactive, don't use this rule                
+               return NULL;    // Context inactive, don't use this rule
        }
-       
+
        if (!_tcscmp(state, s_states[CONTEXT_CLEAR]))
        {
                trace(5, _T("  context '%s' inactive, rule should be skipped"), rule->getContext());
@@ -266,21 +272,21 @@ bool LogParser::matchLogRecord(bool hasAttributes, const TCHAR *source, UINT32 e
                if ((state = checkContext(m_rules[i])) != NULL)
                {
                        bool ruleMatched = hasAttributes ?
-                               m_rules[i]->matchEx(source, eventId, level, line, m_cb, objectId, m_userArg) : 
+                               m_rules[i]->matchEx(source, eventId, level, line, m_cb, objectId, m_userArg) :
                                m_rules[i]->match(line, m_cb, objectId, m_userArg);
                        if (ruleMatched)
                        {
                                trace(5, _T("rule %d \"%s\" matched"), i + 1, m_rules[i]->getDescription());
                                if (!matched)
                                        m_recordsMatched++;
-                               
+
                                // Update context
                                if (m_rules[i]->getContextToChange() != NULL)
                                {
                                        m_contexts.set(m_rules[i]->getContextToChange(), s_states[m_rules[i]->getContextAction()]);
                                        trace(5, _T("rule %d \"%s\": context %s set to %s"), i + 1, m_rules[i]->getDescription(), m_rules[i]->getContextToChange(), s_states[m_rules[i]->getContextAction()]);
                                }
-                               
+
                                // Set context of this rule to inactive if rule context mode is "automatic reset"
                                if (!_tcscmp(state, s_states[CONTEXT_SET_AUTOMATIC]))
                                {
@@ -463,6 +469,9 @@ static void StartElement(void *userData, const char *name, const char **attrs)
        {
                ps->state = XML_STATE_MATCH;
                ps->invertedRule = XMLGetAttrBoolean(attrs, "invert", false);
+               ps->resetRepeat = XMLGetAttrBoolean(attrs, "reset", true);
+               ps->repeatCount = XMLGetAttrInt(attrs, "repeatCount", 0);
+               ps->repeatInterval = XMLGetAttrInt(attrs, "repeatInterval", 0);
        }
        else if (!strcmp(name, "id") || !strcmp(name, "facility"))
        {
@@ -484,13 +493,13 @@ static void StartElement(void *userData, const char *name, const char **attrs)
        else if (!strcmp(name, "context"))
        {
                const char *action;
-               
+
                ps->state = XML_STATE_CONTEXT;
-               
+
                action = XMLGetAttr(attrs, "action");
                if (action == NULL)
                        action = "set";
-                       
+
                if (!strcmp(action, "set"))
                {
                        const char *mode;
@@ -502,11 +511,11 @@ static void StartElement(void *userData, const char *name, const char **attrs)
                        if (!strcmp(mode, "auto"))
                        {
                                ps->contextAction = CONTEXT_SET_AUTOMATIC;
-                       }                       
+                       }
                        else if (!strcmp(mode, "manual"))
                        {
                                ps->contextAction = CONTEXT_SET_MANUAL;
-                       }                       
+                       }
                        else
                        {
                                ps->errorText = _T("Invalid context reset mode");
@@ -580,9 +589,10 @@ static void EndElement(void *userData, const char *name)
                                eventName = (const TCHAR *)ps->event;
                        }
                }
+
                if (ps->regexp.isEmpty())
                        ps->regexp = _T(".*");
-               rule = new LogParserRule(ps->parser, (const TCHAR *)ps->regexp, eventCode, eventName, ps->numEventParams);
+               rule = new LogParserRule(ps->parser, (const TCHAR *)ps->regexp, eventCode, eventName, ps->numEventParams, ps->repeatInterval, ps->repeatCount, ps->resetRepeat);
                if (!ps->ruleContext.isEmpty())
                        rule->setContext(ps->ruleContext);
                if (!ps->context.isEmpty())
@@ -593,7 +603,7 @@ static void EndElement(void *userData, const char *name)
 
                if (!ps->description.isEmpty())
                        rule->setDescription(ps->description);
-               
+
                if (!ps->source.isEmpty())
                        rule->setSource(ps->source);
 
@@ -725,7 +735,7 @@ ObjectArray<LogParser> *LogParser::createFromXml(const char *xml, int xmlLen, TC
                        nx_strncpy(errorText, state.errorText, errBufSize);
        }
        else if (success)
-       { 
+       {
                parsers = new ObjectArray<LogParser>;
                if (state.files.size() > 0)
                {
index 9a50801..4e594d7 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
 ** NetXMS - Network Management System
 ** Log Parsing Library
 ** Copyright (C) 2003-2013 Victor Kirhenshtein
@@ -27,7 +27,8 @@
  * Constructor
  */
 LogParserRule::LogParserRule(LogParser *parser, const TCHAR *regexp, UINT32 eventCode, const TCHAR *eventName,
-                                                                         int numParams, const TCHAR *source, UINT32 level, UINT32 idStart, UINT32 idEnd)
+                                                                         int numParams, int repeatInterval, int repeatCount, bool resetRepeat, const TCHAR *source,
+                                                                         UINT32 level, UINT32 idStart, UINT32 idEnd)
 {
        String expandedRegexp;
 
@@ -49,6 +50,10 @@ LogParserRule::LogParserRule(LogParser *parser, const TCHAR *regexp, UINT32 even
        m_isInverted = FALSE;
        m_breakOnMatch = FALSE;
        m_description = NULL;
+   m_repeatInterval = repeatInterval;
+       m_repeatCount = repeatCount;
+       m_matchArray = new IntegerArray<time_t>();
+       m_resetRepeat = resetRepeat;
 }
 
 /**
@@ -69,10 +74,23 @@ LogParserRule::LogParserRule(LogParserRule *src, LogParser *parser)
        m_idEnd = src->m_idEnd;
        m_context = (src->m_context != NULL) ? _tcsdup(src->m_context) : NULL;
        m_contextAction = src->m_contextAction;
-       m_contextToChange = (src->m_contextToChange != NULL) ? _tcsdup(src->m_contextToChange) : NULL;;
+       m_contextToChange = (src->m_contextToChange != NULL) ? _tcsdup(src->m_contextToChange) : NULL;
        m_isInverted = src->m_isInverted;
        m_breakOnMatch = src->m_breakOnMatch;
-       m_description = (src->m_description != NULL) ? _tcsdup(src->m_description) : NULL;;
+       m_description = (src->m_description != NULL) ? _tcsdup(src->m_description) : NULL;
+   m_repeatInterval = src->m_repeatInterval;
+       m_repeatCount = src->m_repeatCount;
+       m_resetRepeat = src->m_resetRepeat;
+   if (src->m_matchArray != NULL)
+   {
+      m_matchArray = new IntegerArray<time_t>(src->m_matchArray->size(), 16);
+      for(int i = 0; i < src->m_matchArray->size(); i++)
+         m_matchArray->add(src->m_matchArray->get(i));
+   }
+   else
+   {
+      m_matchArray = new IntegerArray<time_t>();
+   }
 }
 
 /**
@@ -89,6 +107,7 @@ LogParserRule::~LogParserRule()
        safe_free(m_eventName);
        safe_free(m_context);
        safe_free(m_contextToChange);
+       delete m_matchArray;
 }
 
 /**
@@ -97,6 +116,7 @@ LogParserRule::~LogParserRule()
 bool LogParserRule::matchInternal(bool extMode, const TCHAR *source, UINT32 eventId, UINT32 level,
                                                                          const TCHAR *line, LogParserCallback cb, UINT32 objectId, void *userArg)
 {
+   matchArrayHousekeeper();
    if (extMode)
    {
           if (m_source != NULL)
@@ -131,7 +151,7 @@ bool LogParserRule::matchInternal(bool extMode, const TCHAR *source, UINT32 even
        if (m_isInverted)
        {
                m_parser->trace(6, _T("  negated matching against regexp %s"), m_regexp);
-               if (_tregexec(&m_preg, line, 0, NULL, 0) != 0)
+               if (_tregexec(&m_preg, line, 0, NULL, 0) != 0 && processMatch())
                {
                        m_parser->trace(6, _T("  matched"));
                        if ((cb != NULL) && ((m_eventCode != 0) || (m_eventName != NULL)))
@@ -142,7 +162,7 @@ bool LogParserRule::matchInternal(bool extMode, const TCHAR *source, UINT32 even
        else
        {
                m_parser->trace(6, _T("  matching against regexp %s"), m_regexp);
-               if (_tregexec(&m_preg, line, (m_numParams > 0) ? m_numParams + 1 : 0, m_pmatch, 0) == 0)
+               if (_tregexec(&m_preg, line, (m_numParams > 0) ? m_numParams + 1 : 0, m_pmatch, 0) == 0 && processMatch())
                {
                        m_parser->trace(6, _T("  matched"));
                        if ((cb != NULL) && ((m_eventCode != 0) || (m_eventName != NULL)))
@@ -174,13 +194,13 @@ bool LogParserRule::matchInternal(bool extMode, const TCHAR *source, UINT32 even
                                }
 
                                cb(m_eventCode, m_eventName, line, source, eventId, level, m_numParams, params, objectId, userArg);
-                               
+
                                for(i = 0; i < m_numParams; i++)
                                        free(params[i]);
 #if !HAVE_ALLOCA
             free(params);
 #endif
-                       }               
+                       }
                        return true;
                }
        }
@@ -248,3 +268,29 @@ void LogParserRule::expandMacros(const TCHAR *regexp, String &out)
        }
        out.append(prev, (size_t)(curr - prev));
 }
+
+void LogParserRule::matchArrayHousekeeper()
+{
+   if(m_matchArray->size() == 0 || m_repeatCount == 0 || m_repeatInterval == 0)
+      return;
+
+   time_t now = time(NULL);
+   for(int i = 0; i < m_matchArray->size(); i++)
+   {
+      if(m_matchArray->get(i) < (now - m_repeatInterval))
+         m_matchArray->remove(i);
+      else
+         break;
+   }
+}
+
+bool LogParserRule::processMatch()
+{
+   if(m_repeatCount == 0 || m_repeatInterval == 0)
+      return true;
+
+   m_matchArray->add(time(NULL));
+   bool match = m_matchArray->size() >= m_repeatCount;
+   if(m_resetRepeat && match) m_matchArray->clear();
+   return match;
+}
index c0f70f9..ece4c09 100644 (file)
@@ -33,6 +33,15 @@ public class LogParserMatch
 
        @Attribute(required=false)
        private String invert = null;
+
+   @Attribute(required=false)
+   private Integer repeatCount = null;
+
+   @Attribute(required=false)
+   private Integer repeatInterval = null;
+
+   @Attribute(required=false)
+   private String reset = null;
        
        /**
         * Protected constructor for XML parser
@@ -45,12 +54,63 @@ public class LogParserMatch
         * @param event
         * @param parameterCount
         */
-       public LogParserMatch(String match, boolean invert)
+       public LogParserMatch(String match, boolean invert, Integer repeatCount, Integer repeatInterval, boolean reset)
        {
                this.match = match;
                setInvert(invert);
+               this.repeatCount = repeatCount;
+               this.repeatInterval = repeatInterval;
+               setReset(reset);
        }
 
+   /**
+    * @return the repeatCount
+    */
+   public Integer getRepeatCount()
+   {
+      return repeatCount == null ? 0 : repeatCount;
+   }
+
+   /**
+    * @param repeatCount the repeatCount to set
+    */
+   public void setRepeatCount(Integer repeatCount)
+   {
+      this.repeatCount = repeatCount;
+   }
+
+   /**
+    * @return the repeatInterval
+    */
+   public Integer getRepeatInterval()
+   {
+      return repeatInterval == null ? 0 : repeatInterval;
+   }
+
+   /**
+    * @param repeatInterval the repeatInterval to set
+    */
+   public void setRepeatInterval(Integer repeatInterval)
+   {
+      this.repeatInterval = repeatInterval;
+   }
+
+   /**
+    * @return the reset
+    */
+   public boolean getReset()
+   {
+      return reset == null ? true : LogParser.stringToBoolean(reset);
+   }
+
+   /**
+    * @param reset the reset to set
+    */
+   public void setReset(boolean reset)
+   {
+      this.reset = reset ? null : "false";
+   }
+
    /**
     * @return the match
     */
@@ -82,4 +142,36 @@ public class LogParserMatch
    {
       this.invert = LogParser.booleanToString(invert);
    }
+   
+   public int getTimeRagne()
+   {
+      if(repeatInterval == null)
+         return 0;
+      int interval = repeatInterval;
+      if((interval % 60) == 0)
+      {
+         interval = interval/60;
+         if((interval % 60) == 0)
+         {
+            interval = interval/60;
+         }
+      }
+      return interval;
+   }
+   
+   public int getTimeUnit()
+   {
+      if (repeatInterval == null)
+         return 0;
+      int unit = 0;
+      if((repeatInterval % 60) == 0)
+      {
+         unit++;
+         if((repeatInterval % 3600) == 0)
+         {
+            unit++;
+         }
+      }      
+      return unit;
+   }   
 }
index f8039aa..7c72cf5 100644 (file)
@@ -39,6 +39,7 @@ import org.eclipse.ui.forms.events.HyperlinkEvent;
 import org.eclipse.ui.forms.widgets.FormToolkit;
 import org.eclipse.ui.forms.widgets.ImageHyperlink;
 import org.netxms.client.NXCSession;
+import org.netxms.client.TimePeriod;
 import org.netxms.client.events.EventTemplate;
 import org.netxms.ui.eclipse.console.resources.SharedIcons;
 import org.netxms.ui.eclipse.eventmanager.widgets.EventSelector;
@@ -65,6 +66,10 @@ public class LogParserRuleEditor extends DashboardComposite
        private LogParserEditor editor;
        private LabeledText regexp;
        private Button checkboxInvert;
+   private Button checkboxReset;
+   private Spinner repeatCount;
+   private Spinner timeRange;
+   private Combo timeUnits;
        private LabeledText severity;
        private LabeledText facility;
        private LabeledText tag;
@@ -224,6 +229,78 @@ public class LogParserRuleEditor extends DashboardComposite
          }
       });          
                checkboxInvert.setSelection(rule.getMatch().getInvert());
+               
+      Composite matcherRepeatConf = new Composite(matcher, SWT.NONE);
+      toolkit.adapt(matcherRepeatConf);
+      gd = new GridData();
+      gd.horizontalAlignment = SWT.FILL;
+      gd.grabExcessHorizontalSpace = true;
+      gd.horizontalSpan = 2;      
+      matcherRepeatConf.setLayoutData(gd);
+      
+      layout = new GridLayout();
+      layout.numColumns = 3;
+      layout.marginWidth = 0;
+      matcherRepeatConf.setLayout(layout);      
+
+      final WidgetFactory factory = new WidgetFactory() {
+         @Override
+         public Control createControl(Composite parent, int style)
+         {
+            return new Spinner(parent, style);
+         }
+      };
+      
+      repeatCount = (Spinner)WidgetHelper.createLabeledControl(matcherRepeatConf, SWT.BORDER, factory, "Repeat count", WidgetHelper.DEFAULT_LAYOUT_DATA);
+      toolkit.adapt(repeatCount);
+      repeatCount.setMinimum(0);
+      repeatCount.setSelection(rule.getMatch().getRepeatCount());
+      repeatCount.addModifyListener(new ModifyListener() {
+         @Override
+         public void modifyText(ModifyEvent e)
+         {
+            editor.fireModifyListeners();
+         }
+      });
+      
+      gd = new GridData();
+      gd.horizontalAlignment = SWT.FILL;
+      gd.grabExcessHorizontalSpace = true;
+      repeatCount.setLayoutData(gd); 
+      Composite timeBackGroup = new Composite(matcherRepeatConf, SWT.NONE);
+      toolkit.adapt(timeBackGroup);
+      layout = new GridLayout();
+      layout.marginWidth = 0;
+      layout.marginHeight = 0;
+      layout.horizontalSpacing = WidgetHelper.OUTER_SPACING;
+      layout.numColumns = 2;
+      timeBackGroup.setLayout(layout);
+      gd = new GridData();
+      gd.horizontalAlignment = SWT.FILL;
+      gd.grabExcessHorizontalSpace = true;
+      timeBackGroup.setLayoutData(gd);
+      
+      timeRange = WidgetHelper.createLabeledSpinner(timeBackGroup, SWT.BORDER, "Repeat interval", 1, 10000, WidgetHelper.DEFAULT_LAYOUT_DATA);
+      timeRange.setSelection(rule.getMatch().getTimeRagne()); 
+      toolkit.adapt(timeRange);
+      
+      timeUnits = WidgetHelper.createLabeledCombo(timeBackGroup, SWT.READ_ONLY, "", WidgetHelper.DEFAULT_LAYOUT_DATA);
+      timeUnits.add("Seconds");
+      timeUnits.add("Minutes");
+      timeUnits.add("Hours");
+      timeUnits.select(rule.getMatch().getTimeUnit()); 
+      toolkit.adapt(timeUnits);
+      //time range
+
+      checkboxReset = toolkit.createButton(matcherRepeatConf, "Reset repeat count", SWT.CHECK);
+      checkboxReset.addSelectionListener(new SelectionAdapter() {
+         @Override
+         public void widgetSelected(SelectionEvent e)
+         {
+            editor.fireModifyListeners();
+         }
+      });          
+      checkboxReset.setSelection(rule.getMatch().getReset());
 
                severity = new LabeledText(area, SWT.NONE);
                toolkit.adapt(severity);
@@ -483,7 +560,9 @@ public class LogParserRuleEditor extends DashboardComposite
         */
        public void save()
        {
-               rule.setMatch(new LogParserMatch(regexp.getText(), checkboxInvert.getSelection()));
+               rule.setMatch(new LogParserMatch(regexp.getText(), checkboxInvert.getSelection(), intOrNull(repeatCount.getText()), 
+                                                Integer.parseInt(timeRange.getText()) *(timeUnits.getSelectionIndex() * 60), 
+                                                checkboxReset.getSelection()));
       rule.setFacilityOrId(intOrNull(facility.getText()));
       rule.setSeverityOrLevel(intOrNull(severity.getText()));
       rule.setTagOrSource(tag.getText());