Django papertrail logging failing mysteriously

303 Views Asked by At

I have set up papertrail for a Django project but logging fails with OSError: [Errno 9] Bad file descriptor. The weird thing is if I call django.setup() before the logging call it works...

settings.py

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'json': {
            '()': 'pythonjsonlogger.jsonlogger.JsonFormatter',
             'fmt': '%(levelname)s %(asctime)s %(message)s',
         },
    },
    'handlers': {
        'papertrail': {
            'level': 'DEBUG',
            'class': 'logging.handlers.SysLogHandler',
            'formatter': 'json',
            'address': ('logsN.papertrailapp.com', 12345)
         },
    ...
    },
    'loggers': {
        'papertrail': {
            'handlers': ['papertrail'],
            'level': 'ERROR',
            'propagate': True,
         },
    ...

/management/commands/logging_test.py

from django.core.management import BaseCommand
import django
import logging

#  uncomment this line to make it work
#django.setup()


def test_logging():
    logger = logging.getLogger('papertrail')
    logger.error(
        'TEST LOGGING'
    )


class Command(BaseCommand):
    def handle(self, *args, **options):
        test_logging()

I can call the test_logging function with:

./manage.py test_logging

If I uncomment the line django.setup() it works. I have no idea why.

1

There are 1 best solutions below

2
grrrrrr On

Have a look at the docs for django.setup() and you'll see that one of the main roles of this function is to set up logging according to your settings. This portion of the docs it mentions a scenario where using django.setup() is required to utilize logging:

If you’re using components of Django “standalone” – for example, writing a Python script which loads some Django templates and renders them, or uses the ORM to fetch some data – there’s one more step you’ll need in addition to configuring settings.

I can't tell what your project does, but the test you are running seems to fall into the category of "standalone".

Looking into the source django.setup() in part calls the following

configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)

And the source for that function does the following:

def configure_logging(logging_config, logging_settings):
    if logging_config:
        # First find the logging configuration function ...
        logging_config_func = import_string(logging_config)

        logging.config.dictConfig(DEFAULT_LOGGING)

        # ... then invoke it with the logging settings
        if logging_settings:
            logging_config_func(logging_settings)