minor debug tags refactoring; implemented call nxlog_get_all_debug_tags; server conso...
authorVictor Kirhenshtein <victor@netxms.org>
Fri, 20 Oct 2017 18:24:52 +0000 (21:24 +0300)
committerVictor Kirhenshtein <victor@netxms.org>
Fri, 20 Oct 2017 18:25:03 +0000 (21:25 +0300)
include/nms_util.h
src/agent/core/nxagentd.cpp
src/libnetxms/debug_tag_tree.cpp
src/libnetxms/debug_tag_tree.h
src/libnetxms/log.cpp
src/server/core/config.cpp
src/server/core/console.cpp
tests/include/testtools.h
tests/test-libnetxms/test-libnetxms.cpp

index 279ea82..88b1ad7 100644 (file)
@@ -2468,6 +2468,27 @@ void LIBNETXMS_EXPORTABLE nxlog_set_debug_level_tag(const TCHAR *tags, int level
 int LIBNETXMS_EXPORTABLE nxlog_get_debug_level();
 int LIBNETXMS_EXPORTABLE nxlog_get_debug_level_tag(const TCHAR *tag);
 
+#ifdef __cplusplus
+
+/**
+ * Debug tag information
+ */
+struct DebugTagInfo
+{
+   TCHAR tag[64];
+   int level;
+
+   DebugTagInfo(const TCHAR *_tag, int _level)
+   {
+      _tcslcpy(tag, _tag, 64);
+      level = _level;
+   }
+};
+
+ObjectArray<DebugTagInfo> LIBNETXMS_EXPORTABLE *nxlog_get_all_debug_tags();
+
+#endif   /* __cplusplus */
+
 typedef void (*NxLogDebugWriter)(const TCHAR *, const TCHAR *);
 void LIBNETXMS_EXPORTABLE nxlog_set_debug_writer(NxLogDebugWriter writer);
 
index d71c862..08d421f 100644 (file)
@@ -759,30 +759,25 @@ BOOL Initialize()
 
    if (s_debugTags != NULL)
    {
-      TCHAR const *ptr;
-      int numTags;
-      TCHAR **tagList = SplitString(s_debugTags, _T(','), &numTags);
+      int count;
+      TCHAR **tagList = SplitString(s_debugTags, _T(','), &count);
       if (tagList != NULL)
       {
-         TCHAR tagBuffer[254], lvlBuffer[2];
-
-         for(int i = 0; i < numTags; i++)
+         for(int i = 0; i < count; i++)
          {
-            ptr = ExtractWord(tagList[i], tagBuffer);
-            if (tagBuffer[0] != 0)
+            TCHAR *level = _tcschr(tagList[i], _T(':'));
+            if (level != NULL)
             {
-               ExtractWord(ptr, lvlBuffer);
-               if (lvlBuffer[0] != 0)
-               {
-                  int lvl = _tcstol(lvlBuffer, NULL, 0);
-                  nxlog_set_debug_level_tag(tagBuffer, lvl);
-               }
+               *level = 0;
+               level++;
+               Trim(tagList[i]);
+               nxlog_set_debug_level_tag(tagList[i], _tcstol(level, NULL, 0));
             }
             free(tagList[i]);
          }
+         free(tagList);
       }
       free(s_debugTags);
-      free(tagList);
    }
 
        nxlog_set_debug_level(s_debugLevel);
index f641ab4..e4c9e08 100644 (file)
@@ -29,9 +29,9 @@ DebugTagTreeNode::DebugTagTreeNode()
 {
    m_value = NULL;
    m_direct = false;
-   m_directLvL = 0;
-   m_asterisk = false;
-   m_asteriskLvL = 0;
+   m_directLevel = 0;
+   m_wildcard = true;
+   m_wildcardLevel = 0;
    m_children = new StringObjectMap<DebugTagTreeNode>(true);
 }
 
@@ -44,36 +44,36 @@ DebugTagTreeNode::DebugTagTreeNode(const TCHAR *value, size_t len)
    memcpy(m_value, value, sizeof(TCHAR) * (int)len);
    m_value[len] = 0;
    m_direct = false;
-   m_directLvL = 0;
-   m_asterisk = false;
-   m_asteriskLvL = 0;
+   m_directLevel = 0;
+   m_wildcard = false;
+   m_wildcardLevel = 0;
    m_children = new StringObjectMap<DebugTagTreeNode>(true);
 }
 
 /**
  * Get debug LvL from tree node, returns longest match (recursive)
  */
