Skip to content

Commit

Permalink
added unittest for full coverage and added the formater handling.
Browse files Browse the repository at this point in the history
  • Loading branch information
darius BERNARD committed Feb 8, 2017
1 parent 1c1c2ae commit aee25ec
Show file tree
Hide file tree
Showing 10 changed files with 253 additions and 18 deletions.
3 changes: 2 additions & 1 deletion dynamic_logging/admin.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# -*- coding: utf-8 -*-
from django.contrib import admin
from django.core.urlresolvers import reverse
from django.db import models
from django.template.defaultfilters import safe
from django.utils.translation import ugettext_lazy as _

from dynamic_logging.scheduler import main_scheduler
from dynamic_logging.widgets import JsonLoggerWidget
from django.db import models

from .models import Config, Trigger


Expand Down
38 changes: 36 additions & 2 deletions dynamic_logging/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def __str__(self):
try:
cfg_name = self.config.name
except Config.DoesNotExist:
cfg_name = ""
cfg_name = "<no config>"
return 'trigger %s from %s to %s for config %s' % (
self.name, self.start_date, self.end_date,
cfg_name)
Expand Down Expand Up @@ -146,7 +146,9 @@ def apply(self, trigger=None):
:return:
"""
config = deepcopy(settings.LOGGING)
config['loggers'] = self.config.get('loggers', {})
# we merge the loggers and handlers into the default config
config['loggers'] = self.create_loggers(self.config.get('loggers', {}))
config['handlers'] = self.merge_handlers(config.get('handlers', {}), self.config.get('handlers', {}))
module_logger.info("[%s] applying logging config %s: %r" % (trigger, self, config))
self._reset_logging()
logging.config.dictConfig(config)
Expand All @@ -158,8 +160,40 @@ def _reset_logging(self):
"""
for logger in get_loggers().values():
logger.handlers = []
logger.filters = []
logger.propagate = True

@staticmethod
def create_loggers(partial_config):
"""
generate a correct logger config for the parital config
:param partial_config:
:return: the dict that can be passed into a logger configrator
"""
default_val = {'propagate': True, 'handlers': [], 'filters': [], 'level': 'INFO'}

res = {}
for logger_name, logger_cfg in partial_config.items():
current = res[logger_name] = {}
current.update(default_val)
current.update({k: v for k, v in logger_cfg.items() if k in default_val.keys()})
return res

@staticmethod
def merge_handlers(settings_handlers, new_config):
"""
merge the handler config. it don't add new handler, and just
merge the level and the filters. nothing else
:param settings_handlers:
:param new_config:
:return:
"""
res = deepcopy(settings_handlers)
for handler_name, handler_cfg_res in res.items():
expected_handler_cfg = new_config.get(handler_name, {})
handler_cfg_res.update({k: v for k, v in expected_handler_cfg.items() if k in ('filters', 'level')})
return res

def __str__(self):
return self.name

