Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit

  • Loading branch information...
commit 089b53ec65418eea29cf0771a130f0ee587c8b36 0 parents
@isagalaev authored
31 README
@@ -0,0 +1,31 @@
+## WHAT'S THIS
+
+By default Django sends server errors by email which might be not convenient
+(you can't setup monitoring) and not always secure (a single bug in a hot place
+could overflow mail server). Django_errorlog enables logging of server errors
+with standard Python logging.
+
+## USAGE
+
+To install in a Django project:
+
+1. Include 'django_errorlog' into INSTALLED_APPS
+
+2. Setup handlers for log channels 'exception' and 'traceback' with usual
+ Python [logging handlers][1]. It's a good idea to have a separate file (or
+ whatever) for 'traceback' logger because its messages don't fit on a single
+ line and hence not easily grep'able.
+
+3. To disable default mailing of server errors you can leave ADMINS setting
+ empty or not setup SMTP_HOST.
+
+The application works automatically: it listents for a signal that Django sends
+on all uncaught server errors and then logs short exception values and full
+tracebacks into their respective log channels.
+
+There are two utility functions in django_errorlog.utils: log_error and
+log_warning. They can be used to manually log exception that you do handle in
+your code. They accept exc_info (a triple of (exceptions, value, traceback) as
+an argument. If called without arguments they get it from sys.exc_info().
+
+[1]: http://docs.python.org/library/logging.html#handlers
17 django_errorlog/__init__.py
@@ -0,0 +1,17 @@
+# -*- coding:utf-8 -*-
+'''
+Приложение вешается на сигнал, который Django посылает в случае фатальных
+ошибок и логирует значения и полные traceback'и исключений в логгеры
+'exception' и 'traceback' соответственно.
+
+Хендлеры для логгеров можно настраивать в проекте, а можно попросить
+django_errorlog создать дефолтные. Для этого надо импортировать к себе
+настройки из django_errorlog.settings и прописать свои имена файлов в
+EXCEPTION_LOG_FILE и TRACEBACK_LOG_FILE.
+
+
+Есть две вспомогательные функции в модуле utils: log_error и log_warning,
+которые нужны, если в общий лог ошибок надо слогировать exception вручную.
+Обе функции принимают либо exc_info, либо, если вызваны без параметров, берут
+его из sys.exc_info() - текущего обрабатываемого исключения.
+'''
110 django_errorlog/models.py
@@ -0,0 +1,110 @@
+# -*- coding:utf-8 -*-
+import sys
+import os
+import re
+import traceback
+import logging
+from logging import handlers
+
+from django.core import signals
+from django.utils.encoding import smart_str
+from django.conf import settings
+
+def _get_logger(name, setting_name):
+ '''
+ Returns a named logger.
+
+ Creates a default file handler for it if there's a setting for it
+ (deprecated).
+ '''
+ if name not in _get_logger.loggers:
+ logger = logging.getLogger(name)
+ if getattr(settings, setting_name, ''):
+ try:
+ handler = handlers.RotatingFileHandler(
+ getattr(settings, setting_name),
+ 'a',
+ settings.LOGGING_MAX_FILE_SIZE,
+ settings.LOGGING_MAX_FILES_COUNT
+ )
+ except:
+ handler = logging.StreamHandler(None)
+ handler.setFormatter(logging.Formatter(settings.LOGGING_FORMAT))
+ logger.addHandler(handler)
+ _get_logger.loggers[name] = logger
+
+ return _get_logger.loggers[name]
+_get_logger.loggers = {}
+
+def exception_str(value):
+ '''
+ Formats Exception object to a string. Unlike default str():
+
+ - can handle unicode strings in exception arguments
+ - tries to format arguments as str(), not as repr()
+ '''
+ try:
+ return ', '.join([smart_str(b) for b in value])
+ except (TypeError, AttributeError): # happens for non-iterable values
+ try:
+ return smart_str(value)
+ except UnicodeEncodeError:
+ try:
+ return repr(value)
+ except Exception:
+ return '<Unprintable value>'
+
+POST_TRUNCATE_SIZE = 1024
+
+def format_post(request):
+ '''
+ Casts request post data to string value. Depending on content type it's
+ either a dict or raw post data.
+ '''
+ if request.method == 'POST' and request.META['CONTENT_TYPE'] not in ['application/x-www-form-urlencoded', 'multipart/form-data']:
+ value = request.raw_post_data[:POST_TRUNCATE_SIZE]
+ if len(request.raw_post_data) > len(value):
+ value += '... (post data truncated at %s bytes)' % POST_TRUNCATE_SIZE
+ return value
+ else:
+ return str(request.POST)
+
+def _log_exc_info(exc_info=None, level=logging.ERROR, aditional_lines=None):
+ '''
+ Logs exception info into 'exception' and 'traceback' loggers calling
+ formatting as necessary.
+ '''
+ exception, value, tb = exc_info or sys.exc_info()
+ exception_logger = _get_logger('exception', 'EXCEPTION_LOG_FILE')
+ # find innermost call
+ inner = tb
+ while inner.tb_next:
+ inner = inner.tb_next
+ lineno = inner.tb_lineno
+ module_name = inner.tb_frame.f_globals['__name__']
+ exception_logger.log(level, '%-20s %s:%s %s' % (
+ exception.__name__,
+ module_name,
+ lineno,
+ exception_str(value),
+ ))
+
+ lines = traceback.format_exception(exception, value, tb)
+ if aditional_lines:
+ lines = aditional_lines + lines
+ traceback_logger = _get_logger('traceback', 'TRACEBACK_LOG_FILE')
+ traceback_logger.log(level, '\n'.join([smart_str(l) for l in lines]))
+
+def _log_request_error(sender, request, **kwargs):
+ '''
+ Handles unhandled request exceptions.
+ '''
+ lines = [
+ 'Path: %s' % request.path,
+ 'GET: %s' % request.GET,
+ 'POST: %s' % format_post(request),
+ ]
+ _log_exc_info(aditional_lines=lines)
+
+signals.got_request_exception.connect(_log_request_error)
+
17 django_errorlog/settings.py
@@ -0,0 +1,17 @@
+# -*- coding:utf-8 -*-
+
+## Settings for creating default handlers for loggers. This is deprecated
+## in favor of setting up handlers in a project manually.
+
+# Filename for logging one-line exception values
+EXCEPTION_LOG_FILE = ''
+
+# Filename for logging full tracebacks
+TRACEBACK_LOG_FILE = ''
+
+# Log format
+LOGGING_FORMAT = '%(asctime)s %(name)-15s %(levelname)s %(message)s'
+
+# Log file rotation settings
+LOGGING_MAX_FILE_SIZE = 1024*1024
+LOGGING_MAX_FILES_COUNT = 10
19 django_errorlog/utils.py
@@ -0,0 +1,19 @@
+# -*- coding:utf-8 -*-
+import logging
+
+from django_errorlog.models import _log_exc_info, exception_str
+
+def log_error(exc_info=None):
+ '''
+ Logs exc_info into 'exception' and 'traceback' logs with ERROR level.
+ If exc_info is None get it from sys.exc_info().
+ '''
+ _log_exc_info(exc_info, level=logging.ERROR)
+
+def log_warning(exc_info=None):
+ '''
+ Logs exc_info into 'exception' and 'traceback' logs with WARNING level.
+ If exc_info is None get it from sys.exc_info().
+ '''
+ _log_exc_info(exc_info, level=logging.WARNING)
+
9 setup.py
@@ -0,0 +1,9 @@
+from distutils.core import setup
+
+setup(
+ name='django_errorlog',
+ description='Django application for logging server (aka "500") errors',
+ packages=[
+ 'django_errorlog',
+ ],
+)
Please sign in to comment.
Something went wrong with that request. Please try again.