-int DebugTagTreeNode::getDebugLvl(const TCHAR *tags)
+int DebugTagTreeNode::getDebugLevel(const TCHAR *tag) const
 {
-   if (tags == NULL)
+   if (tag == NULL)
    {
       if (m_direct)
-         return m_directLvL;
-      if (m_asterisk)
-         return m_asteriskLvL;
+         return m_directLevel;
+      if (m_wildcard)
+         return m_wildcardLevel;
       return -1;
    }
 
    int result = -1;
-   const TCHAR *ptr = _tcschr(tags, _T('.'));
-   size_t len = (ptr == NULL) ? _tcslen(tags) : (ptr - tags);
+   const TCHAR *ptr = _tcschr(tag, _T('.'));
+   size_t len = (ptr == NULL) ? _tcslen(tag) : (ptr - tag);
 
-   DebugTagTreeNode *child = m_children->get(tags, len);
+   DebugTagTreeNode *child = m_children->get(tag, len);
    if (child != NULL)
-      result = child->getDebugLvl((ptr != NULL) ? ptr + 1 : NULL);
+      result = child->getDebugLevel((ptr != NULL) ? ptr + 1 : NULL);
 
-   if (result == -1 && m_asterisk)
-      return m_asteriskLvL;
+   if ((result == -1) && m_wildcard)
+      return m_wildcardLevel;
 
    return result;
 }
@@ -81,87 +81,118 @@ int DebugTagTreeNode::getDebugLvl(const TCHAR *tags)
 /**
  * Add new tree node to its children and grand children etc... (recursive)
  */
-void DebugTagTreeNode::add(const TCHAR *tags, UINT32 lvl)
+void DebugTagTreeNode::add(const TCHAR *tag, int level)
 {
-   if (tags != NULL && !_tcscmp(tags, _T("*")))
+   if ((tag != NULL) && !_tcscmp(tag, _T("*")))
    {
-      if (!m_asterisk)
-         m_asterisk = true;
-      m_asteriskLvL = lvl;
+      if (!m_wildcard)
+         m_wildcard = true;
+      m_wildcardLevel = level;
       return;
    }
 
-   const TCHAR *ptr = (tags == NULL) ? NULL : _tcschr(tags, _T('.'));
+   const TCHAR *ptr = (tag == NULL) ? NULL : _tcschr(tag, _T('.'));
 
-   if (tags == NULL)
+   if (tag == NULL)
    {
       if (!m_direct)
          m_direct = true;
-      m_directLvL = lvl;
+      m_directLevel = level;
       return;
    }
 
-   size_t len = (ptr == NULL) ? _tcslen(tags) : (ptr - tags);
-   DebugTagTreeNode *child = m_children->get(tags, len);
+   size_t len = (ptr == NULL) ? _tcslen(tag) : (ptr - tag);
+   DebugTagTreeNode *child = m_children->get(tag, len);
    if (child != NULL)
-      child->add((ptr != NULL) ? ptr + 1 : NULL, lvl);
+      child->add((ptr != NULL) ? ptr + 1 : NULL, level);
    else
    {
-      child = new DebugTagTreeNode(tags, len);
+      child = new DebugTagTreeNode(tag, len);
       m_children->set(child->getValue(), child);
-      child->add((ptr != NULL) ? ptr + 1 : NULL, lvl);
+      child->add((ptr != NULL) ? ptr + 1 : NULL, level);
    }
 }
 
 /**
  * Remove entry from child list (recursive)
  */
-bool DebugTagTreeNode::remove(const TCHAR *tags)
+bool DebugTagTreeNode::remove(const TCHAR *tag)
 {
-   const TCHAR *ptr = NULL;
-
-   if (tags != NULL)
+   if (tag != NULL)
    {
-      ptr = _tcschr(tags, _T('.'));
-      size_t len = (ptr == NULL) ? _tcslen(tags) : (ptr - tags);
-
-      DebugTagTreeNode *child = m_children->get(tags, len);
-      if (child != NULL && child->remove((ptr != NULL) ? ptr + 1 : NULL))
-         m_children->remove(child->getValue());
+      if (!_tcscmp(tag, _T("*")))
+      {
+         m_wildcard = false;
+         m_wildcardLevel = 0;
+      }
+      else
+      {
+         const TCHAR *ptr = _tcschr(tag, _T('.'));
+         size_t len = (ptr == NULL) ? _tcslen(tag) : (ptr - tag);
+
+         DebugTagTreeNode *child = m_children->get(tag, len);
+         if (child != NULL && child->remove((ptr != NULL) ? ptr + 1 : NULL))
+         {
+            m_children->remove(child->getValue());
+         }
+      }
    }
-
-   if (tags != NULL && !_tcscmp(tags, _T("*")))
-   {
-      m_asterisk = false;
-      m_asteriskLvL = 0;
-   }
-   else if (tags == NULL)
+   else
    {
       m_direct = false;
-      m_directLvL = 0;
+      m_directLevel = 0;
    }
 
-   if ((m_children->size() == 0) && !m_asterisk)
-      return true;
+   return (m_children->size() == 0) && !m_wildcard;
+}
 
