Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Jogging is a thin wrapper around Python's logging functionality to make Django l

With Jogging, you can control the destination, format, and verbosity of logs with fine granularity. Configuring module-level logging is just as easy as configuring logging for specific functions.

To use it, you add a few configurations to settings.py, import Jogging's log functions instead of Python's, and then log away like you normally do.
To use it, you add a few configurations to settings.py, import Jogging's log functions instead of Python's, and then log away like you normally do.

Python's logging module does all the heavy lifting. As a result, you can use Jogging to configure logging for code that already exists. And great care has been taken to make sure logging's power isn't hidden away behind abstractions.

Expand Down Expand Up @@ -50,13 +50,13 @@ Intermediate
from logging import StreamHandler
import logging

LOGGING = {
JOGGING = {
# all logs from myapp1 go to the database
'myapp1': {
'handler': DatabaseHandler(), # an initialized handler object
'level': logging.DEBUG,
},

# ...except for this one view which will log CRITICAL to stderr
'myapp1.views.super_important_view': {
'handler': StreamHandler(),
Expand All @@ -76,24 +76,24 @@ Advanced
from logging import StreamHandler, FileHandler
import logging

LOGGING = {
JOGGING = {
# all logs from myapp1 go to the database
'myapp1': {
'handler': DatabaseHandler(),
'level': logging.DEBUG,
},

# this time, we'll have super_important_view log CRITICAL to stderr again, but
# we'll also have it log everything to the database
'myapp1.views.super_important_view': {
'handlers': [
{ 'handler': StreamHandler(), 'level': logging.CRITICAL,
{ 'handler': StreamHandler(), 'level': logging.CRITICAL,
'format': "%(asctime)-15s %(source): %(message)s %(foo)s" },
{ 'handler': DatabaseHandler(), 'level': logging.DEBUG },
]
},
# this is the name of a logger that a third party app already logs to.

# this is the name of a logger that a third party app already logs to.
# you can configure it just like the others, without breaking anything.
'simple_example': {
'handler': StreamHandler(),
Expand Down Expand Up @@ -151,17 +151,17 @@ Implementation
======================
Much inspiration was taken from `Django's logging proposal <http://groups.google.com/group/django-developers/browse_thread/thread/8551ecdb7412ab22>`_.

Jogging requires a dictionary, ``settings.LOGGING``, that defines the loggers you want to control through Jogging (by name). Here is how Jogging works:
Jogging requires a dictionary, ``settings.JOGGING``, that defines the loggers you want to control through Jogging (by name). Here is how Jogging works:

1. All loggers are created on server startup from ``settings.LOGGING`` (the init code is in models.py, for lack of a better place). Handlers are added to the loggers as defined, and levels are set.
2. When your app calls Jogging's log functions, the calling function is matched against the logger names in ``settings.LOGGING`` and the most specific logger is chosen. For example, say ``myproj.myapp.views.func()`` is the caller; it will match loggers named ``myproj.myapp.views.func``, ``myproj.myapp.views``, ``myproj.myapp``, and ``myproj``. The first (most specific) one that matches will be chosen.
1. All loggers are created on server startup from ``settings.JOGGING`` (the init code is in models.py, for lack of a better place). Handlers are added to the loggers as defined, and levels are set.
2. When your app calls Jogging's log functions, the calling function is matched against the logger names in ``settings.JOGGING`` and the most specific logger is chosen. For example, say ``myproj.myapp.views.func()`` is the caller; it will match loggers named ``myproj.myapp.views.func``, ``myproj.myapp.views``, ``myproj.myapp``, and ``myproj``. The first (most specific) one that matches will be chosen.
3. ``log()`` is called on the chosen logger, and Python's logging module takes over from here.


===========
Resources
===========
List of handlers in Python's logging module:
List of handlers in Python's logging module:
http://docs.python.org/library/logging.html#handler-objects

Format specifiers for Python's logging module:
Expand Down
11 changes: 7 additions & 4 deletions jogging/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def abbrev_msg(self, maxlen=500):
return u'%s ...' % self.msg[:maxlen]
return self.msg
abbrev_msg.short_description = u'abbreviated msg'

class Meta:
get_latest_by = 'datetime'

Expand All @@ -42,8 +42,11 @@ def add_handlers(logger, handlers):
else:
logger.addHandler(handler)

if hasattr(settings, 'LOGGING') and settings.LOGGING:
for module, properties in settings.LOGGING.items():

LOGGING = getattr(settings, 'JOGGING', getattr(settings, 'LOGGING', None))

if LOGGING:
for module, properties in LOGGING.items():
logger = py_logging.getLogger(module)

if 'level' in properties:
Expand All @@ -60,7 +63,7 @@ def add_handlers(logger, handlers):
"A logger in settings.LOGGING doesn't have its log level set. " +
"Either set a level on that logger, or set GLOBAL_LOG_LEVEL.")

handlers = []
handlers = []
if 'handler' in properties:
handlers = [properties['handler']]
elif 'handlers' in properties:
Expand Down
82 changes: 41 additions & 41 deletions jogging/tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
from jogging.models import Log, jogging_init

class DatabaseHandlerTestCase(DjangoTestCase):

def setUp(self):
from jogging.handlers import DatabaseHandler, MockHandler
import logging
self.LOGGING = getattr(settings, 'LOGGING', None)
settings.LOGGING = {

self.JOGGING = getattr(settings, 'JOGGING', None)

settings.JOGGING = {
'database_test': {
'handler': DatabaseHandler(),
'level': logging.DEBUG,
Expand All @@ -27,25 +27,25 @@ def setUp(self):
],
},
}

jogging_init()

def tearDown(self):
import logging

# clear out all handlers on loggers
loggers = [logging.getLogger(""), logging.getLogger("database_test"), logging.getLogger("multi_test")]
for logger in loggers:
logger.handlers = []

# delete all log entries in the database
for l in Log.objects.all():
l.delete()
if self.LOGGING:
settings.LOGGING = self.LOGGING

if self.JOGGING:
settings.JOGGING = self.JOGGING
jogging_init()

def test_basic(self):
logger = logging.getLogger("database_test")
logger.info("My Logging Test")
Expand All @@ -54,18 +54,18 @@ def test_basic(self):
self.assertEquals(log_obj.source, "database_test")
self.assertEquals(log_obj.msg, "My Logging Test")
self.assertTrue(log_obj.host)

def test_multi(self):
logger = logging.getLogger("multi_test")
logger.info("My Logging Test")

log_obj = Log.objects.latest()
self.assertEquals(log_obj.level, "INFO")
self.assertEquals(log_obj.source, "multi_test")
self.assertEquals(log_obj.msg, "My Logging Test")
self.assertTrue(log_obj.host)
log_obj = settings.LOGGING["multi_test"]["handlers"][1]["handler"].msgs[0]

log_obj = settings.JOGGING["multi_test"]["handlers"][1]["handler"].msgs[0]
self.assertEquals(log_obj.levelname, "INFO")
self.assertEquals(log_obj.name, "multi_test")
self.assertEquals(log_obj.msg, "My Logging Test")
Expand All @@ -75,40 +75,40 @@ class DictHandlerTestCase(DjangoTestCase):
def setUp(self):
from jogging.handlers import MockHandler
import logging

self.LOGGING = getattr(settings, 'LOGGING', None)

settings.LOGGING = {
self.JOGGING = getattr(settings, 'JOGGING', None)

settings.JOGGING = {
'dict_handler_test': {
'handlers': [
{ 'handler': MockHandler(), 'level': logging.ERROR },
{ 'handler': MockHandler(), 'level': logging.INFO },
],
},
}

jogging_init()

def tearDown(self):
import logging

# clear out all handlers on loggers
loggers = [logging.getLogger(""), logging.getLogger("database_test"), logging.getLogger("multi_test")]
for logger in loggers:
logger.handlers = []

# delete all log entries in the database
for l in Log.objects.all():
l.delete()
if self.LOGGING:
settings.LOGGING = self.LOGGING

if self.JOGGING:
settings.JOGGING = self.JOGGING
jogging_init()

def test_basic(self):
logger = logging.getLogger("dict_handler_test")
error_handler = settings.LOGGING["dict_handler_test"]["handlers"][0]["handler"]
info_handler = settings.LOGGING["dict_handler_test"]["handlers"][1]["handler"]
error_handler = settings.JOGGING["dict_handler_test"]["handlers"][0]["handler"]
info_handler = settings.JOGGING["dict_handler_test"]["handlers"][1]["handler"]


logger.info("My Logging Test")
Expand All @@ -122,45 +122,45 @@ def test_basic(self):

class GlobalExceptionTestCase(DjangoTestCase):
urls = 'jogging.tests.urls'

def setUp(self):
from jogging.handlers import DatabaseHandler, MockHandler
import logging
self.LOGGING = getattr(settings, 'LOGGING', None)

self.JOGGING = getattr(settings, 'JOGGING', None)
self.GLOBAL_LOG_HANDLERS = getattr(settings, 'GLOBAL_LOG_HANDLERS', None)
self.GLOBAL_LOG_LEVEL = getattr(settings, 'GLOBAL_LOG_LEVEL', None)

loggers = [logging.getLogger("")]
for logger in loggers:
logger.handlers = []
settings.LOGGING = {}

settings.JOGGING = {}
settings.GLOBAL_LOG_HANDLERS = [MockHandler()]
settings.GLOBAL_LOG_LEVEL = logging.DEBUG

jogging_init()

def tearDown(self):
import logging

# clear out all handlers on loggers
loggers = [logging.getLogger("")]
for logger in loggers:
logger.handlers = []

# delete all log entries in the database
for l in Log.objects.all():
l.delete()
if self.LOGGING:
settings.LOGGING = self.LOGGING

if self.JOGGING:
settings.JOGGING = self.JOGGING
if self.GLOBAL_LOG_HANDLERS:
settings.GLOBAL_LOG_HANDLERS = self.GLOBAL_LOG_HANDLERS
if self.GLOBAL_LOG_LEVEL:
settings.GLOBAL_LOG_LEVEL = self.GLOBAL_LOG_LEVEL
jogging_init()

def test_exception(self):
from views import TestException
try:
Expand Down
Empty file modified setup.py
100644 → 100755
Empty file.