Ticket #12012: t12012-rc2.diff
File t12012-rc2.diff, 61.8 KB (added by , 14 years ago) |
---|
-
django/conf/__init__.py
diff -r d861e2426ce4 django/conf/__init__.py
a b 16 16 17 17 ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE" 18 18 19 19 20 class LazySettings(LazyObject): 20 21 """ 21 22 A lazy proxy for either global Django settings or a custom settings object. … … 114 115 os.environ['TZ'] = self.TIME_ZONE 115 116 time.tzset() 116 117 118 # Settings are configured, so we can set up the logger if required 119 if self.LOGGING_CONFIG: 120 # First find the logging configuration function ... 121 logging_config_path, logging_config_func_name = self.LOGGING_CONFIG.rsplit('.', 1) 122 logging_config_module = importlib.import_module(logging_config_path) 123 logging_config_func = getattr(logging_config_module, logging_config_func_name) 124 125 # ... then invoke it with the logging settings 126 logging_config_func(self.LOGGING) 127 117 128 class UserSettingsHolder(object): 118 129 """ 119 130 Holder for user configured settings. -
django/conf/global_settings.py
diff -r d861e2426ce4 django/conf/global_settings.py
a b 499 499 # django.contrib.messages to avoid imports in this settings file. 500 500 501 501 ########### 502 # LOGGING # 503 ########### 504 505 # The callable to use to configure logging 506 LOGGING_CONFIG = 'django.utils.log.dictConfig' 507 508 # The default logging configuration. This sends an email to 509 # the site admins on every HTTP 500 error. All other log 510 # records are sent to the bit bucket. 511 LOGGING = { 512 'version': 1, 513 'disable_existing_loggers': False, 514 'handlers': { 515 'mail_admins': { 516 'level': 'ERROR', 517 'class': 'django.utils.log.AdminEmailHandler' 518 } 519 }, 520 'loggers': { 521 'django.request':{ 522 'handlers': ['mail_admins'], 523 'level': 'ERROR', 524 'propagate': True, 525 }, 526 } 527 } 528 529 ########### 502 530 # TESTING # 503 531 ########### 504 532 -
django/conf/project_template/settings.py
diff -r d861e2426ce4 django/conf/project_template/settings.py
a b 94 94 # Uncomment the next line to enable admin documentation: 95 95 # 'django.contrib.admindocs', 96 96 ) 97 98 # A sample logging configuration. The only tangible logging 99 # performed by this configuration is to send an email to 100 # the site admins on every HTTP 500 error. 101 # See http://docs.djangoproject.com/en/dev/topics/logging for 102 # more details on how to customize your logging configuration. 103 LOGGING = { 104 'version': 1, 105 'disable_existing_loggers': False, 106 'handlers': { 107 'mail_admins': { 108 'level': 'ERROR', 109 'class': 'django.utils.log.AdminEmailHandler' 110 } 111 }, 112 'loggers': { 113 'django.request':{ 114 'handlers': ['mail_admins'], 115 'level': 'ERROR', 116 'propagate': True, 117 }, 118 } 119 } -
django/core/handlers/base.py
diff -r d861e2426ce4 django/core/handlers/base.py
a b 1 import logging 1 2 import sys 2 3 3 4 from django import http … … 5 6 from django.utils.encoding import force_unicode 6 7 from django.utils.importlib import import_module 7 8 9 logger = logging.getLogger('django.request') 10 11 8 12 class BaseHandler(object): 9 13 # Changes that are always applied to a response (in this order). 10 14 response_fixes = [ … … 118 122 119 123 return response 120 124 except http.Http404, e: 125 logger.warning('Not Found: %s' % request.path, 126 extra={ 127 'status_code': 404, 128 'request': request 129 }) 121 130 if settings.DEBUG: 122 131 from django.views import debug 123 132 return debug.technical_404_response(request, e) … … 131 140 finally: 132 141 receivers = signals.got_request_exception.send(sender=self.__class__, request=request) 133 142 except exceptions.PermissionDenied: 143 logger.warning('Forbidden (Permission denied): %s' % request.path, 144 extra={ 145 'status_code': 403, 146 'request': request 147 }) 134 148 return http.HttpResponseForbidden('<h1>Permission denied</h1>') 135 149 except SystemExit: 136 150 # Allow sys.exit() to actually exit. See tickets #1023 and #4701 … … 155 169 available would be an error. 156 170 """ 157 171 from django.conf import settings 158 from django.core.mail import mail_admins159 172 160 173 if settings.DEBUG_PROPAGATE_EXCEPTIONS: 161 174 raise … … 164 177 from django.views import debug 165 178 return debug.technical_500_response(request, *exc_info) 166 179 167 # When DEBUG is False, send an error message to the admins.168 subject = 'Error (%s IP): %s' % ((request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS and 'internal' or 'EXTERNAL'), request.path)169 try:170 request_repr = repr(request)171 except:172 request_repr = "Request repr() unavailable"173 message = "%s\n\n%s" % (self._get_traceback(exc_info), request_repr)174 mail_admins(subject, message, fail_silently=True) 180 logger.error('Internal Server Error: %s' % request.path, 181 exc_info=exc_info, 182 extra={ 183 'status_code': 500, 184 'request':request 185 } 186 ) 187 175 188 # If Http500 handler is not installed, re-raise last exception 176 189 if resolver.urlconf_module is None: 177 190 raise exc_info[1], None, exc_info[2] … … 179 192 callback, param_dict = resolver.resolve500() 180 193 return callback(request, **param_dict) 181 194 182 def _get_traceback(self, exc_info=None):183 "Helper function to return the traceback as a string"184 import traceback185 return '\n'.join(traceback.format_exception(*(exc_info or sys.exc_info())))186 187 195 def apply_response_fixes(self, request, response): 188 196 """ 189 197 Applies each of the functions in self.response_fixes to the request and -
django/core/handlers/modpython.py
diff -r d861e2426ce4 django/core/handlers/modpython.py
a b 1 import logging 1 2 import os 2 3 from pprint import pformat 4 import sys 3 5 from warnings import warn 4 6 5 7 from django import http … … 9 11 from django.utils import datastructures 10 12 from django.utils.encoding import force_unicode, smart_str, iri_to_uri 11 13 14 logger = logging.getLogger('django.request') 15 16 12 17 # NOTE: do *not* import settings (or any module which eventually imports 13 18 # settings) until after ModPythonHandler has been called; otherwise os.environ 14 19 # won't be set up correctly (with respect to settings). … … 200 205 try: 201 206 request = self.request_class(req) 202 207 except UnicodeDecodeError: 208 logger.warning('Bad Request (UnicodeDecodeError): %s' % request.path, 209 exc_info=sys.exc_info(), 210 extra={ 211 'status_code': 400, 212 'request': request 213 } 214 ) 203 215 response = http.HttpResponseBadRequest() 204 216 else: 205 217 response = self.get_response(request) -
django/core/handlers/wsgi.py
diff -r d861e2426ce4 django/core/handlers/wsgi.py
a b 1 import logging 2 from pprint import pformat 3 import sys 1 4 from threading import Lock 2 from pprint import pformat3 5 try: 4 6 from cStringIO import StringIO 5 7 except ImportError: … … 12 14 from django.utils import datastructures 13 15 from django.utils.encoding import force_unicode, iri_to_uri 14 16 17 logger = logging.getLogger('django.request') 18 19 15 20 # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html 16 21 STATUS_CODE_TEXT = { 17 22 100: 'CONTINUE', … … 236 241 try: 237 242 request = self.request_class(environ) 238 243 except UnicodeDecodeError: 244 logger.warning('Bad Request (UnicodeDecodeError): %s' % request.path, 245 exc_info=sys.exc_info(), 246 extra={ 247 'status_code': 400, 248 'request': request 249 } 250 ) 239 251 response = http.HttpResponseBadRequest() 240 252 else: 241 253 response = self.get_response(request) -
django/db/backends/util.py
diff -r d861e2426ce4 django/db/backends/util.py
a b 1 1 import datetime 2 2 import decimal 3 import logging 3 4 from time import time 4 5 5 6 from django.utils.hashcompat import md5_constructor 6 7 8 logger = logging.getLogger('django.db.backends') 9 7 10 class CursorDebugWrapper(object): 8 11 def __init__(self, cursor, db): 9 12 self.cursor = cursor … … 15 18 return self.cursor.execute(sql, params) 16 19 finally: 17 20 stop = time() 21 duration = stop - start 18 22 sql = self.db.ops.last_executed_query(self.cursor, sql, params) 19 23 self.db.queries.append({ 20 24 'sql': sql, 21 'time': "%.3f" % (stop - start),25 'time': "%.3f" % duration, 22 26 }) 27 logger.debug('(%.3f) %s; args=%s' % (duration, sql, params), 28 extra={'duration':duration, 'sql':sql, 'params':params} 29 ) 23 30 24 31 def executemany(self, sql, param_list): 25 32 start = time() … … 27 34 return self.cursor.executemany(sql, param_list) 28 35 finally: 29 36 stop = time() 37 duration = stop - start 30 38 self.db.queries.append({ 31 39 'sql': '%s times: %s' % (len(param_list), sql), 32 'time': "%.3f" % (stop - start),40 'time': "%.3f" % duration, 33 41 }) 42 logger.debug('(%.3f) %s; args=%s' % (duration, sql, param_list), 43 extra={'duration':duration, 'sql':sql, 'params':param_list} 44 ) 34 45 35 46 def __getattr__(self, attr): 36 47 if attr in self.__dict__: -
django/middleware/common.py
diff -r d861e2426ce4 django/middleware/common.py
a b 1 import logging 1 2 import re 2 3 3 4 from django.conf import settings … … 7 8 from django.core import urlresolvers 8 9 from django.utils.hashcompat import md5_constructor 9 10 11 logger = logging.getLogger('django.request') 12 13 10 14 class CommonMiddleware(object): 11 15 """ 12 16 "Common" middleware for taking care of some basic operations: … … 38 42 if 'HTTP_USER_AGENT' in request.META: 39 43 for user_agent_regex in settings.DISALLOWED_USER_AGENTS: 40 44 if user_agent_regex.search(request.META['HTTP_USER_AGENT']): 45 logger.warning('Forbidden (User agent): %s' % request.path, 46 extra={ 47 'status_code': 403, 48 'request': request 49 } 50 ) 41 51 return http.HttpResponseForbidden('<h1>Forbidden</h1>') 42 52 43 53 # Check for a redirect based on settings.APPEND_SLASH -
django/middleware/csrf.py
diff -r d861e2426ce4 django/middleware/csrf.py
a b 6 6 """ 7 7 8 8 import itertools 9 import logging 9 10 import re 10 11 import random 11 12 … … 20 21 21 22 _HTML_TYPES = ('text/html', 'application/xhtml+xml') 22 23 24 logger = logging.getLogger('django.request') 25 23 26 # Use the system (hardware-based) random number generator if it exists. 24 27 if hasattr(random, 'SystemRandom'): 25 28 randrange = random.SystemRandom().randrange … … 169 172 # we can use strict Referer checking. 170 173 referer = request.META.get('HTTP_REFERER') 171 174 if referer is None: 175 logger.warning('Forbidden (%s): %s' % (REASON_NO_COOKIE, request.path), 176 extra={ 177 'status_code': 403, 178 'request': request, 179 } 180 ) 172 181 return reject(REASON_NO_REFERER) 173 182 174 183 # The following check ensures that the referer is HTTPS, 175 184 # the domains match and the ports match - the same origin policy. 176 185 good_referer = 'https://%s/' % request.get_host() 177 186 if not referer.startswith(good_referer): 178 return reject(REASON_BAD_REFERER % 179 (referer, good_referer)) 187 reason = REASON_BAD_REFERER % (referer, good_referer) 188 logger.warning('Forbidden (%s): %s' % (reason, request.path), 189 extra={ 190 'status_code': 403, 191 'request': request, 192 } 193 ) 194 return reject(reason) 180 195 181 196 # If the user didn't already have a CSRF cookie, then fall back to 182 197 # the Django 1.1 method (hash of session ID), so a request is not … … 190 205 # No CSRF cookie and no session cookie. For POST requests, 191 206 # we insist on a CSRF cookie, and in this way we can avoid 192 207 # all CSRF attacks, including login CSRF. 208 logger.warning('Forbidden (%s): %s' % (REASON_NO_COOKIE, request.path), 209 extra={ 210 'status_code': 403, 211 'request': request, 212 } 213 ) 193 214 return reject(REASON_NO_COOKIE) 194 215 else: 195 216 csrf_token = request.META["CSRF_COOKIE"] … … 199 220 if request_csrf_token != csrf_token: 200 221 if cookie_is_new: 201 222 # probably a problem setting the CSRF cookie 223 logger.warning('Forbidden (%s): %s' % (REASON_NO_CSRF_COOKIE, request.path), 224 extra={ 225 'status_code': 403, 226 'request': request, 227 } 228 ) 202 229 return reject(REASON_NO_CSRF_COOKIE) 203 230 else: 231 logger.warning('Forbidden (%s): %s' % (REASON_BAD_TOKEN, request.path), 232 extra={ 233 'status_code': 403, 234 'request': request, 235 } 236 ) 204 237 return reject(REASON_BAD_TOKEN) 205 238 206 239 return accept() -
new file django/utils/dictconfig.py
diff -r d861e2426ce4 django/utils/dictconfig.py
- + 1 # This is a copy of the Python logging.config.dictconfig module, 2 # reproduced with permission. It is provided here for backwards 3 # compatibility for Python versions prior to 2.7. 4 # 5 # Copyright 2009-2010 by Vinay Sajip. All Rights Reserved. 6 # 7 # Permission to use, copy, modify, and distribute this software and its 8 # documentation for any purpose and without fee is hereby granted, 9 # provided that the above copyright notice appear in all copies and that 10 # both that copyright notice and this permission notice appear in 11 # supporting documentation, and that the name of Vinay Sajip 12 # not be used in advertising or publicity pertaining to distribution 13 # of the software without specific, written prior permission. 14 # VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 15 # ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 16 # VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 17 # ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 18 # IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 19 # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 21 import logging.handlers 22 import re 23 import sys 24 import types 25 26 IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I) 27 28 def valid_ident(s): 29 m = IDENTIFIER.match(s) 30 if not m: 31 raise ValueError('Not a valid Python identifier: %r' % s) 32 return True 33 34 # 35 # This function is defined in logging only in recent versions of Python 36 # 37 try: 38 from logging import _checkLevel 39 except ImportError: 40 def _checkLevel(level): 41 if isinstance(level, int): 42 rv = level 43 elif str(level) == level: 44 if level not in logging._levelNames: 45 raise ValueError('Unknown level: %r' % level) 46 rv = logging._levelNames[level] 47 else: 48 raise TypeError('Level not an integer or a ' 49 'valid string: %r' % level) 50 return rv 51 52 # The ConvertingXXX classes are wrappers around standard Python containers, 53 # and they serve to convert any suitable values in the container. The 54 # conversion converts base dicts, lists and tuples to their wrapped 55 # equivalents, whereas strings which match a conversion format are converted 56 # appropriately. 57 # 58 # Each wrapper should have a configurator attribute holding the actual 59 # configurator to use for conversion. 60 61 class ConvertingDict(dict): 62 """A converting dictionary wrapper.""" 63 64 def __getitem__(self, key): 65 value = dict.__getitem__(self, key) 66 result = self.configurator.convert(value) 67 #If the converted value is different, save for next time 68 if value is not result: 69 self[key] = result 70 if type(result) in (ConvertingDict, ConvertingList, 71 ConvertingTuple): 72 result.parent = self 73 result.key = key 74 return result 75 76 def get(self, key, default=None): 77 value = dict.get(self, key, default) 78 result = self.configurator.convert(value) 79 #If the converted value is different, save for next time 80 if value is not result: 81 self[key] = result 82 if type(result) in (ConvertingDict, ConvertingList, 83 ConvertingTuple): 84 result.parent = self 85 result.key = key 86 return result 87 88 def pop(self, key, default=None): 89 value = dict.pop(self, key, default) 90 result = self.configurator.convert(value) 91 if value is not result: 92 if type(result) in (ConvertingDict, ConvertingList, 93 ConvertingTuple): 94 result.parent = self 95 result.key = key 96 return result 97 98 class ConvertingList(list): 99 """A converting list wrapper.""" 100 def __getitem__(self, key): 101 value = list.__getitem__(self, key) 102 result = self.configurator.convert(value) 103 #If the converted value is different, save for next time 104 if value is not result: 105 self[key] = result 106 if type(result) in (ConvertingDict, ConvertingList, 107 ConvertingTuple): 108 result.parent = self 109 result.key = key 110 return result 111 112 def pop(self, idx=-1): 113 value = list.pop(self, idx) 114 result = self.configurator.convert(value) 115 if value is not result: 116 if type(result) in (ConvertingDict, ConvertingList, 117 ConvertingTuple): 118 result.parent = self 119 return result 120 121 class ConvertingTuple(tuple): 122 """A converting tuple wrapper.""" 123 def __getitem__(self, key): 124 value = tuple.__getitem__(self, key) 125 result = self.configurator.convert(value) 126 if value is not result: 127 if type(result) in (ConvertingDict, ConvertingList, 128 ConvertingTuple): 129 result.parent = self 130 result.key = key 131 return result 132 133 class BaseConfigurator(object): 134 """ 135 The configurator base class which defines some useful defaults. 136 """ 137 138 CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$') 139 140 WORD_PATTERN = re.compile(r'^\s*(\w+)\s*') 141 DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*') 142 INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*') 143 DIGIT_PATTERN = re.compile(r'^\d+$') 144 145 value_converters = { 146 'ext' : 'ext_convert', 147 'cfg' : 'cfg_convert', 148 } 149 150 # We might want to use a different one, e.g. importlib 151 importer = __import__ 152 153 def __init__(self, config): 154 self.config = ConvertingDict(config) 155 self.config.configurator = self 156 157 def resolve(self, s): 158 """ 159 Resolve strings to objects using standard import and attribute 160 syntax. 161 """ 162 name = s.split('.') 163 used = name.pop(0) 164 try: 165 found = self.importer(used) 166 for frag in name: 167 used += '.' + frag 168 try: 169 found = getattr(found, frag) 170 except AttributeError: 171 self.importer(used) 172 found = getattr(found, frag) 173 return found 174 except ImportError: 175 e, tb = sys.exc_info()[1:] 176 v = ValueError('Cannot resolve %r: %s' % (s, e)) 177 v.__cause__, v.__traceback__ = e, tb 178 raise v 179 180 def ext_convert(self, value): 181 """Default converter for the ext:// protocol.""" 182 return self.resolve(value) 183 184 def cfg_convert(self, value): 185 """Default converter for the cfg:// protocol.""" 186 rest = value 187 m = self.WORD_PATTERN.match(rest) 188 if m is None: 189 raise ValueError("Unable to convert %r" % value) 190 else: 191 rest = rest[m.end():] 192 d = self.config[m.groups()[0]] 193 #print d, rest 194 while rest: 195 m = self.DOT_PATTERN.match(rest) 196 if m: 197 d = d[m.groups()[0]] 198 else: 199 m = self.INDEX_PATTERN.match(rest) 200 if m: 201 idx = m.groups()[0] 202 if not self.DIGIT_PATTERN.match(idx): 203 d = d[idx] 204 else: 205 try: 206 n = int(idx) # try as number first (most likely) 207 d = d[n] 208 except TypeError: 209 d = d[idx] 210 if m: 211 rest = rest[m.end():] 212 else: 213 raise ValueError('Unable to convert ' 214 '%r at %r' % (value, rest)) 215 #rest should be empty 216 return d 217 218 def convert(self, value): 219 """ 220 Convert values to an appropriate type. dicts, lists and tuples are 221 replaced by their converting alternatives. Strings are checked to 222 see if they have a conversion format and are converted if they do. 223 """ 224 if not isinstance(value, ConvertingDict) and isinstance(value, dict): 225 value = ConvertingDict(value) 226 value.configurator = self 227 elif not isinstance(value, ConvertingList) and isinstance(value, list): 228 value = ConvertingList(value) 229 value.configurator = self 230 elif not isinstance(value, ConvertingTuple) and\ 231 isinstance(value, tuple): 232 value = ConvertingTuple(value) 233 value.configurator = self 234 elif isinstance(value, basestring): # str for py3k 235 m = self.CONVERT_PATTERN.match(value) 236 if m: 237 d = m.groupdict() 238 prefix = d['prefix'] 239 converter = self.value_converters.get(prefix, None) 240 if converter: 241 suffix = d['suffix'] 242 converter = getattr(self, converter) 243 value = converter(suffix) 244 return value 245 246 def configure_custom(self, config): 247 """Configure an object with a user-supplied factory.""" 248 c = config.pop('()') 249 if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType: 250 c = self.resolve(c) 251 props = config.pop('.', None) 252 # Check for valid identifiers 253 kwargs = dict([(k, config[k]) for k in config if valid_ident(k)]) 254 result = c(**kwargs) 255 if props: 256 for name, value in props.items(): 257 setattr(result, name, value) 258 return result 259 260 def as_tuple(self, value): 261 """Utility function which converts lists to tuples.""" 262 if isinstance(value, list): 263 value = tuple(value) 264 return value 265 266 class DictConfigurator(BaseConfigurator): 267 """ 268 Configure logging using a dictionary-like object to describe the 269 configuration. 270 """ 271 272 def configure(self): 273 """Do the configuration.""" 274 275 config = self.config 276 if 'version' not in config: 277 raise ValueError("dictionary doesn't specify a version") 278 if config['version'] != 1: 279 raise ValueError("Unsupported version: %s" % config['version']) 280 incremental = config.pop('incremental', False) 281 EMPTY_DICT = {} 282 logging._acquireLock() 283 try: 284 if incremental: 285 handlers = config.get('handlers', EMPTY_DICT) 286 # incremental handler config only if handler name 287 # ties in to logging._handlers (Python 2.7) 288 if sys.version_info[:2] == (2, 7): 289 for name in handlers: 290 if name not in logging._handlers: 291 raise ValueError('No handler found with ' 292 'name %r' % name) 293 else: 294 try: 295 handler = logging._handlers[name] 296 handler_config = handlers[name] 297 level = handler_config.get('level', None) 298 if level: 299 handler.setLevel(_checkLevel(level)) 300 except StandardError, e: 301 raise ValueError('Unable to configure handler ' 302 '%r: %s' % (name, e)) 303 loggers = config.get('loggers', EMPTY_DICT) 304 for name in loggers: 305 try: 306 self.configure_logger(name, loggers[name], True) 307 except StandardError, e: 308 raise ValueError('Unable to configure logger ' 309 '%r: %s' % (name, e)) 310 root = config.get('root', None) 311 if root: 312 try: 313 self.configure_root(root, True) 314 except StandardError, e: 315 raise ValueError('Unable to configure root ' 316 'logger: %s' % e) 317 else: 318 disable_existing = config.pop('disable_existing_loggers', True) 319 320 logging._handlers.clear() 321 del logging._handlerList[:] 322 323 # Do formatters first - they don't refer to anything else 324 formatters = config.get('formatters', EMPTY_DICT) 325 for name in formatters: 326 try: 327 formatters[name] = self.configure_formatter( 328 formatters[name]) 329 except StandardError, e: 330 raise ValueError('Unable to configure ' 331 'formatter %r: %s' % (name, e)) 332 # Next, do filters - they don't refer to anything else, either 333 filters = config.get('filters', EMPTY_DICT) 334 for name in filters: 335 try: 336 filters[name] = self.configure_filter(filters[name]) 337 except StandardError, e: 338 raise ValueError('Unable to configure ' 339 'filter %r: %s' % (name, e)) 340 341 # Next, do handlers - they refer to formatters and filters 342 # As handlers can refer to other handlers, sort the keys 343 # to allow a deterministic order of configuration 344 handlers = config.get('handlers', EMPTY_DICT) 345 for name in sorted(handlers): 346 try: 347 handler = self.configure_handler(handlers[name]) 348 handler.name = name 349 handlers[name] = handler 350 except StandardError, e: 351 raise ValueError('Unable to configure handler ' 352 '%r: %s' % (name, e)) 353 # Next, do loggers - they refer to handlers and filters 354 355 #we don't want to lose the existing loggers, 356 #since other threads may have pointers to them. 357 #existing is set to contain all existing loggers, 358 #and as we go through the new configuration we 359 #remove any which are configured. At the end, 360 #what's left in existing is the set of loggers 361 #which were in the previous configuration but 362 #which are not in the new configuration. 363 root = logging.root 364 existing = root.manager.loggerDict.keys() 365 #The list needs to be sorted so that we can 366 #avoid disabling child loggers of explicitly 367 #named loggers. With a sorted list it is easier 368 #to find the child loggers. 369 existing.sort() 370 #We'll keep the list of existing loggers 371 #which are children of named loggers here... 372 child_loggers = [] 373 #now set up the new ones... 374 loggers = config.get('loggers', EMPTY_DICT) 375 for name in loggers: 376 if name in existing: 377 i = existing.index(name) 378 prefixed = name + "." 379 pflen = len(prefixed) 380 num_existing = len(existing) 381 i = i + 1 # look at the entry after name 382 while (i < num_existing) and\ 383 (existing[i][:pflen] == prefixed): 384 child_loggers.append(existing[i]) 385 i = i + 1 386 existing.remove(name) 387 try: 388 self.configure_logger(name, loggers[name]) 389 except StandardError, e: 390 raise ValueError('Unable to configure logger ' 391 '%r: %s' % (name, e)) 392 393 #Disable any old loggers. There's no point deleting 394 #them as other threads may continue to hold references 395 #and by disabling them, you stop them doing any logging. 396 #However, don't disable children of named loggers, as that's 397 #probably not what was intended by the user. 398 for log in existing: 399 logger = root.manager.loggerDict[log] 400 if log in child_loggers: 401 logger.level = logging.NOTSET 402 logger.handlers = [] 403 logger.propagate = True 404 elif disable_existing: 405 logger.disabled = True 406 407 # And finally, do the root logger 408 root = config.get('root', None) 409 if root: 410 try: 411 self.configure_root(root) 412 except StandardError, e: 413 raise ValueError('Unable to configure root ' 414 'logger: %s' % e) 415 finally: 416 logging._releaseLock() 417 418 def configure_formatter(self, config): 419 """Configure a formatter from a dictionary.""" 420 if '()' in config: 421 factory = config['()'] # for use in exception handler 422 try: 423 result = self.configure_custom(config) 424 except TypeError, te: 425 if "'format'" not in str(te): 426 raise 427 #Name of parameter changed from fmt to format. 428 #Retry with old name. 429 #This is so that code can be used with older Python versions 430 #(e.g. by Django) 431 config['fmt'] = config.pop('format') 432 config['()'] = factory 433 result = self.configure_custom(config) 434 else: 435 fmt = config.get('format', None) 436 dfmt = config.get('datefmt', None) 437 result = logging.Formatter(fmt, dfmt) 438 return result 439 440 def configure_filter(self, config): 441 """Configure a filter from a dictionary.""" 442 if '()' in config: 443 result = self.configure_custom(config) 444 else: 445 name = config.get('name', '') 446 result = logging.Filter(name) 447 return result 448 449 def add_filters(self, filterer, filters): 450 """Add filters to a filterer from a list of names.""" 451 for f in filters: 452 try: 453 filterer.addFilter(self.config['filters'][f]) 454 except StandardError, e: 455 raise ValueError('Unable to add filter %r: %s' % (f, e)) 456 457 def configure_handler(self, config): 458 """Configure a handler from a dictionary.""" 459 formatter = config.pop('formatter', None) 460 if formatter: 461 try: 462 formatter = self.config['formatters'][formatter] 463 except StandardError, e: 464 raise ValueError('Unable to set formatter ' 465 '%r: %s' % (formatter, e)) 466 level = config.pop('level', None) 467 filters = config.pop('filters', None) 468 if '()' in config: 469 c = config.pop('()') 470 if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType: 471 c = self.resolve(c) 472 factory = c 473 else: 474 klass = self.resolve(config.pop('class')) 475 #Special case for handler which refers to another handler 476 if issubclass(klass, logging.handlers.MemoryHandler) and\ 477 'target' in config: 478 try: 479 config['target'] = self.config['handlers'][config['target']] 480 except StandardError, e: 481 raise ValueError('Unable to set target handler ' 482 '%r: %s' % (config['target'], e)) 483 elif issubclass(klass, logging.handlers.SMTPHandler) and\ 484 'mailhost' in config: 485 config['mailhost'] = self.as_tuple(config['mailhost']) 486 elif issubclass(klass, logging.handlers.SysLogHandler) and\ 487 'address' in config: 488 config['address'] = self.as_tuple(config['address']) 489 factory = klass 490 kwargs = dict([(k, config[k]) for k in config if valid_ident(k)]) 491 try: 492 result = factory(**kwargs) 493 except TypeError, te: 494 if "'stream'" not in str(te): 495 raise 496 #The argument name changed from strm to stream 497 #Retry with old name. 498 #This is so that code can be used with older Python versions 499 #(e.g. by Django) 500 kwargs['strm'] = kwargs.pop('stream') 501 result = factory(**kwargs) 502 if formatter: 503 result.setFormatter(formatter) 504 if level is not None: 505 result.setLevel(_checkLevel(level)) 506 if filters: 507 self.add_filters(result, filters) 508 return result 509 510 def add_handlers(self, logger, handlers): 511 """Add handlers to a logger from a list of names.""" 512 for h in handlers: 513 try: 514 logger.addHandler(self.config['handlers'][h]) 515 except StandardError, e: 516 raise ValueError('Unable to add handler %r: %s' % (h, e)) 517 518 def common_logger_config(self, logger, config, incremental=False): 519 """ 520 Perform configuration which is common to root and non-root loggers. 521 """ 522 level = config.get('level', None) 523 if level is not None: 524 logger.setLevel(_checkLevel(level)) 525 if not incremental: 526 #Remove any existing handlers 527 for h in logger.handlers[:]: 528 logger.removeHandler(h) 529 handlers = config.get('handlers', None) 530 if handlers: 531 self.add_handlers(logger, handlers) 532 filters = config.get('filters', None) 533 if filters: 534 self.add_filters(logger, filters) 535 536 def configure_logger(self, name, config, incremental=False): 537 """Configure a non-root logger from a dictionary.""" 538 logger = logging.getLogger(name) 539 self.common_logger_config(logger, config, incremental) 540 propagate = config.get('propagate', None) 541 if propagate is not None: 542 logger.propagate = propagate 543 544 def configure_root(self, config, incremental=False): 545 """Configure a root logger from a dictionary.""" 546 root = logging.getLogger() 547 self.common_logger_config(root, config, incremental) 548 549 dictConfigClass = DictConfigurator 550 551 def dictConfig(config): 552 """Configure logging using a dictionary.""" 553 dictConfigClass(config).configure() -
new file django/utils/log.py
diff -r d861e2426ce4 django/utils/log.py
- + 1 import logging 2 from django.core import mail 3 4 # Make sure a NullHandler is available 5 # This was added in Python 2.7/3.2 6 try: 7 from logging import NullHandler 8 except ImportError: 9 class NullHandler(logging.Handler): 10 def emit(self, record): 11 pass 12 13 # Make sure that dictConfig is available 14 # This was added in Python 2.7/3.2 15 try: 16 from logging.config import dictConfig 17 except ImportError: 18 from django.utils.dictconfig import dictConfig 19 20 # Ensure the creation of the Django logger 21 logger = logging.getLogger('django') 22 23 24 class AdminEmailHandler(logging.Handler): 25 """An exception log handler that emails log entries to site admins 26 27 If the request is passed as the first argument to the log record, 28 request data will be provided in the 29 """ 30 def emit(self, record): 31 import traceback 32 from django.conf import settings 33 34 try: 35 request = record.request 36 37 subject = '%s (%s IP): %s' % ( 38 record.levelname, 39 (request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS and 'internal' or 'EXTERNAL'), 40 request.path 41 ) 42 request_repr = repr(request) 43 except: 44 subject = 'Error: Unknown URL' 45 request_repr = "Request repr() unavailable" 46 47 if record.exc_info: 48 stack_trace = '\n'.join(traceback.format_exception(*record.exc_info)) 49 else: 50 stack_trace = 'No stack trace available' 51 52 message = "%s\n\n%s" % (stack_trace, request_repr) 53 mail.mail_admins(subject, message, fail_silently=True) -
django/views/decorators/http.py
diff -r d861e2426ce4 django/views/decorators/http.py
a b 10 10 from calendar import timegm 11 11 from datetime import timedelta 12 12 from email.Utils import formatdate 13 import logging 13 14 14 15 from django.utils.decorators import decorator_from_middleware, available_attrs 15 16 from django.utils.http import parse_etags, quote_etag 16 17 from django.middleware.http import ConditionalGetMiddleware 17 18 from django.http import HttpResponseNotAllowed, HttpResponseNotModified, HttpResponse 18 19 20 conditional_page = decorator_from_middleware(ConditionalGetMiddleware) 19 21 20 conditional_page = decorator_from_middleware(ConditionalGetMiddleware) 22 logger = logging.getLogger('django.request') 23 21 24 22 25 def require_http_methods(request_method_list): 23 26 """ … … 33 36 def decorator(func): 34 37 def inner(request, *args, **kwargs): 35 38 if request.method not in request_method_list: 39 logger.warning('Method Not Allowed (%s): %s' % (request.method, request.path), 40 extra={ 41 'status_code': 405, 42 'request': request 43 } 44 ) 36 45 return HttpResponseNotAllowed(request_method_list) 37 46 return func(request, *args, **kwargs) 38 47 return wraps(func, assigned=available_attrs(func))(inner) … … 111 120 if request.method in ("GET", "HEAD"): 112 121 response = HttpResponseNotModified() 113 122 else: 123 logger.warning('Precondition Failed: %s' % request.path, 124 extra={ 125 'status_code': 412, 126 'request': request 127 } 128 ) 114 129 response = HttpResponse(status=412) 115 130 elif if_match and ((not res_etag and "*" in etags) or 116 131 (res_etag and res_etag not in etags)): 132 logger.warning('Precondition Failed: %s' % request.path, 133 extra={ 134 'status_code': 412, 135 'request': request 136 } 137 ) 117 138 response = HttpResponse(status=412) 118 139 elif (not if_none_match and if_modified_since and 119 140 request.method == "GET" and -
django/views/generic/simple.py
diff -r d861e2426ce4 django/views/generic/simple.py
a b 1 import logging 2 1 3 from django.template import loader, RequestContext 2 4 from django.http import HttpResponse, HttpResponseRedirect, HttpResponsePermanentRedirect, HttpResponseGone 3 5 6 logger = logging.getLogger('django.request') 7 8 4 9 def direct_to_template(request, template, extra_context=None, mimetype=None, **kwargs): 5 10 """ 6 11 Render a given template with any extra URL parameters in the context as … … 46 51 klass = permanent and HttpResponsePermanentRedirect or HttpResponseRedirect 47 52 return klass(url % kwargs) 48 53 else: 54 logger.warning('Gone: %s' % request.path, 55 extra={ 56 'status_code': 410, 57 'request': request 58 }) 49 59 return HttpResponseGone() -
docs/index.txt
diff -r d861e2426ce4 docs/index.txt
a b 176 176 * :doc:`Internationalization <topics/i18n/index>` 177 177 * :doc:`Jython support <howto/jython>` 178 178 * :doc:`"Local flavor" <ref/contrib/localflavor>` 179 * :doc:`Logging <topics/logging>` 179 180 * :doc:`Messages <ref/contrib/messages>` 180 181 * :doc:`Pagination <topics/pagination>` 181 182 * :doc:`Redirects <ref/contrib/redirects>` -
docs/ref/settings.txt
diff -r d861e2426ce4 docs/ref/settings.txt
a b 1008 1008 1009 1009 .. setting:: LOGIN_REDIRECT_URL 1010 1010 1011 LOGGING 1012 ------- 1013 1014 Default: A logging configuration dictionary. 1015 1016 A data structure containing configuration information. The contents of 1017 this data structure will be passed as the argument to the 1018 configuration method described in :setting:`LOGGING_CONFIG`. 1019 1020 The default logging configuration passes HTTP 500 server errors to an 1021 email log handler; all other log messages are given to a NullHandler. 1022 1023 .. versionadded:: 1.3 1024 1025 LOGGING_CONFIG 1026 -------------- 1027 1028 Default: ``'django.utils.log.dictConfig'`` 1029 1030 A path to a callable that will be used to configure logging in the 1031 Django project. Points at a instance of Python's `dictConfig`_ 1032 configuration method by default. 1033 1034 If you set :setting:`LOGGING_CONFIG` to ``None``, the logging 1035 configuration process will be skipped. 1036 1037 .. _dictConfig: http://docs.python.org/library/logging.html#logging.dictConfig 1038 1039 .. versionadded:: 1.3 1040 1011 1041 LOGIN_REDIRECT_URL 1012 1042 ------------------ 1013 1043 -
docs/releases/1.3.txt
diff -r d861e2426ce4 docs/releases/1.3.txt
a b 84 84 What's new in Django 1.3 85 85 ======================== 86 86 87 Logging 88 ~~~~~~~ 89 90 Django 1.3 adds framework-level support for Python's logging module. 91 This means you can now esaily configure and control logging as part of 92 your Django project. A number of logging handlers and logging calls 93 have been added to Django's own code as well -- most notably, the 94 error emails sent on a HTTP 500 server error are now handled as a 95 logging activity. See :doc:`the documentation on Django's logging 96 interface </topics/logging>` for more details. -
docs/topics/index.txt
diff -r d861e2426ce4 docs/topics/index.txt
a b 20 20 conditional-view-processing 21 21 email 22 22 i18n/index 23 logging 23 24 pagination 24 25 serialization 25 26 settings -
new file docs/topics/logging.txt
diff -r d861e2426ce4 docs/topics/logging.txt
- + 1 ======= 2 Logging 3 ======= 4 5 .. versionadded:: 1.3 6 7 .. module:: django.utils.log 8 :synopsis: Logging tools for Django applications 9 10 A quick logging primer 11 ====================== 12 13 Django uses Python's builtin logging module to perform system logging. 14 The usage of the logging module is discussed in detail in `Python's 15 own documentation`_. However, if you've never used Python's logging 16 framework (or even if you have), here's a quick primer. 17 18 .. _Python's own documentation: http://docs.python.org/library/logging.html 19 20 The cast of players 21 ------------------- 22 23 A Python logging configuration consists of four parts: 24 25 * :ref:`topic-logging-parts-loggers` 26 * :ref:`topic-logging-parts-handlers` 27 * :ref:`topic-logging-parts-filters` 28 * :ref:`topic-logging-parts-formatters` 29 30 .. _topic-logging-parts-loggers: 31 32 Loggers 33 ~~~~~~~ 34 35 A logger is the entry point into the logging system. Each logger is 36 a named bucket to which messages can be written for processing. 37 38 A logger is configured to have *log level*. This log level describes 39 the severity of the messages that the logger will handle. Python 40 defines the following log levels: 41 42 * ``DEBUG``: Low level system information for debugging purposes 43 44 * ``INFO``: General system information 45 46 * ``WARNING``: Information describing a minor problem that has 47 occurred. 48 49 * ``ERROR``: Information describing a major problem that has 50 occurred. 51 52 * ``CRITICAL``: Information describing a critical problem that has 53 occurred. 54 55 Each message that is written to the logger is a *Log Record*. Each log 56 record also has a *log level* indicating the severity of that specific 57 message. A log record can also contain useful metadata that describes 58 the event that is being logged. This can include details such as a 59 stack trace or an error code. 60 61 When a message is given to the logger, the log level of the message is 62 compare to the log level of the logger. If the log level of the 63 message meets or exceeds the log level of the logger itself, the 64 message will undergo further processing. If it doesn't, the message 65 will be ignored. 66 67 Once a logger has determined that a message needs to be processed, 68 it is passed to a *Handler*. 69 70 .. _topic-logging-parts-handlers: 71 72 Handlers 73 ~~~~~~~~ 74 75 The handler is the engine that determines what happens to each message 76 in a logger. It describes a particular logging behavior, such as 77 writing a message to the screen, to a file, or to a network socket. 78 79 Like loggers, handlers also have a log level. If the log level of a 80 log record doesn't meet or exceed the level of the handler, the 81 handler will ignore the message. 82 83 A logger can have multiple handlers, and each handler can have a 84 different log level. In this way, it is possible to provide different 85 forms of notification depending on the importance of a message. For 86 example, you could install one handler that forwards ``ERROR`` and 87 ``CRITICIAL`` messages to a paging service, while a second handler 88 logs all messages (including ``ERROR`` and ``CRITICAL`` messages) to a 89 file for later analysis. 90 91 .. _topic-logging-parts-filters: 92 93 Filters 94 ~~~~~~~ 95 96 A filter is used to provide additional control over which log records 97 are passed from logger to handler. 98 99 By default, any log message that meets log level requirements will be 100 handled. However, by installing a filter, you can place additional 101 criteria on the logging process. For example, you could install a 102 filter that only allows ``ERROR`` messages from a particular source to 103 be emitted. 104 105 Filters can also be used to modify the logging record prior to being 106 emitted. For example, you could write a filter that downgrades 107 ``ERROR`` log records to ``WARNING`` records if a particular set of 108 criteria are met. 109 110 Filters can be installed on loggers or on handlers; multiple filters 111 can be used in a chain to perform multiple filtering actions. 112 113 .. _topic-logging-parts-formatters: 114 115 Formatters 116 ~~~~~~~~~~ 117 118 Ultimately, a log record needs to be rendered as text. Formatters 119 describe the exact format of that text. A formatter usually consists 120 of a Python formatting string; however, you can also write custom 121 formatters to implement specific formatting behavior. 122 123 Using logging 124 ============= 125 126 Once you have configured your loggers, handlers, filters and 127 formatters, you need to place logging calls into your code. Using the 128 logging framework is very simple. Here's an example:: 129 130 # import the logging library 131 import logging 132 133 # Get an instance of a logger 134 logger = logging.getLogger(__name__) 135 136 def my_view(request, arg1, arg): 137 ... 138 if bad_mojo: 139 # Log an error message 140 logger.error('Something went wrong!') 141 142 And that's it! Every time the ``bad_mojo`` condition is activated, an 143 error log record will be written. 144 145 Naming loggers 146 ~~~~~~~~~~~~~~ 147 148 The call to :meth:`logging.getLogger()` obtains (creating, if 149 necessary) an instance of a logger. The logger instance is identified 150 by a name. This name is used to identify the logger for configuration 151 purposes. 152 153 By convention, the logger name is usually ``__name__``, the name of 154 the python module that contains the logger. This allows you to filter 155 and handle logging calls on a per-module basis. However, if you have 156 some other way of organizing your logging messages, you can provide 157 any dot-separated name to identify your logger:: 158 159 # Get an instance of a specfic named logger 160 logger = logging.getLogger('project.interesting.stuff') 161 162 The dotted paths of logger names define a hierarchy. The 163 ``project.interesting`` logger is considered to be a parent of the 164 ``project.interesting.stuff`` logger; the ``project`` logger 165 is a parent of the ``project.interesting`` logger. 166 167 Why is the hierarchy important? Well, because loggers can be set to 168 *propagate* their logging calls to their parents. In this way, you can 169 define a single set of handlers at the root of a logger tree, and 170 capture all logging calls in the subtree of loggers. A logging handler 171 defined in the ``project`` namespace will catch all logging messages 172 issued on the ``project.interesting`` and 173 ``project.interesting.stuff`` loggers. 174 175 This propagation can be controlled on a per-logger basis. If 176 you don't want a particular logger to propagate to it's parents, you 177 can turn off this behavior. 178 179 Making logging calls 180 ~~~~~~~~~~~~~~~~~~~~ 181 182 The logger instance contains an entry method for each of the default 183 log levels: 184 185 * ``logger.critical()`` 186 * ``logger.error()`` 187 * ``logger.warning()`` 188 * ``logger.info()`` 189 * ``logger.debug()`` 190 191 There are two other logging calls available: 192 193 * ``logger.log()``: manually a logging message with a specific 194 log level. 195 196 * ``logger.exception()``: create a ``ERRORR`` level logging 197 message wrapping the current exception stack frame. 198 199 Configuring logging 200 =================== 201 202 Of course, it isn't enough to just put logging calls into your code. 203 You also need to configure the loggers, handlers, filters and 204 formatters to ensure that logging output is output in a useful way. 205 206 Python's logging library provides several techniques to configure 207 logging, ranging from a programatic interface to configuration files. 208 By default, Django uses the `dictConfig format`_. 209 210 .. note:: 211 ``logging.dictConfig`` is a builtin library in Python 2.7. In 212 order to make this library available for users of earlier Python 213 versions, Django includes a copy as part of ``django.utils.log``. 214 If you have Python 2.7, the system native library will be used; if 215 you have Python 2.6 or earlier, Django's copy will be used. 216 217 In order to configure logging, you use :setting:`LOGGING` to define a 218 dictionary of logging settings. These settings describes the loggers, 219 handlers, filters and formatters that you want in your logging setup, 220 and the log levels and other properties that you want those components 221 to have. 222 223 Logging is configured immediately after settings have been loaded. 224 Since the loading of settings is one of the first things that Django 225 does, you can be certain that loggers are always ready for use in your 226 project code. 227 228 .. _dictConfig format: http://docs.python.org/library/logging.html#configuration-dictionary-schema 229 230 .. _a third-party library: http://bitbucket.org/vinay.sajip/dictconfig 231 232 An example 233 ---------- 234 235 The full documentation for `dictConfig format`_ is the best source of 236 information about logging configuration dictionaries. However, to give 237 you a taste of what is possible, here is an example of a fairly 238 complex logging setup, configured using :meth:`logging.dictConfig`:: 239 240 LOGGING = { 241 'version': 1, 242 'disable_existing_loggers': True, 243 'formatters': { 244 'explicit': { 245 'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s' 246 }, 247 'simple': { 248 'format': '%(levelname)s %(message)s' 249 }, 250 }, 251 'filters': { 252 'special': { 253 '()': 'project.logging.SpecialFilter', 254 'foo': 'bar', 255 } 256 }, 257 'handlers': { 258 'null': { 259 'level':'DEBUG', 260 'class':'django.utils.log.NullHandler', 261 }, 262 'console':{ 263 'level':'DEBUG', 264 'class':'logging.StreamHandler', 265 'formatter': 'simple' 266 }, 267 'mail_admins': { 268 'level': 'ERROR', 269 'class': 'django.utils.log.AdminEmailHandler' 270 'filters': ['special'] 271 } 272 }, 273 'loggers': { 274 'django': { 275 'handlers':['null'], 276 'propagate': True, 277 'level':'INFO', 278 }, 279 'django.request': { 280 'handlers': ['mail_admins'], 281 'level': 'ERROR', 282 'propagate': False, 283 }, 284 'myproject.custom': { 285 'handlers: ['console', 'mail_admins'], 286 'level': 'INFO', 287 'filters': ['special'] 288 } 289 } 290 } 291 292 This logging configuration does the following things: 293 294 * Identifies the configuration as being in 'dictConfig version 1' 295 format. At present, this is the only dictConfig format version. 296 297 * Disables all existing logging configurations. 298 299 * Defines two formatters: 300 301 * ``simple``, that just outputs the log level name (e.g., 302 ``DEBUG``) and the log message. 303 304 The `format` string is a normal Python formatting string 305 describing the details that are to be output on each logging 306 line. The full list of detail that can be output can be 307 found in the `formatter documentation`_. 308 309 * ``verbose``, that outputs the log level name, the log 310 message, plus the time, process, thread and module that 311 generate the log message. 312 313 314 * Defines one filter -- :class:`project.logging.SpecialFilter`, 315 using the alias ``special``. If this filter required additional 316 arguments at time of construction, they can be provided as 317 additional keys in the filter configuration dictionary. In this 318 case, the argument ``foo`` will be given a value of ``bar`` when 319 instantiating the :class:`SpecialFilter`. 320 321 * Defines three handlers: 322 323 * ``null``, a NullHandler, which will pass any `DEBUG` or 324 higher message to ``/dev/null``. 325 326 * ``console``, a StreamHandler, which will print any `DEBUG` 327 message to stdout. This handler uses the `simple` output 328 format. 329 330 * ``mail_admins``, an AdminEmailHandler, which will email any 331 `ERROR` level message to the site admins. This handler uses 332 the ``special`` filter. 333 334 * Configures three loggers: 335 336 * ``django``, which passes all messages at ``INFO`` or higher 337 to the ``null`` handler. 338 339 * ``django.request``, which passes all ``ERROR`` messages to 340 the ``mail_admins`` handler. In addition, this logger is 341 marked to *not* propagate messages. This means that log 342 messages written to ``django.request`` will not be handled 343 by the ``django`` logger. 344 345 * ``myproject.custom``, which passes all messages at ``INFO`` 346 or higher that also pass the ``special`` filter to two 347 handlers -- the ``console``, and ``mail_admins``. This 348 means that all ``INFO`` level messages (or higher) will be 349 printed to the console; ``ERROR`` and ``CRITICIAL`` 350 messages will also be output via e-mail. 351 352 .. _formatter documentation: http://docs.python.org/library/logging.html#formatter-objects 353 354 Custom logging configuration 355 ---------------------------- 356 357 If you don't want to use Python's dictConfig format to configure your 358 logger, you can specify your own configuration scheme. 359 360 The :setting:`LOGGING_CONFIG` setting defines the callable that will 361 be used to configure Django's loggers. By default, it points at 362 Python's :meth:`logging.dictConfig()` method. However, if you want to 363 use a different configuration process, you can use any other callable 364 that takes a single argument. The contents of :setting:`LOGGING` will 365 be provided as the value of that argument when logging is configured. 366 367 Disabling logging configuration 368 ------------------------------- 369 370 If you don't want to configure logging at all (or you want to manually 371 configure logging using your own approach), you can set 372 :setting:`LOGGING_CONFIG` to ``None``. This will disable the 373 configuration process. 374 375 .. note:: 376 Setting :setting:`LOGGING_CONFIG` to ``None`` only means that the 377 configuration process is disabled, not logging itself. If you 378 disable the configuration process, Django will still make logging 379 calls, falling back to whatever default logging behavior is 380 defined. 381 382 Django's logging extensions 383 =========================== 384 385 Django provides a number of utilities to handle the unique 386 requirements of logging in webserver environment. 387 388 Loggers 389 ------- 390 391 Django provides three built-in loggers. 392 393 ``django`` 394 ~~~~~~~~~~ 395 396 ``django`` is the catch-all logger. No messages are posted directly to 397 this logger. 398 399 ``django.requests`` 400 ~~~~~~~~~~~~~~~~~~~ 401 402 Log messages related to the handling of requests. 5XX responses are 403 raised as ``ERROR`` messages; 4XX responses are raised as ``WARNING`` 404 messages. 405 406 Messages to this logger have the following extra context: 407 408 * ``status_code``: The HTTP response code associated with the 409 request. 410 411 * ``request``: The request object that generated the logging 412 message. 413 414 ``django.db.backends`` 415 ~~~~~~~~~~~~~~~~~~~~~~ 416 417 Messages relating to the interaction of code with the database. 418 For example, every SQL statement executed by a request is logged 419 at the ``DEBUG`` level to this logger. 420 421 Messages to this logger have the following extra context: 422 423 * ``duration``: The time taken to execute the SQL statement. 424 * ``sql``: The SQL statement that was executed. 425 * ``params``: The parameters that were used in the SQL call. 426 427 Handlers 428 -------- 429 430 Django provides one log handler in addition to those provided by the 431 Python logging module. 432 433 .. class:: AdminEmailHandler() 434 435 This handler sends an email to the site admins for each log 436 message it receives. 437 438 If the log record contains a 'request' attribute, the full details 439 of the request will be included in the email. 440 441 If the log record contains stack trace information, that stack 442 trace will be included in the email.