-   return false;
+/**
+ * Get all tags under this node
+ */
+void DebugTagTreeNode::getAllTags(const TCHAR *prefix, ObjectArray<DebugTagInfo> *tags) const
+{
+   TCHAR name[1024];
+   _tcslcpy(name, prefix, 1024);
+   if (*prefix != 0)
+      _tcslcat(name, _T("."), 1024);
+   size_t l = _tcslen(name);
+   StructArray<KeyValuePair> *c = m_children->toArray();
+   for(int i = 0; i < c->size(); i++)
+   {
+      KeyValuePair *p = c->get(i);
+      _tcslcpy(&name[l], p->key, 1024 - l);
+      const DebugTagTreeNode *n = static_cast<const DebugTagTreeNode*>(p->value);
+      if (n->m_direct)
+      {
+         tags->add(new DebugTagInfo(name, n->m_directLevel));
+      }
+      if (n->m_wildcard)
+      {
+         _tcslcat(name, _T(".*"), 1024);
+         tags->add(new DebugTagInfo(name, n->m_wildcardLevel));
+         _tcslcpy(&name[l], p->key, 1024 - l);
+      }
+      n->getAllTags(name, tags);
+   }
 }
 
 /**
  * Get debug LvL from tree, returns longest match (recursive)
  */
-UINT32 DebugTagTree::getDebugLvl(const TCHAR *tags)
+int DebugTagTree::getDebugLevel(const TCHAR *tags)
 {
    InterlockedIncrement(&m_readerCount);
    int result;
    if (tags == NULL)
-       result = getRootDebugLvl();
-    else
-    {
-       result = m_root->getDebugLvl(tags);
-       if (result == -1)
-          result = 0;
-    }
+   {
+       result = m_root->getWildcardDebugLevel();
+   }
+   else
+   {
+      result = m_root->getDebugLevel(tags);
+      if (result == -1)
+         result = 0;
+   }
    InterlockedDecrement(&m_readerCount);
    return result;
 }
@@ -169,42 +200,22 @@ UINT32 DebugTagTree::getDebugLvl(const TCHAR *tags)
 /**
  * Get main debug level
  */
-UINT32 DebugTagTree::getRootDebugLvl()
+int DebugTagTree::getRootDebugLevel()
 {
    InterlockedIncrement(&m_readerCount);
-   UINT32 level = m_root->m_asteriskLvL;
+   int level = m_root->getWildcardDebugLevel();
    InterlockedDecrement(&m_readerCount);
    return level;
 }
 
 /**
- * Set debug lvl for root node
+ * Get all configured tags
  */
-void DebugTagTree::setRootDebugLvl(UINT32 lvl)
+ObjectArray<DebugTagInfo> *DebugTagTree::getAllTags()
 {
-   m_root->m_asteriskLvL = lvl;
-   if (!m_root->m_asterisk)
-      m_root->m_asterisk = true;
-}
-
-/**
- * Add new entry to the tree (recursive)
- */
-void DebugTagTree::add(const TCHAR *tags, UINT32 lvl)
-{
-   if (tags == NULL)
-      setRootDebugLvl(lvl);
-   else
-      m_root->add(tags, lvl);
-}
-
-/**
- * Remove entry from the tree
- */
-void DebugTagTree::remove(const TCHAR *tags)
-{
-   if (tags == NULL)
-       setRootDebugLvl(0);
-    else
-       m_root->remove(tags);
+   ObjectArray<DebugTagInfo> *tags = new ObjectArray<DebugTagInfo>(64, 64, true);
+   InterlockedIncrement(&m_readerCount);
+   m_root->getAllTags(_T(""), tags);
+   InterlockedDecrement(&m_readerCount);
+   return tags;
 }
index 23de90e..e714e50 100644 (file)
 **
 **
 */
+
+#ifndef _debug_tag_tree_h_
+#define _debug_tag_tree_h_
+
 #include <nms_util.h>
 
