简体   繁体   English

Django:如何在测试期间覆盖 logfile 变量?

[英]Django: How do you override logfile variable during testing?

I'm trying to get my test suite to create a different logfile than the one I use in development, but for some reason the override_settings decorator doesn't seem to be working.我试图让我的测试套件创建一个与我在开发中使用的不同的日志文件,但由于某种原因,override_settings 装饰器似乎不起作用。 When I run the test, the same 'project/project/debug_logfile' is written.当我运行测试时,会写入相同的“项目/项目/调试日志文件”。 Where am I messing up?我在哪里搞砸了?

# settings.py
...
LOGFILE = ROOT + '/debug_logfile'

LOGGING = {
    'version': 1,
    'disable_existing_loggers': True,
    'formatters': {
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
        },
        'simple': {
            'format': '%(levelname)s %(message)s'
        },
        'standard': {
             'format' : "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s",
             'datefmt' : "%d/%b/%Y %H:%M:%S"
        },
    },
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse'
        }
    },
    'handlers': {
        'null': {
            'level': 'DEBUG',
            'class': 'django.utils.log.NullHandler',
        },
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'mail_admins': {
            'level': 'ERROR',
            'class': 'django.utils.log.AdminEmailHandler',
            'filters': ['require_debug_false']
        },
        'logfile': {
            'level':'DEBUG',
            'class':'logging.handlers.RotatingFileHandler',
            'filename': LOGFILE,
            'maxBytes': 50000,
            'backupCount': 2,
            'formatter': 'standard',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['null'],
            'propagate': True,
            'level': 'INFO',
        },
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': False,
        },
        'clients': {
            'handlers': ['console', 'logfile'],
            'level': 'DEBUG',
        },
        # display the db queries
        #'django.db.backends': {
        #    'handlers': ['console'],
        #    'level': 'DEBUG',
        #}
    }
}

# clients.management.commands.remindmanagers.py

class Command(BaseCommand):
    help = 'Creates task reminders and send emails to account and senior \
            managers when their client\'s contracts are about to expire'

    def handle(self, *args, **kwargs):
        three_months = datetime.date.today() + datetime.timedelta(days=90)
        # get all contracts that will be completed in the next 3 months
        contracts = Contract.objects.filter(finish__lte=three_months
                                   ).exclude(notices_left=0)

        if not contracts.exists():
            log.info('Tried running but there are no contracts about to expire.')
            return 0
        ...

# tests.test_clients

...
from django.core.management import call_command

@override_settings(LOGFILE=settings.ROOT + "/test_logfile")
class ClientCommandTest(BaseTestCase):
    def _file_exists(file_path):
        return os.path.exists(file_path)

    def test_remindmanagers_no_contracts(self):
        args = []
        kwargs = {}
        #self.assertFalse()
        # since there are no contracts yet, this should create an entry in ROOT + /logfile
        call_command('remindmanagers', *args, **kwargs)                                      # this should log in project/project/test_logfile not debug_logfile

It is not enough to override_settings . override_settings是不够的。 Because logging is configured before override_settings take effect.因为日志记录是在override_settings生效之前配置的。 Call configure_logging ( django.utils.log ) to change logging settings, and don't forget to return the original settings back.调用configure_logging ( django.utils.log ) 来更改日志设置,不要忘记返回原始设置。

Look at the example below:看下面的例子:

settings.py设置.py

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'formatter1': {
            'format': 'settings logger: {message}',
            'style': '{',
        },
    },
    'handlers': {
        'stream': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'formatter1',
        },
    },
    'loggers': {
        'test_logging_override': {
            'handlers': ['stream'],
            'level': 'DEBUG',
            'propogate': True,
        },
    },
}

tests.py测试.py

from django.test import TestCase, override_settings
from django.utils.log import configure_logging

class TestLoggingOverride(TestCase):
    def test_logging_override(self):
        logger = logging.getLogger('test_logging_override')
        logger.debug('one')  # settings logger: one
        with override_settings(LOGGING={
            'version': 1,
            'disable_existing_loggers': False,
            'formatters': {
                'formatter1': {
                    'format': 'overridden logger: {message}',  # !!!!
                    'style': '{',
                },
            },
            'handlers': {
                'stream': {
                    'level': 'DEBUG',
                    'class': 'logging.StreamHandler',  # 'class': 'logging.FileHandler'
                    # 'filename': 'path/to/file',  # in case of FileHandler
                    'formatter': 'formatter1',
                },
            },
            'loggers': {
                'test_logging_override': {
                    'handlers': ['stream'],
                    'level': 'DEBUG',
                    'propogate': True,
                },
            },
        }):
            logger.debug('two')  # settings logger: two
            configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)  # really change settings
            logger.debug('three')  # overridden logger: three
        logger.debug('four')  # overridden logger: four
        configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)  # return to native settings
        logger.debug('five')  # settings logger: five

Also, you can write tests for logging.此外,您可以编写日志记录测试。

from unittest.mock import MagicMock, patch, call
from django.test import TestCase, override_settings
from django.utils.log import configure_logging

class TestLoggingOverride(TestCase):
    @patch(
         target='sys.stderr',
         wraps=sys.stderr,  # wraps all of sys.stderr. Remove this line, if you want to disable writing to stderr in this test
     )
     def test_patched_stream_logger(self, mocked_stderr):
         logger = logging.getLogger('test_logging_override')
         with override_settings(LOGGING={
             'version': 1,
             'disable_existing_loggers': False,
             'formatters': {
                 'formatter1': {
                     'format': 'overridden logger: {message}',  # !!!!
                     'style': '{',
                 },
             },
             'handlers': {
                 'stream': {
                     'level': 'DEBUG',
                     'class': 'logging.StreamHandler',
                     'formatter': 'formatter1',
                 },
             },
             'loggers': {
                 'test_logging_override': {
                     'handlers': ['stream'],
                     'level': 'DEBUG',
                     'propogate': True,
                 },
             },
         }):
             configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)  # really change settings
             logger.debug('lorem ipsum')
         configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)  # return to native settings
         self.assertEqual(
             mocked_stderr.method_calls,
             [call.flush(),
              call.flush(),
              call.write('overridden logger: lorem ipsum\n'),
              call.flush(),
              call.flush(),
              call.flush(),
              call.flush()]
         )
         # print(mocked_stderr.method_calls)

Your LOGGING dict literal contains the variable LOGFILE , which gets evaluated immediately when processing the dict literal.您的LOGGING dict 文字包含变量LOGFILE ,在处理 dict 文字时会立即对其进行评估。 By overriding LOGFILE later you aren't changing the entry in the LOGGING dict because the LOGFILE string is simply replaced;通过稍后覆盖LOGFILE ,您不会更改LOGGING字典中的条目,因为LOGFILE字符串只是被替换了; the string itself is not mutated, so you end up with LOGFILE simply pointing to another string.字符串本身没有发生变异,所以你最终得到的LOGFILE只是指向另一个字符串。

I think you should be able to override just the logfile handler with the new filename like so:我认为您应该能够使用新文件名仅覆盖日志文件处理程序,如下所示:

@override_settings(LOGGING={
  'handlers': {
    'logfile': {
       'filename': settings.ROOT + "/test_logfile"
    }
  }
})
class ClientCommandTest(BaseTestCase):
  ...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM