-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 089b53e
Showing
6 changed files
with
203 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() - текущего обрабатываемого исключения. | ||
''' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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', | ||
], | ||
) |