+/**
+ * Node of debug tag tree
+ */
 class DebugTagTreeNode
 {
-   friend class DebugTagTree;
-
 private:
    TCHAR *m_value;
    StringObjectMap<DebugTagTreeNode> *m_children;
    bool m_direct;
-   bool m_asterisk;
-   UINT32 m_directLvL;
-   UINT32 m_asteriskLvL;
+   bool m_wildcard;
+   int m_directLevel;
+   int m_wildcardLevel;
 
+public:
    DebugTagTreeNode();
    DebugTagTreeNode(const TCHAR *value, size_t len);
+   ~DebugTagTreeNode() { free(m_value); delete m_children; }
 
-   int getDebugLvl(const TCHAR *tags);
+   int getDebugLevel(const TCHAR *tag) const;
+   int getWildcardDebugLevel() const { return m_wildcardLevel; }
    const TCHAR *getValue() const { return m_value; }
-   void add(const TCHAR *tags, UINT32 lvl);
-   bool remove(const TCHAR *tags);
+   void getAllTags(const TCHAR *prefix, ObjectArray<DebugTagInfo> *tags) const;
 
-public:
-   ~DebugTagTreeNode() { free(m_value); delete(m_children); }
+   void add(const TCHAR *tag, int level);
+   bool remove(const TCHAR *tag);
+   void setWildcardDebugLevel(int level) { m_wildcardLevel = level; }
 };
 
+/**
+ * Debug tag tree
+ */
 class DebugTagTree
 {
 private:
@@ -55,14 +66,16 @@ private:
 
 public:
    DebugTagTree() { m_root = new DebugTagTreeNode(); m_readerCount = 0; }
-   ~DebugTagTree() { delete(m_root); }
-
-   void add(const TCHAR *tags, UINT32 lvl);
-   void remove(const TCHAR *tags);
-   UINT32 getDebugLvl(const TCHAR *tags);
+   ~DebugTagTree() { delete m_root; }
 
-   void setRootDebugLvl(UINT32 lvl);
-   UINT32 getRootDebugLvl();
+   int getDebugLevel(const TCHAR *tags);
+   int getRootDebugLevel();
+   int getReaderCount() { return (int)m_readerCount; }
+   ObjectArray<DebugTagInfo> *getAllTags();
 
-   INT32 getReaderCount() { return (INT32)m_readerCount; }
+   void add(const TCHAR *tag, int level) { m_root->add(tag, level); }
+   void remove(const TCHAR *tag) { m_root->remove(tag); }
+   void setRootDebugLevel(int level) { m_root->setWildcardDebugLevel(level); }
 };
+
+#endif
index 34c8204..ac7358f 100644 (file)
@@ -84,9 +84,9 @@ void LIBNETXMS_EXPORTABLE nxlog_set_debug_level(int level)
    if ((level >= 0) && (level <= 9))
    {
       MutexLock(m_mutexDebugTagTreeWrite);
-      tagTreeSecondary->setRootDebugLvl(level); // Update the secondary tree
+      tagTreeSecondary->setRootDebugLevel(level); // Update the secondary tree
       SwapAndWait();
-      tagTreeSecondary->setRootDebugLvl(level); // Update the previously active tree
+      tagTreeSecondary->setRootDebugLevel(level); // Update the previously active tree
       MutexUnlock(m_mutexDebugTagTreeWrite);
    }
 }
