papi: allow client control over loggers

This change enables a client to set debug levels
globally as well as individually.

exposes loggers as
  vpp_papi
  vpp_papi.serializer
  vpp_papi.transport

Type: improvement

Change-Id: Ib6bd1a1f552b51a22c9fe3de819a5fb970963ae5
Signed-off-by: Paul Vinciguerra <pvinci@vinciconsulting.com>
diff --git a/src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py b/src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py
index d0c7249..7effe68 100644
--- a/src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py
+++ b/src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py
@@ -114,3 +114,25 @@
         self.assertTrue(str(t).startswith("VPPEnumType"))
         self.assertEqual(t.name, type_name)
 
+
+class TestVppPapiLogging(unittest.TestCase):
+
+    def test_logger(self):
+        class Transport:
+            connected = True
+
+        class Vpp:
+            transport = Transport()
+
+            def disconnect(self):
+                pass
+
+        client = Vpp
+        with self.assertLogs('vpp_papi', level='DEBUG') as cm:
+            vpp_papi.vpp_atexit(client)
+        self.assertEqual(cm.output, ['DEBUG:vpp_papi:Cleaning up VPP on exit'])
+
+        with self.assertRaises(AssertionError):
+            with self.assertLogs('vpp_papi.serializer', level='DEBUG') as cm:
+                vpp_papi.vpp_atexit(client)
+        self.assertEqual(cm.output, [])
diff --git a/src/vpp-api/python/vpp_papi/tests/test_vpp_serializer.py b/src/vpp-api/python/vpp_papi/tests/test_vpp_serializer.py
index 317fea7..21b23bd 100755
--- a/src/vpp-api/python/vpp_papi/tests/test_vpp_serializer.py
+++ b/src/vpp-api/python/vpp_papi/tests/test_vpp_serializer.py
@@ -47,7 +47,6 @@
         self.assertEqual(nt.name, 'foobar')
         self.assertEqual(len(nt.name), len('foobar'))
 
-
     def test_limit(self):
         limited_type = VPPType('limited_type_t',
                                [['string', 'name', 0, {'limit': 16}]])
@@ -105,6 +104,7 @@
         self.assertEqual(len(b), size)
         self.assertEqual(nt.e, 1)
 
+
 class TestAddType(unittest.TestCase):
 
     def test_union(self):
@@ -201,7 +201,6 @@
                           [['vl_api_address_family_t', 'af'],
                            ['vl_api_address_union_t', 'un']])
 
-
         prefix = VPPType('vl_api_prefix_t',
                          [['vl_api_address_t', 'address'],
                           ['u8', 'len']])
@@ -274,7 +273,6 @@
         self.assertTrue(isinstance(nt.address, IPv4Interface))
         self.assertEqual(str(nt.address), '1.2.3.4/24')
 
-
     def test_recursive_address(self):
         af = VPPEnumType('vl_api_address_family_t', [["ADDRESS_IP4", 0],
                                                      ["ADDRESS_IP6", 1],
@@ -557,7 +555,6 @@
 
         self.assertEqual(len(b), 20)
 
-
     def test_lisp(self):
         VPPEnumType('vl_api_eid_type_t',
                     [["EID_TYPE_API_PREFIX", 0],
@@ -609,5 +606,27 @@
         self.assertIsNone(nt.address.prefix)
 
 
+class TestVppSerializerLogging(unittest.TestCase):
+
+    def test_logger(self):
+        # test logger name 'vpp_papi.serializer'
+        with self.assertRaises(VPPSerializerValueError) as ctx:
+            with self.assertLogs('vpp_papi.serializer', level='DEBUG') as cm:
+                u = VPPUnionType('vl_api_eid_address_t',
+                                 [["vl_api_prefix_t", "prefix"],
+                                  ["vl_api_mac_address_t", "mac"],
+                                  ["vl_api_nsh_t", "nsh"]])
+        self.assertEqual(cm.output, ["DEBUG:vpp_papi.serializer:Unknown union type vl_api_prefix_t"])
+
+        # test parent logger name 'vpp_papi'
+        with self.assertRaises(VPPSerializerValueError) as ctx:
+            with self.assertLogs('vpp_papi', level='DEBUG') as cm:
+                u = VPPUnionType('vl_api_eid_address_t',
+                                 [["vl_api_prefix_t", "prefix"],
+                                  ["vl_api_mac_address_t", "mac"],
+                                  ["vl_api_nsh_t", "nsh"]])
+        self.assertEqual(cm.output, ["DEBUG:vpp_papi.serializer:Unknown union type vl_api_prefix_t"])
+
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/src/vpp-api/python/vpp_papi/vpp_papi.py b/src/vpp-api/python/vpp_papi/vpp_papi.py
index 1b4df06..5c375df 100644
--- a/src/vpp-api/python/vpp_papi/vpp_papi.py
+++ b/src/vpp-api/python/vpp_papi/vpp_papi.py
@@ -43,7 +43,7 @@
 
     VppTransport = V
 
-logger = logging.getLogger(__name__)
+logger = logging.getLogger('vpp_papi')
 logger.addHandler(logging.NullHandler())
 
 if sys.version[0] == '2':
@@ -80,7 +80,7 @@
     """Clean up VPP connection on shutdown."""
     vpp_instance = vpp_weakref()
     if vpp_instance and vpp_instance.transport.connected:
-        vpp_instance.logger.debug('Cleaning up VPP on exit')
+        logger.debug('Cleaning up VPP on exit')
         vpp_instance.disconnect()
 
 
@@ -621,7 +621,7 @@
 
     def decode_incoming_msg(self, msg, no_type_conversion=False):
         if not msg:
-            self.logger.warning('vpp_api.read failed')
+            logger.warning('vpp_api.read failed')
             return
 
         (i, ci), size = self.header.unpack(msg, 0)
diff --git a/src/vpp-api/python/vpp_papi/vpp_serializer.py b/src/vpp-api/python/vpp_papi/vpp_serializer.py
index f46f207..d31a0bb 100644
--- a/src/vpp-api/python/vpp_papi/vpp_serializer.py
+++ b/src/vpp-api/python/vpp_papi/vpp_serializer.py
@@ -27,7 +27,7 @@
 # logger = logging.getLogger('vpp_serializer')
 # logger.setLevel(logging.DEBUG)
 #
-logger = logging.getLogger(__name__)
+logger = logging.getLogger('vpp_papi.serializer')
 
 if sys.version[0] == '2':
     def check(d): type(d) is dict
diff --git a/src/vpp-api/python/vpp_papi/vpp_transport_shmem.py b/src/vpp-api/python/vpp_papi/vpp_transport_shmem.py
index a7ba26b..f1ab728 100644
--- a/src/vpp-api/python/vpp_papi/vpp_transport_shmem.py
+++ b/src/vpp-api/python/vpp_papi/vpp_transport_shmem.py
@@ -2,10 +2,14 @@
 # A transport class. With two implementations.
 # One for socket and one for shared memory.
 #
+import logging
 
 from cffi import FFI
 import cffi
 
+logger = logging.getLogger('vpp_papi.transport')
+logger.addHandler(logging.NullHandler())
+
 ffi = FFI()
 ffi.cdef("""
 typedef void (*vac_callback_t)(unsigned char * data, int len);
diff --git a/src/vpp-api/python/vpp_papi/vpp_transport_socket.py b/src/vpp-api/python/vpp_papi/vpp_transport_socket.py
index d6431ca..d88082f 100644
--- a/src/vpp-api/python/vpp_papi/vpp_transport_socket.py
+++ b/src/vpp-api/python/vpp_papi/vpp_transport_socket.py
@@ -13,6 +13,9 @@
 import logging
 from . import vpp_papi
 
+logger = logging.getLogger('vpp_papi.transport')
+logger.addHandler(logging.NullHandler())
+
 
 class VppTransportSocketIOError(IOError):
     # TODO: Document different values of error number (first numeric argument).