Wczoraj przyszło mi do głowy, że powiadomienia o błędach to jednocześnie jeden z bardziej przydatnych i najmniej widocznych elementów Django. To dzięki nim zdarza mi się poprawić błędy i wysłać do użytkownika informację, że jakaś strona działa już poprawnie nawet jeśli nie chciało mu się zgłaszać usterki.
Na przykład:
from django.shortcuts import render_to_response
def gather_user_data(user):
# in reality this would be more complex
return {'email': user.email}
def profile_view(request):
data = {'user': request.user}
data.update(gather_user_data(request.user))
return render_to_response('profile_view.html',
dictionary = data)
Widok profile_view zawiera błąd ujawniający się tylko w niektórych sytuacjach. Jeśli trafi na niego ktoś na serwerze produkcyjnym, zobaczy standardowe 500 Internal server error, a Django automatycznie wyśle do mnie taki raport:
Traceback (most recent call last):
File "/home/marcink/checkout/django-trunk/django/core/handlers/base.py",
line 86, in get_response
response = callback(request, *callback_args, **callback_kwargs)
File "/home/marcink/checkout/utils/blogowe/errtest/../errtest/errapp/views.py",
line 8, in profile_view
data.update(gather_user_data(request.user))
File "/home/marcink/checkout/utils/blogowe/errtest/../errtest/errapp/views.py",
line 4, in gather_user_data
return {'email': user.email}
AttributeError: 'AnonymousUser' object has no attribute 'email'
< wsgirequest GET:< QueryDict: {}>,
POST:< querydict : {}>,
COOKIES:{'django_log_selectedLevel': '0',
'django_log_showLocation': '1',
'sessionid': '915922a5710cf210e2f5fae47e0f8900'},
META:{'COLORTERM': 'gnome-terminal',
'CONTENT_LENGTH': '',
'CONTENT_TYPE': 'text/plain',
'DBUS_SESSION_BUS_ADDRESS': 'unix:abstract=/tmp/dbus-BibuFgosrz,guid=c50cabac45d33af5b6936d0049024c87',
'DESKTOP_SESSION': 'default',
'DISPLAY': ':0.0',
'DJANGO_DIR': '/home/marcink/checkout/django-trunk',
'DJANGO_SETTINGS_MODULE': 'errtest.settings',
'FCGI_PORT': '9014',
'GATEWAY_INTERFACE': 'CGI/1.1',
'GDMSESSION': 'default',
'GDM_LANG': 'en_US.UTF-8',
'GDM_XSERVER_LOCATION': 'local',
'GNOME_DESKTOP_SESSION_ID': 'Default',
'GNOME_KEYRING_PID': '7904',
'GNOME_KEYRING_SOCKET': '/tmp/keyring-glnuCS/socket',
'GTK_RC_FILES': '/etc/gtk/gtkrc:/home/marcink/.gtkrc-1.2-gnome2',
'HISTCONTROL': 'ignoreboth',
'HOME': '/home/marcink',
'HTTP_ACCEPT': 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5',
'HTTP_ACCEPT_CHARSET': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
'HTTP_ACCEPT_ENCODING': 'gzip,deflate',
'HTTP_ACCEPT_LANGUAGE': 'en-us,en;q=0.5',
'HTTP_CACHE_CONTROL': 'max-age=0',
'HTTP_CONNECTION': 'keep-alive',
'HTTP_COOKIE': 'sessionid=915922a5710cf210e2f5fae47e0f8900; django_log_showLocation=1; django_log_selectedLevel=0',
'HTTP_HOST': '127.0.0.1:5003',
'HTTP_KEEP_ALIVE': '300',
'HTTP_USER_AGENT': 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.17) Gecko/20080922 Ubuntu/7.10 (gutsy) Firefox/2.0.0.17',
'JAVA_HOME': '/home/marcink/devel/jre1.6.0/',
'LANG': 'en_US.UTF-8',
'LD_LIBRARY_PATH': '/home/marcink/checkout/ldev_comicspot/comicspot/../other',
'LESSCLOSE': '/usr/bin/lesspipe %s %s',
'LESSOPEN': '| /usr/bin/lesspipe %s',
'LOGNAME': 'marcink',
'LS_COLORS': 'no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.avi=01;35:*.fli=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.flac=01;35:*.mp3=01;35:*.mpc=01;35:*.ogg=01;35:*.wav=01;35:',
'OLDPWD': '/home/marcink',
'OTHER_DIR': '/home/marcink/checkout/ldev_comicspot/comicspot/../other',
'OUTER_DIR': '/home/marcink/checkout/ldev_comicspot/comicspot/..',
'PATH': '/home/marcink/bin:/home/marcink/local/bin:/home/marcink/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games',
'PATH_INFO': u'/profile/',
'PROJECT_BASE_DIR': '/home/marcink/checkout/ldev_comicspot/comicspot',
'PROJECT_DIR': '/home/marcink/checkout/ldev_comicspot/comicspot/comicspot',
'PROJECT_NAME': 'comicspot',
'PWD': '/home/marcink/checkout/utils/blogowe/errtest',
'PYTHONPATH': '/home/marcink/checkout/django-trunk',
'QUERY_STRING': '',
'REMOTE_ADDR': '127.0.0.1',
'REMOTE_HOST': '',
'REQUEST_METHOD': 'GET',
'RUN_MAIN': 'true',
'SCRIPT_NAME': u'',
'SERVER_NAME': 'raven.loc',
'SERVER_PORT': '5003',
'SERVER_PROTOCOL': 'HTTP/1.1',
'SERVER_SOFTWARE': 'WSGIServer/0.1 Python/2.5.1',
'SESSION_MANAGER': 'local/raven:/tmp/.ICE-unix/7907',
'SHELL': '/bin/bash',
'SHLVL': '1',
'SSH_AGENT_PID': '7947',
'SSH_AUTH_SOCK': '/tmp/ssh-lBRzQZ7907/agent.7907',
'TERM': 'xterm',
'TZ': 'America/Chicago',
'USER': 'marcink',
'USERNAME': 'marcink',
'WINDOWID': '39852701',
'WINDOWPATH': '7',
'XAUTHORITY': '/tmp/.gdmELMXJU',
'XDG_DATA_DIRS': '/usr/local/share/:/usr/share/:/usr/share/gdm/',
'XDG_SESSION_COOKIE': '90de2c2a7bf31a95a76c4900471ce700-1224887428.976645-1927173936',
'_': '/usr/bin/python',
'wsgi.errors': < open file '< stderr>', mode 'w' at 0xb7dc30b0>,
'wsgi.file_wrapper': < class 'django.core.servers.basehttp.FileWrapper'>,
'wsgi.input': < socket ._fileobject object at 0x854a72c>,
'wsgi.multiprocess': False,
'wsgi.multithread': True,
'wsgi.run_once': False,
'wsgi.url_scheme': 'http',
'wsgi.version': (1, 0)}>
Po pierwsze – dowiaduję się o problemie bez żadnej akcji ze strony użytkownika. Po drugie – dostaję mnóstwo informacji o błędzie: opis wyjątku (tutaj – brak atrybutu email w AnonymousUser), dokładnie wskazane miejsce wystąpienia razem z pełnym stosem wywołań funkcji, zawartość parametrów przekazanych przez GET i POST i zawartość zmiennych środowiskowych. W praktyce zwykle wystarcza to do złożenia testu pozwalającego na powtórzenie i usunięcie błędu.