BIND 10 trac2157_2, updated. 4c3dc79c413854edcf660874f8bd58d626b808c8 [2157] add ChangeLog

BIND 10 source code commits bind10-changes at lists.isc.org
Thu Nov 22 05:54:13 UTC 2012


The branch, trac2157_2 has been updated
       via  4c3dc79c413854edcf660874f8bd58d626b808c8 (commit)
       via  9b47527a3fd00d3504dbdecc5e6b073834825c66 (commit)
       via  db2a410da1fb7553909b47ddbc7ce34456081f47 (commit)
       via  efbe16c46b234712eaa7629ffda12b1a906542af (commit)
       via  4d9785cd658ba91b5f01c90c1855d239596c5f53 (commit)
       via  7d02b9cc8837e089433be66f7973ba578e72ee39 (commit)
       via  91a81c3b9a1ed47103c00e50fe3d141a2e9929b9 (commit)
       via  ab7f77f9ea18ce289ccc7484c4a40dc73548bca0 (commit)
       via  cb5a5f6e3c043b2f94884f5cb9101b661a084fc6 (commit)
       via  d5d8fa746c2cb23166e93f76423ae7ca645ef6a3 (commit)
       via  3e2516ee09187b5c9af57e0cd6552bb298ebf02d (commit)
       via  8c169e9a6032d69dd852f49d567eda1ecd7de706 (commit)
       via  a1107af9f039334d628220553229476beb41143f (commit)
       via  a0f35ebd59dee9b3f9b87c6b16d9185766f1d1be (commit)
       via  6f61065e702d8ccd5b3b2da2c5e07803270ed120 (commit)
       via  a44025da797abe06f2d1b1a7de98f68b37e0c5d4 (commit)
       via  34dd2565e959d9307cd17a1664d514ba76b68e24 (commit)
       via  50c6b204fe5c51af7bed0f5538b8d6817b654a24 (commit)
       via  5abc52b3f407f042b7fd189aa42a6a23228c0be7 (commit)
       via  92cf23f15e80e32f2983eb69d7c5ca13c336ebc4 (commit)
      from  7a821cf7fe79d72a294053865a65fe52b29394ee (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 4c3dc79c413854edcf660874f8bd58d626b808c8
Author: Yoshitaka Aharen <aharen at jprs.co.jp>
Date:   Thu Nov 22 14:53:22 2012 +0900

    [2157] add ChangeLog

commit 9b47527a3fd00d3504dbdecc5e6b073834825c66
Author: Yoshitaka Aharen <aharen at jprs.co.jp>
Date:   Thu Nov 22 14:46:21 2012 +0900

    [2157] update systest

commit db2a410da1fb7553909b47ddbc7ce34456081f47
Author: Yoshitaka Aharen <aharen at jprs.co.jp>
Date:   Thu Nov 22 13:05:31 2012 +0900

    [2157] update lettuce feature

commit efbe16c46b234712eaa7629ffda12b1a906542af
Author: Yoshitaka Aharen <aharen at jprs.co.jp>
Date:   Wed Nov 21 21:57:03 2012 +0900

    [2157] add testcase

commit 4d9785cd658ba91b5f01c90c1855d239596c5f53
Author: Yoshitaka Aharen <aharen at jprs.co.jp>
Date:   Wed Nov 21 21:48:17 2012 +0900

    [2157] corrected condition for "request.badednsver"

commit 7d02b9cc8837e089433be66f7973ba578e72ee39
Author: Yoshitaka Aharen <aharen at jprs.co.jp>
Date:   Tue Nov 20 20:28:32 2012 +0900

    [2157] add testcase

commit 91a81c3b9a1ed47103c00e50fe3d141a2e9929b9
Author: Yoshitaka Aharen <aharen at jprs.co.jp>
Date:   Mon Nov 19 17:00:28 2012 +0900

    [2157] use ADD_FAILURE instead of ASSERT_FALSE("string")

commit ab7f77f9ea18ce289ccc7484c4a40dc73548bca0
Author: Yoshitaka Aharen <aharen at jprs.co.jp>
Date:   Mon Nov 19 16:11:30 2012 +0900

    [2157] opcode is unsigned

commit cb5a5f6e3c043b2f94884f5cb9101b661a084fc6
Author: Yoshitaka Aharen <aharen at jprs.co.jp>
Date:   Mon Nov 19 15:56:52 2012 +0900

    [2157] corrected conditions for compound attributes

commit d5d8fa746c2cb23166e93f76423ae7ca645ef6a3
Author: Yoshitaka Aharen <aharen at jprs.co.jp>
Date:   Mon Nov 19 15:26:57 2012 +0900

    [2157] renamed statistics items generator

commit 3e2516ee09187b5c9af57e0cd6552bb298ebf02d
Author: Yoshitaka Aharen <aharen at jprs.co.jp>
Date:   Mon Nov 19 15:18:54 2012 +0900

    [2157] add notes for fillNodes

commit 8c169e9a6032d69dd852f49d567eda1ecd7de706
Author: Yoshitaka Aharen <aharen at jprs.co.jp>
Date:   Mon Nov 19 14:51:16 2012 +0900

    [2157] add description for CounterSpec

commit a1107af9f039334d628220553229476beb41143f
Author: Yoshitaka Aharen <aharen at jprs.co.jp>
Date:   Mon Nov 19 13:36:24 2012 +0900

    [2157] renamed CounterTypeTree to CounterSpec

commit a0f35ebd59dee9b3f9b87c6b16d9185766f1d1be
Author: Yoshitaka Aharen <aharen at jprs.co.jp>
Date:   Mon Nov 19 13:25:59 2012 +0900

    [2157] add documentation

commit 6f61065e702d8ccd5b3b2da2c5e07803270ed120
Author: Yoshitaka Aharen <aharen at jprs.co.jp>
Date:   Mon Nov 19 11:03:49 2012 +0900

    [2157] remove per-zone statistics code

commit a44025da797abe06f2d1b1a7de98f68b37e0c5d4
Author: Yoshitaka Aharen <aharen at jprs.co.jp>
Date:   Fri Nov 16 19:49:09 2012 +0900

    [2157] use std::bitset, add notes

commit 34dd2565e959d9307cd17a1664d514ba76b68e24
Author: Yoshitaka Aharen <aharen at jprs.co.jp>
Date:   Fri Nov 16 19:18:41 2012 +0900

    [2157] add getter method

commit 50c6b204fe5c51af7bed0f5538b8d6817b654a24
Author: Yoshitaka Aharen <aharen at jprs.co.jp>
Date:   Fri Nov 16 18:11:42 2012 +0900

    [2157] renamed according to coding style, moved to anonymous namespace

commit 5abc52b3f407f042b7fd189aa42a6a23228c0be7
Author: Yoshitaka Aharen <aharen at jprs.co.jp>
Date:   Fri Nov 16 16:41:12 2012 +0900

    [2157] use a constant instead of a magic number, typo fix

commit 92cf23f15e80e32f2983eb69d7c5ca13c336ebc4
Author: Yoshitaka Aharen <aharen at jprs.co.jp>
Date:   Fri Nov 16 15:01:57 2012 +0900

    [2157] rename following the naming policy

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                                          |    8 +
 configure.ac                                       |    3 +-
 src/bin/auth/Makefile.am                           |   26 +-
 src/bin/auth/auth_srv.cc                           |   21 +-
 ...ems.py.pre.in => gen-statisticsitems.py.pre.in} |  118 +++-
 src/bin/auth/statistics.cc.pre                     |  236 +++----
 src/bin/auth/statistics.h                          |  183 ++++--
 src/bin/auth/statistics_items.h.pre                |   16 +-
 src/bin/auth/statistics_msg_items.def              |   48 ++
 src/bin/auth/statistics_qr_items.def               |   48 --
 src/bin/auth/tests/auth_srv_unittest.cc            |  322 ++++++++--
 src/bin/auth/tests/statistics_unittest.cc.pre      |  658 ++++++++++++++++++--
 .../configurations/example.org.inmem.config        |    3 +
 tests/lettuce/features/queries.feature             |  501 ++++++++++++++-
 tests/lettuce/features/terrain/bind10_control.py   |   18 +-
 tests/system/bindctl/tests.sh                      |  109 ++--
 16 files changed, 1913 insertions(+), 405 deletions(-)
 rename src/bin/auth/{gen-statistics_items.py.pre.in => gen-statisticsitems.py.pre.in} (68%)
 create mode 100644 src/bin/auth/statistics_msg_items.def
 delete mode 100644 src/bin/auth/statistics_qr_items.def

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 84d4378..6838bc7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+TBD.	[func]		y-aharen
+	Added statistics items in b10-auth based on
+	http://bind10.isc.org/wiki/StatisticsItems. Qtype counters are
+	dropped as it requires further spec design discussion.
+	(Trac #2154, Trac #2155,
+	             git 61d7c3959eb991b22bc1c0ef8f4ecb96b65d9325)
+	(Trac #2157, git TBD)
+
 482.	[func]		team
 	Memory footprint of the in-memory data source has been
 	substantially improved.  For example, b10-auth now requires much
diff --git a/configure.ac b/configure.ac
index 2241067..2d294eb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1279,7 +1279,7 @@ AC_OUTPUT([doc/version.ent
            src/bin/msgq/run_msgq.sh
            src/bin/auth/auth.spec.pre
            src/bin/auth/spec_config.h.pre
-           src/bin/auth/gen-statistics_items.py.pre
+           src/bin/auth/gen-statisticsitems.py.pre
            src/bin/dhcp4/spec_config.h.pre
            src/bin/dhcp6/spec_config.h.pre
            src/bin/tests/process_rename_test.py
@@ -1342,7 +1342,6 @@ AC_OUTPUT([doc/version.ent
            chmod +x src/bin/usermgr/run_b10-cmdctl-usermgr.sh
            chmod +x src/bin/msgq/run_msgq.sh
            chmod +x src/bin/msgq/tests/msgq_test
-           chmod +x src/bin/auth/gen-statistics_items.py.pre
            chmod +x src/lib/dns/gen-rdatacode.py
            chmod +x src/lib/log/tests/console_test.sh
            chmod +x src/lib/log/tests/destination_test.sh
diff --git a/src/bin/auth/Makefile.am b/src/bin/auth/Makefile.am
index 1362498..df9a354 100644
--- a/src/bin/auth/Makefile.am
+++ b/src/bin/auth/Makefile.am
@@ -18,8 +18,8 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@
 
 CLEANFILES  = *.gcno *.gcda auth.spec spec_config.h
 CLEANFILES += auth_messages.h auth_messages.cc
-CLEANFILES += gen-statistics_items.py
-# auto-generated by gen-statistics_items.py
+CLEANFILES += gen-statisticsitems.py
+# auto-generated by gen-statisticsitems.py
 CLEANFILES += statistics.cc statistics_items.h b10-auth.xml tests/statistics_unittest.cc
 
 man_MANS = b10-auth.8
@@ -39,18 +39,18 @@ $(man_MANS):
 
 endif
 
-auth.spec: auth.spec.pre statistics_qr_items.def
-b10-auth.xml: b10-auth.xml.pre statistics_qr_items.def
-statistics_items.h: statistics_items.h.pre statistics_qr_items.def
-statistics.cc: statistics.cc.pre statistics_qr_items.def
-tests/statistics_unittest.cc: tests/statistics_unittest.cc.pre statistics_qr_items.def
+auth.spec: auth.spec.pre statistics_msg_items.def
+b10-auth.xml: b10-auth.xml.pre statistics_msg_items.def
+statistics_items.h: statistics_items.h.pre statistics_msg_items.def
+statistics.cc: statistics.cc.pre statistics_msg_items.def
+tests/statistics_unittest.cc: tests/statistics_unittest.cc.pre statistics_msg_items.def
 
-gen-statistics_items.py: gen-statistics_items.py.pre
-	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" gen-statistics_items.py.pre >$@
+gen-statisticsitems.py: gen-statisticsitems.py.pre
+	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" gen-statisticsitems.py.pre >$@
 	chmod +x $@
 
-auth.spec b10-auth.xml statistics_items.h statistics.cc tests/statistics_unittest.cc: Makefile gen-statistics_items.py
-	./gen-statistics_items.py
+auth.spec b10-auth.xml statistics_items.h statistics.cc tests/statistics_unittest.cc: Makefile gen-statisticsitems.py
+	./gen-statisticsitems.py
 
 spec_config.h: spec_config.h.pre
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" spec_config.h.pre >$@
@@ -59,7 +59,7 @@ auth_messages.h auth_messages.cc: auth_messages.mes
 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/bin/auth/auth_messages.mes
 
 BUILT_SOURCES = spec_config.h auth_messages.h auth_messages.cc
-# auto-generated by gen-statistics_items.py
+# auto-generated by gen-statisticsitems.py
 BUILT_SOURCES += statistics_items.h statistics.cc
 
 pkglibexec_PROGRAMS = b10-auth
@@ -76,7 +76,7 @@ b10_auth_SOURCES += main.cc
 nodist_b10_auth_SOURCES = auth_messages.h auth_messages.cc
 nodist_b10_auth_SOURCES += statistics.cc statistics_items.h
 EXTRA_DIST += auth_messages.mes
-EXTRA_DIST += statistics_qr_items.def
+EXTRA_DIST += statistics_msg_items.def
 EXTRA_DIST += b10-auth.xml.pre
 EXTRA_DIST += statistics_items.h.pre statistics.cc.pre
 EXTRA_DIST += tests/statistics_unittest.cc.pre
diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc
index 5c3de37..74c2984 100644
--- a/src/bin/auth/auth_srv.cc
+++ b/src/bin/auth/auth_srv.cc
@@ -83,7 +83,7 @@ using namespace isc::asiolink;
 using namespace isc::asiodns;
 using namespace isc::server_common::portconfig;
 using isc::auth::statistics::Counters;
-using isc::auth::statistics::QRAttributes;
+using isc::auth::statistics::MessageAttributes;
 
 namespace {
 // A helper class for cleaning up message renderer.
@@ -272,8 +272,8 @@ public:
     std::map<RRClass, boost::shared_ptr<ConfigurableClientList> >
         client_lists_;
 
-    /// Query / Response attributes
-    QRAttributes stats_attrs_;
+    /// Message attributes
+    MessageAttributes stats_attrs_;
 
     boost::shared_ptr<ConfigurableClientList> getClientList(const RRClass&
                                                             rrclass)
@@ -494,9 +494,9 @@ AuthSrv::processMessage(const IOMessage& io_message, Message& message,
 {
     InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
 
-    impl_->stats_attrs_.setQueryIPVersion(
+    impl_->stats_attrs_.setRequestIPVersion(
         io_message.getRemoteEndpoint().getFamily());
-    impl_->stats_attrs_.setQueryTransportProtocol(
+    impl_->stats_attrs_.setRequestTransportProtocol(
         io_message.getRemoteEndpoint().getProtocol());
 
     // First, check the header part.  If we fail even for the base header,
@@ -553,8 +553,8 @@ AuthSrv::processMessage(const IOMessage& io_message, Message& message,
                                            **impl_->keyring_));
         tsig_error = tsig_context->verify(tsig_record, io_message.getData(),
                                           io_message.getDataSize());
-        impl_->stats_attrs_.setQuerySig(true, false,
-                                        tsig_error != TSIGError::NOERROR());
+        impl_->stats_attrs_.setRequestSig(true, false,
+                                          tsig_error != TSIGError::NOERROR());
     }
 
     if (tsig_error != TSIGError::NOERROR()) {
@@ -570,13 +570,12 @@ AuthSrv::processMessage(const IOMessage& io_message, Message& message,
         // note: This can only be reliable after TSIG check succeeds.
         ConstEDNSPtr edns = message.getEDNS();
         if (edns) {
-            impl_->stats_attrs_.setQueryEDNS(edns->getVersion() == 0,
-                                             edns->getVersion() != 0);
-            impl_->stats_attrs_.setQueryDO(edns->getDNSSECAwareness());
+            impl_->stats_attrs_.setRequestEDNS0(true);
+            impl_->stats_attrs_.setRequestDO(edns->getDNSSECAwareness());
         }
 
         // note: This can only be reliable after TSIG check succeeds.
-        impl_->stats_attrs_.setQueryOpCode(opcode.getCode());
+        impl_->stats_attrs_.setRequestOpCode(opcode.getCode());
 
         if (opcode == Opcode::NOTIFY()) {
             send_answer = impl_->processNotify(io_message, message, buffer,
diff --git a/src/bin/auth/gen-statistics_items.py.pre.in b/src/bin/auth/gen-statistics_items.py.pre.in
deleted file mode 100755
index 816e426..0000000
--- a/src/bin/auth/gen-statistics_items.py.pre.in
+++ /dev/null
@@ -1,279 +0,0 @@
-#!@PYTHON@
-
-# Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-# PERFORMANCE OF THIS SOFTWARE.
-
-import os
-import re
-import sys
-import json
-from xml.etree import ElementTree
-
-item_list = []
-localstatedir = '@@LOCALSTATEDIR@@'
-builddir = '@builddir@'
-srcdir = '@srcdir@'
-pre_suffix = '.pre'
-
-xmldocument_command_name = 'b10-auth'
-
-def need_generate(filepath, mtime):
-    '''Check if we need to generate the specified file.
-
-    To avoid unnecessary compilation, we skip (re)generating the file when
-    the file already exists and newer than the base file.
-    '''
-    if os.path.exists(filepath) and\
-        (os.path.getmtime(filepath) > mtime or
-         os.path.getmtime(filepath) > os.path.getmtime(filepath+pre_suffix)):
-        return False
-    return True
-
-def import_definitions():
-    global item_list
-
-    items_definition_file = srcdir + os.sep + 'statistics_qr_items.def'
-    item_definition = open(items_definition_file, 'r')
-
-    re_splitter = re.compile('\t+')
-    l = item_list
-    lp = None
-    for line in item_definition.readlines():
-        element = re_splitter.split(line.rstrip())
-        if element[0] == '':
-            element.pop(0)
-
-        if element[-1] == '=':
-            l.append({'name': element[0], 'child': [], 'index': element[1],
-                      'description': element[2], 'parent': lp})
-            lp = l
-            l = l[-1]['child']
-        elif element[-1] == ';':
-            l = lp
-            lp = l[-1]['parent']
-        else:
-            l.append({'name': element[0], 'child': None, 'index': element[1],
-                      'description': element[2], 'parent': lp})
-
-    item_definition.close()
-    return os.path.getmtime(items_definition_file)
-
-def generate_specfile(specfile, def_mtime):
-    global item_list
-
-    def convert_list(items, prefix = ''):
-        spec_list = []
-        default_map = {}
-        for item in items:
-            full_item_name = prefix + item['name']
-            if item['child'] is None:
-                default_map[item['name']] = 0
-                spec_list.append({
-                        'item_name': item['name'],
-                        'item_optional': False,
-                        'item_type': 'integer',
-                        'item_default': 0,
-                        'item_title': full_item_name,
-                        'item_description': item['description'],
-                    })
-            else:
-                child_spec_list, child_default_map = \
-                    convert_list(item['child'], full_item_name + '.')
-                spec_list.append({
-                        'item_name': item['name'],
-                        'item_type': 'map',
-                        'item_optional': False,
-                        'item_title': full_item_name,
-                        'item_description': item['description'],
-                        'item_default': child_default_map,
-                        'map_item_spec': child_spec_list,
-                    })
-                default_map[item['name']] = child_default_map
-        return spec_list, default_map
-
-    item_spec_list, item_default_map = convert_list(item_list)
-
-    statistics_spec_list = [{
-        'item_name': 'zones',
-        'item_type': 'named_set',
-        'item_optional': False,
-        'item_title': 'Zone statistics',
-        'item_description':
-                'Zone statistics items. ' +
-                "Items for all zones are stored in '_SERVER_'.",
-        'item_default': { '_SERVER_': item_default_map },
-        'named_set_item_spec': {
-            'item_name': 'zone',
-            'item_type': 'map',
-            'item_optional': False,
-            'item_default': {},
-            'map_item_spec': item_spec_list,
-            },
-        }]
-
-    if need_generate(builddir+os.sep+specfile, def_mtime):
-        stats_pre = open(builddir+os.sep+specfile+pre_suffix, 'r')
-        stats_pre_json = json.loads(stats_pre.read().replace('@@LOCAL'+'STATEDIR@@',
-                                                             localstatedir))
-        stats_pre.close()
-        stats_pre_json['module_spec']['statistics'] = statistics_spec_list
-        statistics_spec_json = json.dumps(stats_pre_json, sort_keys = True,
-                                          indent = 2)
-        stats_spec = open(builddir+os.sep+specfile, 'w')
-        stats_spec.write(statistics_spec_json)
-        stats_spec.close()
-    else:
-        print('skip generating ' + specfile)
-    return
-
-def generate_docfile(docfile, def_mtime):
-    global item_list
-
-    def convert_list(items, tree, prefix = ''):
-        for item in items:
-            full_item_name = prefix + item['name']
-            if item['child'] is None:
-                child_element = ElementTree.SubElement(tree, 'varlistentry')
-                term = ElementTree.SubElement(child_element, 'term')
-                term.text = full_item_name
-                list_item = ElementTree.SubElement(child_element, 'listitem')
-                sim_para = ElementTree.SubElement(list_item, 'simpara')
-                sim_para.text = ''
-                prev = None
-                for word in item['description'].split():
-                    if word == xmldocument_command_name:
-                        command = ElementTree.SubElement(sim_para, 'command')
-                        command.text = word
-                        para_tail = command
-                        command.tail = ' '
-                        prev = command
-                    else:
-                        if prev is None:
-                            sim_para.text += word + ' '
-                        else:
-                            prev.tail += word + ' '
-            else:
-                convert_list(item['child'], tree, full_item_name + '.')
-        return
-
-    if need_generate(builddir+os.sep+docfile, def_mtime):
-        doc_pre = open(srcdir+os.sep+docfile+pre_suffix, 'r')
-        doc_pre_xml = doc_pre.read().replace('@@LOCAL'+'STATEDIR@@',
-                                             localstatedir)
-        doc_pre.close
-
-        variable_tree = ElementTree.Element('variablelist')
-        convert_list(item_list, variable_tree)
-        doc = open(builddir+os.sep+docfile, 'w')
-        doc.write(doc_pre_xml.replace(
-            '<!-- ### STATISTICS DATA PLACEHOLDER ### -->',
-            str(ElementTree.tostring(variable_tree))))
-        doc.close()
-    else:
-        print('skip generating ' + docfile)
-    return
-
-def generate_cxx(itemsfile, ccfile, utfile, def_mtime):
-    global item_list
-
-    qr_counter_types = 'enum QRCounterType {\n'
-    item_names = []
-
-    def convert_list(items, qr_counter_types, item_names_current, item_names):
-        for item in items:
-            if item['child'] is None:
-                qr_counter_types += '    ' + item['index'] + ',    ' +\
-                                    '///< ' + item['description'] + '\n'
-                item_names_current.append('    { "' + item['name'] +
-                                          '", NULL, ' + item['index'] + ' },\n'
-                                          )
-            else:
-                item_names_current_ = ['const struct CounterTypeTree ' +
-                                       item['index'] + '[] = {\n']
-                qr_counter_types, item_names_current_, item_names = \
-                        convert_list(item['child'], qr_counter_types,
-                                     item_names_current_, item_names)
-                item_names_current_.append('    { NULL, NULL, -1 }\n' +
-                              '};\n')
-                item_names.extend(item_names_current_)
-                item_names_current.append('    { "' + item['name'] + '", ' +
-                                          item['index'] + ', -1 },\n')
-        return qr_counter_types, item_names_current, item_names
-
-    qr_counter_types, item_names_current, item_names = \
-            convert_list(item_list, qr_counter_types, [], item_names)
-    item_names.append('const struct CounterTypeTree QRCounterTree[] = {\n')
-    item_names.extend(item_names_current)
-    item_names.append('    { NULL, NULL, -1 }\n' +
-                  '};\n')
-
-    qr_counter_types += \
-        '    // End of counter types\n' +\
-        '    QR_COUNTER_TYPES  ///< The number of defined counters\n' +\
-        '};\n'
-
-    item_defs = qr_counter_types
-    item_decls = ''.join(item_names)
-
-    if need_generate(builddir+os.sep+itemsfile, def_mtime):
-        statistics_items_h_pre = open(srcdir+os.sep+itemsfile+pre_suffix, 'r')
-        items_pre = statistics_items_h_pre.read()
-        statistics_items_h_pre.close
-
-        statistics_items_h = open(builddir+os.sep+itemsfile, 'w')
-        statistics_items_h.write(items_pre.replace(
-            '// ### STATISTICS ITEMS DECLARATION ###', item_defs))
-        statistics_items_h.close()
-    else:
-        print('skip generating ' + itemsfile)
-
-    if need_generate(builddir+os.sep+ccfile, def_mtime):
-        statistics_cc_pre = open(srcdir+os.sep+ccfile+pre_suffix, 'r')
-        items_pre = statistics_cc_pre.read()
-        statistics_cc_pre.close
-
-        statistics_cc = open(builddir+os.sep+ccfile, 'w')
-        statistics_cc.write(items_pre.replace(
-            '// ### STATISTICS ITEMS DEFINITION ###', item_decls))
-        statistics_cc.close()
-    else:
-        print('skip generating ' + ccfile)
-
-    if need_generate(builddir+os.sep+utfile, def_mtime):
-        statistics_ut_cc_pre = open(srcdir+os.sep+utfile+pre_suffix, 'r')
-        items_pre = statistics_ut_cc_pre.read()
-        statistics_ut_cc_pre.close
-
-        statistics_ut_cc = open(builddir+os.sep+utfile, 'w')
-        statistics_ut_cc.write(items_pre.replace(
-            '// ### STATISTICS ITEMS DEFINITION ###', item_decls))
-        statistics_ut_cc.close()
-    else:
-        print('skip generating ' + utfile)
-
-    return
-
-if __name__ == "__main__":
-    try:
-        def_mtime = import_definitions()
-        generate_specfile('auth.spec', def_mtime)
-        generate_docfile('b10-auth.xml', def_mtime)
-        generate_cxx('statistics_items.h',
-                     'statistics.cc',
-                     'tests'+os.sep+'statistics_unittest.cc',
-                     def_mtime)
-    except:
-        sys.stderr.write('Code generation failed due to exception: %s\n' %
-                sys.exc_info()[1])
-        exit(1)
diff --git a/src/bin/auth/gen-statisticsitems.py.pre.in b/src/bin/auth/gen-statisticsitems.py.pre.in
new file mode 100755
index 0000000..5908554
--- /dev/null
+++ b/src/bin/auth/gen-statisticsitems.py.pre.in
@@ -0,0 +1,343 @@
+#!@PYTHON@
+
+# Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+"""\
+This script generates spec file, docbook XML and some part of statistics code
+from statistics_msg_items.def.
+"""
+
+import os
+import re
+import sys
+import json
+from xml.etree import ElementTree
+
+item_list = []
+localstatedir = '@@LOCALSTATEDIR@@'
+builddir = '@builddir@'
+srcdir = '@srcdir@'
+pre_suffix = '.pre'
+
+xmldocument_command_name = 'b10-auth'
+
+def need_generate(filepath, mtime):
+    '''Check if we need to generate the specified file.
+
+    To avoid unnecessary compilation, we skip (re)generating the file when
+    the file already exists and newer than the base file, and definition file
+    specified with mtime.
+    '''
+    if os.path.exists(filepath) and\
+        (os.path.getmtime(filepath) > mtime and
+         os.path.getmtime(filepath) > os.path.getmtime(filepath+pre_suffix)):
+        return False
+    return True
+
+def import_definitions():
+    '''Load statsitics items definitions from statistics_msg_items.def.
+
+    statistics_msg_items.def defines a tree of message statistics items.
+    Syntax:
+        Each line describes a node; branch node for subset of counters,
+        leaf node for a counter item.
+        Each fields are separated with one or more field separator (Tab).
+        Field separator in the head of a line is ignored.
+
+        branch node:
+        (item name)\t+(internal branch name)\t+(description of the item)\t+'='
+        leaf node:
+        (item name)\t+(internal item counter name)\t+(description of the item)
+
+        Branch nodes contains leaf nodes and/or branch nodes. The end of
+        a branch node is indicated with ';' as item name (first column).
+
+        Internal branch name and internal item counter name must be unique.
+
+    Returns mtime of statistics_msg_items.def. It will be used to check
+    auto-generated files need to be regenerated.
+    '''
+    global item_list
+
+    items_definition_file = srcdir + os.sep + 'statistics_msg_items.def'
+    item_definition = open(items_definition_file, 'r')
+
+    re_splitter = re.compile('\t+')
+    l = item_list
+    lp = None
+    for line in item_definition.readlines():
+        element = re_splitter.split(line.rstrip())
+        if element[0] == '':
+            element.pop(0)
+
+        if element[-1] == '=':
+            l.append({'name': element[0], 'child': [], 'index': element[1],
+                      'description': element[2], 'parent': lp})
+            lp = l
+            l = l[-1]['child']
+        elif element[-1] == ';':
+            l = lp
+            lp = l[-1]['parent']
+        else:
+            l.append({'name': element[0], 'child': None, 'index': element[1],
+                      'description': element[2], 'parent': lp})
+
+    item_definition.close()
+    return os.path.getmtime(items_definition_file)
+
+def generate_specfile(specfile, def_mtime):
+    '''Generate spec in specfile from skeleton (specfille+'.pre').
+    If the specfile is newer than both sleketon and def_mtime, file generation
+    will be skipped.
+
+    This method reads the content of skeleton and repaces
+    <!-- ### STATISTICS DATA PLACEHOLDER ### --> with statistics items
+    definition. LOCALSTATEDIR is also expanded.
+
+    Returns nothing.
+    '''
+    global item_list
+
+    def convert_list(items, prefix = ''):
+        spec_list = []
+        default_map = {}
+        for item in items:
+            full_item_name = prefix + item['name']
+            if item['child'] is None:
+                default_map[item['name']] = 0
+                spec_list.append({
+                        'item_name': item['name'],
+                        'item_optional': False,
+                        'item_type': 'integer',
+                        'item_default': 0,
+                        'item_title': full_item_name,
+                        'item_description': item['description'],
+                    })
+            else:
+                child_spec_list, child_default_map = \
+                    convert_list(item['child'], full_item_name + '.')
+                spec_list.append({
+                        'item_name': item['name'],
+                        'item_type': 'map',
+                        'item_optional': False,
+                        'item_title': full_item_name,
+                        'item_description': item['description'],
+                        'item_default': child_default_map,
+                        'map_item_spec': child_spec_list,
+                    })
+                default_map[item['name']] = child_default_map
+        return spec_list, default_map
+
+    item_spec_list, item_default_map = convert_list(item_list)
+
+    statistics_spec_list = [{
+        'item_name': 'zones',
+        'item_type': 'named_set',
+        'item_optional': False,
+        'item_title': 'Zone statistics',
+        'item_description':
+                'Zone statistics items. ' +
+                "Items for all zones are stored in '_SERVER_'.",
+        'item_default': { '_SERVER_': item_default_map },
+        'named_set_item_spec': {
+            'item_name': 'zone',
+            'item_type': 'map',
+            'item_optional': False,
+            'item_default': {},
+            'map_item_spec': item_spec_list,
+            },
+        }]
+
+    if need_generate(builddir+os.sep+specfile, def_mtime):
+        stats_pre = open(builddir+os.sep+specfile+pre_suffix, 'r')
+        stats_pre_json = \
+            json.loads(stats_pre.read().replace('@@LOCAL'+'STATEDIR@@',
+                                                localstatedir))
+        stats_pre.close()
+        stats_pre_json['module_spec']['statistics'] = statistics_spec_list
+        statistics_spec_json = json.dumps(stats_pre_json, sort_keys = True,
+                                          indent = 2)
+        stats_spec = open(builddir+os.sep+specfile, 'w')
+        stats_spec.write(statistics_spec_json)
+        stats_spec.close()
+    else:
+        print('skip generating ' + specfile)
+    return
+
+def generate_docfile(docfile, def_mtime):
+    '''Generate docbook XML in docfile from skeleton (docfile+'.pre').
+    If the docfile is newer than both sleketon and def_mtime, file generation
+    will be skipped.
+
+    This method reads the content of skeleton and repaces
+    <!-- ### STATISTICS DATA PLACEHOLDER ### --> with statistics items
+    definition. LOCALSTATEDIR is also expanded.
+
+    Returns nothing.
+    '''
+    global item_list
+
+    def convert_list(items, tree, prefix = ''):
+        for item in items:
+            full_item_name = prefix + item['name']
+            if item['child'] is None:
+                child_element = ElementTree.SubElement(tree, 'varlistentry')
+                term = ElementTree.SubElement(child_element, 'term')
+                term.text = full_item_name
+                list_item = ElementTree.SubElement(child_element, 'listitem')
+                sim_para = ElementTree.SubElement(list_item, 'simpara')
+                sim_para.text = ''
+                prev = None
+                for word in item['description'].split():
+                    if word == xmldocument_command_name:
+                        command = ElementTree.SubElement(sim_para, 'command')
+                        command.text = word
+                        para_tail = command
+                        command.tail = ' '
+                        prev = command
+                    else:
+                        if prev is None:
+                            sim_para.text += word + ' '
+                        else:
+                            prev.tail += word + ' '
+            else:
+                convert_list(item['child'], tree, full_item_name + '.')
+        return
+
+    if need_generate(builddir+os.sep+docfile, def_mtime):
+        doc_pre = open(srcdir+os.sep+docfile+pre_suffix, 'r')
+        doc_pre_xml = doc_pre.read().replace('@@LOCAL'+'STATEDIR@@',
+                                             localstatedir)
+        doc_pre.close
+
+        variable_tree = ElementTree.Element('variablelist')
+        convert_list(item_list, variable_tree)
+        doc = open(builddir+os.sep+docfile, 'w')
+        doc.write(doc_pre_xml.replace(
+            '<!-- ### STATISTICS DATA PLACEHOLDER ### -->',
+            str(ElementTree.tostring(variable_tree))))
+        doc.close()
+    else:
+        print('skip generating ' + docfile)
+    return
+
+def generate_cxx(itemsfile, ccfile, utfile, def_mtime):
+    '''Generate some part of statistics code in itemsfile, ccfile, utfile from
+    skeleton (itemsfile+'.pre', ccfile+'.pre', utfile+'.pre').
+    If the file is newer than both sleketon and def_mtime, file generation
+    will be skipped.
+
+    This method reads the content of skeleton and repaces
+    // ### STATISTICS ITEMS DEFINITION ### with statistics items definition in
+    ccfile and utfile,
+    // ### STATISTICS ITEMS DECLARATION ### with statistics items declaration
+    in itemsfile.
+
+    Returns nothing.
+    '''
+    global item_list
+
+    msg_counter_types = 'enum MSGCounterType {\n'
+    item_names =  ['// using -1 as counter_id to state it is not a '
+                   + 'counter item\n']
+    item_names += ['const int NOT_ITEM = -1;\n', '\n']
+
+    def convert_list(items, msg_counter_types, item_names_current, item_names):
+        for item in items:
+            if item['child'] is None:
+                msg_counter_types += '    ' + item['index'] + ',    ' +\
+                                    '///< ' + item['description'] + '\n'
+                item_names_current.append('    { "' + item['name'] +
+                                          '", NULL, ' + item['index'] + ' },\n'
+                                          )
+            else:
+                item_names_current_ = ['const struct CounterSpec ' +
+                                       item['index'] + '[] = {\n']
+                msg_counter_types, item_names_current_, item_names = \
+                        convert_list(item['child'], msg_counter_types,
+                                     item_names_current_, item_names)
+                item_names_current_.append('    { NULL, NULL, NOT_ITEM }\n' +
+                              '};\n')
+                item_names.extend(item_names_current_)
+                item_names_current.append('    { "' + item['name'] + '", ' +
+                                          item['index'] + ', NOT_ITEM },\n')
+        return msg_counter_types, item_names_current, item_names
+
+    msg_counter_types, item_names_current, item_names = \
+            convert_list(item_list, msg_counter_types, [], item_names)
+    item_names.append('const struct CounterSpec msg_counter_tree[] = {\n')
+    item_names.extend(item_names_current)
+    item_names.append('    { NULL, NULL, NOT_ITEM }\n' +
+                  '};\n')
+
+    msg_counter_types += \
+        '    // End of counter types\n' +\
+        '    MSG_COUNTER_TYPES  ///< The number of defined counters\n' +\
+        '};\n'
+
+    item_decls = msg_counter_types
+    item_defs = ''.join(item_names)
+
+    if need_generate(builddir+os.sep+itemsfile, def_mtime):
+        statistics_items_h_pre = open(srcdir+os.sep+itemsfile+pre_suffix, 'r')
+        items_pre = statistics_items_h_pre.read()
+        statistics_items_h_pre.close
+
+        statistics_items_h = open(builddir+os.sep+itemsfile, 'w')
+        statistics_items_h.write(items_pre.replace(
+            '// ### STATISTICS ITEMS DECLARATION ###', item_decls))
+        statistics_items_h.close()
+    else:
+        print('skip generating ' + itemsfile)
+
+    if need_generate(builddir+os.sep+ccfile, def_mtime):
+        statistics_cc_pre = open(srcdir+os.sep+ccfile+pre_suffix, 'r')
+        items_pre = statistics_cc_pre.read()
+        statistics_cc_pre.close
+
+        statistics_cc = open(builddir+os.sep+ccfile, 'w')
+        statistics_cc.write(items_pre.replace(
+            '// ### STATISTICS ITEMS DEFINITION ###', item_defs))
+        statistics_cc.close()
+    else:
+        print('skip generating ' + ccfile)
+
+    if need_generate(builddir+os.sep+utfile, def_mtime):
+        statistics_ut_cc_pre = open(srcdir+os.sep+utfile+pre_suffix, 'r')
+        items_pre = statistics_ut_cc_pre.read()
+        statistics_ut_cc_pre.close
+
+        statistics_ut_cc = open(builddir+os.sep+utfile, 'w')
+        statistics_ut_cc.write(items_pre.replace(
+            '// ### STATISTICS ITEMS DEFINITION ###', item_defs))
+        statistics_ut_cc.close()
+    else:
+        print('skip generating ' + utfile)
+
+    return
+
+if __name__ == "__main__":
+    try:
+        def_mtime = import_definitions()
+        generate_specfile('auth.spec', def_mtime)
+        generate_docfile('b10-auth.xml', def_mtime)
+        generate_cxx('statistics_items.h',
+                     'statistics.cc',
+                     'tests'+os.sep+'statistics_unittest.cc',
+                     def_mtime)
+    except:
+        sys.stderr.write('File generation failed due to exception: %s\n' %
+                sys.exc_info()[1])
+        exit(1)
diff --git a/src/bin/auth/statistics.cc.pre b/src/bin/auth/statistics.cc.pre
index d84eeb1..5ead3a2 100644
--- a/src/bin/auth/statistics.cc.pre
+++ b/src/bin/auth/statistics.cc.pre
@@ -38,22 +38,27 @@
 using namespace isc::dns;
 using namespace isc::auth;
 using namespace isc::statistics;
+using namespace isc::auth::statistics;
 
 namespace {
 
+/// \brief Fill ItemTreePtr with given counter.
+/// \param counter Counter which stores values to fill
+/// \param type_tree CounterSpec corresponding to counter for building item name
+/// \param trees ItemTreePtr to be filled in; caller has ownership of ItemTreePtr
 void
 fillNodes(const Counter& counter,
-          const struct isc::auth::statistics::CounterTypeTree type_tree[],
+          const struct isc::auth::statistics::CounterSpec type_tree[],
           isc::auth::statistics::Counters::ItemTreePtr& trees)
 {
     using namespace isc::data;
 
     for (int i = 0; type_tree[i].name != NULL; ++i) {
-        if (type_tree[i].sub_tree != NULL) {
-            isc::auth::statistics::Counters::ItemTreePtr sub_tree =
+        if (type_tree[i].sub_counters != NULL) {
+            isc::auth::statistics::Counters::ItemTreePtr sub_counters =
                 Element::createMap();
-            trees->set(type_tree[i].name, sub_tree);
-            fillNodes(counter, type_tree[i].sub_tree, sub_tree);
+            trees->set(type_tree[i].name, sub_counters);
+            fillNodes(counter, type_tree[i].sub_counters, sub_counters);
         } else {
             trees->set(type_tree[i].name,
                        Element::create(static_cast<long int>(
@@ -63,130 +68,127 @@ fillNodes(const Counter& counter,
     }
 }
 
+// ### STATISTICS ITEMS DEFINITION ###
+
 } // anonymous namespace
 
 namespace isc {
 namespace auth {
 namespace statistics {
 
-// ### STATISTICS ITEMS DEFINITION ###
-
 // Note: opcode in this array must be start with 0 and be sequential
-const int opcode_to_qrcounter[] = {
-    QR_OPCODE_QUERY,    // Opcode =  0: Query
-    QR_OPCODE_IQUERY,   // Opcode =  1: Iquery
-    QR_OPCODE_STATUS,   // Opcode =  2: STATUS
-    QR_OPCODE_OTHER,    // Opcode =  3: (Unassigned)
-    QR_OPCODE_NOTIFY,   // Opcode =  4: Notify
-    QR_OPCODE_UPDATE,   // Opcode =  5: Update
-    QR_OPCODE_OTHER,    // Opcode =  6: (Unassigned)
-    QR_OPCODE_OTHER,    // Opcode =  7: (Unassigned)
-    QR_OPCODE_OTHER,    // Opcode =  8: (Unassigned)
-    QR_OPCODE_OTHER,    // Opcode =  9: (Unassigned)
-    QR_OPCODE_OTHER,    // Opcode = 10: (Unassigned)
-    QR_OPCODE_OTHER,    // Opcode = 11: (Unassigned)
-    QR_OPCODE_OTHER,    // Opcode = 12: (Unassigned)
-    QR_OPCODE_OTHER,    // Opcode = 13: (Unassigned)
-    QR_OPCODE_OTHER,    // Opcode = 14: (Unassigned)
-    QR_OPCODE_OTHER     // Opcode = 15: (Unassigned)
+const int opcode_to_msgcounter[] = {
+    MSG_OPCODE_QUERY,    // Opcode =  0: Query
+    MSG_OPCODE_IQUERY,   // Opcode =  1: Iquery
+    MSG_OPCODE_STATUS,   // Opcode =  2: STATUS
+    MSG_OPCODE_OTHER,    // Opcode =  3: (Unassigned)
+    MSG_OPCODE_NOTIFY,   // Opcode =  4: Notify
+    MSG_OPCODE_UPDATE,   // Opcode =  5: Update
+    MSG_OPCODE_OTHER,    // Opcode =  6: (Unassigned)
+    MSG_OPCODE_OTHER,    // Opcode =  7: (Unassigned)
+    MSG_OPCODE_OTHER,    // Opcode =  8: (Unassigned)
+    MSG_OPCODE_OTHER,    // Opcode =  9: (Unassigned)
+    MSG_OPCODE_OTHER,    // Opcode = 10: (Unassigned)
+    MSG_OPCODE_OTHER,    // Opcode = 11: (Unassigned)
+    MSG_OPCODE_OTHER,    // Opcode = 12: (Unassigned)
+    MSG_OPCODE_OTHER,    // Opcode = 13: (Unassigned)
+    MSG_OPCODE_OTHER,    // Opcode = 14: (Unassigned)
+    MSG_OPCODE_OTHER     // Opcode = 15: (Unassigned)
 };
-const size_t num_opcode_to_qrcounter =
-    sizeof(opcode_to_qrcounter) / sizeof(opcode_to_qrcounter[0]);
+const size_t num_opcode_to_msgcounter =
+    sizeof(opcode_to_msgcounter) / sizeof(opcode_to_msgcounter[0]);
 
 // Note: rcode in this array must be start with 0 and be sequential
-const int rcode_to_qrcounter[] = {
-    QR_RCODE_NOERROR,       // Rcode =  0: NoError
-    QR_RCODE_FORMERR,       // Rcode =  1: FormErr
-    QR_RCODE_SERVFAIL,      // Rcode =  2: ServFail
-    QR_RCODE_NXDOMAIN,      // Rcode =  3: NXDomain
-    QR_RCODE_NOTIMP,        // Rcode =  4: NotImp
-    QR_RCODE_REFUSED,       // Rcode =  5: Refused
-    QR_RCODE_YXDOMAIN,      // Rcode =  6: YXDomain
-    QR_RCODE_YXRRSET,       // Rcode =  7: YXRRSet
-    QR_RCODE_NXRRSET,       // Rcode =  8: NXRRSet
-    QR_RCODE_NOTAUTH,       // Rcode =  9: NotAuth
-    QR_RCODE_NOTZONE,       // Rcode = 10: NotZone
-    QR_RCODE_OTHER,         // Rcode = 11: (Unassigned)
-    QR_RCODE_OTHER,         // Rcode = 12: (Unassigned)
-    QR_RCODE_OTHER,         // Rcode = 13: (Unassigned)
-    QR_RCODE_OTHER,         // Rcode = 14: (Unassigned)
-    QR_RCODE_OTHER,         // Rcode = 15: (Unassigned)
-    QR_RCODE_BADVERS        // Rcode = 16: (BADVERS)
+const int rcode_to_msgcounter[] = {
+    MSG_RCODE_NOERROR,       // Rcode =  0: NoError
+    MSG_RCODE_FORMERR,       // Rcode =  1: FormErr
+    MSG_RCODE_SERVFAIL,      // Rcode =  2: ServFail
+    MSG_RCODE_NXDOMAIN,      // Rcode =  3: NXDomain
+    MSG_RCODE_NOTIMP,        // Rcode =  4: NotImp
+    MSG_RCODE_REFUSED,       // Rcode =  5: Refused
+    MSG_RCODE_YXDOMAIN,      // Rcode =  6: YXDomain
+    MSG_RCODE_YXRRSET,       // Rcode =  7: YXRRSet
+    MSG_RCODE_NXRRSET,       // Rcode =  8: NXRRSet
+    MSG_RCODE_NOTAUTH,       // Rcode =  9: NotAuth
+    MSG_RCODE_NOTZONE,       // Rcode = 10: NotZone
+    MSG_RCODE_OTHER,         // Rcode = 11: (Unassigned)
+    MSG_RCODE_OTHER,         // Rcode = 12: (Unassigned)
+    MSG_RCODE_OTHER,         // Rcode = 13: (Unassigned)
+    MSG_RCODE_OTHER,         // Rcode = 14: (Unassigned)
+    MSG_RCODE_OTHER,         // Rcode = 15: (Unassigned)
+    MSG_RCODE_BADVERS        // Rcode = 16: BADVERS
 };
-const size_t num_rcode_to_qrcounter =
-    sizeof(rcode_to_qrcounter) / sizeof(rcode_to_qrcounter[0]);
+const size_t num_rcode_to_msgcounter =
+    sizeof(rcode_to_msgcounter) / sizeof(rcode_to_msgcounter[0]);
 
 Counters::Counters() :
-    // size of server_qr_counter_, zone_qr_counters_: QR_COUNTER_TYPES
-    server_qr_counter_(QR_COUNTER_TYPES),
-    zone_qr_counters_(QR_COUNTER_TYPES)
+    server_msg_counter_(MSG_COUNTER_TYPES)
 {}
 
 void
-Counters::incRequest(const QRAttributes& qrattrs) {
+Counters::incRequest(const MessageAttributes& msgattrs) {
     // protocols carrying request
-    if (qrattrs.req_ip_version_ == AF_INET) {
-        server_qr_counter_.inc(QR_REQUEST_IPV4);
-    } else if (qrattrs.req_ip_version_ == AF_INET6) {
-        server_qr_counter_.inc(QR_REQUEST_IPV6);
+    if (msgattrs.getRequestIPVersion() == AF_INET) {
+        server_msg_counter_.inc(MSG_REQUEST_IPV4);
+    } else if (msgattrs.getRequestIPVersion() == AF_INET6) {
+        server_msg_counter_.inc(MSG_REQUEST_IPV6);
     }
-    if (qrattrs.req_transport_protocol_ == IPPROTO_UDP) {
-        server_qr_counter_.inc(QR_REQUEST_UDP);
-    } else if (qrattrs.req_transport_protocol_ == IPPROTO_TCP) {
-        server_qr_counter_.inc(QR_REQUEST_TCP);
+    if (msgattrs.getRequestTransportProtocol() == IPPROTO_UDP) {
+        server_msg_counter_.inc(MSG_REQUEST_UDP);
+    } else if (msgattrs.getRequestTransportProtocol() == IPPROTO_TCP) {
+        server_msg_counter_.inc(MSG_REQUEST_TCP);
     }
 
-    // query TSIG
-    if (qrattrs.req_is_tsig_) {
-        server_qr_counter_.inc(QR_REQUEST_TSIG);
+    // request TSIG
+    if (msgattrs.getRequestSigTSIG()) {
+        server_msg_counter_.inc(MSG_REQUEST_TSIG);
     }
-    if (qrattrs.req_is_sig0_) {
-        server_qr_counter_.inc(QR_REQUEST_SIG0);
+    if (msgattrs.getRequestSigSIG0()) {
+        server_msg_counter_.inc(MSG_REQUEST_SIG0);
     }
-    if (qrattrs.req_is_badsig_) {
-        server_qr_counter_.inc(QR_REQUEST_BADSIG);
+    if (msgattrs.getRequestSigBadSig()) {
+        server_msg_counter_.inc(MSG_REQUEST_BADSIG);
         // If signature validation is failed, no other query attributes are
         // reliable. Skip processing of the rest of query counters.
         return;
     }
 
-    // query EDNS
-    if (qrattrs.req_is_edns_0_) {
-        server_qr_counter_.inc(QR_REQUEST_EDNS0);
-    }
-    if (qrattrs.req_is_edns_badver_) {
-        server_qr_counter_.inc(QR_REQUEST_BADEDNSVER);
+    // request EDNS
+    if (msgattrs.getRequestEDNS0()) {
+        server_msg_counter_.inc(MSG_REQUEST_EDNS0);
     }
 
-    // query DNSSEC
-    if (qrattrs.req_is_dnssec_ok_) {
-        server_qr_counter_.inc(QR_REQUEST_DNSSEC_OK);
+    // request DNSSEC
+    if (msgattrs.getRequestDO()) {
+        server_msg_counter_.inc(MSG_REQUEST_DNSSEC_OK);
     }
 
     // OPCODE
-    server_qr_counter_.inc(opcode_to_qrcounter[qrattrs.req_opcode_]);
+    server_msg_counter_.inc(opcode_to_msgcounter[msgattrs.getRequestOpCode()]);
 }
 
 void
-Counters::incResponse(const QRAttributes& qrattrs, const Message& response) {
+Counters::incResponse(const MessageAttributes& msgattrs,
+                      const Message& response)
+{
     // responded
-    server_qr_counter_.inc(QR_RESPONSE);
+    server_msg_counter_.inc(MSG_RESPONSE);
 
     // response truncated
-    if (qrattrs.res_is_truncated_) {
-        server_qr_counter_.inc(QR_RESPONSE_TRUNCATED);
+    if (msgattrs.getResponseTruncated()) {
+        server_msg_counter_.inc(MSG_RESPONSE_TRUNCATED);
     }
 
     // response EDNS
     ConstEDNSPtr response_edns = response.getEDNS();
     if (response_edns && response_edns->getVersion() == 0) {
-        server_qr_counter_.inc(QR_RESPONSE_EDNS0);
+        server_msg_counter_.inc(MSG_RESPONSE_EDNS0);
     }
 
     // response TSIG
-    if (qrattrs.req_is_tsig_) {
+    if (msgattrs.getRequestSigTSIG()) {
         // assume response is TSIG signed if request is TSIG signed
-        server_qr_counter_.inc(QR_RESPONSE_TSIG);
+        server_msg_counter_.inc(MSG_RESPONSE_TSIG);
     }
 
     // response SIG(0) is currently not implemented
@@ -194,52 +196,60 @@ Counters::incResponse(const QRAttributes& qrattrs, const Message& response) {
     // RCODE
     const unsigned int rcode = response.getRcode().getCode();
     const unsigned int rcode_type =
-        rcode < num_rcode_to_qrcounter ?
-        rcode_to_qrcounter[rcode] : QR_RCODE_OTHER;
-    server_qr_counter_.inc(rcode_type);
-
-    // compound attributes
-    const unsigned int answer_rrs =
-        response.getRRCount(Message::SECTION_ANSWER);
-    const bool is_aa_set = response.getHeaderFlag(Message::HEADERFLAG_AA);
-
-    if (is_aa_set) {
-        // QryAuthAns
-        server_qr_counter_.inc(QR_QRYAUTHANS);
-    } else {
-        // QryNoAuthAns
-        server_qr_counter_.inc(QR_QRYNOAUTHANS);
+        rcode < num_rcode_to_msgcounter ?
+        rcode_to_msgcounter[rcode] : MSG_RCODE_OTHER;
+    server_msg_counter_.inc(rcode_type);
+    // Unsupported EDNS version
+    if (rcode == Rcode::BADVERS().getCode()) {
+        server_msg_counter_.inc(MSG_REQUEST_BADEDNSVER);
     }
 
-    if (rcode == Rcode::NOERROR_CODE) {
-        if (answer_rrs > 0) {
-            // QrySuccess
-            server_qr_counter_.inc(QR_QRYSUCCESS);
+    if (msgattrs.getRequestOpCode() == Opcode::QUERY_CODE) {
+        // compound attributes
+        const unsigned int answer_rrs =
+            response.getRRCount(Message::SECTION_ANSWER);
+        const bool is_aa_set = response.getHeaderFlag(Message::HEADERFLAG_AA);
+
+        if (is_aa_set) {
+            // QryAuthAns
+            server_msg_counter_.inc(MSG_QRYAUTHANS);
         } else {
-            if (is_aa_set) {
-                // QryNxrrset
-                server_qr_counter_.inc(QR_QRYNXRRSET);
+            // QryNoAuthAns
+            server_msg_counter_.inc(MSG_QRYNOAUTHANS);
+        }
+
+        if (rcode == Rcode::NOERROR_CODE) {
+            if (answer_rrs > 0) {
+                // QrySuccess
+                server_msg_counter_.inc(MSG_QRYSUCCESS);
             } else {
-                // QryReferral
-                server_qr_counter_.inc(QR_QRYREFERRAL);
+                if (is_aa_set) {
+                    // QryNxrrset
+                    server_msg_counter_.inc(MSG_QRYNXRRSET);
+                } else {
+                    // QryReferral
+                    server_msg_counter_.inc(MSG_QRYREFERRAL);
+                }
+            }
+        } else if (rcode == Rcode::REFUSED_CODE) {
+            if (!response.getHeaderFlag(Message::HEADERFLAG_RD)) {
+                // AuthRej
+                server_msg_counter_.inc(MSG_QRYREJECT);
             }
         }
-    } else if (rcode == Rcode::REFUSED_CODE) {
-        // AuthRej
-        server_qr_counter_.inc(QR_QRYREJECT);
     }
 }
 
 void
-Counters::inc(const QRAttributes& qrattrs, const Message& response,
+Counters::inc(const MessageAttributes& msgattrs, const Message& response,
               const bool done)
 {
     // increment request counters
-    incRequest(qrattrs);
+    incRequest(msgattrs);
 
     if (done) {
         // increment response counters if answer was sent
-        incResponse(qrattrs, response);
+        incResponse(msgattrs, response);
     }
 }
 
@@ -253,7 +263,7 @@ Counters::get() const {
     item_tree->set("zones", zones);
 
     Counters::ItemTreePtr server = Element::createMap();
-    fillNodes(server_qr_counter_, QRCounterTree, server);
+    fillNodes(server_msg_counter_, msg_counter_tree, server);
     zones->set("_SERVER_", server);
 
     return (item_tree);
diff --git a/src/bin/auth/statistics.h b/src/bin/auth/statistics.h
index 5b8134d..313e99f 100644
--- a/src/bin/auth/statistics.h
+++ b/src/bin/auth/statistics.h
@@ -25,7 +25,7 @@
 
 #include <boost/noncopyable.hpp>
 
-#include <string>
+#include <bitset>
 
 #include <stdint.h>
 
@@ -33,83 +33,154 @@ namespace isc {
 namespace auth {
 namespace statistics {
 
-/// \brief Query/Response attributes for statistics.
+/// \brief DNS Message attributes for statistics.
 ///
-/// This class holds some attributes related to a query/response
+/// This class holds some attributes related to a DNS message
 /// for statistics data collection.
-///
-/// This class does not have getter methods since it exposes private members
-/// to \c Counters directly.
-class QRAttributes {
-friend class Counters;
+class MessageAttributes {
 private:
     // request attributes
     int req_ip_version_;            // IP version
     int req_transport_protocol_;    // Transport layer protocol
-    int req_opcode_;                // OpCode
-    bool req_is_edns_0_;            // EDNS ver.0
-    bool req_is_edns_badver_;       // other EDNS version
-    bool req_is_dnssec_ok_;         // DO bit
-    bool req_is_tsig_;              // signed with valid TSIG
-    bool req_is_sig0_;              // signed with valid SIG(0)
-    bool req_is_badsig_;            // signed but bad signature
-    // response attributes
-    bool res_is_truncated_;         // DNS message is truncated
+    uint8_t req_opcode_;            // OpCode
+    enum BitAttributes {
+        REQ_IS_EDNS_0,              // EDNS ver.0
+        REQ_IS_DNSSEC_OK,           // DNSSEC OK (DO) bit is set
+        REQ_IS_TSIG,                // signed with valid TSIG
+        REQ_IS_SIG0,                // signed with valid SIG(0)
+        REQ_IS_BADSIG,              // signed but bad signature,
+        RES_IS_TRUNCATED,           // DNS message is truncated
+        BIT_ATTRIBUTES_TYPES
+    };
+    std::bitset<BIT_ATTRIBUTES_TYPES> bit_attributes_;
 public:
     /// \brief The constructor.
     ///
     /// This constructor is mostly exception free. But it may still throw
     /// a standard exception if memory allocation fails inside the method.
     ///
-    QRAttributes() {
+    MessageAttributes() {
         reset();
     };
 
-    /// \brief Set query opcode.
+    /// \brief Get request opcode.
+    /// \return opcode of a request
+    /// \throw None
+    uint8_t getRequestOpCode() const {
+        return (req_opcode_);
+    };
+
+    /// \brief Set request opcode.
     /// \throw None
-    void setQueryOpCode(const int opcode) {
+    void setRequestOpCode(const uint8_t opcode) {
         req_opcode_ = opcode;
     };
 
-    /// \brief Set IP version carrying a query.
+    /// \brief Get IP version carrying a request.
+    /// \return IP version carrying a request (AF_INET or AF_INET6)
+    /// \throw None
+    int getRequestIPVersion() const {
+        return (req_ip_version_);
+    };
+
+    /// \brief Set IP version carrying a request.
+    /// \param ip_version AF_INET or AF_INET6
     /// \throw None
-    void setQueryIPVersion(const int ip_version) {
+    void setRequestIPVersion(const int ip_version) {
         req_ip_version_ = ip_version;
     };
 
-    /// \brief Set transport protocol carrying a query.
+    /// \brief Get transport protocol carrying a request.
+    /// \return Transport protocol carrying a request
+    ///         (IPPROTO_UDP or IPPROTO_TCP)
     /// \throw None
-    void setQueryTransportProtocol(const int transport_protocol) {
+    int getRequestTransportProtocol() const {
+        return (req_transport_protocol_);
+    };
+
+    /// \brief Set transport protocol carrying a request.
+    /// \param transport_protocol IPPROTO_UDP or IPPROTO_TCP
+    /// \throw None
+    void setRequestTransportProtocol(const int transport_protocol) {
         req_transport_protocol_ = transport_protocol;
     };
 
-    /// \brief Set query EDNS attributes.
+    /// \brief Get request is EDNS version 0.
+    /// \return true if EDNS version 0
     /// \throw None
-    void setQueryEDNS(const bool is_edns_0, const bool is_edns_badver) {
-        req_is_edns_0_ = is_edns_0;
-        req_is_edns_badver_ = is_edns_badver;
+    bool getRequestEDNS0() const {
+        return (bit_attributes_[REQ_IS_EDNS_0]);
     };
 
-    /// \brief Set query DO bit.
+    /// \brief Set request EDNS attributes.
+    /// \param is_edns_0 true if request is EDNS version 0
+    /// \param is_edns_badver true if request is EDNS version other than 0
     /// \throw None
-    void setQueryDO(const bool is_dnssec_ok) {
-        req_is_dnssec_ok_ = is_dnssec_ok;
+    void setRequestEDNS0(const bool is_edns_0) {
+        bit_attributes_[REQ_IS_EDNS_0] = is_edns_0;
     };
 
-    /// \brief Set query TSIG attributes.
+    /// \brief Get request DNSSEC OK (DO) bit.
+    /// \return true if DNSSEC OK (DO) bit is set
     /// \throw None
-    void setQuerySig(const bool is_tsig, const bool is_sig0,
-                            const bool is_badsig)
+    bool getRequestDO() const {
+        return (bit_attributes_[REQ_IS_DNSSEC_OK]);
+    };
+
+    /// \brief Set request DNSSEC OK (DO) bit.
+    /// \param is_dnssec_ok true if DNSSEC OK (DO) bit is set
+    /// \throw None
+    void setRequestDO(const bool is_dnssec_ok) {
+        bit_attributes_[REQ_IS_DNSSEC_OK] = is_dnssec_ok;
+    };
+
+    /// \brief Get request TSIG signed and verified.
+    /// \return true if request is TSIG signed and verified
+    /// \throw None
+    bool getRequestSigTSIG() const {
+        return (bit_attributes_[REQ_IS_TSIG]);
+    };
+
+    /// \brief Get request SIG(0) signed and verified.
+    /// \return true if request is SIG(0) signed and verified
+    /// \throw None
+    bool getRequestSigSIG0() const {
+        return (bit_attributes_[REQ_IS_SIG0]);
+    };
+
+    /// \brief Get request signature is bad.
+    /// \return true if request signature is bad
+    /// \throw None
+    bool getRequestSigBadSig() const {
+        return (bit_attributes_[REQ_IS_BADSIG]);
+    };
+
+    /// \brief Set request TSIG attributes.
+    /// \param is_tsig true if request is TSIG signed
+    /// \param is_sig0 true if request is SIG(0) signed
+    /// \param is_badsig true if request signature is bad
+    /// \throw None
+    void setRequestSig(const bool is_tsig, const bool is_sig0,
+                       const bool is_badsig)
     {
-        req_is_tsig_ = is_tsig;
-        req_is_sig0_ = is_sig0;
-        req_is_badsig_ = is_badsig;
+        assert(!(is_tsig && is_sig0));
+        bit_attributes_[REQ_IS_TSIG] = is_tsig;
+        bit_attributes_[REQ_IS_SIG0] = is_sig0;
+        bit_attributes_[REQ_IS_BADSIG] = is_badsig;
+    };
+
+    /// \brief Get if the response is truncated.
+    /// \return true if the response is truncated
+    /// \throw None
+    bool getResponseTruncated() const {
+        return (bit_attributes_[RES_IS_TRUNCATED]);
     };
 
     /// \brief Set if the response is truncated.
+    /// \param is_truncated true if the response is truncated
     /// \throw None
     void setResponseTruncated(const bool is_truncated) {
-        res_is_truncated_ = is_truncated;
+        bit_attributes_[RES_IS_TRUNCATED] = is_truncated;
     };
 
     /// \brief Reset attributes.
@@ -118,27 +189,21 @@ public:
         req_ip_version_ = 0;
         req_transport_protocol_ = 0;
         req_opcode_ = 0;
-        req_is_edns_0_ = false;
-        req_is_edns_badver_ = false;
-        req_is_dnssec_ok_ = false;
-        req_is_tsig_ = false;
-        req_is_sig0_ = false;
-        req_is_badsig_ = false;
-        res_is_truncated_ = false;
+        bit_attributes_.reset();
     };
 };
 
-/// \brief Set of query counters.
+/// \brief Set of DNS message counters.
 ///
-/// \c Counters is set of query counters class. It holds query counters
-/// and provides an interface to increment the counter of specified type
-/// (e.g. UDP query, TCP query).
+/// \c Counters is set of DNS message counters class. It holds DNS message
+/// counters and provides an interface to increment the counter of specified
+/// type (e.g. UDP message, TCP message).
 ///
 /// This class also provides a function to send statistics information to
 /// statistics module.
 ///
 /// This class is designed to be a part of \c AuthSrv.
-/// Call \c inc() to increment a counter for the query.
+/// Call \c inc() to increment a counter for the message.
 /// Call \c getStatistics() to answer statistics information to statistics
 /// module with statistics_session, when the command \c getstats is received.
 ///
@@ -150,16 +215,14 @@ public:
 /// This class is constructed on startup of the server, so
 /// construction overhead of this approach should be acceptable.
 ///
-/// \todo Hold counters for each query types (Notify, Axfr, Ixfr, Normal)
+/// \todo Hold counters for each message types (Notify, Axfr, Ixfr, Normal)
 /// \todo Consider overhead of \c Counters::inc()
 class Counters : boost::noncopyable {
 private:
-    // counter for query/response
-    isc::statistics::Counter server_qr_counter_;
-    // set of counters for zones
-    isc::statistics::CounterDictionary zone_qr_counters_;
-    void incRequest(const QRAttributes& qrattrs);
-    void incResponse(const QRAttributes& qrattrs,
+    // counter for DNS message attributes
+    isc::statistics::Counter server_msg_counter_;
+    void incRequest(const MessageAttributes& msgattrs);
+    void incResponse(const MessageAttributes& msgattrs,
                      const isc::dns::Message& response);
 public:
     /// \brief A type of statistics item tree in isc::data::MapElement.
@@ -189,14 +252,14 @@ public:
     /// This constructor is mostly exception free. But it may still throw
     /// a standard exception if memory allocation fails inside the method.
     ///
-    /// \param qrattrs Query/Response attributes.
+    /// \param msgattrs DNS message attributes.
     /// \param response DNS response message.
     /// \param done DNS response was sent to the client.
     ///
     /// \throw std::bad_alloc Internal resource allocation fails
     ///
-    void inc(const QRAttributes& qrattrs, const isc::dns::Message& response,
-             const bool done);
+    void inc(const MessageAttributes& msgattrs,
+             const isc::dns::Message& response, const bool done);
 
     /// \brief Get statistics counters.
     ///
diff --git a/src/bin/auth/statistics_items.h.pre b/src/bin/auth/statistics_items.h.pre
index 8145378..eb76637 100644
--- a/src/bin/auth/statistics_items.h.pre
+++ b/src/bin/auth/statistics_items.h.pre
@@ -22,16 +22,24 @@ namespace isc {
 namespace auth {
 namespace statistics {
 
-struct CounterTypeTree {
+struct CounterSpec {
+    // item name: Name of this node. This appears in the spec file.
     const char* const name;
-    const struct CounterTypeTree* const sub_tree;
+    // sub counters: If this is a branch node, sub_counters points to
+    //               CounterSpec which contains child nodes. Otherwise, for
+    //               leaf nodes, sub_counters is NULL.
+    const struct CounterSpec* const sub_counters;
+    // counter id: If this is a leaf node, counter_id is an enumerator of this
+    //             item. Otherwise, for branch nodes, counter_id is NOT_ITEM.
     const int counter_id;
 };
 
 // ### STATISTICS ITEMS DECLARATION ###
 
-extern const int opcode_to_qrcounter[];
-extern const int rcode_to_qrcounter[];
+extern const int opcode_to_msgcounter[];
+extern const size_t num_opcode_to_msgcounter;
+extern const int rcode_to_msgcounter[];
+extern const size_t num_rcode_to_msgcounter;
 
 } // namespace statistics
 } // namespace auth
diff --git a/src/bin/auth/statistics_msg_items.def b/src/bin/auth/statistics_msg_items.def
new file mode 100644
index 0000000..467ead2
--- /dev/null
+++ b/src/bin/auth/statistics_msg_items.def
@@ -0,0 +1,48 @@
+request	msg_counter_request		Request statistics	=
+	v4		MSG_REQUEST_IPV4	Number of IPv4 requests received by the b10-auth server.
+	v6		MSG_REQUEST_IPV6	Number of IPv6 requests received by the b10-auth server.
+	edns0		MSG_REQUEST_EDNS0	Number of requests with EDNS0 received by the b10-auth server.
+	badednsver	MSG_REQUEST_BADEDNSVER	Number of requests with unsupported EDNS version received by the b10-auth server.
+	tsig		MSG_REQUEST_TSIG	Number of requests with TSIG received by the b10-auth server.
+	sig0		MSG_REQUEST_SIG0	Number of requests with SIG(0) received by the b10-auth server; currently not implemented in BIND 10.
+	badsig		MSG_REQUEST_BADSIG	Number of requests with invalid TSIG or SIG(0) signature received by the b10-auth server.
+	udp		MSG_REQUEST_UDP		Number of UDP requests received by the b10-auth server.
+	tcp		MSG_REQUEST_TCP		Number of UDP requests received by the b10-auth server.
+	dnssec_ok	MSG_REQUEST_DNSSEC_OK	Number of requests with "DNSSEC OK" (DO) bit was set received by the b10-auth server.
+	;
+opcode	msg_counter_opcode		Opcode statistics	=
+	query		MSG_OPCODE_QUERY		Number of Opcode=QUERY requests received by the b10-auth server.
+	iquery		MSG_OPCODE_IQUERY	Number of Opcode=IQUERY requests received by the b10-auth server.
+	status		MSG_OPCODE_STATUS	Number of Opcode=STATUS requests received by the b10-auth server.
+	notify		MSG_OPCODE_NOTIFY	Number of Opcode=NOTIFY requests received by the b10-auth server.
+	update		MSG_OPCODE_UPDATE	Number of Opcode=UPDATE requests received by the b10-auth server.
+	other		MSG_OPCODE_OTHER	Number of requests in other OpCode received by the b10-auth server.
+	;
+responses	MSG_RESPONSE			Number of responses sent by the b10-auth server.
+response	msg_counter_response	Response statistics	=
+	truncated	MSG_RESPONSE_TRUNCATED	Number of truncated responses sent by the b10-auth server.
+	edns0		MSG_RESPONSE_EDNS0	Number of responses with EDNS0 sent by the b10-auth server.
+	tsig		MSG_RESPONSE_TSIG	Number of responses with TSIG sent by the b10-auth server.
+	sig0		MSG_RESPONSE_SIG0	Number of responses with SIG(0) sent by the b10-auth server; currently not implemented in BIND 10.
+	;
+qrysuccess	MSG_QRYSUCCESS			Number of queries received by the b10-auth server resulted in rcode = NOERROR and the number of answer RR >= 1.
+qryauthans	MSG_QRYAUTHANS			Number of queries received by the b10-auth server resulted in authoritative answer.
+qrynoauthans	MSG_QRYNOAUTHANS		Number of queries received by the b10-auth server resulted in non-authoritative answer.
+qryreferral	MSG_QRYREFERRAL			Number of queries received by the b10-auth server resulted in referral answer.
+qrynxrrset	MSG_QRYNXRRSET			Number of queries received by the b10-auth server resulted in NOERROR but the number of answer RR == 0.
+authqryrej	MSG_QRYREJECT			Number of authoritative queries rejected by the b10-auth server.
+rcode		msg_counter_rcode	Rcode statistics	=
+	noerror		MSG_RCODE_NOERROR	Number of requests received by the b10-auth server resulted in RCODE = 0 (NoError).
+	formerr		MSG_RCODE_FORMERR	Number of requests received by the b10-auth server resulted in RCODE = 1 (FormErr).
+	servfail	MSG_RCODE_SERVFAIL	Number of requests received by the b10-auth server resulted in RCODE = 2 (ServFail).
+	nxdomain	MSG_RCODE_NXDOMAIN	Number of requests received by the b10-auth server resulted in RCODE = 3 (NXDomain).
+	notimp		MSG_RCODE_NOTIMP	Number of requests received by the b10-auth server resulted in RCODE = 4 (NotImp).
+	refused		MSG_RCODE_REFUSED	Number of requests received by the b10-auth server resulted in RCODE = 5 (Refused).
+	yxdomain	MSG_RCODE_YXDOMAIN	Number of requests received by the b10-auth server resulted in RCODE = 6 (YXDomain).
+	yxrrset		MSG_RCODE_YXRRSET	Number of requests received by the b10-auth server resulted in RCODE = 7 (YXRRSet).
+	nxrrset		MSG_RCODE_NXRRSET	Number of requests received by the b10-auth server resulted in RCODE = 8 (NXRRSet).
+	notauth		MSG_RCODE_NOTAUTH	Number of requests received by the b10-auth server resulted in RCODE = 9 (NotAuth).
+	notzone		MSG_RCODE_NOTZONE	Number of requests received by the b10-auth server resulted in RCODE = 10 (NotZone).
+	badvers		MSG_RCODE_BADVERS	Number of requests received by the b10-auth server resulted in RCODE = 16 (BADVERS).
+	other		MSG_RCODE_OTHER		Number of requests received by the b10-auth server resulted in other RCODEs.
+	;
diff --git a/src/bin/auth/statistics_qr_items.def b/src/bin/auth/statistics_qr_items.def
deleted file mode 100644
index c17da60..0000000
--- a/src/bin/auth/statistics_qr_items.def
+++ /dev/null
@@ -1,48 +0,0 @@
-request	QRCounterRequest	Request statistics	=
-	v4		QR_REQUEST_IPV4		Number of IPv4 requests received by the b10-auth server.
-	v6		QR_REQUEST_IPV6		Number of IPv6 requests received by the b10-auth server.
-	edns0		QR_REQUEST_EDNS0	Number of requests with EDNS0 received by the b10-auth server.
-	badednsver	QR_REQUEST_BADEDNSVER	Number of requests with unsupported EDNS version received by the b10-auth server.
-	tsig		QR_REQUEST_TSIG		Number of requests with TSIG received by the b10-auth server.
-	sig0		QR_REQUEST_SIG0		Number of requests with SIG(0) received by the b10-auth server; currently not implemented in BIND 10.
-	badsig		QR_REQUEST_BADSIG	Number of requests with invalid TSIG or SIG(0) signature received by the b10-auth server.
-	udp		QR_REQUEST_UDP		Number of UDP requests received by the b10-auth server.
-	tcp		QR_REQUEST_TCP		Number of UDP requests received by the b10-auth server.
-	dnssec_ok	QR_REQUEST_DNSSEC_OK	Number of requests with "DNSSEC OK" (DO) bit was set received by the b10-auth server.
-	;
-opcode	QRCounterOpcode		Opcode statistics	=
-	query		QR_OPCODE_QUERY		Number of Opcode=QUERY requests received by the b10-auth server.
-	iquery		QR_OPCODE_IQUERY	Number of Opcode=IQUERY requests received by the b10-auth server.
-	status		QR_OPCODE_STATUS	Number of Opcode=STATUS requests received by the b10-auth server.
-	notify		QR_OPCODE_NOTIFY	Number of Opcode=NOTIFY requests received by the b10-auth server.
-	update		QR_OPCODE_UPDATE	Number of Opcode=UPDATE requests received by the b10-auth server.
-	other		QR_OPCODE_OTHER		Number of requests in other OpCode received by the b10-auth server.
-	;
-responses	QR_RESPONSE			Number of responses sent by the b10-auth server.
-response	QRCounterResponse	Response statistics	=
-	truncated	QR_RESPONSE_TRUNCATED	Number of truncated responses sent by the b10-auth server.
-	edns0		QR_RESPONSE_EDNS0	Number of responses with EDNS0 sent by the b10-auth server.
-	tsig		QR_RESPONSE_TSIG	Number of responses with TSIG sent by the b10-auth server.
-	sig0		QR_RESPONSE_SIG0	Number of responses with SIG(0) sent by the b10-auth server; currently not implemented in BIND 10.
-	;
-qrysuccess	QR_QRYSUCCESS			Number of queries received by the b10-auth server resulted in rcode = NOERROR and the number of answer RR >= 1.
-qryauthans	QR_QRYAUTHANS			Number of queries received by the b10-auth server resulted in authoritative answer.
-qrynoauthans	QR_QRYNOAUTHANS			Number of queries received by the b10-auth server resulted in non-authoritative answer.
-qryreferral	QR_QRYREFERRAL			Number of queries received by the b10-auth server resulted in referral answer.
-qrynxrrset	QR_QRYNXRRSET			Number of queries received by the b10-auth server resulted in NOERROR but the number of answer RR == 0.
-authqryrej	QR_QRYREJECT			Number of authoritative queries rejected by the b10-auth server.
-rcode		QRCounterRcode	Rcode statistics	=
-	noerror		QR_RCODE_NOERROR	Number of queries received by the b10-auth server resulted in RCODE = 0 (NoError).
-	formerr		QR_RCODE_FORMERR	Number of queries received by the b10-auth server resulted in RCODE = 1 (FormErr).
-	servfail	QR_RCODE_SERVFAIL	Number of queries received by the b10-auth server resulted in RCODE = 2 (ServFail).
-	nxdomain	QR_RCODE_NXDOMAIN	Number of queries received by the b10-auth server resulted in RCODE = 3 (NXDomain).
-	notimp		QR_RCODE_NOTIMP		Number of queries received by the b10-auth server resulted in RCODE = 4 (NotImp).
-	refused		QR_RCODE_REFUSED	Number of queries received by the b10-auth server resulted in RCODE = 5 (Refused).
-	yxdomain	QR_RCODE_YXDOMAIN	Number of queries received by the b10-auth server resulted in RCODE = 6 (YXDomain).
-	yxrrset		QR_RCODE_YXRRSET	Number of queries received by the b10-auth server resulted in RCODE = 7 (YXRRSet).
-	nxrrset		QR_RCODE_NXRRSET	Number of queries received by the b10-auth server resulted in RCODE = 8 (NXRRSet).
-	notauth		QR_RCODE_NOTAUTH	Number of queries received by the b10-auth server resulted in RCODE = 9 (NotAuth).
-	notzone		QR_RCODE_NOTZONE	Number of queries received by the b10-auth server resulted in RCODE = 10 (NotZone).
-	badvers		QR_RCODE_BADVERS	Number of queries received by the b10-auth server resulted in RCODE = 16 (BADVERS).
-	other		QR_RCODE_OTHER		Number of queries received by the b10-auth server resulted in other RCODEs.
-	;
diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc
index 47b744b..22cd2ca 100644
--- a/src/bin/auth/tests/auth_srv_unittest.cc
+++ b/src/bin/auth/tests/auth_srv_unittest.cc
@@ -89,7 +89,8 @@ const char* const STATIC_DSRC_FILE = DSRC_DIR "/static.zone";
 
 // This is a configuration that uses the in-memory data source containing
 // a signed example zone.
-const char* const CONFIG_INMEMORY_EXAMPLE = TEST_DATA_DIR "/rfc5155-example.zone.signed";
+const char* const CONFIG_INMEMORY_EXAMPLE =
+    TEST_DATA_DIR "/rfc5155-example.zone.signed";
 
 class AuthSrvTest : public SrvTestBase {
 protected:
@@ -120,7 +121,8 @@ protected:
 
     // Helper for checking Rcode statistic counters;
     // Checks for one specific Rcode statistics counter value
-    void checkRcodeCounter(const std::string& rcode_name, const int rcode_value,
+    void checkRcodeCounter(const std::string& rcode_name,
+                           const int rcode_value,
                            const int expected_value) const
     {
             EXPECT_EQ(expected_value, rcode_value) <<
@@ -244,6 +246,13 @@ expectCounterItem(ConstElementPtr stats,
 
 // We did not configure any client lists. Therefore it should be REFUSED
 TEST_F(AuthSrvTest, noClientList) {
+    // The counters should be initialized to 0.
+    ConstElementPtr stats_init = server.getStatistics()->
+        get("zones")->get("_SERVER_");
+    expectCounterItem(stats_init->get("opcode"), "query", 0);
+    expectCounterItem(stats_init, "responses", 0);
+    expectCounterItem(stats_init->get("rcode"), "refused", 0);
+
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                        default_qid, Name("version.bind"),
                                        RRClass::CH(), RRType::TXT());
@@ -254,6 +263,12 @@ TEST_F(AuthSrvTest, noClientList) {
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::REFUSED(),
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
+
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats_after->get("opcode"), "query", 1);
+    expectCounterItem(stats_after, "responses", 1);
+    expectCounterItem(stats_after->get("rcode"), "refused", 1);
 }
 
 // Unsupported requests.  Should result in NOTIMP.
@@ -272,15 +287,33 @@ TEST_F(AuthSrvTest, multiQuestion) {
 // Incoming data doesn't even contain the complete header.  Must be silently
 // dropped.
 TEST_F(AuthSrvTest, shortMessage) {
+    // The counters should be initialized to 0.
+    ConstElementPtr stats_init = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats_init, "responses", 0);
+
     shortMessage();
+
     checkAllRcodeCountersZero();
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats_after, "responses", 0);
 }
 
 // Response messages.  Must be silently dropped, whether it's a valid response
 // or malformed or could otherwise cause a protocol error.
 TEST_F(AuthSrvTest, response) {
+    // The counters should be initialized to 0.
+    ConstElementPtr stats_init = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats_init, "responses", 0);
+
     response();
+
     checkAllRcodeCountersZero();
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats_after, "responses", 0);
 }
 
 // Query with a broken question
@@ -297,8 +330,19 @@ TEST_F(AuthSrvTest, shortAnswer) {
 
 // Query with unsupported version of EDNS.
 TEST_F(AuthSrvTest, ednsBadVers) {
+    // The counters should be initialized to 0.
+    ConstElementPtr stats_init = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats_init, "responses", 0);
+    expectCounterItem(stats_init->get("request"), "badednsver", 0);
+
     ednsBadVers();
+
     checkAllRcodeCountersZeroExcept(Rcode::BADVERS(), 1);
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats_after, "responses", 1);
+    expectCounterItem(stats_after->get("request"), "badednsver", 1);
 }
 
 TEST_F(AuthSrvTest, AXFROverUDP) {
@@ -306,6 +350,13 @@ TEST_F(AuthSrvTest, AXFROverUDP) {
 }
 
 TEST_F(AuthSrvTest, AXFRSuccess) {
+    // The counters should be initialized to 0.
+    ConstElementPtr stats_init = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats_init->get("opcode"), "query", 0);
+    expectCounterItem(stats_init, "responses", 0);
+    expectCounterItem(stats_init->get("response"), "truncated", 0);
+
     EXPECT_FALSE(xfrout.isConnected());
     UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                                        Name("example.com"), RRClass::IN(),
@@ -317,12 +368,26 @@ TEST_F(AuthSrvTest, AXFRSuccess) {
                           &dnsserv);
     EXPECT_FALSE(dnsserv.hasAnswer());
     EXPECT_TRUE(xfrout.isConnected());
+
     checkAllRcodeCountersZero();
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats_after->get("opcode"), "query", 1);
+    expectCounterItem(stats_after, "responses", 0);
+    expectCounterItem(stats_after->get("response"), "truncated", 0);
 }
 
 // Give the server a signed request, but don't give it the key. It will
 // not be able to verify it, returning BADKEY
 TEST_F(AuthSrvTest, TSIGSignedBadKey) {
+    // The counters should be initialized to 0.
+    ConstElementPtr stats_init = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats_init->get("request"), "tsig", 0);
+    expectCounterItem(stats_init->get("request"), "badsig", 0);
+    expectCounterItem(stats_init, "responses", 0);
+    expectCounterItem(stats_init->get("response"), "tsig", 0);
+
     TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
     TSIGContext context(key);
     UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
@@ -351,12 +416,27 @@ TEST_F(AuthSrvTest, TSIGSignedBadKey) {
     EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
         "It should be unsigned with this error";
 
+    // check Rcode counters and TSIG counters
     checkAllRcodeCountersZeroExcept(Rcode::NOTAUTH(), 1);
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats_after->get("request"), "tsig", 1);
+    expectCounterItem(stats_after->get("request"), "badsig", 1);
+    expectCounterItem(stats_after, "responses", 1);
+    expectCounterItem(stats_after->get("response"), "tsig", 1);
 }
 
 // Give the server a signed request, but signed by a different key
 // (with the same name). It should return BADSIG
 TEST_F(AuthSrvTest, TSIGBadSig) {
+    // The counters should be initialized to 0.
+    ConstElementPtr stats_init = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats_init->get("request"), "tsig", 0);
+    expectCounterItem(stats_init->get("request"), "badsig", 0);
+    expectCounterItem(stats_init, "responses", 0);
+    expectCounterItem(stats_init->get("response"), "tsig", 0);
+
     TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
     TSIGContext context(key);
     UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
@@ -387,12 +467,27 @@ TEST_F(AuthSrvTest, TSIGBadSig) {
         "It should be unsigned with this error";
 
     checkAllRcodeCountersZeroExcept(Rcode::NOTAUTH(), 1);
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats_after->get("request"), "tsig", 1);
+    expectCounterItem(stats_after->get("request"), "badsig", 1);
+    expectCounterItem(stats_after, "responses", 1);
+    expectCounterItem(stats_after->get("response"), "tsig", 1);
 }
 
 // Give the server a signed unsupported request with a bad signature.
 // This checks the server first verifies the signature before anything
 // else.
 TEST_F(AuthSrvTest, TSIGCheckFirst) {
+    // The counters should be initialized to 0.
+    ConstElementPtr stats_init = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats_init->get("request"), "tsig", 0);
+    expectCounterItem(stats_init->get("request"), "badsig", 0);
+    expectCounterItem(stats_init, "responses", 0);
+    expectCounterItem(stats_init->get("response"), "tsig", 0);
+    expectCounterItem(stats_init->get("opcode"), "other", 0);
+
     TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
     TSIGContext context(key);
     // Pass a wrong opcode there. The server shouldn't know what to do
@@ -423,12 +518,16 @@ TEST_F(AuthSrvTest, TSIGCheckFirst) {
     EXPECT_EQ(TSIGError::BAD_SIG_CODE, tsig->getRdata().getError());
     EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
         "It should be unsigned with this error";
+
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats_after->get("request"), "tsig", 1);
+    expectCounterItem(stats_after->get("request"), "badsig", 1);
+    expectCounterItem(stats_after, "responses", 1);
+    expectCounterItem(stats_after->get("response"), "tsig", 1);
     // TSIG should have failed, and so the per opcode counter shouldn't be
     // incremented.
-    ConstElementPtr stats = server.getStatistics();
-    expectCounterItem(stats->get("zones")->get("_SERVER_")->get("opcode"),
-                      "other", 0);
-
+    expectCounterItem(stats_after->get("opcode"), "other", 0);
     checkAllRcodeCountersZeroExcept(Rcode::NOTAUTH(), 1);
 }
 
@@ -554,6 +653,12 @@ TEST_F(AuthSrvTest, IXFRDisconnectFail) {
 }
 
 TEST_F(AuthSrvTest, notify) {
+    // The counters should be initialized to 0.
+    ConstElementPtr stats_init = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats_init->get("opcode"), "notify", 0);
+    expectCounterItem(stats_init, "responses", 0);
+
     UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                        default_qid, Name("example.com"),
                                        RRClass::IN(), RRType::SOA());
@@ -567,7 +672,8 @@ TEST_F(AuthSrvTest, notify) {
     // external module.  Check them.
     EXPECT_EQ("Zonemgr", notify_session.getMessageDest());
     EXPECT_EQ("notify",
-              notify_session.getSentMessage()->get("command")->get(0)->stringValue());
+              notify_session.getSentMessage()->get("command")->get(0)->
+                  stringValue());
     ConstElementPtr notify_args =
         notify_session.getSentMessage()->get("command")->get(1);
     EXPECT_EQ("example.com.", notify_args->get("zone_name")->stringValue());
@@ -586,9 +692,19 @@ TEST_F(AuthSrvTest, notify) {
     EXPECT_EQ(RRType::SOA(), question->getType());
 
     checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats_after->get("opcode"), "notify", 1);
+    expectCounterItem(stats_after, "responses", 1);
 }
 
 TEST_F(AuthSrvTest, notifyForCHClass) {
+    // The counters should be initialized to 0.
+    ConstElementPtr stats_init = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats_init->get("opcode"), "notify", 0);
+    expectCounterItem(stats_init, "responses", 0);
+
     // Same as the previous test, but for the CH RRClass.
     UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                        default_qid, Name("example.com"),
@@ -604,9 +720,22 @@ TEST_F(AuthSrvTest, notifyForCHClass) {
     ConstElementPtr notify_args =
         notify_session.getSentMessage()->get("command")->get(1);
     EXPECT_EQ("CH", notify_args->get("zone_class")->stringValue());
+
+    checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats_after->get("opcode"), "notify", 1);
+    expectCounterItem(stats_after, "responses", 1);
 }
 
 TEST_F(AuthSrvTest, notifyEmptyQuestion) {
+    // The counters should be initialized to 0.
+    ConstElementPtr stats_init = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats_init->get("opcode"), "notify", 0);
+    expectCounterItem(stats_init, "responses", 0);
+    expectCounterItem(stats_init->get("rcode"), "formerr", 0);
+
     request_message.clear(Message::RENDER);
     request_message.setOpcode(Opcode::NOTIFY());
     request_message.setRcode(Rcode::NOERROR());
@@ -619,6 +748,12 @@ TEST_F(AuthSrvTest, notifyEmptyQuestion) {
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
                 Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
+
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats_after->get("opcode"), "notify", 1);
+    expectCounterItem(stats_after, "responses", 1);
+    expectCounterItem(stats_after->get("rcode"), "formerr", 1);
 }
 
 TEST_F(AuthSrvTest, notifyMultiQuestions) {
@@ -791,6 +926,15 @@ TEST_F(AuthSrvTest, DISABLED_TSIGSigned) { // Needs builtin
 #else
 TEST_F(AuthSrvTest, TSIGSigned) {
 #endif
+    // The counters should be initialized to 0.
+    ConstElementPtr stats_init = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats_init->get("request"), "tsig", 0);
+    expectCounterItem(stats_init->get("request"), "sig0", 0);
+    expectCounterItem(stats_init->get("request"), "badsig", 0);
+    expectCounterItem(stats_init, "responses", 0);
+    expectCounterItem(stats_init, "qryauthans", 0);
+
     // Prepare key, the client message, etc
     updateBuiltin(&server);
     const TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
@@ -824,6 +968,13 @@ TEST_F(AuthSrvTest, TSIGSigned) {
         "The server signed the response, but it doesn't seem to be valid";
 
     checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats_after->get("request"), "tsig", 1);
+    expectCounterItem(stats_after->get("request"), "sig0", 0);
+    expectCounterItem(stats_after->get("request"), "badsig", 0);
+    expectCounterItem(stats_after, "responses", 1);
+    expectCounterItem(stats_after, "qryauthans", 1);
 }
 
 // Same test emulating the UDPServer class behavior (defined in libasiolink).
@@ -924,8 +1075,8 @@ TEST_F(AuthSrvTest, updateConfig) {
     server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
-    headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
-                QR_FLAG | AA_FLAG, 1, 1, 1, 0);
+    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
+                opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 1, 0);
 }
 
 #ifdef USE_STATIC_LINK
@@ -945,6 +1096,11 @@ TEST_F(AuthSrvTest, datasourceFail) {
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
+
+    checkAllRcodeCountersZeroExcept(Rcode::SERVFAIL(), 1);
+    ConstElementPtr stats = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    expectCounterItem(stats, "responses", 1);
 }
 
 #ifdef USE_STATIC_LINK
@@ -1058,10 +1214,30 @@ TEST_F(AuthSrvTest, queryCounterUDPNormal) {
     // The counters should be initialized to 0.
     ConstElementPtr stats_init = server.getStatistics()->
         get("zones")->get("_SERVER_");
+    expectCounterItem(stats_init->get("request"), "v4", 0);
+    expectCounterItem(stats_init->get("request"), "v6", 0);
     expectCounterItem(stats_init->get("request"), "udp", 0);
     expectCounterItem(stats_init->get("request"), "tcp", 0);
+    expectCounterItem(stats_init->get("request"), "edns0", 0);
+    expectCounterItem(stats_init->get("request"), "badednsver", 0);
+    expectCounterItem(stats_init->get("request"), "tsig", 0);
+    expectCounterItem(stats_init->get("request"), "sig0", 0);
+    expectCounterItem(stats_init->get("request"), "badsig", 0);
+    expectCounterItem(stats_init->get("request"), "dnssec_ok", 0);
     expectCounterItem(stats_init->get("opcode"), "query", 0);
+    expectCounterItem(stats_init, "responses", 0);
+    expectCounterItem(stats_init->get("response"), "truncated", 0);
+    expectCounterItem(stats_init->get("response"), "edns0", 0);
+    expectCounterItem(stats_init->get("response"), "tsig", 0);
+    expectCounterItem(stats_init->get("response"), "sig0", 0);
     expectCounterItem(stats_init->get("rcode"), "refused", 0);
+    expectCounterItem(stats_init, "qrysuccess", 0);
+    expectCounterItem(stats_init, "qryauthans", 0);
+    expectCounterItem(stats_init, "qrynoauthans", 0);
+    expectCounterItem(stats_init, "qryreferral", 0);
+    expectCounterItem(stats_init, "qrynxrrset", 0);
+    expectCounterItem(stats_init, "authqryrej", 0);
+    //
     // Create UDP message and process.
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                        default_qid, Name("example.com"),
@@ -1069,27 +1245,107 @@ TEST_F(AuthSrvTest, queryCounterUDPNormal) {
     createRequestPacket(request_message, IPPROTO_UDP);
     server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
-    // After processing the UDP query, these counters should be incremented:
-    //   request.udp, opcode.query, rcode.refused
-    // and these counters should not be incremented:
-    //   request.tcp
+
     ConstElementPtr stats_after = server.getStatistics()->
         get("zones")->get("_SERVER_");
+    expectCounterItem(stats_after->get("request"), "v4", 1);
+    expectCounterItem(stats_after->get("request"), "v6", 0);
     expectCounterItem(stats_after->get("request"), "udp", 1);
     expectCounterItem(stats_after->get("request"), "tcp", 0);
+    expectCounterItem(stats_after->get("request"), "edns0", 0);
+    expectCounterItem(stats_after->get("request"), "badednsver", 0);
+    expectCounterItem(stats_after->get("request"), "tsig", 0);
+    expectCounterItem(stats_after->get("request"), "sig0", 0);
+    expectCounterItem(stats_after->get("request"), "badsig", 0);
+    expectCounterItem(stats_after->get("request"), "dnssec_ok", 0);
     expectCounterItem(stats_after->get("opcode"), "query", 1);
+    expectCounterItem(stats_after, "responses", 1);
+    expectCounterItem(stats_after->get("response"), "truncated", 0);
+    expectCounterItem(stats_after->get("response"), "edns0", 0);
+    expectCounterItem(stats_after->get("response"), "tsig", 0);
+    expectCounterItem(stats_after->get("response"), "sig0", 0);
     expectCounterItem(stats_after->get("rcode"), "refused", 1);
+    expectCounterItem(stats_after, "qrysuccess", 0);
+    expectCounterItem(stats_after, "qryauthans", 0);
+    expectCounterItem(stats_after, "qrynoauthans", 1);
+    expectCounterItem(stats_after, "qryreferral", 0);
+    expectCounterItem(stats_after, "qrynxrrset", 0);
+    expectCounterItem(stats_after, "authqryrej", 1);
 }
 
-// Submit TCP normal query and check query counter
-TEST_F(AuthSrvTest, queryCounterTCPNormal) {
+// Submit UDP normal query with DNSSEC and check query counter
+TEST_F(AuthSrvTest, queryCounterUDPNormalWithDNSSEC) {
     // The counters should be initialized to 0.
     ConstElementPtr stats_init = server.getStatistics()->
         get("zones")->get("_SERVER_");
+    expectCounterItem(stats_init->get("request"), "v4", 0);
+    expectCounterItem(stats_init->get("request"), "v6", 0);
     expectCounterItem(stats_init->get("request"), "udp", 0);
     expectCounterItem(stats_init->get("request"), "tcp", 0);
+    expectCounterItem(stats_init->get("request"), "edns0", 0);
+    expectCounterItem(stats_init->get("request"), "badednsver", 0);
+    expectCounterItem(stats_init->get("request"), "tsig", 0);
+    expectCounterItem(stats_init->get("request"), "sig0", 0);
+    expectCounterItem(stats_init->get("request"), "badsig", 0);
+    expectCounterItem(stats_init->get("request"), "dnssec_ok", 0);
     expectCounterItem(stats_init->get("opcode"), "query", 0);
+    expectCounterItem(stats_init, "responses", 0);
+    expectCounterItem(stats_init->get("response"), "truncated", 0);
+    expectCounterItem(stats_init->get("response"), "edns0", 0);
+    expectCounterItem(stats_init->get("response"), "tsig", 0);
+    expectCounterItem(stats_init->get("response"), "sig0", 0);
     expectCounterItem(stats_init->get("rcode"), "refused", 0);
+    expectCounterItem(stats_init, "qrysuccess", 0);
+    expectCounterItem(stats_init, "qryauthans", 0);
+    expectCounterItem(stats_init, "qrynoauthans", 0);
+    expectCounterItem(stats_init, "qryreferral", 0);
+    expectCounterItem(stats_init, "qrynxrrset", 0);
+    expectCounterItem(stats_init, "authqryrej", 0);
+    //
+    // Create UDP message and process.
+    UnitTestUtil::createDNSSECRequestMessage(request_message, Opcode::QUERY(),
+                                             default_qid, Name("example.com"),
+                                             RRClass::IN(), RRType::NS());
+    createRequestPacket(request_message, IPPROTO_UDP);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
+
+    ConstElementPtr stats_after = server.getStatistics()->
+        get("zones")->get("_SERVER_");
+    expectCounterItem(stats_after->get("request"), "v4", 1);
+    expectCounterItem(stats_after->get("request"), "v6", 0);
+    expectCounterItem(stats_after->get("request"), "udp", 1);
+    expectCounterItem(stats_after->get("request"), "tcp", 0);
+    expectCounterItem(stats_after->get("request"), "edns0", 1);
+    expectCounterItem(stats_after->get("request"), "badednsver", 0);
+    expectCounterItem(stats_after->get("request"), "tsig", 0);
+    expectCounterItem(stats_after->get("request"), "sig0", 0);
+    expectCounterItem(stats_after->get("request"), "badsig", 0);
+    expectCounterItem(stats_after->get("request"), "dnssec_ok", 1);
+    expectCounterItem(stats_after->get("opcode"), "query", 1);
+    expectCounterItem(stats_after, "responses", 1);
+    expectCounterItem(stats_after->get("response"), "truncated", 0);
+    // XXX: with the current implementation, EDNS0 is omitted in
+    // makeErrorMessage.
+    expectCounterItem(stats_after->get("response"), "edns0", 0);
+    expectCounterItem(stats_after->get("response"), "tsig", 0);
+    expectCounterItem(stats_after->get("response"), "sig0", 0);
+    expectCounterItem(stats_after->get("rcode"), "refused", 1);
+    expectCounterItem(stats_after, "qrysuccess", 0);
+    expectCounterItem(stats_after, "qryauthans", 0);
+    expectCounterItem(stats_after, "qrynoauthans", 1);
+    expectCounterItem(stats_after, "qryreferral", 0);
+    expectCounterItem(stats_after, "qrynxrrset", 0);
+    expectCounterItem(stats_after, "authqryrej", 1);
+}
+
+// Submit TCP normal query and check query counter
+TEST_F(AuthSrvTest, queryCounterTCPNormal) {
+    // The counters should be initialized to 0.
+    ConstElementPtr stats_init = server.getStatistics()->
+        get("zones")->get("_SERVER_");
+    expectCounterItem(stats_init->get("request"), "udp", 0);
+    expectCounterItem(stats_init->get("request"), "tcp", 0);
     // Create TCP message and process.
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                        default_qid, Name("example.com"),
@@ -1105,8 +1361,6 @@ TEST_F(AuthSrvTest, queryCounterTCPNormal) {
         get("zones")->get("_SERVER_");
     expectCounterItem(stats_after->get("request"), "udp", 0);
     expectCounterItem(stats_after->get("request"), "tcp", 1);
-    expectCounterItem(stats_after->get("opcode"), "query", 1);
-    expectCounterItem(stats_after->get("rcode"), "refused", 1);
 }
 
 // Submit TCP AXFR query and check query counter
@@ -1114,9 +1368,9 @@ TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
     // The counters should be initialized to 0.
     ConstElementPtr stats_init = server.getStatistics()->
         get("zones")->get("_SERVER_");
-    expectCounterItem(stats_init->get("request"), "udp", 0);
-    expectCounterItem(stats_init->get("request"), "tcp", 0);
     expectCounterItem(stats_init->get("opcode"), "query", 0);
+    expectCounterItem(stats_init, "responses", 0);
+    expectCounterItem(stats_init->get("response"), "truncated", 0);
     UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                          Name("example.com"), RRClass::IN(), RRType::AXFR());
     createRequestPacket(request_message, IPPROTO_TCP);
@@ -1125,16 +1379,12 @@ TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
     server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
     EXPECT_FALSE(dnsserv.hasAnswer());
-    // After processing the TCP AXFR query, these counters should be
-    // incremented:
-    //   request.tcp, opcode.query
-    // and these counters should not be incremented:
-    //   request.udp, response
+
     ConstElementPtr stats_after = server.getStatistics()->
         get("zones")->get("_SERVER_");
-    expectCounterItem(stats_after->get("request"), "udp", 0);
-    expectCounterItem(stats_after->get("request"), "tcp", 1);
     expectCounterItem(stats_after->get("opcode"), "query", 1);
+    expectCounterItem(stats_after, "responses", 0);
+    expectCounterItem(stats_after->get("response"), "truncated", 0);
 }
 
 // Submit TCP IXFR query and check query counter
@@ -1142,9 +1392,9 @@ TEST_F(AuthSrvTest, queryCounterTCPIXFR) {
     // The counters should be initialized to 0.
     ConstElementPtr stats_init = server.getStatistics()->
         get("zones")->get("_SERVER_");
-    expectCounterItem(stats_init->get("request"), "udp", 0);
-    expectCounterItem(stats_init->get("request"), "tcp", 0);
     expectCounterItem(stats_init->get("opcode"), "query", 0);
+    expectCounterItem(stats_init, "responses", 0);
+    expectCounterItem(stats_init->get("response"), "truncated", 0);
     UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                          Name("example.com"), RRClass::IN(), RRType::IXFR());
     createRequestPacket(request_message, IPPROTO_TCP);
@@ -1153,25 +1403,21 @@ TEST_F(AuthSrvTest, queryCounterTCPIXFR) {
     server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
     EXPECT_FALSE(dnsserv.hasAnswer());
-    // After processing the TCP IXFR query, these counters should be
-    // incremented:
-    //   request.tcp, opcode.query
-    // and these counters should not be incremented:
-    //   request.udp, response
+
     ConstElementPtr stats_after = server.getStatistics()->
         get("zones")->get("_SERVER_");
-    expectCounterItem(stats_after->get("request"), "udp", 0);
-    expectCounterItem(stats_after->get("request"), "tcp", 1);
     expectCounterItem(stats_after->get("opcode"), "query", 1);
+    expectCounterItem(stats_after, "responses", 0);
+    expectCounterItem(stats_after->get("response"), "truncated", 0);
 }
 
 TEST_F(AuthSrvTest, queryCounterOpcodes) {
     int other_expected = 0;
-    for (int i = 0; i < 16; ++i) {
+    for (int i = 0; i < isc::auth::statistics::num_opcode_to_msgcounter; ++i) {
         std::string item_name;
         int expected;
-        if (isc::auth::statistics::opcode_to_qrcounter[i] ==
-            isc::auth::statistics::QR_OPCODE_OTHER) {
+        if (isc::auth::statistics::opcode_to_msgcounter[i] ==
+            isc::auth::statistics::MSG_OPCODE_OTHER) {
             item_name = "OTHER";
             other_expected += i + 1;
             expected = other_expected;
diff --git a/src/bin/auth/tests/statistics_unittest.cc.pre b/src/bin/auth/tests/statistics_unittest.cc.pre
index 22881f8..6599853 100644
--- a/src/bin/auth/tests/statistics_unittest.cc.pre
+++ b/src/bin/auth/tests/statistics_unittest.cc.pre
@@ -14,14 +14,13 @@
 
 #include <config.h>
 
-#include <string>
-
 #include <gtest/gtest.h>
 
 #include <boost/bind.hpp>
 
 #include <dns/opcode.h>
 #include <dns/rcode.h>
+#include <dns/rrttl.h>
 
 #include <cc/data.h>
 #include <cc/session.h>
@@ -31,6 +30,9 @@
 
 #include <dns/tests/unittest_util.h>
 
+#include <string>
+#include <map>
+
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -54,14 +56,15 @@ protected:
     Counters counters;
 };
 
+// flatten counters
 void
 flatten(std::map<std::string, int>& flat_map, const std::string& prefix,
         const isc::data::ConstElementPtr map_element) {
     std::map<std::string, ConstElementPtr> map = map_element->mapValue();
     for (std::map<std::string, ConstElementPtr>::const_iterator
-            i = map.begin(), e = map.end();
-            i != e;
-            ++i)
+             i = map.begin(), e = map.end();
+         i != e;
+         ++i)
     {
         switch (i->second->getType()) {
             case isc::data::Element::map:
@@ -71,16 +74,17 @@ flatten(std::map<std::string, int>& flat_map, const std::string& prefix,
                 flat_map[prefix + i->first] = i->second->intValue();
                 break;
             default:
-                EXPECT_FALSE("Element Parse Error");
+                FAIL() << "Element Parse Error";
         }
     }
 }
 
-// Test if the values of the counters are all zero except for the items
-// specified in except_for.
+// Test if the counters has expected values specified in expect and the others
+// are zero.
 void
-checkCountersAllZeroExcept(const isc::data::ConstElementPtr counters,
-                           const std::set<std::string>& except_for) {
+checkCounters(const isc::data::ConstElementPtr counters,
+              const std::map<std::string, int>& expect)
+{
     std::map<std::string, int> stats_map;
     flatten(stats_map, "", counters);
 
@@ -89,66 +93,628 @@ checkCountersAllZeroExcept(const isc::data::ConstElementPtr counters,
             i != e;
             ++i)
     {
-        int expect = 0;
-        if (except_for.count(i->first) != 0) {
-            expect = 1;
-        }
-        EXPECT_EQ(expect, i->second) << "Expected counter "
-            << i->first << " = " << expect << ", actual: "
+        const int value =
+            expect.find(i->first) == expect.end() ?
+                0 : expect.find(i->first)->second;
+        EXPECT_EQ(value, i->second) << "Expected counter "
+            << i->first << " = " << value << ", actual: "
             << i->second;
     }
 }
 
-TEST_F(CountersTest, incrementNormalQuery) {
+TEST_F(CountersTest, incrementResponse) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
+
+    // Test response counters are incremented only if responded == true.
+    for (int i = 0; i < 2; ++i) {
+        const bool responded = i & 1;
+
+        msgattrs.setRequestIPVersion(AF_INET);
+        msgattrs.setRequestTransportProtocol(IPPROTO_UDP);
+        msgattrs.setRequestOpCode(Opcode::QUERY_CODE);
+        msgattrs.setRequestEDNS0(true);
+        msgattrs.setRequestDO(true);
+
+        response.setRcode(Rcode::REFUSED());
+        response.addQuestion(Question(Name("example.com"),
+                                      RRClass::IN(), RRType::AAAA()));
+        response.setHeaderFlag(Message::HEADERFLAG_QR);
+
+        counters.inc(msgattrs, response, responded);
+
+        expect.clear();
+        expect["opcode.query"] = i+1;
+        expect["request.v4"] = i+1;
+        expect["request.udp"] = i+1;
+        expect["request.edns0"] = i+1;
+        expect["request.badednsver"] = 0;
+        expect["request.dnssec_ok"] = i+1;
+        expect["responses"] = responded ? 1 : 0;
+        expect["qrynoauthans"] = responded ? 1 : 0;
+        expect["rcode.refused"] = responded ? 1 : 0;
+        expect["authqryrej"] = responded ? 1 : 0;
+        checkCounters(counters.get()->get("zones")->get("_SERVER_"), expect);
+    }
+}
+
+TEST_F(CountersTest, incrementProtocolType) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
+
+    // Test these patterns:
+    //      af     protocol
+    //     -----------------
+    //      ipv4   udp
+    //      ipv6   udp
+    //      ipv4   tcp
+    //      ipv6   tcp
+    int count_v4 = 0, count_v6 = 0, count_udp = 0, count_tcp = 0;
+    for (int i = 0; i < 4; ++i) {
+        const int af = i & 1 ? AF_INET : AF_INET6;
+        const int proto = i & 2 ? IPPROTO_UDP : IPPROTO_TCP;
+
+        msgattrs.setRequestIPVersion(af);
+        msgattrs.setRequestTransportProtocol(proto);
+        msgattrs.setRequestOpCode(Opcode::QUERY_CODE);
+        msgattrs.setRequestEDNS0(true);
+        msgattrs.setRequestDO(true);
+
+        response.setRcode(Rcode::REFUSED());
+        response.addQuestion(Question(Name("example.com"),
+                                      RRClass::IN(), RRType::AAAA()));
+        response.setHeaderFlag(Message::HEADERFLAG_QR);
+
+        counters.inc(msgattrs, response, true);
+
+        if (af == AF_INET) {
+            ++count_v4;
+        } else {
+            ++count_v6;
+        }
+        if (proto == IPPROTO_UDP) {
+            ++count_udp;
+        } else {
+            ++count_tcp;
+        }
+
+        expect.clear();
+        expect["opcode.query"] = i+1;
+        expect["request.v4"] = count_v4;
+        expect["request.v6"] = count_v6;
+        expect["request.udp"] = count_udp;
+        expect["request.tcp"] = count_tcp;
+        expect["request.edns0"] = i+1;
+        expect["request.badednsver"] = 0;
+        expect["request.dnssec_ok"] = i+1;
+        expect["responses"] = i+1;
+        expect["qrynoauthans"] = i+1;
+        expect["rcode.refused"] = i+1;
+        expect["authqryrej"] = i+1;
+        checkCounters(counters.get()->get("zones")->get("_SERVER_"), expect);
+    }
+}
+
+TEST_F(CountersTest, incrementDO) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
+
+    // Test these patterns:
+    //     DNSSEC OK
+    //    -----------
+    //     false
+    //     true
+    for (int i = 0; i < 2; ++i) {
+        const bool is_dnssec_ok = i & 1;
+        msgattrs.setRequestIPVersion(AF_INET);
+        msgattrs.setRequestTransportProtocol(IPPROTO_UDP);
+        msgattrs.setRequestOpCode(Opcode::QUERY_CODE);
+        msgattrs.setRequestEDNS0(true);
+        msgattrs.setRequestDO(is_dnssec_ok);
+
+        response.setRcode(Rcode::REFUSED());
+        response.addQuestion(Question(Name("example.com"),
+                                      RRClass::IN(), RRType::AAAA()));
+        response.setHeaderFlag(Message::HEADERFLAG_QR);
+
+        counters.inc(msgattrs, response, true);
+
+        expect.clear();
+        expect["opcode.query"] = i+1;
+        expect["request.v4"] = i+1;
+        expect["request.udp"] = i+1;
+        expect["request.edns0"] = i+1;
+        expect["request.badednsver"] = 0;
+        expect["request.dnssec_ok"] = i & 1;
+        expect["responses"] = i+1;
+        expect["qrynoauthans"] = i+1;
+        expect["rcode.refused"] = i+1;
+        expect["authqryrej"] = i+1;
+        checkCounters(counters.get()->get("zones")->get("_SERVER_"), expect);
+    }
+}
+
+TEST_F(CountersTest, incrementEDNS) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
+
+    // Test these patterns:
+    //     request edns0   response edns0
+    //    --------------------------------
+    //     false           true
+    //     true            false
+    //
+    // They can't be both true since edns0 and badednsver are exclusive.
+    int count_req_edns0 = 0, count_res_edns0 = 0;
+    for (int i = 0; i < 2; ++i) {
+        const bool is_edns0 = i & 1;
+        msgattrs.setRequestIPVersion(AF_INET);
+        msgattrs.setRequestTransportProtocol(IPPROTO_UDP);
+        msgattrs.setRequestOpCode(Opcode::QUERY_CODE);
+        msgattrs.setRequestEDNS0(is_edns0);
+        msgattrs.setRequestDO(true);
+
+        if (!is_edns0) {
+            ConstEDNSPtr edns = EDNSPtr(new EDNS(0));
+            response.setEDNS(edns);
+        } else {
+            response.setEDNS(EDNSPtr());
+        }
+        response.setRcode(Rcode::REFUSED());
+        response.addQuestion(Question(Name("example.com"),
+                                      RRClass::IN(), RRType::AAAA()));
+        response.setHeaderFlag(Message::HEADERFLAG_QR);
+
+        counters.inc(msgattrs, response, true);
+
+        if (is_edns0) {
+            ++count_req_edns0;
+        } else {
+            ++count_res_edns0;
+        }
+
+        expect.clear();
+        expect["opcode.query"] = i+1;
+        expect["request.v4"] = i+1;
+        expect["request.udp"] = i+1;
+        expect["request.edns0"] = count_req_edns0;
+        expect["response.edns0"] = count_res_edns0;
+        expect["request.badednsver"] = 0;
+        expect["request.dnssec_ok"] = i+1;
+        expect["responses"] = i+1;
+        expect["qrynoauthans"] = i+1;
+        expect["rcode.refused"] = i+1;
+        expect["authqryrej"] = i+1;
+        checkCounters(counters.get()->get("zones")->get("_SERVER_"), expect);
+    }
+}
+
+TEST_F(CountersTest, incrementTSIG) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
+
+    // Test these patterns:
+    //      signature  badsig
+    //     -------------------
+    //      (none)     false
+    //      TSIG       false
+    //      TSIG       true
+    //      SIG(0)     false
+    //      SIG(0)     true
+    //
+    // badsig can't be true if the message does not have signature.
+    int count_req_tsig = 0, count_res_tsig = 0, count_req_sig0 = 0,
+        count_badsig = 0;
+    for (int i = 0; i < 5; ++i) {
+        const bool is_tsig = (i == 0 ? i : i+1) & 2;
+        const bool is_sig0 = (i == 0 ? i : i+1) & 4;
+        const bool is_badsig = (i == 0 ? i : i+1) & 1;
+        msgattrs.setRequestIPVersion(AF_INET);
+        msgattrs.setRequestTransportProtocol(IPPROTO_UDP);
+        msgattrs.setRequestOpCode(Opcode::QUERY_CODE);
+        msgattrs.setRequestEDNS0(true);
+        msgattrs.setRequestDO(true);
+        msgattrs.setRequestSig(is_tsig, is_sig0, is_badsig);
+
+        response.setRcode(Rcode::REFUSED());
+        response.addQuestion(Question(Name("example.com"),
+                                      RRClass::IN(), RRType::AAAA()));
+        response.setHeaderFlag(Message::HEADERFLAG_QR);
+
+        // don't increment response counters if signature is bad
+        counters.inc(msgattrs, response, !is_badsig);
+
+        if (is_tsig) {
+            ++count_req_tsig;
+            if (!is_badsig) {
+                ++count_res_tsig;
+            }
+        }
+        if (is_sig0) {
+            ++count_req_sig0;
+        }
+        if (is_badsig) {
+            ++count_badsig;
+        }
+
+        expect.clear();
+        expect["request.v4"] = i+1;
+        expect["request.udp"] = i+1;
+        expect["opcode.query"] = i+1 - count_badsig;
+        expect["request.edns0"] = i+1 - count_badsig;
+        expect["request.badednsver"] = 0;
+        expect["request.dnssec_ok"] = i+1 - count_badsig;
+        expect["request.tsig"] = count_req_tsig;
+        expect["response.tsig"] = count_res_tsig;
+        expect["request.sig0"] = count_req_sig0;
+        expect["request.badsig"] = count_badsig;
+        expect["responses"] = i+1 - count_badsig;
+        expect["qrynoauthans"] = i+1 - count_badsig;
+        expect["rcode.refused"] = i+1 - count_badsig;
+        expect["authqryrej"] = i+1 - count_badsig;
+        checkCounters(counters.get()->get("zones")->get("_SERVER_"), expect);
+    }
+}
+
+TEST_F(CountersTest, incrementOpcode) {
     Message response(Message::RENDER);
-    QRAttributes qrattrs;
-    std::set<std::string> expect_nonzero;
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
 
-    expect_nonzero.clear();
-    checkCountersAllZeroExcept(counters.get()->get("zones")->get("_SERVER_"),
-                               expect_nonzero);
+    // Test all opcodes (QUERY..RESERVED15)
+    int count_all = 0, count_opcode_other = 0;
+    for (uint8_t i = Opcode::QUERY().getCode(),
+                 e = Opcode::RESERVED15().getCode();
+         i <= e;
+         ++i)
+    {
+        msgattrs.setRequestIPVersion(AF_INET);
+        msgattrs.setRequestTransportProtocol(IPPROTO_UDP);
+        msgattrs.setRequestOpCode(i);
+        msgattrs.setRequestEDNS0(true);
+        msgattrs.setRequestDO(true);
+        msgattrs.setRequestSig(false, false, false);
+
+        response.setRcode(Rcode::REFUSED());
+        response.addQuestion(Question(Name("example.com"),
+                                      RRClass::IN(), RRType::AAAA()));
+        response.setHeaderFlag(Message::HEADERFLAG_QR);
+
+        for (uint8_t j = 0; j < i; ++j) {
+            // count up i times for i-th opcode to identify counters
+            counters.inc(msgattrs, response, true);
+            ++count_all;
+        }
+
+        expect.clear();
+        expect["request.v4"] = count_all;
+        expect["request.udp"] = count_all;
+        expect["request.edns0"] = count_all;
+        expect["request.badednsver"] = 0;
+        expect["request.dnssec_ok"] = count_all;
+        expect["request.tsig"] = 0;
+        expect["request.sig0"] = 0;
+        expect["request.badsig"] = 0;
+        expect["responses"] = count_all;
+        expect["rcode.refused"] = count_all;
+        if (opcode_to_msgcounter[i] == MSG_OPCODE_OTHER) {
+            count_opcode_other += i;
+        }
+        for (uint8_t j = 0; j <= i; ++j) {
+            if (opcode_to_msgcounter[j] == MSG_OPCODE_OTHER) {
+                expect["opcode.other"] = count_opcode_other;
+            } else {
+                std::string code_text = Opcode(j).toText();
+                std::transform(code_text.begin(), code_text.end(),
+                               code_text.begin(), ::tolower);
+                expect["opcode."+code_text] = j;
+            }
+        }
+        checkCounters(counters.get()->get("zones")->get("_SERVER_"), expect);
+    }
+}
+
+TEST_F(CountersTest, incrementRcode) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
+
+    // Test all rcodes (NOERROR..BADVERS)
+    int count_all = 0, count_rcode_other = 0, count_ednsbadver = 0;
+    for (uint16_t i = Rcode::NOERROR().getCode(),
+                  e = Rcode::BADVERS().getCode();
+         i <= e;
+         ++i)
+    {
+        msgattrs.setRequestIPVersion(AF_INET);
+        msgattrs.setRequestTransportProtocol(IPPROTO_UDP);
+        msgattrs.setRequestOpCode(Opcode::IQUERY_CODE);
+        msgattrs.setRequestEDNS0(true);
+        msgattrs.setRequestDO(true);
+        msgattrs.setRequestSig(false, false, false);
+
+        response.setRcode(Rcode(i));
+        response.addQuestion(Question(Name("example.com"),
+                                      RRClass::IN(), RRType::AAAA()));
+        response.setHeaderFlag(Message::HEADERFLAG_QR);
+
+        for (uint16_t j = 0; j < i; ++j) {
+            // count up i times for i-th rcode to identify counters
+            counters.inc(msgattrs, response, true);
+            ++count_all;
+        }
+
+        expect.clear();
+        expect["opcode.iquery"] = count_all;
+        expect["request.v4"] = count_all;
+        expect["request.udp"] = count_all;
+        expect["request.edns0"] = count_all;
+        expect["request.dnssec_ok"] = count_all;
+        expect["request.tsig"] = 0;
+        expect["request.sig0"] = 0;
+        expect["request.badsig"] = 0;
+        expect["responses"] = count_all;
+        if (rcode_to_msgcounter[i] == MSG_RCODE_OTHER) {
+            count_rcode_other += i;
+        }
+        // "request.badednsver" counts for Rcode == BADVERS
+        if (rcode_to_msgcounter[i] == MSG_RCODE_BADVERS) {
+            count_ednsbadver += i;
+        }
+        expect["request.badednsver"] = count_ednsbadver;
+        for (uint16_t j = 0; j <= i; ++j) {
+            if (rcode_to_msgcounter[j] == MSG_RCODE_OTHER) {
+                expect["rcode.other"] = count_rcode_other;
+            } else {
+                std::string code_text = Rcode(j).toText();
+                std::transform(code_text.begin(), code_text.end(),
+                               code_text.begin(), ::tolower);
+                expect["rcode."+code_text] = j;
+            }
+        }
+        checkCounters(counters.get()->get("zones")->get("_SERVER_"), expect);
+    }
+}
+
+TEST_F(CountersTest, incrementTruncated) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
+
+    // Test these patterns:
+    //      truncated
+    //     -----------
+    //      false
+    //      true
+    int count_truncated = 0;
+    for (int i = 0; i < 2; ++i) {
+        const bool is_truncated = i & 1;
+        msgattrs.setRequestIPVersion(AF_INET);
+        msgattrs.setRequestTransportProtocol(IPPROTO_UDP);
+        msgattrs.setRequestOpCode(Opcode::IQUERY_CODE);
+        msgattrs.setRequestEDNS0(true);
+        msgattrs.setRequestDO(true);
+        msgattrs.setRequestSig(false, false, false);
+        msgattrs.setResponseTruncated(is_truncated);
+
+        response.setRcode(Rcode::SERVFAIL());
+        response.addQuestion(Question(Name("example.com"),
+                                      RRClass::IN(), RRType::TXT()));
+        response.setHeaderFlag(Message::HEADERFLAG_QR);
+
+        counters.inc(msgattrs, response, true);
+
+        if (is_truncated) {
+            ++count_truncated;
+        }
+
+        expect.clear();
+        expect["opcode.iquery"] = i+1;
+        expect["request.v4"] = i+1;
+        expect["request.udp"] = i+1;
+        expect["request.edns0"] = i+1;
+        expect["request.dnssec_ok"] = i+1;
+        expect["responses"] = i+1;
+        expect["rcode.servfail"] = i+1;
+        expect["response.truncated"] = count_truncated;
+        checkCounters(counters.get()->get("zones")->get("_SERVER_"), expect);
+    }
+}
+
+TEST_F(CountersTest, incrementQryAuthAnsAndNoAuthAns) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
+
+    // Opcode = QUERY, ANCOUNT = 0 (don't care), Rcode = SERVFAIL (don't care)
+    // Test these patterns:
+    //      AA flag
+    //     -----------------------
+    //      false -> QryNoAuthAns
+    //      true  -> QryAuthAns
+    int count_authans = 0, count_noauthans = 0;
+    for (int i = 0; i < 2; ++i) {
+        const bool is_aa_set = i & 1;
+        msgattrs.setRequestIPVersion(AF_INET);
+        msgattrs.setRequestTransportProtocol(IPPROTO_UDP);
+        msgattrs.setRequestOpCode(Opcode::QUERY_CODE);
+        msgattrs.setRequestEDNS0(true);
+        msgattrs.setRequestDO(true);
+        msgattrs.setRequestSig(false, false, false);
+
+        response.setRcode(Rcode::SERVFAIL());
+        response.addQuestion(Question(Name("example.com"),
+                                      RRClass::IN(), RRType::TXT()));
+        response.setHeaderFlag(Message::HEADERFLAG_QR);
+        if (is_aa_set) {
+            response.setHeaderFlag(Message::HEADERFLAG_AA);
+            ++count_authans;
+        } else {
+            ++count_noauthans;
+        }
+
+        counters.inc(msgattrs, response, true);
+
+        expect.clear();
+        expect["opcode.query"] = i+1;
+        expect["request.v4"] = i+1;
+        expect["request.udp"] = i+1;
+        expect["request.edns0"] = i+1;
+        expect["request.dnssec_ok"] = i+1;
+        expect["responses"] = i+1;
+        expect["rcode.servfail"] = i+1;
+        expect["qryauthans"] = count_authans;
+        expect["qrynoauthans"] = count_noauthans;
+        checkCounters(counters.get()->get("zones")->get("_SERVER_"), expect);
+    }
+}
+
+TEST_F(CountersTest, incrementQrySuccess) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
+
+    // Opcode = QUERY, Rcode = NOERROR, ANCOUNT > 0
+    msgattrs.setRequestIPVersion(AF_INET);
+    msgattrs.setRequestTransportProtocol(IPPROTO_UDP);
+    msgattrs.setRequestOpCode(Opcode::QUERY_CODE);
+    msgattrs.setRequestEDNS0(true);
+    msgattrs.setRequestDO(true);
+    msgattrs.setRequestSig(false, false, false);
+
+    response.setRcode(Rcode::NOERROR());
+    response.addQuestion(Question(Name("example.com"),
+                                  RRClass::IN(), RRType::TXT()));
+    RRsetPtr answer_rrset(new RRset(Name("example.com"),
+                                    RRClass::IN(), RRType::TXT(),
+                                    RRTTL(3600)));
+    answer_rrset->addRdata(rdata::createRdata(RRType::TXT(),
+                                              RRClass::IN(),
+                                              "Answer"));
+    response.addRRset(Message::SECTION_ANSWER, answer_rrset);
+    response.setHeaderFlag(Message::HEADERFLAG_QR);
+
+    counters.inc(msgattrs, response, true);
+
+    expect.clear();
+    expect["opcode.query"] = 1;
+    expect["request.v4"] = 1;
+    expect["request.udp"] = 1;
+    expect["request.edns0"] = 1;
+    expect["request.dnssec_ok"] = 1;
+    expect["responses"] = 1;
+    expect["rcode.noerror"] = 1;
+    expect["qrysuccess"] = 1;
+    // noauthans is also incremented
+    expect["qrynoauthans"] = 1;
+    checkCounters(counters.get()->get("zones")->get("_SERVER_"), expect);
+}
+
+TEST_F(CountersTest, incrementQryReferralAndNxrrset) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
+
+    // Opcode = QUERY, Rcode = NOERROR, ANCOUNT = 0
+    // Test these patterns:
+    //      AA flag
+    //     ----------------------
+    //      false -> QryReferral
+    //      true  -> QryNxrrset
+    int count_referral = 0, count_nxrrset = 0;
+    for (int i = 0; i < 2; ++i) {
+        const bool is_aa_set = i & 1;
+        msgattrs.setRequestIPVersion(AF_INET);
+        msgattrs.setRequestTransportProtocol(IPPROTO_UDP);
+        msgattrs.setRequestOpCode(Opcode::QUERY_CODE);
+        msgattrs.setRequestEDNS0(true);
+        msgattrs.setRequestDO(true);
+        msgattrs.setRequestSig(false, false, false);
+
+        response.setRcode(Rcode::NOERROR());
+        response.addQuestion(Question(Name("example.com"),
+                                      RRClass::IN(), RRType::TXT()));
+        response.setHeaderFlag(Message::HEADERFLAG_QR);
+        if (is_aa_set) {
+            response.setHeaderFlag(Message::HEADERFLAG_AA);
+            ++count_nxrrset;
+        } else {
+            ++count_referral;
+        }
+
+        counters.inc(msgattrs, response, true);
+
+        expect.clear();
+        expect["opcode.query"] = i+1;
+        expect["request.v4"] = i+1;
+        expect["request.udp"] = i+1;
+        expect["request.edns0"] = i+1;
+        expect["request.dnssec_ok"] = i+1;
+        expect["responses"] = i+1;
+        expect["rcode.noerror"] = i+1;
+        expect["qrynxrrset"] = count_nxrrset;
+        expect["qryreferral"] = count_referral;
+        // qryauthans or qrynoauthans is also incremented
+        expect["qryauthans"] = count_nxrrset;
+        expect["qrynoauthans"] = count_referral;
+        checkCounters(counters.get()->get("zones")->get("_SERVER_"), expect);
+    }
+}
+
+TEST_F(CountersTest, incrementAuthQryRej) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
 
-    qrattrs.setQueryIPVersion(AF_INET6);
-    qrattrs.setQueryTransportProtocol(IPPROTO_UDP);
-    qrattrs.setQueryOpCode(Opcode::QUERY_CODE);
-    qrattrs.setQueryEDNS(true, false);
-    qrattrs.setQueryDO(true);
+    // Opcode = QUERY, Rcode = REFUSED, ANCOUNT = 0 (don't care)
+    msgattrs.setRequestIPVersion(AF_INET);
+    msgattrs.setRequestTransportProtocol(IPPROTO_UDP);
+    msgattrs.setRequestOpCode(Opcode::QUERY_CODE);
+    msgattrs.setRequestEDNS0(true);
+    msgattrs.setRequestDO(true);
+    msgattrs.setRequestSig(false, false, false);
 
     response.setRcode(Rcode::REFUSED());
     response.addQuestion(Question(Name("example.com"),
-                                  RRClass::IN(), RRType::AAAA()));
+                                  RRClass::IN(), RRType::TXT()));
+    response.setHeaderFlag(Message::HEADERFLAG_QR);
 
-    counters.inc(qrattrs, response, true);
+    counters.inc(msgattrs, response, true);
 
-    expect_nonzero.clear();
-    expect_nonzero.insert("opcode.query");
-    expect_nonzero.insert("request.v6");
-    expect_nonzero.insert("request.udp");
-    expect_nonzero.insert("request.edns0");
-    expect_nonzero.insert("request.dnssec_ok");
-    expect_nonzero.insert("responses");
-    expect_nonzero.insert("qrynoauthans");
-    expect_nonzero.insert("rcode.refused");
-    expect_nonzero.insert("authqryrej");
-    checkCountersAllZeroExcept(counters.get()->get("zones")->get("_SERVER_"),
-                               expect_nonzero);
+    expect.clear();
+    expect["opcode.query"] = 1;
+    expect["request.v4"] = 1;
+    expect["request.udp"] = 1;
+    expect["request.edns0"] = 1;
+    expect["request.dnssec_ok"] = 1;
+    expect["responses"] = 1;
+    expect["rcode.refused"] = 1;
+    expect["authqryrej"] = 1;
+    // noauthans is also incremented since AA bit is not set
+    expect["qrynoauthans"] = 1;
+    checkCounters(counters.get()->get("zones")->get("_SERVER_"), expect);
 }
 
 int
-countTreeElements(const struct CounterTypeTree* tree) {
+countTreeElements(const struct CounterSpec* tree) {
     int count = 0;
     for (int i = 0; tree[i].name != NULL; ++i) {
-        if (tree[i].sub_tree == NULL) {
+        if (tree[i].sub_counters == NULL) {
             ++count;
         } else {
-            count += countTreeElements(tree[i].sub_tree);
+            count += countTreeElements(tree[i].sub_counters);
         }
     }
     return (count);
 }
 
-TEST(StatisticsItemsTest, QRItemNamesCheck) {
-    EXPECT_EQ(QR_COUNTER_TYPES, countTreeElements(QRCounterTree));
+TEST(StatisticsItemsTest, MSGItemNamesCheck) {
+    EXPECT_EQ(MSG_COUNTER_TYPES, countTreeElements(msg_counter_tree));
 }
 
 }
diff --git a/tests/lettuce/configurations/example.org.inmem.config b/tests/lettuce/configurations/example.org.inmem.config
index 7ea34b3..48f4379 100644
--- a/tests/lettuce/configurations/example.org.inmem.config
+++ b/tests/lettuce/configurations/example.org.inmem.config
@@ -26,9 +26,12 @@
             ]
         }
     },
+    "Stats": {
+    },
     "Boss": {
         "components": {
             "b10-auth": { "kind": "needed", "special": "auth" },
+            "b10-stats": { "address": "Stats", "kind": "dispensable" },
             "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
         }
     }
diff --git a/tests/lettuce/features/queries.feature b/tests/lettuce/features/queries.feature
index 7ae2284..834b425 100644
--- a/tests/lettuce/features/queries.feature
+++ b/tests/lettuce/features/queries.feature
@@ -8,15 +8,59 @@ Feature: Querying feature
         And wait for bind10 stderr message BIND10_STARTED_CC
         And wait for bind10 stderr message CMDCTL_STARTED
         And wait for bind10 stderr message AUTH_SERVER_STARTED
+        And wait for bind10 stderr message STATS_STARTING
 
         bind10 module Auth should be running
+        And bind10 module Stats should be running
         And bind10 module Resolver should not be running
         And bind10 module Xfrout should not be running
         And bind10 module Zonemgr should not be running
         And bind10 module Xfrin should not be running
-        And bind10 module Stats should not be running
         And bind10 module StatsHttpd should not be running
 
+        When I query statistics zones of bind10 module Auth
+        last bindctl output should not contain "error"
+        Then the statistics counter v4 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter v6 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter udp in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter tcp in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter edns0 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter badednsver in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter tsig in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter sig0 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter badsig in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter dnssec_ok in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter query in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter iquery in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter status in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter notify in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter update in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter other in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter responses for the zone _SERVER_ should be 0
+        Then the statistics counter truncated in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter edns0 in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter tsig in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter sig0 in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter qrysuccess for the zone _SERVER_ should be 0
+        Then the statistics counter qryauthans for the zone _SERVER_ should be 0
+        Then the statistics counter qrynoauthans for the zone _SERVER_ should be 0
+        Then the statistics counter qryreferral for the zone _SERVER_ should be 0
+        Then the statistics counter qrynxrrset for the zone _SERVER_ should be 0
+        Then the statistics counter authqryrej for the zone _SERVER_ should be 0
+        Then the statistics counter noerror in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter formerr in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter servfail in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter nxdomain in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notimp in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter refused in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter yxdomain in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter yxrrset in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter nxrrset in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notauth in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notzone in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter badvers in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter other in the category rcode for the zone _SERVER_ should be 0
+
         A query for www.example.org should have rcode NOERROR
         The last query response should have flags qr aa rd
         The last query response should have ancount 1
@@ -38,6 +82,53 @@ Feature: Querying feature
         ns2.example.org.        3600    IN      A       192.0.2.4
         """
 
+        And wait for new bind10 stderr message STATS_SEND_STATISTICS_REQUEST
+        And wait for new bind10 stderr message AUTH_RECEIVED_COMMAND
+
+        When I query statistics zones of bind10 module Auth
+        last bindctl output should not contain "error"
+        Then the statistics counter v4 in the category request for the zone _SERVER_ should be 1
+        Then the statistics counter v6 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter udp in the category request for the zone _SERVER_ should be 1
+        Then the statistics counter tcp in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter edns0 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter badednsver in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter tsig in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter sig0 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter badsig in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter dnssec_ok in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter query in the category opcode for the zone _SERVER_ should be 1
+        Then the statistics counter iquery in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter status in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter notify in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter update in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter other in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter responses for the zone _SERVER_ should be 1
+        Then the statistics counter truncated in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter edns0 in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter tsig in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter sig0 in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter qrysuccess for the zone _SERVER_ should be 1
+        Then the statistics counter qryauthans for the zone _SERVER_ should be 1
+        Then the statistics counter qrynoauthans for the zone _SERVER_ should be 0
+        Then the statistics counter qryreferral for the zone _SERVER_ should be 0
+        Then the statistics counter qrynxrrset for the zone _SERVER_ should be 0
+        Then the statistics counter authqryrej for the zone _SERVER_ should be 0
+        Then the statistics counter noerror in the category rcode for the zone _SERVER_ should be 1
+        Then the statistics counter formerr in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter servfail in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter nxdomain in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notimp in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter refused in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter yxdomain in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter yxrrset in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter nxrrset in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notauth in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notzone in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter badvers in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter other in the category rcode for the zone _SERVER_ should be 0
+
+
         # Repeat of the above
         A query for www.example.org should have rcode NOERROR
         The last query response should have flags qr aa rd
@@ -60,6 +151,52 @@ Feature: Querying feature
         ns2.example.org.        3600    IN      A       192.0.2.4
         """
 
+        And wait for new bind10 stderr message STATS_SEND_STATISTICS_REQUEST
+        And wait for new bind10 stderr message AUTH_RECEIVED_COMMAND
+
+        When I query statistics zones of bind10 module Auth
+        last bindctl output should not contain "error"
+        Then the statistics counter v4 in the category request for the zone _SERVER_ should be 2
+        Then the statistics counter v6 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter udp in the category request for the zone _SERVER_ should be 2
+        Then the statistics counter tcp in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter edns0 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter badednsver in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter tsig in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter sig0 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter badsig in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter dnssec_ok in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter query in the category opcode for the zone _SERVER_ should be 2
+        Then the statistics counter iquery in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter status in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter notify in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter update in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter other in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter responses for the zone _SERVER_ should be 2
+        Then the statistics counter truncated in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter edns0 in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter tsig in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter sig0 in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter qrysuccess for the zone _SERVER_ should be 2
+        Then the statistics counter qryauthans for the zone _SERVER_ should be 2
+        Then the statistics counter qrynoauthans for the zone _SERVER_ should be 0
+        Then the statistics counter qryreferral for the zone _SERVER_ should be 0
+        Then the statistics counter qrynxrrset for the zone _SERVER_ should be 0
+        Then the statistics counter authqryrej for the zone _SERVER_ should be 0
+        Then the statistics counter noerror in the category rcode for the zone _SERVER_ should be 2
+        Then the statistics counter formerr in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter servfail in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter nxdomain in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notimp in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter refused in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter yxdomain in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter yxrrset in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter nxrrset in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notauth in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notzone in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter badvers in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter other in the category rcode for the zone _SERVER_ should be 0
+
         # And now query something completely different
         A query for nosuchname.example.org should have rcode NXDOMAIN
         The last query response should have flags qr aa rd
@@ -71,6 +208,52 @@ Feature: Querying feature
         example.org.            3600    IN      SOA     ns1.example.org. admin.example.org. 1234 3600 1800 2419200 7200
         """
 
+        And wait for new bind10 stderr message STATS_SEND_STATISTICS_REQUEST
+        And wait for new bind10 stderr message AUTH_RECEIVED_COMMAND
+
+        When I query statistics zones of bind10 module Auth
+        last bindctl output should not contain "error"
+        Then the statistics counter v4 in the category request for the zone _SERVER_ should be 3
+        Then the statistics counter v6 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter udp in the category request for the zone _SERVER_ should be 3
+        Then the statistics counter tcp in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter edns0 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter badednsver in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter tsig in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter sig0 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter badsig in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter dnssec_ok in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter query in the category opcode for the zone _SERVER_ should be 3
+        Then the statistics counter iquery in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter status in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter notify in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter update in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter other in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter responses for the zone _SERVER_ should be 3
+        Then the statistics counter truncated in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter edns0 in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter tsig in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter sig0 in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter qrysuccess for the zone _SERVER_ should be 2
+        Then the statistics counter qryauthans for the zone _SERVER_ should be 3
+        Then the statistics counter qrynoauthans for the zone _SERVER_ should be 0
+        Then the statistics counter qryreferral for the zone _SERVER_ should be 0
+        Then the statistics counter qrynxrrset for the zone _SERVER_ should be 0
+        Then the statistics counter authqryrej for the zone _SERVER_ should be 0
+        Then the statistics counter noerror in the category rcode for the zone _SERVER_ should be 2
+        Then the statistics counter formerr in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter servfail in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter nxdomain in the category rcode for the zone _SERVER_ should be 1
+        Then the statistics counter notimp in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter refused in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter yxdomain in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter yxrrset in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter nxrrset in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notauth in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notzone in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter badvers in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter other in the category rcode for the zone _SERVER_ should be 0
+
     Scenario: ANY query
         Given I have bind10 running with configuration example.org.inmem.config
         And wait for bind10 stderr message BIND10_STARTED_CC
@@ -78,13 +261,56 @@ Feature: Querying feature
         And wait for bind10 stderr message AUTH_SERVER_STARTED
 
         bind10 module Auth should be running
+        And bind10 module Stats should be running
         And bind10 module Resolver should not be running
         And bind10 module Xfrout should not be running
         And bind10 module Zonemgr should not be running
         And bind10 module Xfrin should not be running
-        And bind10 module Stats should not be running
         And bind10 module StatsHttpd should not be running
 
+        When I query statistics zones of bind10 module Auth
+        last bindctl output should not contain "error"
+        Then the statistics counter v4 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter v6 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter udp in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter tcp in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter edns0 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter badednsver in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter tsig in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter sig0 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter badsig in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter dnssec_ok in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter query in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter iquery in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter status in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter notify in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter update in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter other in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter responses for the zone _SERVER_ should be 0
+        Then the statistics counter truncated in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter edns0 in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter tsig in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter sig0 in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter qrysuccess for the zone _SERVER_ should be 0
+        Then the statistics counter qryauthans for the zone _SERVER_ should be 0
+        Then the statistics counter qrynoauthans for the zone _SERVER_ should be 0
+        Then the statistics counter qryreferral for the zone _SERVER_ should be 0
+        Then the statistics counter qrynxrrset for the zone _SERVER_ should be 0
+        Then the statistics counter authqryrej for the zone _SERVER_ should be 0
+        Then the statistics counter noerror in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter formerr in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter servfail in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter nxdomain in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notimp in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter refused in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter yxdomain in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter yxrrset in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter nxrrset in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notauth in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notzone in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter badvers in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter other in the category rcode for the zone _SERVER_ should be 0
+
         A query for example.org type ANY should have rcode NOERROR
         The last query response should have flags qr aa rd
         The last query response should have ancount 4
@@ -104,11 +330,101 @@ Feature: Querying feature
         mail.example.org.       3600    IN      A       192.0.2.10
         """
 
+        And wait for new bind10 stderr message STATS_SEND_STATISTICS_REQUEST
+        And wait for new bind10 stderr message AUTH_RECEIVED_COMMAND
+
+        When I query statistics zones of bind10 module Auth
+        last bindctl output should not contain "error"
+        Then the statistics counter v4 in the category request for the zone _SERVER_ should be 1
+        Then the statistics counter v6 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter udp in the category request for the zone _SERVER_ should be 1
+        Then the statistics counter tcp in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter edns0 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter badednsver in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter tsig in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter sig0 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter badsig in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter dnssec_ok in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter query in the category opcode for the zone _SERVER_ should be 1
+        Then the statistics counter iquery in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter status in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter notify in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter update in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter other in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter responses for the zone _SERVER_ should be 1
+        Then the statistics counter truncated in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter edns0 in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter tsig in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter sig0 in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter qrysuccess for the zone _SERVER_ should be 1
+        Then the statistics counter qryauthans for the zone _SERVER_ should be 1
+        Then the statistics counter qrynoauthans for the zone _SERVER_ should be 0
+        Then the statistics counter qryreferral for the zone _SERVER_ should be 0
+        Then the statistics counter qrynxrrset for the zone _SERVER_ should be 0
+        Then the statistics counter authqryrej for the zone _SERVER_ should be 0
+        Then the statistics counter noerror in the category rcode for the zone _SERVER_ should be 1
+        Then the statistics counter formerr in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter servfail in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter nxdomain in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notimp in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter refused in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter yxdomain in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter yxrrset in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter nxrrset in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notauth in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notzone in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter badvers in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter other in the category rcode for the zone _SERVER_ should be 0
+
     Scenario: Delegation query for unsigned child zone
         Given I have bind10 running with configuration example.org.inmem.config
         And wait for bind10 stderr message BIND10_STARTED_CC
         And wait for bind10 stderr message CMDCTL_STARTED
         And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        When I query statistics zones of bind10 module Auth
+        last bindctl output should not contain "error"
+        Then the statistics counter v4 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter v6 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter udp in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter tcp in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter edns0 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter badednsver in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter tsig in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter sig0 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter badsig in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter dnssec_ok in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter query in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter iquery in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter status in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter notify in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter update in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter other in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter responses for the zone _SERVER_ should be 0
+        Then the statistics counter truncated in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter edns0 in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter tsig in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter sig0 in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter qrysuccess for the zone _SERVER_ should be 0
+        Then the statistics counter qryauthans for the zone _SERVER_ should be 0
+        Then the statistics counter qrynoauthans for the zone _SERVER_ should be 0
+        Then the statistics counter qryreferral for the zone _SERVER_ should be 0
+        Then the statistics counter qrynxrrset for the zone _SERVER_ should be 0
+        Then the statistics counter authqryrej for the zone _SERVER_ should be 0
+        Then the statistics counter noerror in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter formerr in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter servfail in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter nxdomain in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notimp in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter refused in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter yxdomain in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter yxrrset in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter nxrrset in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notauth in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notzone in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter badvers in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter other in the category rcode for the zone _SERVER_ should be 0
+
         A dnssec query for www.sub.example.org type AAAA should have rcode NOERROR
         The last query response should have flags qr rd
         The last query response should have edns_flags do
@@ -124,6 +440,52 @@ Feature: Querying feature
         ns.sub.example.org.	3600	IN	A	192.0.2.101
         """
 
+        And wait for new bind10 stderr message STATS_SEND_STATISTICS_REQUEST
+        And wait for new bind10 stderr message AUTH_RECEIVED_COMMAND
+
+        When I query statistics zones of bind10 module Auth
+        last bindctl output should not contain "error"
+        Then the statistics counter v4 in the category request for the zone _SERVER_ should be 1
+        Then the statistics counter v6 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter udp in the category request for the zone _SERVER_ should be 1
+        Then the statistics counter tcp in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter edns0 in the category request for the zone _SERVER_ should be 1
+        Then the statistics counter badednsver in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter tsig in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter sig0 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter badsig in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter dnssec_ok in the category request for the zone _SERVER_ should be 1
+        Then the statistics counter query in the category opcode for the zone _SERVER_ should be 1
+        Then the statistics counter iquery in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter status in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter notify in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter update in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter other in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter responses for the zone _SERVER_ should be 1
+        Then the statistics counter truncated in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter edns0 in the category response for the zone _SERVER_ should be 1
+        Then the statistics counter tsig in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter sig0 in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter qrysuccess for the zone _SERVER_ should be 0
+        Then the statistics counter qryauthans for the zone _SERVER_ should be 0
+        Then the statistics counter qrynoauthans for the zone _SERVER_ should be 1
+        Then the statistics counter qryreferral for the zone _SERVER_ should be 1
+        Then the statistics counter qrynxrrset for the zone _SERVER_ should be 0
+        Then the statistics counter authqryrej for the zone _SERVER_ should be 0
+        Then the statistics counter noerror in the category rcode for the zone _SERVER_ should be 1
+        Then the statistics counter formerr in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter servfail in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter nxdomain in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notimp in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter refused in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter yxdomain in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter yxrrset in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter nxrrset in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notauth in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notzone in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter badvers in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter other in the category rcode for the zone _SERVER_ should be 0
+
     Scenario: SSHFP query
         # We are testing one more RR type for a normal successful case
         Given I have bind10 running with configuration example.org.inmem.config
@@ -132,18 +494,151 @@ Feature: Querying feature
         And wait for bind10 stderr message AUTH_SERVER_STARTED
 
         bind10 module Auth should be running
+        And bind10 module Stats should be running
         And bind10 module Resolver should not be running
         And bind10 module Xfrout should not be running
         And bind10 module Zonemgr should not be running
         And bind10 module Xfrin should not be running
-        And bind10 module Stats should not be running
         And bind10 module StatsHttpd should not be running
 
+        When I query statistics zones of bind10 module Auth
+        last bindctl output should not contain "error"
+        Then the statistics counter v4 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter v6 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter udp in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter tcp in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter edns0 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter badednsver in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter tsig in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter sig0 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter badsig in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter dnssec_ok in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter query in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter iquery in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter status in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter notify in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter update in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter other in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter responses for the zone _SERVER_ should be 0
+        Then the statistics counter truncated in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter edns0 in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter tsig in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter sig0 in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter qrysuccess for the zone _SERVER_ should be 0
+        Then the statistics counter qryauthans for the zone _SERVER_ should be 0
+        Then the statistics counter qrynoauthans for the zone _SERVER_ should be 0
+        Then the statistics counter qryreferral for the zone _SERVER_ should be 0
+        Then the statistics counter qrynxrrset for the zone _SERVER_ should be 0
+        Then the statistics counter authqryrej for the zone _SERVER_ should be 0
+        Then the statistics counter noerror in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter formerr in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter servfail in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter nxdomain in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notimp in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter refused in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter yxdomain in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter yxrrset in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter nxrrset in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notauth in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notzone in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter badvers in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter other in the category rcode for the zone _SERVER_ should be 0
+
         A query for example.org type SSHFP should have rcode NOERROR
         The last query response should have ancount 0
+
+        When I query statistics zones of bind10 module Auth
+        last bindctl output should not contain "error"
+        Then the statistics counter v4 in the category request for the zone _SERVER_ should be 1
+        Then the statistics counter v6 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter udp in the category request for the zone _SERVER_ should be 1
+        Then the statistics counter tcp in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter edns0 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter badednsver in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter tsig in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter sig0 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter badsig in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter dnssec_ok in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter query in the category opcode for the zone _SERVER_ should be 1
+        Then the statistics counter iquery in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter status in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter notify in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter update in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter other in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter responses for the zone _SERVER_ should be 1
+        Then the statistics counter truncated in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter edns0 in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter tsig in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter sig0 in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter qrysuccess for the zone _SERVER_ should be 0
+        Then the statistics counter qryauthans for the zone _SERVER_ should be 1
+        Then the statistics counter qrynoauthans for the zone _SERVER_ should be 0
+        Then the statistics counter qryreferral for the zone _SERVER_ should be 0
+        Then the statistics counter qrynxrrset for the zone _SERVER_ should be 1
+        Then the statistics counter authqryrej for the zone _SERVER_ should be 0
+        Then the statistics counter noerror in the category rcode for the zone _SERVER_ should be 1
+        Then the statistics counter formerr in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter servfail in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter nxdomain in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notimp in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter refused in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter yxdomain in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter yxrrset in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter nxrrset in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notauth in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notzone in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter badvers in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter other in the category rcode for the zone _SERVER_ should be 0
+
         A query for shell.example.org type SSHFP should have rcode NOERROR
         The last query response should have ancount 1
         The answer section of the last query response should be
         """
         shell.example.org.      3600    IN      SSHFP   2 1 123456789abcdef67890123456789abcdef67890
         """
+
+        And wait for new bind10 stderr message STATS_SEND_STATISTICS_REQUEST
+        And wait for new bind10 stderr message AUTH_RECEIVED_COMMAND
+
+        When I query statistics zones of bind10 module Auth
+        last bindctl output should not contain "error"
+        Then the statistics counter v4 in the category request for the zone _SERVER_ should be 2
+        Then the statistics counter v6 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter udp in the category request for the zone _SERVER_ should be 2
+        Then the statistics counter tcp in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter edns0 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter badednsver in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter tsig in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter sig0 in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter badsig in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter dnssec_ok in the category request for the zone _SERVER_ should be 0
+        Then the statistics counter query in the category opcode for the zone _SERVER_ should be 2
+        Then the statistics counter iquery in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter status in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter notify in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter update in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter other in the category opcode for the zone _SERVER_ should be 0
+        Then the statistics counter responses for the zone _SERVER_ should be 2
+        Then the statistics counter truncated in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter edns0 in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter tsig in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter sig0 in the category response for the zone _SERVER_ should be 0
+        Then the statistics counter qrysuccess for the zone _SERVER_ should be 1
+        Then the statistics counter qryauthans for the zone _SERVER_ should be 2
+        Then the statistics counter qrynoauthans for the zone _SERVER_ should be 0
+        Then the statistics counter qryreferral for the zone _SERVER_ should be 0
+        Then the statistics counter qrynxrrset for the zone _SERVER_ should be 1
+        Then the statistics counter authqryrej for the zone _SERVER_ should be 0
+        Then the statistics counter noerror in the category rcode for the zone _SERVER_ should be 2
+        Then the statistics counter formerr in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter servfail in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter nxdomain in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notimp in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter refused in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter yxdomain in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter yxrrset in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter nxrrset in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notauth in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter notzone in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter badvers in the category rcode for the zone _SERVER_ should be 0
+        Then the statistics counter other in the category rcode for the zone _SERVER_ should be 0
diff --git a/tests/lettuce/features/terrain/bind10_control.py b/tests/lettuce/features/terrain/bind10_control.py
index 142a78e..394d18a 100644
--- a/tests/lettuce/features/terrain/bind10_control.py
+++ b/tests/lettuce/features/terrain/bind10_control.py
@@ -391,14 +391,16 @@ def find_value(dictionary, key):
         for v in dictionary.values():
             return find_value(v, key)
 
- at step('the statistics counter (\S+)(?: for the zone (\S+))? should be' + \
+ at step('the statistics counter (\S+)(?: in the category (\S+))?'+ \
+          '(?: for the zone (\S+))? should be' + \
           '(?:( greater than| less than| between))? (\-?\d+)(?: and (\-?\d+))?')
-def check_statistics(step, counter, zone, gtltbt, number, upper):
+def check_statistics(step, counter, category, zone, gtltbt, number, upper):
     """
     check the output of bindctl for statistics of specified counter
     and zone.
     Parameters:
     counter ('counter <counter>'): The counter name of statistics.
+    category ('category <category>', optional): The category of counter.
     zone ('zone <zone>', optional): The zone name.
     gtltbt (' greater than'|' less than'|' between', optional): greater than
           <number> or less than <number> or between <number> and <upper>.
@@ -409,14 +411,22 @@ def check_statistics(step, counter, zone, gtltbt, number, upper):
     """
     output = parse_bindctl_output_as_data_structure()
     found = None
+    category_str = ""
     zone_str = ""
+    depth = []
+    if category:
+        depth.insert(0, category)
+        category_str = " for category %s" % category
     if zone:
-        found = find_value(find_value(output, zone), counter)
+        depth.insert(0, zone)
         zone_str = " for zone %s" % zone
+    for level in depth:
+        output = find_value(output, level)
     else:
         found = find_value(output, counter)
     assert found is not None, \
-        'Not found statistics counter %s%s' % (counter, zone_str)
+        'Not found statistics counter %s%s%s' % \
+            (counter, category_str, zone_str)
     msg = "Got %s, expected%s %s as counter %s%s" % \
         (found, gtltbt, number, counter, zone_str)
     if gtltbt and 'between' in gtltbt and upper:
diff --git a/tests/system/bindctl/tests.sh b/tests/system/bindctl/tests.sh
index 848c09a..090791c 100755
--- a/tests/system/bindctl/tests.sh
+++ b/tests/system/bindctl/tests.sh
@@ -25,12 +25,66 @@ status=0
 n=0
 
 # TODO: consider consistency with statistics definition in auth.spec
-cnt_name1="\<tcp\>"
-cnt_name2="\<udp\>"
-cnt_name3="\<query\>"
-cnt_value1=0
-cnt_value2=0
-cnt_value3=0
+
+# flatten JSON
+awk_flatten_json='
+function join(ary, len) {
+    ret = "";
+    for (i = 1; i <= len; ++i) {
+        ret = ret""ary[i];
+    }
+    return ret;
+}
+BEGIN {
+    depth = 0;
+}
+/.+{$/ {
+    label[++depth] = $1;
+    next;
+}
+/},?/ {
+    --depth;
+    next;
+}
+/:/ {
+    print join(label,depth)""$1" "$2;
+}
+'
+# Check the counters have expected values given with 1st argument.
+# This function tests only these counters will be incremented in every checks
+# since the content of datasource and requests are not changed in this test. 
+test_counters () {
+    status=0
+    awk "$awk_flatten_json" bindctl.out.$n | \
+        grep '"Auth":"zones":"_SERVER_":"request":"v4": '$1 > \
+        /dev/null || status=1
+    awk "$awk_flatten_json" bindctl.out.$n | \
+        grep '"Auth":"zones":"_SERVER_":"request":"v6": '0 > \
+        /dev/null || status=1
+    awk "$awk_flatten_json" bindctl.out.$n | \
+        grep '"Auth":"zones":"_SERVER_":"request":"udp": '$1 > \
+        /dev/null || status=1
+    awk "$awk_flatten_json" bindctl.out.$n | \
+        grep '"Auth":"zones":"_SERVER_":"request":"tcp": '0 > \
+        /dev/null || status=1
+    awk "$awk_flatten_json" bindctl.out.$n | \
+        grep '"Auth":"zones":"_SERVER_":"opcode":"query": '$1 > \
+        /dev/null || status=1
+    awk "$awk_flatten_json" bindctl.out.$n | \
+        grep '"Auth":"zones":"_SERVER_":"responses": '$1 > \
+        /dev/null || status=1
+    awk "$awk_flatten_json" bindctl.out.$n | \
+        grep '"Auth":"zones":"_SERVER_":"rcode":"noerror": '$1 > \
+        /dev/null || status=1
+    awk "$awk_flatten_json" bindctl.out.$n | \
+        grep '"Auth":"zones":"_SERVER_":"qrysuccess": '$1 > \
+        /dev/null || status=1
+    awk "$awk_flatten_json" bindctl.out.$n | \
+        grep '"Auth":"zones":"_SERVER_":"qryauthans": '$1 > \
+        /dev/null || status=1
+    return $status
+}
+expected_count=0
 
 echo "I:Checking b10-auth is disabled by default ($n)"
 $DIG +norec @10.53.0.1 -p 53210 ns.example.com. A > /dev/null && status=1
@@ -56,15 +110,10 @@ sleep 2
 echo 'Stats show
 ' | $RUN_BINDCTL \
 	--csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1
-# the server should have received 1 UDP and 0 TCP queries (the server
-# startup script no longer sends any TCP queries)
-cnt_value1=`expr $cnt_value1 + 0`
-cnt_value2=`expr $cnt_value2 + 1`
-cnt_value3=`expr $cnt_value1 + $cnt_value2`
-grep $cnt_name1".*\<"$cnt_value1"\>" bindctl.out.$n > /dev/null || status=1
-grep $cnt_name2".*\<"$cnt_value2"\>" bindctl.out.$n > /dev/null || status=1
-grep $cnt_name3".*\<"$cnt_value3"\>" bindctl.out.$n > /dev/null || status=1
-if [ $status != 0 ]; then echo "I:failed"; fi
+# the server should have received 1 request
+expected_count=`expr $expected_count + 1`
+test_counters $expected_count
+if [ $? != 0 ]; then echo "I:failed"; fi
 n=`expr $n + 1`
 
 echo "I:Stopping b10-auth and checking that ($n)"
@@ -101,13 +150,9 @@ echo 'Stats show
 # auth sent. Then it cumulates them and new counts which the living
 # auth sends. This note assumes that the issue would have been
 # resolved : "#1941 stats lossage (multiple auth servers)".
-cnt_value1=`expr $cnt_value1 + 0`
-cnt_value2=`expr $cnt_value2 + 1`
-cnt_value3=`expr $cnt_value1 + $cnt_value2`
-grep $cnt_name1".*\<"$cnt_value1"\>" bindctl.out.$n > /dev/null || status=1
-grep $cnt_name2".*\<"$cnt_value2"\>" bindctl.out.$n > /dev/null || status=1
-grep $cnt_name3".*\<"$cnt_value3"\>" bindctl.out.$n > /dev/null || status=1
-if [ $status != 0 ]; then echo "I:failed"; fi
+expected_count=`expr $expected_count + 1`
+test_counters $expected_count
+if [ $? != 0 ]; then echo "I:failed"; fi
 n=`expr $n + 1`
 
 echo "I:Changing the data source from sqlite3 to in-memory ($n)"
@@ -129,13 +174,9 @@ echo 'Stats show
 ' | $RUN_BINDCTL \
 	--csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1
 # The statistics counters shouldn't be reset due to hot-swapping datasource.
-cnt_value1=`expr $cnt_value1 + 0`
-cnt_value2=`expr $cnt_value2 + 1`
-cnt_value3=`expr $cnt_value1 + $cnt_value2`
-grep $cnt_name1".*\<"$cnt_value1"\>" bindctl.out.$n > /dev/null || status=1
-grep $cnt_name2".*\<"$cnt_value2"\>" bindctl.out.$n > /dev/null || status=1
-grep $cnt_name3".*\<"$cnt_value3"\>" bindctl.out.$n > /dev/null || status=1
-if [ $status != 0 ]; then echo "I:failed"; fi
+expected_count=`expr $expected_count + 1`
+test_counters $expected_count
+if [ $? != 0 ]; then echo "I:failed"; fi
 n=`expr $n + 1`
 
 echo "I:Starting more b10-auths and checking that ($n)"
@@ -155,9 +196,7 @@ n=`expr $n + 1`
 
 echo "I:Rechecking BIND 10 statistics consistency after a pause ($n)"
 sleep 2
-cnt_value1=`expr $cnt_value1 + 0`
-cnt_value2=`expr $cnt_value2 + 1`
-cnt_value3=`expr $cnt_value1 + $cnt_value2`
+expected_count=`expr $expected_count + 1`
 # Rechecking some times
 for i in 1 2 3 4
 do
@@ -167,10 +206,8 @@ do
     # The statistics counters should keep being consistent even while
     # multiple b10-auths are running.
 
-    grep $cnt_name1".*\<"$cnt_value1"\>" bindctl.out.$n > /dev/null || status=1
-    grep $cnt_name2".*\<"$cnt_value2"\>" bindctl.out.$n > /dev/null || status=1
-    grep $cnt_name3".*\<"$cnt_value3"\>" bindctl.out.$n > /dev/null || status=1
-    if [ $status != 0 ]; then echo "I:failed "; break ; fi
+    test_counters $expected_count
+    if [ $? != 0 ]; then echo "I:failed "; break ; fi
 done
 n=`expr $n + 1`
 



More information about the bind10-changes mailing list