M docs/source/simplelog.rst +26 -0
@@ 65,11 65,37 @@ the ``add_file`` function tries to be cl
>>> cat test_log.log
2016.11.23 19:54:10 test_log INFO:: Information message to file
+---
+Structured logging
+---
+
+This module now includes support for structured logging using the approach described in the `logging cookbook`_
+
+We have included a class which can be used to wrap messages sent to the `info`, `error` or `warn` logging methods to present
+the output as a Json object. In keeping with the rest of the module this is designed to be simple to use
+
+ >>> from simple_log import get_log, StructuredMessage as sm
+ >>> my_log = get_log('test_log')
+ >>> my_log.info(sm('Test message with additions', key=1, value=one))
+ 2023.03.08 18:00:48 test_log INFO:: Test message with additions >>> {"key": 1, "value": "one"}
+
+If you already have some key information in a dictionary or other mapping object you can just pass the whole thing
+
+ >>> from simple_log import get_log, StructuredMessage as sm
+ >>> my_log = get_log('test_log')
+ >>> my_dict = {'id': 1, 'length': 200, 'name': 'block of wood', 'owner': 'carpenter'}
+ >>> my_log.info(sm('Test with dict', **my_dict))
+ 2023.03.08 18:04:59 test_log INFO:: Test with dict >>> {"id": 1, "length": 200, "name": "block of wood", "owner": "carpenter"}
+
+.. _logging cookbook: https://docs.python.org/3/howto/logging-cookbook.html#implementing-structured-logging
+
Code Structure
--------------
The only dependency for ``simple_log`` is the Python standard library :py:mod:`logging` module
+The structured message class has increased this to also include the :py:mod:`json` module as well.
+
This package is made up of one Python module called ``log_utilities.py`` within the ``simple_log`` module. It has two functions
.. autofunction:: simple_log.get_log
M simple_log/__init__.py +1 -1
@@ 1,1 1,1 @@
-from simple_log.log_utilities import get_log, add_file
No newline at end of file
+from simple_log.log_utilities import get_log, add_file, StructuredMessage
No newline at end of file
M simple_log/log_utilities.py +10 -1
@@ 9,6 9,7 @@ Licensed under the MIT license - https:/
__date__ = (2016, 11, 11)
__author__ = "Andrew J Todd esq. <andy47@halfcooked.com>"
+import json
import logging
MESSAGE_FORMAT = '%(asctime)s %(name)s %(levelname)s:: %(message)s'
@@ 16,6 17,14 @@ DATE_FORMAT = '%Y.%m.%d %T'
logs = {}
+class StructuredMessage:
+ def __init__(self, message, /, **kwargs):
+ self.message = message
+ self.kwargs = kwargs
+
+ def __str__(self):
+ return '%s >>> %s' % (self.message, json.dumps(self.kwargs))
+
def get_log(logname=None):
"""Return a log object called `<logname>`
@@ 54,4 63,4 @@ def add_file(log_name, file_name):
if type(log_name) is logging.Logger:
log_name.addHandler(file_handler)
elif log_name in logs:
- logs[log_name].addHandler(file_handler)
+ logs[log_name].addHandler(file_handler)
No newline at end of file
M tests/test_simple_log.py +5 -1
@@ 9,7 9,7 @@ import logging
import os
import tempfile
-from simple_log import get_log, add_file
+from simple_log import get_log, add_file, StructuredMessage as sm
def test_get_log_returns_something():
@@ 70,3 70,7 @@ def test_set_level():
test_log.setLevel('DEBUG')
assert test_log.level == logging.DEBUG
+
+def test_structured_message():
+ my_obj = sm('message', first=1, second='two')
+ assert my_obj.__str__() == 'message >>> {"first": 1, "second": "two"}'
No newline at end of file