spy: option to write to log instead of file/stdout
diff --git a/documentation/url_handlers.rst b/documentation/url_handlers.rst
index 42a53fa..5c57615 100644
--- a/documentation/url_handlers.rst
+++ b/documentation/url_handlers.rst
@@ -140,6 +140,13 @@
   hex dump). In this mode, no control line and other commands are logged.
 - ``all`` also show ``in_waiting`` and empty ``read()`` calls (hidden by
   default because of high traffic).
+- ``log`` or ``log=LOGGERNAME`` output to stdlib ``logging`` module. Default
+  channel name is ``serial``. This variant outputs hex dump.
+- ``rawlog`` or ``rawlog=LOGGERNAME`` output to stdlib ``logging`` module. Default
+  channel name is ``serial``. This variant outputs text (``repr``).
+
+The ``log`` and ``rawlog`` options require that the logging is set up, in order
+to see the log output.
 
 Example::
 
@@ -208,6 +215,7 @@
 The spy output will be live in the second terminal window.
 
 .. versionadded:: 3.0
+.. versionchanged:: 3.6 Added ``log`` and ``rawlog`` options
 
 
 ``alt://``
@@ -236,9 +244,9 @@
 
 .. versionadded:: 3.0
 
+
 ``cp2110://``
 =============
-
 This backend implements support for HID-to-UART devices manufactured by Silicon
 Labs and marketed as CP2110 and CP2114. The implementation is (mostly)
 OS-independent and in userland. It relies on `cython-hidapi`_.
@@ -264,4 +272,3 @@
 - ``spy://COM54?file=log.txt``
 - ``alt:///dev/ttyUSB0?class=PosixPollSerial``
 - ``cp2110://0001:004a:00``
-
diff --git a/serial/urlhandler/protocol_spy.py b/serial/urlhandler/protocol_spy.py
index 67c700b..55e3765 100644
--- a/serial/urlhandler/protocol_spy.py
+++ b/serial/urlhandler/protocol_spy.py
@@ -22,6 +22,7 @@
 
 from __future__ import absolute_import
 
+import logging
 import sys
 import time
 
@@ -152,6 +153,46 @@
         self.write_line(time.time() - self.start_time, name, value)
 
 
+class FormatLog(object):
+    """\
+    Write data to logging module.
+    """
+
+    def __init__(self, output, color):
+        # output and color is ignored
+        self.log = logging.getLogger(output)
+
+    def rx(self, data):
+        """show received data"""
+        if data:
+            self.log.info('RX {!r}'.format(data))
+
+    def tx(self, data):
+        """show transmitted data"""
+        self.log.info('TX {!r}'.format(data))
+
+    def control(self, name, value):
+        """show control calls"""
+        self.log.info('{}: {}'.format(name, value))
+
+
+class FormatLogHex(FormatLog):
+    """\
+    Write data to logging module.
+    """
+
+    def rx(self, data):
+        """show received data"""
+        if data:
+            for offset, row in hexdump(data):
+                self.log.info('RX {}{}'.format('{:04X}  '.format(offset), row))
+
+    def tx(self, data):
+        """show transmitted data"""
+        for offset, row in hexdump(data):
+            self.log.info('TX {}{}'.format('{:04X}  '.format(offset), row))
+
+
 class Serial(serial.Serial):
     """\
     Inherit the native Serial port implementation and wrap all the methods and
@@ -189,6 +230,12 @@
                     color = True
                 elif option == 'raw':
                     formatter = FormatRaw
+                elif option == 'rawlog':
+                    formatter = FormatLog
+                    output = values[0] if values[0] else 'serial'
+                elif option == 'log':
+                    formatter = FormatLogHex
+                    output = values[0] if values[0] else 'serial'
                 elif option == 'all':
                     self.show_all = True
                 else: