JsonLogger ========== .. module:: aiologger.loggers.json A simple, featureful, drop-in replacement to the default ``aiologger.Logger`` that grants to always log valid, single line, JSON output. It logs everything ------------------ .. code:: python import asyncio from datetime import datetime from aiologger.loggers.json import JsonLogger async def main(): logger = JsonLogger.with_default_handlers() await logger.info("Im a string") # {"logged_at": "2018-06-14T09:34:56.482817", "line_number": 9, "function": "main", "level": "INFO", "file_path": "/Users/diogo.mmartins/Library/Preferences/PyCharm2018.1/scratches/scratch_47.py", "msg": "Im a string"} await logger.info({ 'date_objects': datetime.now(), 'exceptions': KeyError("Boooom"), 'types': JsonLogger }) # {"logged_at": "2018-06-14T09:34:56.483000", "line_number": 13, "function": "main", "level": "INFO", "file_path": "/Users/diogo.mmartins/Library/Preferences/PyCharm2018.1/scratches/scratch_47.py", "msg": {"date_objects": "2018-06-14T09:34:56.482953", "exceptions": "Exception: KeyError('Boooom',)", "types": ""}} await logger.shutdown() loop = asyncio.get_event_loop() loop.run_until_complete(main()) loop.close() Logging callables with CallableWrapper -------------------------------------- ``Callable[[], str]`` log values may also be used to generate dynamic content that are evaluated at serialization time. All you need to do is wrap the callable using ``CallableWrapper``: .. code:: python import asyncio import logging from random import randint from aiologger.loggers.json import JsonLogger from aiologger.utils import CallableWrapper def rand(): return randint(1, 100) logger = JsonLogger.with_default_handlers(level=logging.DEBUG) async def main(): await logger.info(CallableWrapper(rand)) # {"logged_at": "2018-06-14T09:37:52.624123", "line_number": 15, "function": "main", "level": "INFO", "file_path": "/Users/diogo.mmartins/Library/Preferences/PyCharm2018.1/scratches/scratch_47.py", "msg": 70} await logger.info({"Xablau": CallableWrapper(rand)}) # {"logged_at": "2018-06-14T09:37:52.624305", "line_number": 18, "function": "main", "level": "INFO", "file_path": "/Users/diogo.mmartins/Library/Preferences/PyCharm2018.1/scratches/scratch_47.py", "msg": {"Xablau": 29}} await logger.shutdown() loop = asyncio.get_event_loop() loop.run_until_complete(main()) loop.close() Adding content to root ---------------------- By default, everything passed to the log methods is inserted inside the ``msg`` root attribute, but sometimes we want to add content to the root level. Flatten ~~~~~~~ This behavior may be achieved using ``flatten``. Which is available both as a method parameter and instance attribute. As an instance attribute, every call to a log method would "flat" the dict attributes. .. code:: python import asyncio import logging from aiologger.loggers.json import JsonLogger async def main(): logger = JsonLogger.with_default_handlers(level=logging.DEBUG, flatten=True) await logger.info({"status_code": 200, "response_time": 0.00534534}) # {"status_code": 200, "response_time": 0.534534, "logged_at": "2017-08-11T16:18:58.446985", "line_number": 6, "function": "", "level": "INFO", "path": "/Users/diogo/PycharmProjects/aiologger/bla.py"} await logger.error({"status_code": 404, "response_time": 0.00134534}) # {"status_code": 200, "response_time": 0.534534, "logged_at": "2017-08-11T16:18:58.446986", "line_number": 6, "function": "", "level": "INFO", "path": "/Users/diogo/PycharmProjects/aiologger/bla.py"} await logger.shutdown() loop = asyncio.get_event_loop() loop.run_until_complete(main()) loop.close() As a method parameter, only the specific call would add the content to the root. .. code:: python import asyncio import logging from aiologger.loggers.json import JsonLogger async def main(): logger = await JsonLogger.with_default_handlers(level=logging.DEBUG) await logger.info({"status_code": 200, "response_time": 0.00534534}, flatten=True) # {"logged_at": "2017-08-11T16:23:16.312441", "line_number": 6, "function": "", "level": "INFO", "path": "/Users/diogo/PycharmProjects/aiologger/bla.py", "status_code": 200, "response_time": 0.00534534} await logger.error({"status_code": 404, "response_time": 0.00134534}) # {"logged_at": "2017-08-11T16:23:16.312618", "line_number": 8, "function": "", "level": "ERROR", "path": "/Users/diogo/PycharmProjects/aiologger/bla.py", "msg": {"status_code": 404, "response_time": 0.00134534}} await logger.shutdown() loop = asyncio.get_event_loop() loop.run_until_complete(main()) loop.close() **Warning**: It is possible to overwrite keys that are already present at root level. .. code:: python import asyncio import logging from aiologger.loggers.json import JsonLogger async def main(): logger = JsonLogger.with_default_handlers(level=logging.DEBUG) await logger.info({'logged_at': 'Yesterday'}, flatten=True) # {"logged_at": "Yesterday", "line_number": 6, "function": "", "level": "INFO", "path": "/Users/diogo/PycharmProjects/aiologger/bla.py"} await logger.shutdown() loop = asyncio.get_event_loop() loop.run_until_complete(main()) loop.close() Extra ~~~~~ The ``extra`` parameter allow you to add specific content to root: .. code:: python import asyncio import logging from aiologger.loggers.json import JsonLogger async def main(): a = 69 b = 666 c = [a, b] logger = JsonLogger.with_default_handlers(level=logging.DEBUG) await logger.info("I'm a simple log") # {"msg": "I'm a simple log", "logged_at": "2017-08-11T12:21:05.722216", "line_number": 5, "function": "", "level": "INFO", "path": "/Users/diogo/PycharmProjects/aiologger/bla.py"} await logger.info({"dog": "Xablau"}, extra=locals()) # {"logged_at": "2018-06-14T09:47:29.477705", "line_number": 14, "function": "main", "level": "INFO", "file_path": "/Users/diogo.mmartins/Library/Preferences/PyCharm2018.1/scratches/scratch_47.py", "msg": {"dog": "Xablau"}, "logger": "", "c": [69, 666], "b": 666, "a": 69} await logger.shutdown() loop = asyncio.get_event_loop() loop.run_until_complete(main()) loop.close() It also allows you to override the default root content: .. code:: python import asyncio import logging from aiologger.loggers.json import JsonLogger async def main(): logger = JsonLogger.with_default_handlers(level=logging.DEBUG) await logger.info("I'm a simple log") # {"msg": "I'm a simple log", "logged_at": "2017-08-11T12:21:05.722216", "line_number": 6, "function": "", "level": "INFO", "path": "/Users/diogo/PycharmProjects/aiologger/bla.py"} await logger.info("I'm a simple log", extra={'logged_at': 'Yesterday'}) # {"msg": "I'm a simple log", "logged_at": "Yesterday", "line_number": 6, "function": "", "level": "INFO", "path": "/Users/diogo/PycharmProjects/aiologger/bla.py"} await logger.shutdown() loop = asyncio.get_event_loop() loop.run_until_complete(main()) loop.close() and it may also be used as an instance attribute: .. code:: python import asyncio import logging from aiologger.loggers.json import JsonLogger async def main(): logger = JsonLogger.with_default_handlers(level=logging.DEBUG, extra={'logged_at': 'Yesterday'}) await logger.info("I'm a simple log") # {"msg": "I'm a simple log", "logged_at": "Yesterday", "line_number": 6, "function": "", "level": "INFO", "path": "/Users/diogo/PycharmProjects/aiologger/bla.py"} await logger.info("I'm a simple log") # {"msg": "I'm a simple log", "logged_at": "Yesterday", "line_number": 6, "function": "", "level": "INFO", "path": "/Users/diogo/PycharmProjects/aiologger/bla.py"} await logger.shutdown() loop = asyncio.get_event_loop() loop.run_until_complete(main()) loop.close() Exclude default logger fields ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you think that the default fields are too much, it's also possible to exclude fields from the output message. .. code:: python import asyncio import logging from aiologger.loggers.json import JsonLogger from aiologger.formatters.json import FUNCTION_NAME_FIELDNAME, LOGGED_AT_FIELDNAME async def main(): logger = JsonLogger.with_default_handlers( level=logging.DEBUG, exclude_fields=[FUNCTION_NAME_FIELDNAME, LOGGED_AT_FIELDNAME, 'file_path', 'line_number'] ) await logger.info("Function, file path and line number wont be printed") # {"level": "INFO", "msg": "Function, file path and line number wont be printed"} await logger.shutdown() loop = asyncio.get_event_loop() loop.run_until_complete(main()) loop.close() Serializer options ------------------ ``serializer_kwargs`` is available both as instance attribute and as a log method parameter and may be used to pass keyword arguments to the ``serializer`` function. (See more: `https://docs.python.org/3/library/json.html`_) For pretty printing the output, you may use the ``indent`` kwarg. Ex.: .. code:: python import asyncio import logging from aiologger.loggers.json import JsonLogger async def main(): logger = JsonLogger.with_default_handlers( level=logging.DEBUG, serializer_kwargs={'indent': 4} ) await logger.info({ "artist": "Black Country Communion", "song": "Cold" }) await logger.shutdown() loop = asyncio.get_event_loop() loop.run_until_complete(main()) loop.close() Would result in a pretty indented output: .. code:: javascript { "logged_at": "2017-08-11T21:04:21.559070", "line_number": 5, "function": "", "level": "INFO", "file_path": "/Users/diogo/Library/Preferences/PyCharm2017.1/scratches/scratch_32.py", "msg": { "artist": "Black Country Communion", "song": "Cold" } } The same result can be achieved making a log call with ``serializer_kwargs`` as a parameter. .. code:: python await logger.warning({'artist': 'Black Country Communion', 'song': 'Cold'}, serializer_kwargs={'indent': 4}) .. _`https://docs.python.org/3/library/json.html`: https://docs.python.org/3/library/json.html