Search code examples
djangopython-2.7django-1.7django-logging

Django logging, log to file in development without needing to create the file in production


I'm implementing Django logging in a project. I've got it working such that log entries are inserted into a file in our dev environment, and into a database in our production environment.

The problem I'm having is that Django needs to create the log file, even though it will be empty, in non-dev environments.

Is there a better way to implement this so that the empty log file isn't created in non-dev environments?

My code:

settings.py logging configuration:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse'
        },
        'is_not_dev_environment': {
            '()': 'lib.log.log.IsNotDevEnvironment'
        },
        'is_dev_environment': {
            '()': 'lib.log.log.IsDevEnvironment'
        }
    },
    'formatters': {
        'standard': {
            'format': "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s",
            'datefmt': "%d/%b/%Y %H:%M:%S"
        },
        'custom': {
            'format': CUSTOM_LOGGING_FORMAT,
            'datefmt': "%d/%b/%Y %H:%M:%S"
        },
    },
    'handlers': {
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'class': 'django.utils.log.AdminEmailHandler'
        },
        'db': {
            'level': LOGGING_LEVEL,
            'filters': ['is_not_dev_environment'],
            'class': 'lib.log.log.DBLogHandler',
            'formatter': 'custom'
        },
        'file': {
            'level': LOGGING_LEVEL,
            'filters': ['is_dev_environment'],
            'filename': /var/log/django.log,
            'class': 'lib.log.log.FileLogHandler',
            'formatter': 'custom',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['mail_admins'],
            'level': LOGGING_LEVEL,
            'propagate': True,
        }
    }
}
LOGGING['loggers'].update({app: copy.deepcopy(LOCAL_APP_LOGGER) for app in LOCAL_APPS})

lib/log/log.py which holds the filtering classes:

class IsNotDevEnvironment(logging.Filter):
    def filter(self, record):
        return settings.ENVIRONMENT != 'DEV'


class IsDevEnvironment(logging.Filter):
    def filter(self, record):
        return settings.ENVIRONMENT == 'DEV'

Solution

  • I settled on a different option that met my requirements. The main issue I was having was that in our test environment we couldn't create the log file (can't manipulate files with sudo on our CI provider's system). I resolved that by adding logic to disable logging based on an environment variable in the test environment.

    tests/config.py:

    from logging.config import dictConfigClass
    
    from django.conf import settings
    
    
    def logging_config(config):
        disable_logging = getattr(settings, 'DISABLE_LOGGING', False)
        if not disable_logging:
            dictConfigClass(config).configure()
    

    An alternative would be to set the handler based on some logic:

    if ENVIRONMENT == 'DEV':
        LOGGING['handlers'].update({
            """ Dict specifying dev handler """
        })
    else:
        LOGGING['handlers'].update({
            """ Dict specifying production handler """
        })