@@ -96,10 +96,14 @@ void LIBNETXMS_EXPORTABLE nxlog_set_debug_level(int level)
  */
 void LIBNETXMS_EXPORTABLE nxlog_set_debug_level_tag(const TCHAR *tag, int level)
 {
-   if (tag != NULL)
+   if ((tag == NULL) || !_tcscmp(tag, _T("*")))
+   {
+      nxlog_set_debug_level(level);
+   }
+   else
    {
       MutexLock(m_mutexDebugTagTreeWrite);
-      if((level >= 0) && (level <= 9))
+      if ((level >= 0) && (level <= 9))
       {
          tagTreeSecondary->add(tag, level);
          SwapAndWait();
@@ -120,7 +124,7 @@ void LIBNETXMS_EXPORTABLE nxlog_set_debug_level_tag(const TCHAR *tag, int level)
  */
 int LIBNETXMS_EXPORTABLE nxlog_get_debug_level()
 {
-   return tagTreeActive->getRootDebugLvl();
+   return tagTreeActive->getRootDebugLevel();
 }
 
 /**
@@ -128,7 +132,15 @@ int LIBNETXMS_EXPORTABLE nxlog_get_debug_level()
  */
 int LIBNETXMS_EXPORTABLE nxlog_get_debug_level_tag(const TCHAR *tag)
 {
-   return tagTreeActive->getDebugLvl(tag);
+   return tagTreeActive->getDebugLevel(tag);
+}
+
+/**
+ * Get all configured debug tags
+ */
+ObjectArray<DebugTagInfo> LIBNETXMS_EXPORTABLE *nxlog_get_all_debug_tags()
+{
+   return tagTreeActive->getAllTags();
 }
 
 /**
index 301f325..53bc6c2 100644 (file)
@@ -184,30 +184,25 @@ stop_search:
 
    if (s_debugTags != NULL)
    {
-      TCHAR const *ptr;
-      int numTags;
-      TCHAR **tagList = SplitString(s_debugTags, _T(','), &numTags);
+      int count;
+      TCHAR **tagList = SplitString(s_debugTags, _T(','), &count);
       if (tagList != NULL)
       {
-         TCHAR tagBuffer[254], lvlBuffer[2];
-
-         for(int i = 0; i < numTags; i++)
+         for(int i = 0; i < count; i++)
          {
-            ptr = ExtractWord(tagList[i], tagBuffer);
-            if (tagBuffer[0] != 0)
+            TCHAR *level = _tcschr(tagList[i], _T(':'));
+            if (level != NULL)
             {
-               ExtractWord(ptr, lvlBuffer);
-               if (lvlBuffer[0] != 0)
-               {
-                  int lvl = _tcstol(lvlBuffer, NULL, 0);
-                  nxlog_set_debug_level_tag(tagBuffer, lvl);
-               }
+               *level = 0;
+               level++;
+               Trim(tagList[i]);
+               nxlog_set_debug_level_tag(tagList[i], _tcstol(level, NULL, 0));
             }
             free(tagList[i]);
          }
+         free(tagList);
       }
       free(s_debugTags);
-      free(tagList);
    }
 
        // Decrypt password
index 76b095a..bc1e6ac 100644 (file)
@@ -46,19 +46,17 @@ UINT32 UnbindAgentTunnel(UINT32 nodeId);
 /**
  * Compare given string to command template with abbreviation possibility
  */
-static bool IsCommand(const TCHAR *cmdTemplate, TCHAR *pszString, int iMinChars)
+static bool IsCommand(const TCHAR *cmdTemplate, const TCHAR *str, int minChars)
 {
-   int i;
-
-   // Convert given string to uppercase
-   _tcsupr(pszString);
+   TCHAR temp[256];
+   _tcslcpy(temp, str, 256);
+   _tcsupr(temp);
 
-   for(i = 0; pszString[i] != 0; i++)
-      if (pszString[i] != cmdTemplate[i])
+   int i;
+   for(i = 0; temp[i] != 0; i++)
+      if (temp[i] != cmdTemplate[i])
          return false;
-   if (i < iMinChars)
-      return false;
-   return true;
+   return i >= minChars;
 }
 
 /**
@@ -95,6 +93,14 @@ static void DumpIndex(CONSOLE_CTX pCtx, ObjectIndex *index)
 }
 
 /**
+ * Compare debug tags for alphabetical sorting
+ */
+static int CompareDebugTags(const DebugTagInfo **t1, const DebugTagInfo **t2)
+{
+   return _tcsicmp((*t1)->tag, (*t2)->tag);
+}
+
+/**
  * Process command entered from command line in standalone mode
  * Return TRUE if command was _T("down")
  */
@@ -122,42 +128,62 @@ int ProcessConsoleCommand(const TCHAR *pszCmdLine, CONSOLE_CTX pCtx)
    else if (IsCommand(_T("DEBUG"), szBuffer, 2))
    {
       StringList *list = ParseCommandLine(pArg);
-
-      int level;
-      if (list->size() == 1)
-         level = (int)_tcstol(list->get(0), &eptr, 0);
-      else if (list->size() == 2)
-         level = (int)_tcstol(list->get(1), &eptr, 0);
-
-      if ((*eptr == 0) && (level >= -1) && (level <= 9))
+      if (list->size() == 0)
       {
-         if (list->size() == 1)
-         {
-            nxlog_set_debug_level(level);
-            ConsolePrintf(pCtx, (level == 0) ? _T("Debug mode turned off\n") : _T("Debug level set to %d\n"), level);
-         }
-         else if (list->size() == 2)
+         ConsolePrintf(pCtx, _T("Current debug levels:\n"));
+         ConsolePrintf(pCtx, _T("   DEFAULT              = %d\n"), nxlog_get_debug_level());
+
+         ObjectArray<DebugTagInfo> *tags = nxlog_get_all_debug_tags();
+         tags->sort(CompareDebugTags);
+         for(int i = 0; i < tags->size(); i++)
          {
-            nxlog_set_debug_level_tag(list->get(0), level);
-            if (level == -1)
-               ConsolePrintf(pCtx,  _T("Debug tag <%s> removed\n"), list->get(0));
-            else
-               ConsolePrintf(pCtx,  _T("Debug level for tag <%s> set to %d\n"), list->get(0), level);
+            const DebugTagInfo *t = tags->get(i);
+            ConsolePrintf(pCtx, _T("   %-20s = %d\n"), t->tag, t->level);
          }
-      }
-      else if (IsCommand(_T("OFF"), szBuffer, 2))
-      {
-         nxlog_set_debug_level(0);
-         ConsoleWrite(pCtx, _T("Debug mode turned off\n"));
+         delete tags;
       }
       else
       {
-         if (szBuffer[0] == 0)
-            ConsoleWrite(pCtx, _T("ERROR: Missing argument\n\n"));
+         int index = (list->size() == 1) ? 0 : 1;
+         int level;
+         if (!_tcsicmp(list->get(index), _T("OFF")))
+         {
+            level = 0;
+         }
+         else if (IsCommand(_T("DEFAULT"), list->get(index), 3))
+         {
+            level = -1;
+         }
+         else
+         {
+            level = (int)_tcstol(list->get(index), &eptr, 0);
+            if (*eptr != 0)
+               level = -99;   // mark as invalid
+         }
+         if ((level >= -1) && (level <= 9))
+         {
+            if (list->size() == 1)
+            {
+               if (level < 0)
+                  level = 0;
+               nxlog_set_debug_level(level);
+               ConsolePrintf(pCtx, (level == 0) ? _T("Debug mode turned off\n") : _T("Debug level set to %d\n"), level);
+            }
+            else if (list->size() == 2)
+            {
+               nxlog_set_debug_level_tag(list->get(0), level);
+               if (level == -1)
+                  ConsolePrintf(pCtx,  _T("Debug level for tag \"%s\" set to default\n"), list->get(0));
+               else
+                  ConsolePrintf(pCtx,  _T("Debug level for tag \"%s\" set to %d\n"), list->get(0), level);
+            }
+         }
          else
+         {
             ConsoleWrite(pCtx, _T("ERROR: Invalid debug level\n\n"));
+         }
       }
-      delete(list);
+      delete list;
    }
    else if (IsCommand(_T("DOWN"), szBuffer, 4))
    {
index e34f33a..092bdcd 100644 (file)
 #define AssertEquals(x, y) Assert((x) == (y))
 
 /**
+ * Assert that two values are not equal
+ */
+#define AssertNotEquals(x, y) Assert((x) != (y))
+
+/**
  * Assert that value is TRUE
  */
 #define AssertTrue(x) Assert((x))
index 4ae19d0..0ffa4ae 100644 (file)
@@ -1083,7 +1083,7 @@ static void TestDebugTags()
    AssertEquals(nxlog_get_debug_level_tag(_T("server.db")), 0);
    EndTest();
 
-   StartTest(_T("Debug Tags"));
+   StartTest(_T("Debug tags: set debug level"));
    nxlog_set_debug_level_tag(_T("*"), 1);
    nxlog_set_debug_level_tag(_T("db"), 2);
    nxlog_set_debug_level_tag(_T("db.*"), 3);
@@ -1095,30 +1095,41 @@ static void TestDebugTags()
    nxlog_set_debug_level_tag(_T("db.local.sql.testing.*"), 9);
    nxlog_set_debug_level_tag(_T("server.objects.lock"), 9);
 
+   AssertEquals(nxlog_get_debug_level_tag(_T("*")), nxlog_get_debug_level());
+
    AssertEquals(nxlog_get_debug_level_tag(_T("server.objects.db")), 1);
 
-   AssertTrue(nxlog_get_debug_level_tag(_T("node")) == 1);
-   AssertTrue(nxlog_get_debug_level_tag(_T("node.status")) == 1);
-   AssertTrue(nxlog_get_debug_level_tag(_T("node.status.test")) == 1);
+   AssertEquals(nxlog_get_debug_level_tag(_T("node")), 1);
+   AssertEquals(nxlog_get_debug_level_tag(_T("node.status")), 1);
+   AssertEquals(nxlog_get_debug_level_tag(_T("node.status.test")), 1);
+
+   AssertEquals(nxlog_get_debug_level_tag(_T("db")), 2);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.status")), 3);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.status.test")), 3);
 
-   AssertTrue(nxlog_get_debug_level_tag(_T("db")) == 2);
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.status")) == 3);
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.status.test")) == 3);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local")), 4);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.server")), 5);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.server.test")), 5);
 
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.local")) == 4);
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.local.server")) == 5);
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.local.server.test")) == 5);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.sql")), 6);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.sql.server")), 7);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.sql.server.status")), 7);
 
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.local.sql")) == 6);
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.local.sql.server")) == 7);
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.local.sql.server.status")) == 7);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.sql.testing")), 8);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.sql.testing.server")), 9);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.sql.testing.server.status")), 9);
+   EndTest();
 
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.local.sql.testing")) == 8);
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.local.sql.testing.server")) == 9);
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.local.sql.testing.server.status")) == 9);
+   StartTest(_T("Debug tags: get all tags"));
+   ObjectArray<DebugTagInfo> *tags = nxlog_get_all_debug_tags();
+   AssertNotNull(tags);
+   AssertEquals(tags->size(), 9);
+   AssertTrue(!_tcscmp(tags->get(0)->tag, _T("db")));
+   AssertTrue(!_tcscmp(tags->get(1)->tag, _T("db.*")));
+   delete tags;
    EndTest();
 
