Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/jrobichaud/django-structlog
Structured Logging for Django
https://github.com/jrobichaud/django-structlog
celery django django-rest-framework hacktoberfest logging structlog structured-logging
Last synced: about 2 months ago
JSON representation
Structured Logging for Django
- Host: GitHub
- URL: https://github.com/jrobichaud/django-structlog
- Owner: jrobichaud
- License: mit
- Created: 2019-04-07T21:29:34.000Z (over 5 years ago)
- Default Branch: main
- Last Pushed: 2024-04-08T11:39:58.000Z (9 months ago)
- Last Synced: 2024-04-09T12:38:32.843Z (9 months ago)
- Topics: celery, django, django-rest-framework, hacktoberfest, logging, structlog, structured-logging
- Language: Python
- Homepage: https://django-structlog.readthedocs.io/en/latest/
- Size: 3.98 MB
- Stars: 383
- Watchers: 6
- Forks: 32
- Open Issues: 0
-
Metadata Files:
- Readme: README.rst
- License: LICENSE.rst
Awesome Lists containing this project
README
.. inclusion-marker-introduction-begin
django-structlog
================| |pypi| |wheels| |build-status| |docs| |coverage| |open_issues| |pull_requests|
| |django| |python| |license| |black| |ruff|
| |watchers| |stars| |forks|.. |build-status| image:: https://github.com/jrobichaud/django-structlog/actions/workflows/main.yml/badge.svg?branch=main
:target: https://github.com/jrobichaud/django-structlog/actions
:alt: Build Status.. |pypi| image:: https://img.shields.io/pypi/v/django-structlog.svg
:target: https://pypi.org/project/django-structlog/
:alt: PyPI version.. |docs| image:: https://readthedocs.org/projects/django-structlog/badge/?version=latest
:target: https://django-structlog.readthedocs.io/en/latest/?badge=latest
:alt: Documentation Status.. |coverage| image:: https://img.shields.io/codecov/c/github/jrobichaud/django-structlog.svg
:target: https://codecov.io/gh/jrobichaud/django-structlog
:alt: codecov.. |python| image:: https://img.shields.io/pypi/pyversions/django-structlog.svg
:target: https://pypi.org/project/django-structlog/
:alt: Supported Python versions.. |license| image:: https://img.shields.io/pypi/l/django-structlog.svg
:target: https://github.com/jrobichaud/django-structlog/blob/main/LICENSE.rst
:alt: License.. |open_issues| image:: https://img.shields.io/github/issues/jrobichaud/django-structlog.svg
:target: https://github.com/jrobichaud/django-structlog/issues
:alt: GitHub issues.. |django| image:: https://img.shields.io/pypi/djversions/django-structlog.svg
:target: https://pypi.org/project/django-structlog/
:alt: PyPI - Django Version.. |pull_requests| image:: https://img.shields.io/github/issues-pr/jrobichaud/django-structlog.svg
:target: https://github.com/jrobichaud/django-structlog/pulls
:alt: GitHub pull requests.. |forks| image:: https://img.shields.io/github/forks/jrobichaud/django-structlog.svg?style=social
:target: https://github.com/jrobichaud/django-structlog/
:alt: GitHub forks.. |stars| image:: https://img.shields.io/github/stars/jrobichaud/django-structlog.svg?style=social
:target: https://github.com/jrobichaud/django-structlog/
:alt: GitHub stars.. |watchers| image:: https://img.shields.io/github/watchers/jrobichaud/django-structlog.svg?style=social
:target: https://github.com/jrobichaud/django-structlog/
:alt: GitHub watchers.. |wheels| image:: https://img.shields.io/pypi/wheel/django-structlog.svg
:target: https://pypi.org/project/django-structlog/
:alt: PyPI - Wheel.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/python/black
:alt: Black.. |ruff| image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
:target: https://github.com/astral-sh/ruff
:alt: Ruffdjango-structlog is a structured logging integration for `Django `_ project using `structlog `_
Logging will then produce additional cohesive metadata on each logs that makes it easier to track events or incidents.
Additional Popular Integrations
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^`Django REST framework `_
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~``Django REST framework`` is supported by default. But when using it with ``rest_framework.authentication.TokenAuthentication`` (or other DRF authentications) ``user_id`` will be only be in ``request_finished`` and ``request_failed`` instead of each logs.
See `#37 `_ for details.
`django-ninja `_
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~``django-ninja`` is supported by default π₯·.
`Celery `_
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Celery's task logging requires additional configurations, see `documentation `_ for details.Logging comparison
^^^^^^^^^^^^^^^^^^Standard logging:
~~~~~~~~~~~~~~~~~.. code-block:: python
>>> import logging
>>> logger = logging.get_logger(__name__)
>>> logger.info("An error occurred").. code-block:: bash
An error occurred
Well... ok
With django-structlog and flat_line:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~.. code-block:: python
>>> import structlog
>>> logger = structlog.get_logger(__name__)
>>> logger.info("an_error_occurred", bar="Buz").. code-block:: bash
timestamp='2019-04-13T19:39:31.089925Z' level='info' event='an_error_occurred' logger='my_awesome_project.my_awesome_module' request_id='3a8f801c-072b-4805-8f38-e1337f363ed4' user_id=1 ip='0.0.0.0' bar='Buz'
Then you can search with commands like:
.. code-block:: bash
$ cat logs/flat_line.log | grep request_id='3a8f801c-072b-4805-8f38-e1337f363ed4'
With django-structlog and json
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~.. code-block:: python
>>> import structlog
>>> logger = structlog.get_logger(__name__)
>>> logger.info("an_error_occurred", bar="Buz").. code-block:: json
{"request_id": "3a8f801c-072b-4805-8f38-e1337f363ed4", "user_id": 1, "ip": "0.0.0.0", "event": "an_error_occurred", "timestamp": "2019-04-13T19:39:31.089925Z", "logger": "my_awesome_project.my_awesome_module", "level": "info", "bar": "Buz"}
Then you can search with commands like:
.. code-block:: bash
$ cat logs/json.log | jq '.[] | select(.request_id="3a8f801c-072b-4805-8f38-e1337f363ed4")' -s
.. inclusion-marker-introduction-end
.. inclusion-marker-getting-started-begin
Getting Started
===============These steps will show how to integrate the middleware to your awesome application.
Installation
^^^^^^^^^^^^Install the library
.. code-block:: bash
pip install django-structlog
Add app
.. code-block:: python
INSTALLED_APP = [
# ...
"django_structlog",
# ...
]Add middleware
.. code-block:: python
MIDDLEWARE = [
# ...
"django_structlog.middlewares.RequestMiddleware",
]Add appropriate structlog configuration to your ``settings.py``
.. code-block:: python
import structlog
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"json_formatter": {
"()": structlog.stdlib.ProcessorFormatter,
"processor": structlog.processors.JSONRenderer(),
},
"plain_console": {
"()": structlog.stdlib.ProcessorFormatter,
"processor": structlog.dev.ConsoleRenderer(),
},
"key_value": {
"()": structlog.stdlib.ProcessorFormatter,
"processor": structlog.processors.KeyValueRenderer(key_order=['timestamp', 'level', 'event', 'logger']),
},
},
"handlers": {
# Important notes regarding handlers.
#
# 1. Make sure you use handlers adapted for your project.
# These handlers configurations are only examples for this library.
# See python's logging.handlers: https://docs.python.org/3/library/logging.handlers.html
#
# 2. You might also want to use different logging configurations depending of the environment.
# Different files (local.py, tests.py, production.py, ci.py, etc.) or only conditions.
# See https://docs.djangoproject.com/en/dev/topics/settings/#designating-the-settings
"console": {
"class": "logging.StreamHandler",
"formatter": "plain_console",
},
"json_file": {
"class": "logging.handlers.WatchedFileHandler",
"filename": "logs/json.log",
"formatter": "json_formatter",
},
"flat_line_file": {
"class": "logging.handlers.WatchedFileHandler",
"filename": "logs/flat_line.log",
"formatter": "key_value",
},
},
"loggers": {
"django_structlog": {
"handlers": ["console", "flat_line_file", "json_file"],
"level": "INFO",
},
# Make sure to replace the following logger's name for yours
"django_structlog_demo_project": {
"handlers": ["console", "flat_line_file", "json_file"],
"level": "INFO",
},
}
}structlog.configure(
processors=[
structlog.contextvars.merge_contextvars,
structlog.stdlib.filter_by_level,
structlog.processors.TimeStamper(fmt="iso"),
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
structlog.processors.UnicodeDecoder(),
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
],
logger_factory=structlog.stdlib.LoggerFactory(),
cache_logger_on_first_use=True,
)Start logging with ``structlog`` instead of ``logging``.
.. code-block:: python
import structlog
logger = structlog.get_logger(__name__).. _django_signals:
Extending Request Log Metadata
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^By default only a ``request_id`` and the ``user_id`` are bound from the request but pertinent log metadata may vary from a project to another.
If you need to add more metadata from the request you can implement a convenient signal receiver to bind them. You can also override existing bound metadata the same way.
.. code-block:: python
from django.contrib.sites.shortcuts import get_current_site
from django.dispatch import receiver
from django_structlog import signals
import structlog@receiver(signals.bind_extra_request_metadata)
def bind_domain(request, logger, **kwargs):
current_site = get_current_site(request)
structlog.contextvars.bind_contextvars(domain=current_site.domain)Standard Loggers
^^^^^^^^^^^^^^^^It is also possible to log using standard python logger.
In your formatters, add the ``foreign_pre_chain`` section, and then add ``structlog.contextvars.merge_contextvars``:
.. code-block:: python
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"json_formatter": {
"()": structlog.stdlib.ProcessorFormatter,
"processor": structlog.processors.JSONRenderer(),
# Add this section:
"foreign_pre_chain": [
structlog.contextvars.merge_contextvars, # <---- add this
# customize the rest as you need
structlog.processors.TimeStamper(fmt="iso"),
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.stdlib.PositionalArgumentsFormatter(),
],
},
},
...
}.. inclusion-marker-getting-started-end
.. inclusion-marker-example-outputs-begin
Example outputs
===============Flat lines file (\ ``logs/flat_lines.log``\ )
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^.. code-block:: bash
timestamp='2019-04-13T19:39:29.321453Z' level='info' event='request_started' logger='django_structlog.middlewares.request' request_id='c53dff1d-3fc5-4257-a78a-9a567c937561' user_id=1 ip='0.0.0.0' request=GET / user_agent='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'
timestamp='2019-04-13T19:39:29.345207Z' level='info' event='request_finished' logger='django_structlog.middlewares.request' request_id='c53dff1d-3fc5-4257-a78a-9a567c937561' user_id=1 ip='0.0.0.0' code=200
timestamp='2019-04-13T19:39:31.086155Z' level='info' event='request_started' logger='django_structlog.middlewares.request' request_id='3a8f801c-072b-4805-8f38-e1337f363ed4' user_id=1 ip='0.0.0.0' request=POST /success_task user_agent='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'
timestamp='2019-04-13T19:39:31.089925Z' level='info' event='Enqueuing successful task' logger='django_structlog_demo_project.home.views' request_id='3a8f801c-072b-4805-8f38-e1337f363ed4' user_id=1 ip='0.0.0.0'
timestamp='2019-04-13T19:39:31.147590Z' level='info' event='task_enqueued' logger='django_structlog.middlewares.celery' request_id='3a8f801c-072b-4805-8f38-e1337f363ed4' user_id=1 ip='0.0.0.0' child_task_id='6b11fd80-3cdf-4de5-acc2-3fd4633aa654'
timestamp='2019-04-13T19:39:31.153081Z' level='info' event='This is a successful task' logger='django_structlog_demo_project.taskapp.celery' task_id='6b11fd80-3cdf-4de5-acc2-3fd4633aa654' request_id='3a8f801c-072b-4805-8f38-e1337f363ed4' user_id=1 ip='0.0.0.0'
timestamp='2019-04-13T19:39:31.160043Z' level='info' event='request_finished' logger='django_structlog.middlewares.request' request_id='3a8f801c-072b-4805-8f38-e1337f363ed4' user_id=1 ip='0.0.0.0' code=201
timestamp='2019-04-13T19:39:31.162372Z' level='info' event='task_succeed' logger='django_structlog.middlewares.celery' task_id='6b11fd80-3cdf-4de5-acc2-3fd4633aa654' request_id='3a8f801c-072b-4805-8f38-e1337f363ed4' user_id=1 ip='0.0.0.0' result='None'Json file (\ ``logs/json.log``\ )
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^.. code-block:: json
{"request_id": "c53dff1d-3fc5-4257-a78a-9a567c937561", "user_id": 1, "ip": "0.0.0.0", "request": "GET /", "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36", "event": "request_started", "timestamp": "2019-04-13T19:39:29.321453Z", "logger": "django_structlog.middlewares.request", "level": "info"}
{"request_id": "c53dff1d-3fc5-4257-a78a-9a567c937561", "user_id": 1, "ip": "0.0.0.0", "code": 200, "event": "request_finished", "timestamp": "2019-04-13T19:39:29.345207Z", "logger": "django_structlog.middlewares.request", "level": "info"}
{"request_id": "3a8f801c-072b-4805-8f38-e1337f363ed4", "user_id": 1, "ip": "0.0.0.0", "request": "POST /success_task", "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36", "event": "request_started", "timestamp": "2019-04-13T19:39:31.086155Z", "logger": "django_structlog.middlewares.request", "level": "info"}
{"request_id": "3a8f801c-072b-4805-8f38-e1337f363ed4", "user_id": 1, "ip": "0.0.0.0", "event": "Enqueuing successful task", "timestamp": "2019-04-13T19:39:31.089925Z", "logger": "django_structlog_demo_project.home.views", "level": "info"}
{"request_id": "3a8f801c-072b-4805-8f38-e1337f363ed4", "user_id": 1, "ip": "0.0.0.0", "child_task_id": "6b11fd80-3cdf-4de5-acc2-3fd4633aa654", "event": "task_enqueued", "timestamp": "2019-04-13T19:39:31.147590Z", "logger": "django_structlog.middlewares.celery", "level": "info"}
{"task_id": "6b11fd80-3cdf-4de5-acc2-3fd4633aa654", "request_id": "3a8f801c-072b-4805-8f38-e1337f363ed4", "user_id": 1, "ip": "0.0.0.0", "event": "This is a successful task", "timestamp": "2019-04-13T19:39:31.153081Z", "logger": "django_structlog_demo_project.taskapp.celery", "level": "info"}
{"request_id": "3a8f801c-072b-4805-8f38-e1337f363ed4", "user_id": 1, "ip": "0.0.0.0", "code": 201, "event": "request_finished", "timestamp": "2019-04-13T19:39:31.160043Z", "logger": "django_structlog.middlewares.request", "level": "info"}
{"task_id": "6b11fd80-3cdf-4de5-acc2-3fd4633aa654", "request_id": "3a8f801c-072b-4805-8f38-e1337f363ed4", "user_id": 1, "ip": "0.0.0.0", "result": "None", "event": "task_succeed", "timestamp": "2019-04-13T19:39:31.162372Z", "logger": "django_structlog.middlewares.celery", "level": "info"}.. inclusion-marker-example-outputs-end
.. inclusion-marker-upgrade-guide-begin
Upgrade Guide
=============.. _upgrade_8.0:
Upgrading to 8.0+
^^^^^^^^^^^^^^^^^A new keyword argument ``log_kwargs`` was added to the the optional signals:
- ``django_structlog.signals.bind_extra_request_metadata``;
- ``django_structlog.signals.bind_extra_request_finished_metadata``;
- ``django_structlog.signals.bind_extra_request_failed_metadata``.It should not affect you if you have a ``**kwargs`` in the signature of your receivers.
``log_kwargs`` is a dictionary containing the log metadata that will be added to their respective logs (``"request_started"``, ``"request_finished"``, ``"request_failed"``).
If you use any of these signals, you may need to update your receiver to accept this new argument:
.. code-block:: python
from django.contrib.sites.shortcuts import get_current_site
from django.dispatch import receiver
from django_structlog import signals
import structlog@receiver(signals.bind_extra_request_metadata)
def my_receiver(request, logger, log_kwargs, **kwargs): # <- add `log_kwargs` if necessary
...@receiver(signals.bind_extra_request_finished_metadata)
def my_receiver_finished(request, logger, response, log_kwargs, **kwargs): # <- add `log_kwargs` if necessary
...@receiver(signals.bind_extra_request_failed_metadata)
def my_receiver_failed(request, logger, exception, log_kwargs, **kwargs): # <- add `log_kwargs` if necessary
..... _upgrade_7.0:
Upgrading to 7.0+
^^^^^^^^^^^^^^^^^The dependency `django-ipware `_ was upgraded to version 6. This library is used to retrieve the request's ip address.
Version 6 may have some `breaking changes `_ if you did customizations.
It should not affect most of the users but if you did some customizations, you might need to update your configurations.
.. _upgrade_6.0:
Upgrading to 6.0+
^^^^^^^^^^^^^^^^^Minimum requirements
~~~~~~~~~~~~~~~~~~~~
- requires python 3.8+Changes to do
~~~~~~~~~~~~~Add ``django_structlog`` to installed app
-----------------------------------------
.. code-block:: pythonINSTALLED_APP = [
# ...
"django_structlog",
# ...
]Make sure you use ``django_structlog.middlewares.RequestMiddleware``
--------------------------------------------------------------------If you used any of the experimental async or sync middlewares, you do not need to anymore.
Make sure you use ``django_structlog.middlewares.RequestMiddleware`` instead of any of the other request middlewares commented below:.. code-block:: python
MIDDLEWARE += [
# "django_structlog.middlewares.request_middleware_router", # <- remove
# "django_structlog.middlewares.requests.SyncRequestMiddleware", # <- remove
# "django_structlog.middlewares.requests.AsyncRequestMiddleware", # <- remove
"django_structlog.middlewares.RequestMiddleware", # <- make sure you use this one
](If you use celery) Make sure you use ``DJANGO_STRUCTLOG_CELERY_ENABLED = True``
--------------------------------------------------------------------------------It is only applicable if you use celery integration.
``django_structlog.middlewares.CeleryMiddleware`` has been remove in favor of a django settings.
.. code-block:: python
MIDDLEWARE += [
"django_structlog.middlewares.RequestMiddleware",
# "django_structlog.middlewares.CeleryMiddleware", # <- remove this
]DJANGO_STRUCTLOG_CELERY_ENABLED = True # <-- add this
.. _upgrade_5.0:
Upgrading to 5.0+
^^^^^^^^^^^^^^^^^Minimum requirements
~~~~~~~~~~~~~~~~~~~~
- requires asgiref 3.6+.. _upgrade_4.0:
Upgrading to 4.0+
^^^^^^^^^^^^^^^^^``django-structlog`` drops support of django below 3.2.
Minimum requirements
~~~~~~~~~~~~~~~~~~~~
- requires django 3.2+
- requires python 3.7+
- requires structlog 21.4.0+
- (optionally) requires celery 5.1+Changes if you use ``celery``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~You can now install ``django-structlog`` explicitly with ``celery`` extra in order to validate the compatibility with your version of ``celery``.
.. code-block:: bash
django-structlog[celery]==4.0.0
See `Installing βExtrasβ `_ for more information about this ``pip`` feature.
.. _upgrade_3.0:
Upgrading to 3.0+
^^^^^^^^^^^^^^^^^``django-structlog`` now use `structlog.contextvars.bind_contextvars `_ instead of ``threadlocal``.
Minimum requirements
~~~~~~~~~~~~~~~~~~~~
- requires python 3.7+
- requires structlog 21.4.0+Changes you need to do
~~~~~~~~~~~~~~~~~~~~~~1. Update structlog settings
----------------------------- add ``structlog.contextvars.merge_contextvars`` as first ``processors``
- remove ``context_class=structlog.threadlocal.wrap_dict(dict),``
- (if you use standard loggers) add ``structlog.contextvars.merge_contextvars`` in `foreign_pre_chain`
- (if you use standard loggers) remove ``django_structlog.processors.inject_context_dict,``.. code-block:: python
structlog.configure(
processors=[
structlog.contextvars.merge_contextvars, # <---- add this
structlog.stdlib.filter_by_level,
structlog.processors.TimeStamper(fmt="iso"),
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
structlog.processors.UnicodeDecoder(),
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
],
# context_class=structlog.threadlocal.wrap_dict(dict), # <---- remove this
logger_factory=structlog.stdlib.LoggerFactory(),
cache_logger_on_first_use=True,
)# If you use standard logging
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"json_formatter": {
"()": structlog.stdlib.ProcessorFormatter,
"processor": structlog.processors.JSONRenderer(),
"foreign_pre_chain": [
structlog.contextvars.merge_contextvars, # <---- add this
# django_structlog.processors.inject_context_dict, # <---- remove this
structlog.processors.TimeStamper(fmt="iso"),
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.stdlib.PositionalArgumentsFormatter(),
],
},
},
...
}2. Replace all ``logger.bind`` with ``structlog.contextvars.bind_contextvars``
------------------------------------------------------------------------------.. code-block:: python
@receiver(bind_extra_request_metadata)
def bind_domain(request, logger, **kwargs):
current_site = get_current_site(request)
# logger.bind(domain=current_site.domain)
structlog.contextvars.bind_contextvars(domain=current_site.domain).. _upgrade_2.0:
Upgrading to 2.0+
^^^^^^^^^^^^^^^^^``django-structlog`` was originally developed using the debug configuration `ExceptionPrettyPrinter `_ which led to incorrect handling of exception.
- remove ``structlog.processors.ExceptionPrettyPrinter(),`` of your processors.
- make sure you have ``structlog.processors.format_exc_info,`` in your processors if you want appropriate exception logging... inclusion-marker-upgrade-guide-end
.. inclusion-marker-running-tests-begin
Running the tests
=================Note: For the moment redis is needed to run the tests. The easiest way is to start docker demo's redis.
.. code-block:: bash
docker compose up -d redis
pip install -r requirements.txt
env CELERY_BROKER_URL=redis://0.0.0.0:6379 DJANGO_SETTINGS_MODULE=config.settings.test pytest test_app
env CELERY_BROKER_URL=redis://0.0.0.0:6379 DJANGO_SETTINGS_MODULE=config.settings.test_demo_app pytest django_structlog_demo_project
docker compose stop redis.. inclusion-marker-running-tests-end
.. inclusion-marker-demo-begin
Demo app
========.. code-block:: bash
docker compose up --build
Open ``http://127.0.0.1:8000/`` in your browser.
Navigate while looking into the log files and shell's output.
.. inclusion-marker-demo-end
.. inclusion-marker-authors-begin
Authors
=======* **Jules Robichaud-Gagnon** - *Initial work* - `jrobichaud `_
See also the list of `contributors `_ who participated in this project.
.. inclusion-marker-authors-end
.. inclusion-marker-acknowledgements-begin
Acknowledgments
===============* Very huge thanks to my awesome π¦ and generous employer `TLM π©΅πβ€οΈπ§‘ππββ¬ `_ for letting me maintain this project on my work hours because it believes in open source.
* Big thanks to `@ferd `_ for his `bad opinions `_ that inspired the author enough to spend time on this library.
* `This issue `_ helped the author to figure out how to integrate ``structlog`` in Django.
* `This stack overflow question `_ was also helpful... inclusion-marker-acknowledgements-end
License
=======This project is licensed under the MIT License - see the `LICENSE `_ file for details