Django logging with dictionaries as context
This view in realestate\auth2\views.py validates form data and checks if the email or phone has already been taken. It also logs messages depending on situation and I pass dictionaries as logging context.
from globals import log_messages
from globals.logger import logger
# ...
def register_post(request: HttpRequest, template: str):
form = RegisterForm(request.POST)
# 2 Input validation & authorization
if not form.is_valid():
return render(request, template, {"form": form})
data = form.cleaned_data
# 6 Error handling
if service.email_taken(data["email"]):
messages.error(request, "Korisnik sa ovom imejl adresom već postoji.")
# 5 Security logging
logger.warning(log_messages.auth_view["register_email_taken"], extra={"email": data["email"]})
return redirect(reverse("register"))
# 6 Error handling
if service.phone_taken(data["phone"]):
messages.error(request, "Korisnik sa ovim brojem telefona već postoji.")
# 5 Security logging
logger.warning(log_messages.auth_view["register_phone_taken"], extra={"phone": data["phone"]})
return redirect(reverse("register"))
user = service.create(data) # method CRUD service that I made
login2(request, user) # from django.contrib.auth import login as login2
# 5 Security logging
logger.info(log_messages.auth_view["register_success"], extra={"user_pk": user.pk})
return redirect(reverse("listing-list"))
The global logger is instantiated in globals/logger.py:
import logging
logger = logging.getLogger(__name__)
The messages are present in globals/log_messages.py:
auth_view = {
"register_email_taken": "Registration failed. Email address is already taken.",
"register_phone_taken": "Registration failed. Phone number is already taken.",
"register_success": "User registered successfully.",
"login_success": "User logged in successfully.",
"login_missing_credentials": "Login failed. Credentials do not exist.",
"logout_success": "User logged out."
}
How logging is configured in realestate/settings.py:
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"standard": {
"format": "%(asctime)s [%(levelname)s] %(message)s"
}
},
"handlers": {
"file": {
"level": "DEBUG",
"class": "logging.FileHandler",
"filename": "realestate.log",
"formatter": "standard"
},
"console": {
"class": "logging.StreamHandler",
"formatter": "standard"
}
},
"loggers": {
"":
{
"handlers": ["file", "console"],
"level": "DEBUG"
}
}
}
When I tried printing log messages with context to console, the context was not output along messages.
2026-07-11 12:55:43,185 [WARNING] Registration failed. Email address is already taken.
I tried adding extra parameter to the logger format `"format": "%(asctime)s [%(levelname)s] %(message)s %(extra)s", but then the errors were printed.
--- Logging error ---
Traceback (most recent call last):
File "C:\_dev\Python314\Lib\logging\__init__.py", line 472, in format
return self._format(record)
~~~~~~~~~~~~^^^^^^^^
File "C:\_dev\Python314\Lib\logging\__init__.py", line 468, in _format
return self._fmt % values
~~~~~~~~~~^~~~~~~~
KeyError: 'extra'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\_dev\Python314\Lib\logging\__init__.py", line 1151, in emit
msg = self.format(record)
File "C:\_dev\Python314\Lib\logging\__init__.py", line 999, in format
return fmt.format(record)
~~~~~~~~~~^^^^^^^^
File "C:\_dev\Python314\Lib\logging\__init__.py", line 715, in format
s = self.formatMessage(record)
File "C:\_dev\Python314\Lib\logging\__init__.py", line 684, in formatMessage
return self._style.format(record)
~~~~~~~~~~~~~~~~~~^^^^^^^^
File "C:\_dev\Python314\Lib\logging\__init__.py", line 474, in format
raise ValueError('Formatting field not found in record: %s' % e)
ValueError: Formatting field not found in record: 'extra'
Call stack:
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\realestate\manage.py", line 22, in <module>
main()
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\realestate\manage.py", line 18, in main
execute_from_command_line(sys.argv)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\management\__init__.py", line 443, in execute_from_command_line
utility.execute()
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\management\__init__.py", line 437, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\management\commands\test.py", line 24, in run_from_argv
super().run_from_argv(argv)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\management\base.py", line 420, in run_from_argv
self.execute(*args, **cmd_options)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\management\base.py", line 464, in execute
output = self.handle(*args, **options)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\management\commands\test.py", line 63, in handle
failures = test_runner.run_tests(test_labels)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\test\runner.py", line 1134, in run_tests
result = self.run_suite(suite)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\test\runner.py", line 1061, in run_suite
return runner.run(suite)
File "C:\_dev\Python314\Lib\unittest\runner.py", line 257, in run
test(result)
File "C:\_dev\Python314\Lib\unittest\suite.py", line 84, in __call__
return self.run(*args, **kwds)
File "C:\_dev\Python314\Lib\unittest\suite.py", line 122, in run
test(result)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\test\testcases.py", line 313, in __call__
self._setup_and_call(result)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\test\testcases.py", line 368, in _setup_and_call
super().__call__(result)
File "C:\_dev\Python314\Lib\unittest\case.py", line 725, in __call__
return self.run(*args, **kwds)
File "C:\_dev\Python314\Lib\unittest\case.py", line 669, in run
self._callTestMethod(testMethod)
File "C:\_dev\Python314\Lib\unittest\case.py", line 615, in _callTestMethod
result = method()
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\realestate\auth2\tests.py", line 44, in test_register_email_taken
response = self.client.post(reverse("register"), data)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\test\client.py", line 1156, in post
response = super().post(
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\test\client.py", line 499, in post
return self.generic(
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\test\client.py", line 671, in generic
return self.request(**r)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\test\client.py", line 1085, in request
response = self.handler(environ)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\test\client.py", line 186, in __call__
response = self.get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\handlers\base.py", line 141, in get_response
response = self._middleware_chain(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
response = get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\utils\deprecation.py", line 309, in __call__
response = response or self.get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
response = get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\utils\deprecation.py", line 309, in __call__
response = response or self.get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
response = get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\utils\deprecation.py", line 309, in __call__
response = response or self.get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
response = get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\utils\deprecation.py", line 309, in __call__
response = response or self.get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
response = get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\utils\deprecation.py", line 309, in __call__
response = response or self.get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
response = get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\utils\deprecation.py", line 309, in __call__
response = response or self.get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
response = get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\utils\deprecation.py", line 309, in __call__
response = response or self.get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
response = get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\handlers\base.py", line 198, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\realestate\auth2\views.py", line 92, in register
return register_post(request, template) if request.method == "POST" else register_get(request, template)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django_ratelimit\decorators.py", line 27, in _wrapped
return fn(request, *args, **kw)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\realestate\auth2\views.py", line 38, in register_post
logger.warning(log_messages.auth_view["register_email_taken"], extra={"email": data["email"]})
Message: 'Registration failed. Email address is already taken.'
Arguments: ()
I also tried this format, but I don't think it's suitable for context that varies in size and content.
"format": "%(asctime)s [%(levelname)s] %(message)s %(email)s %(phone)s %(user_pk)s"
Errors again:
Traceback (most recent call last):
File "C:\_dev\Python314\Lib\logging\__init__.py", line 472, in format
return self._format(record)
~~~~~~~~~~~~^^^^^^^^
File "C:\_dev\Python314\Lib\logging\__init__.py", line 468, in _format
return self._fmt % values
~~~~~~~~~~^~~~~~~~
KeyError: 'phone'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\_dev\Python314\Lib\logging\__init__.py", line 1151, in emit
msg = self.format(record)
File "C:\_dev\Python314\Lib\logging\__init__.py", line 999, in format
return fmt.format(record)
~~~~~~~~~~^^^^^^^^
File "C:\_dev\Python314\Lib\logging\__init__.py", line 715, in format
s = self.formatMessage(record)
File "C:\_dev\Python314\Lib\logging\__init__.py", line 684, in formatMessage
return self._style.format(record)
~~~~~~~~~~~~~~~~~~^^^^^^^^
File "C:\_dev\Python314\Lib\logging\__init__.py", line 474, in format
raise ValueError('Formatting field not found in record: %s' % e)
ValueError: Formatting field not found in record: 'phone'
Call stack:
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\realestate\manage.py", line 22, in <module>
main()
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\realestate\manage.py", line 18, in main
execute_from_command_line(sys.argv)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\management\__init__.py", line 443, in execute_from_command_line
utility.execute()
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\management\__init__.py", line 437, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\management\commands\test.py", line 24, in run_from_argv
super().run_from_argv(argv)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\management\base.py", line 420, in run_from_argv
self.execute(*args, **cmd_options)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\management\base.py", line 464, in execute
output = self.handle(*args, **options)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\management\commands\test.py", line 63, in handle
failures = test_runner.run_tests(test_labels)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\test\runner.py", line 1134, in run_tests
result = self.run_suite(suite)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\test\runner.py", line 1061, in run_suite
return runner.run(suite)
File "C:\_dev\Python314\Lib\unittest\runner.py", line 257, in run
test(result)
File "C:\_dev\Python314\Lib\unittest\suite.py", line 84, in __call__
return self.run(*args, **kwds)
File "C:\_dev\Python314\Lib\unittest\suite.py", line 122, in run
test(result)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\test\testcases.py", line 313, in __call__
self._setup_and_call(result)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\test\testcases.py", line 368, in _setup_and_call
super().__call__(result)
File "C:\_dev\Python314\Lib\unittest\case.py", line 725, in __call__
return self.run(*args, **kwds)
File "C:\_dev\Python314\Lib\unittest\case.py", line 669, in run
self._callTestMethod(testMethod)
File "C:\_dev\Python314\Lib\unittest\case.py", line 615, in _callTestMethod
result = method()
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\realestate\auth2\tests.py", line 44, in test_register_email_taken
response = self.client.post(reverse("register"), data)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\test\client.py", line 1156, in post
response = super().post(
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\test\client.py", line 499, in post
return self.generic(
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\test\client.py", line 671, in generic
return self.request(**r)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\test\client.py", line 1085, in request
response = self.handler(environ)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\test\client.py", line 186, in __call__
response = self.get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\handlers\base.py", line 141, in get_response
response = self._middleware_chain(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
response = get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\utils\deprecation.py", line 309, in __call__
response = response or self.get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
response = get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\utils\deprecation.py", line 309, in __call__
response = response or self.get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
response = get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\utils\deprecation.py", line 309, in __call__
response = response or self.get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
response = get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\utils\deprecation.py", line 309, in __call__
response = response or self.get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
response = get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\utils\deprecation.py", line 309, in __call__
response = response or self.get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
response = get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\utils\deprecation.py", line 309, in __call__
response = response or self.get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
response = get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\utils\deprecation.py", line 309, in __call__
response = response or self.get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
response = get_response(request)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django\core\handlers\base.py", line 198, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\realestate\auth2\views.py", line 92, in register
return register_post(request, template) if request.method == "POST" else register_get(request, template)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\venv\Lib\site-packages\django_ratelimit\decorators.py", line 27, in _wrapped
return fn(request, *args, **kw)
File "D:\Documents\_Dokumenti\Projekti\_Master\Real Estate App\Django\secure\realestate\auth2\views.py", line 38, in register_post
logger.warning(log_messages.auth_view["register_email_taken"], extra={"email": data["email"]})
Message: 'Registration failed. Email address is already taken.'
Arguments: ()
I also did:
"format": "%(asctime)s [%(levelname)s] %(message)s %(extra)s"
logger.warning(log_messages.auth_view["register_email_taken"], extra={"extra": {"email": data["email"]}})
which also printed the context.
2026-07-11 13:27:48,145 [WARNING] Registration failed. Email address is already taken. {'email': 'test@example.com'}
But I'd rather not have a dictionary be nested in another.