-   StartTest(_T("Debug Tags: change debug level"));
+   StartTest(_T("Debug tags: change debug level"));
    nxlog_set_debug_level_tag(_T("*"), 9);
    nxlog_set_debug_level_tag(_T("db"), 8);
    nxlog_set_debug_level_tag(_T("db.*"), 7);
@@ -1129,34 +1140,36 @@ static void TestDebugTags()
    nxlog_set_debug_level_tag(_T("db.local.sql.testing"), 2);
    nxlog_set_debug_level_tag(_T("db.local.sql.testing.*"), 1);
 
-   AssertTrue(nxlog_get_debug_level_tag(_T("node")) == 9);
-   AssertTrue(nxlog_get_debug_level_tag(_T("node.status")) == 9);
-   AssertTrue(nxlog_get_debug_level_tag(_T("node.status.test")) == 9);
+   AssertEquals(nxlog_get_debug_level(), 9);
+   AssertEquals(nxlog_get_debug_level_tag(_T("*")), nxlog_get_debug_level());
 
-   AssertTrue(nxlog_get_debug_level_tag(_T("db")) == 8);
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.status")) == 7);
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.status.test")) == 7);
+   AssertEquals(nxlog_get_debug_level_tag(_T("node")), 9);
+   AssertEquals(nxlog_get_debug_level_tag(_T("node.status")), 9);
+   AssertEquals(nxlog_get_debug_level_tag(_T("node.status.test")), 9);
 
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.local")) == 6);
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.local.server")) == 5);
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.local.server.test")) == 5);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db")), 8);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.status")), 7);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.status.test")), 7);
 
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.local.sql")) == 4);
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.local.sql.server")) == 3);
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.local.sql.server.status")) == 3);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local")), 6);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.server")), 5);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.server.test")), 5);
 
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.local.sql.testing")) == 2);
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.local.sql.testing.server")) == 1);
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.local.sql.testing.server.status")) == 1);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.sql")), 4);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.sql.server")), 3);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.sql.server.status")), 3);
+
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.sql.testing")), 2);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.sql.testing.server")), 1);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.sql.testing.server.status")), 1);
    EndTest();
 
