On Sep 29, 11:29 pm, Simon Willison <si...@simonwillison.net> wrote:
>
> Here's my understanding of what we need to do for Django.
>
> 1. Create a 'django' logger somewhere in the framework that will
> always be executed if part of Django has been imported - would the
> django/__init__.py file work? Configure that with a NullHandler, and
> set it NOT to propagate events to the root logger. That way we can
> start using 'django.*' loggers throughout the framework but none of
> the messages will be sent anywhere without further configuration. The
> code should look something like this:
>
> import logging
>
> class NullHandler(logging.Handler):
>     def emit(self, record):
>         pass
>
> logger = logging.getLogger('django')

I tend to prefer the pattern logging.getLogger(__name__) in general,
but
this is equivalent.

> logger.addHandler(NullHandler())
> logger.propagate = False

I'm not sure about propagate=False here, though I can see why you
might want
to set it to this. Generally, many users set handlers up on the root
logger
and expect libraries they use to feed through to those handlers:
setting
propagate to False prevents this, and would require handlers to be set
just
for Django. This might not be a problem when Django is being run as a
server,
but if Django is being used as a library in any sense then this would
not be
the best thing to do.

If you set the 'django' logger's level to e.g. WARNING, then DEBUG
and INFO messages would never get through, but those >= WARNINGS
would. You
could of course set the bar higher, say to ERROR. This would prevent
any e.g.
DEBUG messages from Django code being generated, no matter what logger
levels
were set to elsewhere in the logger namespace.

> NullHandler is a generally useful class (I'm surprised it's not part
> of the core logging library), so we should probably put that in
> django.utils.log. The NullHandler is there purely to prevent the one-
> off "you haven't configured a handler yet" error that gets sent to
> sys.stderr.

Mea culpa. Leaving NullHandler out was an oversight originally,
because I
hadn't thought carefully enough about libraries that use logging
working with
applications that don't use or configure logging. It's now part of the
stdlib,
but it's not in Python 2.6 or earlier.

> 2. Start using loggers within the Django framework itself. The most
> obvious place to start is SQL queries - at the point where we execute
> SQL, we should do this:
>
> logging.getLogger('django.db.sql').debug(sql_to_execute)

Normally I do

logger = logging.getLogger(__name__)

followed by

logger.debug(...)

or similar. In this case, though, Perhaps the specific name is better
because
it's easier to remember than 'django.db.backends.XXX'. People familiar
with
Django internals would probably prefer the module name as it pinpoints
where
the event was raised.

>
> That logs the SQL to a 'django.db.sql' logger - which is a child of
> the 'django' logger and hence will be swallowed by the NullHandler.
>
> Another place we could use logging is to record cache gets and misses:
>
> logging.getLogger('django.core.cache').debug('Cache miss for key %s',
> key)
>
> And for sending e-mail:
>
> logging.getLogger('django.core.mail').info('Mail sent to %s',
> to_email)
>
> The logger names reflect the structure of Django up to a point
> (django.db) but I don't think there's anything wrong with straying
> from that convention (django.db.sql for example) provided the prefixes
> are sensible.
>

See my comments earlier about choice of logger names.

> 3. Add a bunch of syntactic sugar for making log messages visible at
> various points within Django. For example:
>
> ./manage.py runserver --log-level=django:info
>
> Would cause the runserver to first configure the 'django' logger to
> print all messages of level 'info' and higher to stdout.
>
> ./manage.py runserver --log-level=django:info --log-
> level=django.db:debug
>
> Same as above, but also configures django.db messages of debug or
> higher to display (I'm sure the command line syntax could be
> improved).
>
> By default, runserver would display no log messages at all.
>
> The same arguments could also work for ./manage.py test:
>
> ./manage.py test --log-level=django.db.sql:debug
>
> Hmm... maybe the --log-level argument could work for ALL management
> commands.
>
> My philosophy here is that log messages should be ignored unless
> explicitly told otherwise. Django gets run in various different ways -
> runserver, test and in deployment under mod_wsgi/FastCGI/etc - and
> it's not at all obvious what the default log output behaviour should
> be. As long as we make it extremely easy to see log messages if we
> want them I think having them off by default makes sense. It also
> ensures no surprises for people upgrading from 1.1 to 1.2.
>