Expand Down
7 changes: 6 additions & 1 deletion dynamic_logging/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,15 @@ def activate_current(self):
"""
try:
t = Trigger.objects.filter(is_active=True).valid_at(timezone.now()).latest('start_date')
except Trigger.DoesNotExist:
return None
try:
t.apply()
self.current_trigger = t
return t
except Trigger.DoesNotExist:
except ValueError as e:
logger.exception("error with current logger activation trigger=%s, config=%s => %s",
t.id, t.config_id, str(e))
return None

def reload(self, interval=None):
Expand Down
4 changes: 2 additions & 2 deletions dynamic_logging/templatetags/dynamic_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@


@register.filter
def getitem(dict, key):
return dict[key]
def getitem(dict_, key):
return dict_[key]


@register.inclusion_tag('dynamic_logging/display_config.html')
Expand Down
147 changes: 147 additions & 0 deletions dynamic_logging/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from dynamic_logging.handlers import MockHandler
from dynamic_logging.models import Config, Trigger, get_loggers
from dynamic_logging.scheduler import Scheduler, main_scheduler
from dynamic_logging.templatetags.dynamic_logging import display_config, getitem


def load_tests(loader, tests, ignore):
Expand Down Expand Up @@ -327,3 +328,149 @@ def test_config_reversed(self):
Config.default().apply()
logger.warn("hey")
self.assertEqual(messages['warning'], [])

def test_filter_apply(self):
logger = logging.getLogger('testproject.testapp')
# handler not attached to this logger
# setup new config
cfg_filtered = Config(name='empty')
cfg_filtered.config = {"loggers": {
"testproject.testapp": {
"handlers": ["mock"],
"level": "WARN",
"filters": ['polite']
}
}}
cfg_passall = Config(name='empty')
cfg_passall.config = {"loggers": {
"testproject.testapp": {
"handlers": ["mock"],
"level": "WARN",
"filters": []
}
}}
cfg_filtered.apply()
# log debug ineficient
with MockHandler.capture() as messages:
logger.warn("hey")
self.assertEqual(messages['warning'], [])
logger.warn("hello, you")
self.assertEqual(messages['warning'], ['hello, you'])

with MockHandler.capture() as messages:
# default config does not add
cfg_passall.apply()
logger.warn("hey")
self.assertEqual(messages['warning'], ['hey'])
logger.warn("hello, you")
self.assertEqual(messages['warning'], ['hey', 'hello, you'])

def test_handler_merging(self):
original_cfg = {
'mail_admins': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'django.utils.log.AdminEmailHandler',
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'colored',
'propagate': False
}
}
new_cfg = {
'dont exists': { # ignored
'level': 'ERROR'
},
'console': {
'level': 'INFO', # ok
'class': 'logging.HACKME', # ignored
'formatter': 'prout', # ignored
'ignored': True, # ignored
'filters': ['nofilter'] # ok
}
}
res = Config.merge_handlers(original_cfg, new_cfg)
self.assertEqual(res, {
'mail_admins': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'django.utils.log.AdminEmailHandler',
},
'console': {
'level': 'INFO',
'class': 'logging.StreamHandler',
'formatter': 'colored',
'propagate': False,
'filters': ['nofilter']
}
})

def test_loggers_creation(self):
asked_cfg = {
'django': {
'level': 'ERROR',
'propagate': False,
},
'django.request': {
'handlers': ['null'],
'filters': ['filter']
},
'testproject.testapp': {
'handlers': ['null', 'devnull'],
},
'lol': {
'ignored': 'lol',
}
}
self.maxDiff = None
self.assertEqual(Config.create_loggers(asked_cfg), {
'django': {
'handlers': [],
'level': 'ERROR',
'propagate': False,
'filters': []
},
'django.request': {
'handlers': ['null'],
'level': 'INFO',
'propagate': True,
'filters': ['filter']
},
'testproject.testapp': {
'handlers': ['null', 'devnull'],
'level': 'INFO',
'propagate': True,
'filters': []
},
'lol': {
'handlers': [],
'level': 'INFO',
'propagate': True,
'filters': []
},
}
)


class TestTag(TestCase):
def test_display_config_current_auto(self):
config = display_config()
self.assertEqual(config['config'], main_scheduler.current_trigger.config)
self.assertGreater(len(config['handlers']), 0)

def test_display_config_given(self):
c = Config(name="lol", config_json='{}')
config = display_config(c)
self.assertEqual(config['config'], c)
self.assertGreater(len(config['handlers']), 0)

def test_display_fallback_bad_config(self):
c = Config(name="lol", config_json='}')
self.assertRaises(ValueError, getattr, c, 'config')
config = display_config(c)
self.assertEqual(config, {})

def test_getitem(self):
self.assertEqual(getitem({'a': True}, 'a'), True)
14 changes: 6 additions & 8 deletions dynamic_logging/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@

import json
import logging
import random

from django.conf import settings
from django.forms.widgets import Widget, Textarea
from django.utils.encoding import force_text
from django.forms.widgets import Textarea
from django.utils.html import format_html
from django.utils.safestring import mark_safe

Expand All @@ -20,9 +17,6 @@ class JsonLoggerWidget(Textarea):
class Media:
js = (
'admin/js/minimal.js',
# 'admin/js/jquery-2.2.4.min.js',
# 'admin/js/select2.min.js',
# 'admin/js/jquerymy-1.2.8.js',
'admin/js/logging_widget.js',
)

Expand All @@ -34,7 +28,11 @@ def render(self, name, value, attrs=None):
return res + format_html(
'<div id="{anchorid}"></div>'
'<script type="application/javascript">'
'(function ($) {{$(function() {{logging_widget($("#{anchorid}"), $("#{areaid}"), {handlers});}});}}(jQuery));'
'(function ($) {{'
' $(function() {{'
' logging_widget($("#{anchorid}"), $("#{areaid}"), {handlers});'
' }});'
'}}(jQuery));'
'</script>',
anchorid=attrs.get('id', name) + '_display',
areaid=attrs.get('id', name),
Expand Down
3 changes: 3 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ universal = 1
include =
dynamic_logging
testproject
omit =
setup.py
manage.py

[isort]
line_length=119
Expand Down
13 changes: 13 additions & 0 deletions testproject/filter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals

import logging
from logging import Filter

logger = logging.getLogger(__name__)


class PoliteFilter(Filter):

def filter(self, record):
return record.msg.startswith('hello')
9 changes: 6 additions & 3 deletions testproject/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,11 @@
}
}

try:
try: # pragma: nocover
import teamcity
if teamcity.is_running_under_teamcity():
if teamcity.is_running_under_teamcity(): # pragma: nocover
TEST_RUNNER = "teamcity.django.TeamcityDjangoRunner"
except ImportError:
except ImportError: # pragma: nocover
pass

# Internationalization
Expand Down Expand Up @@ -134,6 +134,9 @@
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse'
},
'polite': {
'()': 'testproject.filter.PoliteFilter'
}
},
'handlers': {
Expand Down
33 changes: 32 additions & 1 deletion testproject/testapp/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ def setUp(self):
u.save()
self.client.login(username='admin', password='password')
main_scheduler.reload()
self.c = c = Config.objects.create(name='my_config', config_json='{}')
self.c = c = Config.objects.create(name='my_config', config_json='{"loggers":{"blablabla":{"level":"ERROR",'
'"handlers":["console"],"propagate":true}}}')
self.t = Trigger.objects.create(name='in1hour', config=c, start_date=now_plus(2), end_date=now_plus(4))

def tearDown(self):
Expand All @@ -83,3 +84,33 @@ def test_config_change(self):
def test_trigger_change(self):
response = self.client.get(reverse('admin:dynamic_logging_trigger_change', args=(self.t.pk,)))
self.assertContains(response, 'in1hour')

def test_trigger_summary(self):
self.t.start_date = now_plus(-2)
self.t.save()
response = self.client.get(reverse('admin:app_list', kwargs={'app_label': 'dynamic_logging'}))
self.assertContains(response, 'blablabla')

def test_display_bad_conf(self):
self.t.start_date = now_plus(-2)
self.t.save()
self.t.config.config_json = 'oops bad json'
self.t.config.save()

response = self.client.get(reverse('admin:app_list', kwargs={'app_label': 'dynamic_logging'}))
self.assertNotContains(response, 'blablabla')

def test_trigger_broken(self):
self.t.config_id = 99
self.t.save()
self.assertIn('<no config>', str(Trigger.objects.get(pk=self.t.pk)))

def test_create_bad_config(self):
res = self.client.post(reverse('admin:dynamic_logging_config_add'), data={
'name': 'new config',
'config_json': '{bda bconfig oops'
})
self.assertContains(res, 'not a valid json')

def test_wsgi_import(self):
import testproject.wsgi # NOQA

0 comments on commit aee25ec

Please sign in to comment.