-   StartTest(_T("Debug Tags: remove"));
+   StartTest(_T("Debug tags: remove"));
    nxlog_set_debug_level_tag(_T("db.test.remove.*"), 4);
    nxlog_set_debug_level_tag(_T("db.test.remove.child.tag"), 3);
    nxlog_set_debug_level_tag(_T("db.local.child"), 2);
    nxlog_set_debug_level_tag(_T("db.local.child.*"), 5);
 
-   nxlog_set_debug_level_tag(_T("*"), -1);
    nxlog_set_debug_level_tag(_T("db"), -1);
    nxlog_set_debug_level_tag(_T("db.*"), -1);
    nxlog_set_debug_level_tag(_T("db.local"), -1);
@@ -1164,7 +1177,6 @@ static void TestDebugTags()
    nxlog_set_debug_level_tag(_T("db.local.sql"), -1);
    nxlog_set_debug_level_tag(_T("db.local.sql.*"), -1);;
 
-   nxlog_set_debug_level_tag(_T("*"), 9);
    nxlog_set_debug_level_tag(_T("server"), 8);
    nxlog_set_debug_level_tag(_T("server.*"), 7);
    nxlog_set_debug_level_tag(_T("server.node"), 6);
@@ -1174,46 +1186,46 @@ static void TestDebugTags()
    nxlog_set_debug_level_tag(_T("server.node.status.poll"), 2);
    nxlog_set_debug_level_tag(_T("server.node.status.poll.*"), 1);
 
-   AssertTrue(nxlog_get_debug_level_tag(_T("node")) == 9);
-   AssertTrue(nxlog_get_debug_level_tag(_T("node.status")) == 9);
-   AssertTrue(nxlog_get_debug_level_tag(_T("node.status.test")) == 9);
+   AssertEquals(nxlog_get_debug_level_tag(_T("node")), 9);
+   AssertEquals(nxlog_get_debug_level_tag(_T("node.status")), 9);
+   AssertEquals(nxlog_get_debug_level_tag(_T("node.status.test")), 9);
 