Agreed.

> 4. Add a way to configure loggers in Django's settings.py file, in
> particular for use in production. I'm not sure if these settings
> should be ignored when running under other circumstances (./manage.py
> test etc) in favour of the command line option, or if they should
> still take effect. I quite liked Ivan's suggestion:
>
> LOGGING = {
>      'django.db.sql': {
>          'handler': 'django.logging.FileLogger', # WatchedFileLogger
> copy
>          'filename': '/tmp/django-sql.log',
>          'level': 'debug',
>      },
>      'django.core.cache': {
>          'handler': 'logging.handlers.HTTPHandler',
>          'host': '...',
>          'url': '....',
>          'format': '....'
>      },
>      'django.errors': {
>          'handler': 'logging.handlers.SMTPHandler',
>          ...
>      }
>
> }
>
> django.errors is where we get to replace the current "e-mail 500
> errors to an ADMIN" functionality.
>

We should provide a configure_logging(dict) utility function which
takes a
dict in an agreed format (such as Ivan's suggestion). Then in
settings.py
users can invoke configure_logging(LOGGING) after defining the
configuration
right there, in settings.py itself, or obtaining the dict from any
other
source. This avoids any "magic" (by LOGGING being automagically
processed) and
allows advanced users to configure "by hand" if they so wish.

> There should also be an option for advanced users to ignore the
> declarative syntax entirely and just provide a bunch of code that
> configures logging in the traditional way:
>
> import logging
> django_logger = logging.getLogger('django')
> django_logger.setLevel(...)
> django_logger.addHandler(...) # etc
>
> I'm not sure where that custom code should live - right there in

Seems reasonable to use settings.py for shortish bits of
configuration: if it
gets any hairier they can always define it in a separate module and
import
into settings.py.

> settings.py? At any rate, I think it's vital that people can use the
> low level logging configuration API if they want to.
>

Agreed, because we can't think of all eventualities.

> I'd rather not encourage people to use the logging module's support
> for .ini style configuration, just because Django already has its own
> configuration standards. That's not to say people can't call that from
> their own code if they want to, but I don't think we should emphasise
> that ability.

Fair enough.

> 5. Document how users should go about implementing their own loggers.
> I think we should tell people NOT to put them within the 'django'
> logger namespace - that should be reserved for core code. Instead,
> application developers should use logging.getLogger('their-app-name')
> - my OpenID library might do getLogger('django_openid') for example.
>
>
Using __name__ is a good convention. We should recommend that.
Otherwise
you might get clashes (a bit like app_label) and that would be a bit
confusing, at best.

> We might need to tell developers doing this to set up the NullHandler
> on their base logger to again suppress that warning message. If so, we
> could provide a utility method in django.utils.log:
>
> def getLogger(name):
>     logger = logging.getLogger(name)
>     if not logger.handlers:
>         logger.addHandler(NullHandler())
>     return logger
>
> There may well be a better way of doing this, or I may be
> misunderstanding the logging library.
>

There's a possibility of misuse here - if a user mistakenly used this
getLogger rather than logging.getLogger, you would get a NullHandler
attached to every logger they get - probably not what's intended.

> Once they've got their logger, they can do whatever they like with it.
> Log messages can be turned on using the same mechanisms I described
> above.
>
> The above suggestions are based on my limited experience doing simple
> logging, plus information gleaned from the two above documents. I have
> absolutely no idea if this is the right approach - Vinjay, does this
> look about right?

Yes, it does. Good stuff!

Oh, and it's

Vinay

The j is in my surname :-)
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-developers@googlegroups.com
To unsubscribe from this group, send email to 
django-developers+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to