-   AssertTrue(nxlog_get_debug_level_tag(_T("server")) == 8);
-   AssertTrue(nxlog_get_debug_level_tag(_T("server.object")) == 7);
-   AssertTrue(nxlog_get_debug_level_tag(_T("server.object.add")) == 7);
+   AssertEquals(nxlog_get_debug_level_tag(_T("server")), 8);
+   AssertEquals(nxlog_get_debug_level_tag(_T("server.object")), 7);
+   AssertEquals(nxlog_get_debug_level_tag(_T("server.object.add")), 7);
 
-   AssertTrue(nxlog_get_debug_level_tag(_T("server.node")) == 6);
-   AssertTrue(nxlog_get_debug_level_tag(_T("server.node.add")) == 5);
-   AssertTrue(nxlog_get_debug_level_tag(_T("server.node.add.new")) == 5);
+   AssertEquals(nxlog_get_debug_level_tag(_T("server.node")), 6);
+   AssertEquals(nxlog_get_debug_level_tag(_T("server.node.add")), 5);
+   AssertEquals(nxlog_get_debug_level_tag(_T("server.node.add.new")), 5);
 
-   AssertTrue(nxlog_get_debug_level_tag(_T("server.node.status")) == 4);
-   AssertTrue(nxlog_get_debug_level_tag(_T("server.node.status.interface")) == 3);
-   AssertTrue(nxlog_get_debug_level_tag(_T("server.node.status.interface.state")) == 3);
+   AssertEquals(nxlog_get_debug_level_tag(_T("server.node.status")), 4);
+   AssertEquals(nxlog_get_debug_level_tag(_T("server.node.status.interface")), 3);
+   AssertEquals(nxlog_get_debug_level_tag(_T("server.node.status.interface.state")), 3);
 
-   AssertTrue(nxlog_get_debug_level_tag(_T("server.node.status.poll")) == 2);
-   AssertTrue(nxlog_get_debug_level_tag(_T("server.node.status.poll.time")) == 1);
-   AssertTrue(nxlog_get_debug_level_tag(_T("server.node.status.poll.time.ms")) == 1);
+   AssertEquals(nxlog_get_debug_level_tag(_T("server.node.status.poll")), 2);
+   AssertEquals(nxlog_get_debug_level_tag(_T("server.node.status.poll.time")), 1);
+   AssertEquals(nxlog_get_debug_level_tag(_T("server.node.status.poll.time.ms")), 1);
 
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.local.sql.testing")) == 2);
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.local.sql.testing.server")) == 1);
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.local.sql.testing.server.status")) == 1);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.sql.testing")), 2);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.sql.testing.server")), 1);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.sql.testing.server.status")), 1);
 
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.test.remove.something")) == 4);
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.test.remove.child.tag")) == 3);
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.local.child")) == 2);
-   AssertTrue(nxlog_get_debug_level_tag(_T("db.local.child.remove.something.else")) == 5);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.test.remove.something")), 4);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.test.remove.child.tag")), 3);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.child")), 2);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.child.remove.something.else")), 5);
 
-   AssertFalse(nxlog_get_debug_level_tag(_T("db")) == 8);
-   AssertFalse(nxlog_get_debug_level_tag(_T("db.status")) == 7);
-   AssertFalse(nxlog_get_debug_level_tag(_T("db.status.test")) == 7);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db")), nxlog_get_debug_level());
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.status")), nxlog_get_debug_level());
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.status.test")), nxlog_get_debug_level());
 
-   AssertFalse(nxlog_get_debug_level_tag(_T("db.local")) == 6);
-   AssertFalse(nxlog_get_debug_level_tag(_T("db.local.server")) == 5);
-   AssertFalse(nxlog_get_debug_level_tag(_T("db.local.server.test")) == 5);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local")), nxlog_get_debug_level());
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.server")), nxlog_get_debug_level());
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.server.test")), nxlog_get_debug_level());
 
-   AssertFalse(nxlog_get_debug_level_tag(_T("db.local.sql")) == 4);
-   AssertFalse(nxlog_get_debug_level_tag(_T("db.local.sql.server")) == 3);
-   AssertFalse(nxlog_get_debug_level_tag(_T("db.local.sql.server.status")) == 3);
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.sql")), nxlog_get_debug_level());
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.sql.server")), nxlog_get_debug_level());
+   AssertEquals(nxlog_get_debug_level_tag(_T("db.local.sql.server.status")), nxlog_get_debug_level());
    EndTest();
 
    StartTest(_T("nxlog_get_debug_level performance with multiple tags"));