Logging in Django: How to set up and log errors step by step
Content Index
- What is logging in Django and why is it important?
- How the Django logging system works
- Implementing the logger
- Registering errors in Django with logger to a file
- Saving logs to a file in Django
- Best practices for logging in Django
- Advanced logging integration
- Frequent errors and how to solve them
- Frequently asked questions about logging in Django
- Conclusion
When you work with Django, one of the most important tasks is maintaining good error control. Logging in Django allows you to record events, warnings, and failures in a file for later consultation. This is key in production: without logs, finding an error would be like looking for a needle in a haystack.
In my experience, I've seen many developers stick with the default log that appears in the console, and when something fails on the client... there's no trace left. That's precisely where well-configured logging saves your life.
What is logging in Django and why is it important?
Logging is a system that records what happens inside your application. Django uses the standard Python logging module, which means you can leverage all of Python's power directly in your project.
Why is it so important?
- It allows you to quickly detect errors.
- It facilitates the traceability of critical processes (payments, authentications, email sending).
- It serves as an audit history in production projects.
How the Django logging system works
The logging system is based on four main elements:
- Loggers → are the ones that generate the messages. Example: logger = logging.getLogger(__name__).
- Handlers → decide where the log goes (file, console, email, etc.).
- Formatters → define the output format: date, level, message, etc.
- Logging levels → mark the importance of the event:
- DEBUG → debugging messages.
- INFO → general information.
- WARNING → warnings.
- ERROR → errors that interrupt processes.
- CRITICAL → serious failures.
Implementing the logger
This is very simple:
import logging
logger = logging.getLogger(__name__)
def example_token(self) -> str:
***
logger.error(f"Error en PayPal process order: {e}", exc_info=True)
return ""
That is, we import the logging module:
import logging
We create an instance (logger). (We pass it a name, which can be anything (e.g., PayPal)):
logger = logging.getLogger('paypal')
A convention is usually used for the logger to automatically take the application name, but it doesn't really matter what you put:
logger = logging.getLogger(__name__)
With that instance:
we can now register any internal error, simply by passing the corresponding error message:
logger.error(f"Error en PayPal process order: {e}", exc_info=True)
This can be done anywhere in the code.
For example, in a try/except when generating a token, or when processing a payment.
Registering errors in Django with logger to a file
The problem is that, by default, the log appears in the terminal, which is not what we want. The ideal is to save it in a separate file that we can consult later.
For this, additional configuration is required in Django:
settings.py
LOGGING = {
'version': 1, # version definida por nosotros
'disable_existing_loggers': False, # importante para que Django siga mandando logs
'formatters': {
'verbose': {
'format': '[{asctime}] {levelname} {name}: {message}', # formato de salida, ej 2025-09-07 12:25:10,456] ERROR store.views: Error en PayPal process order
'style': '{',
},
},
'handlers': {
'file': {
# 'level': 'DEBUG', # captura todo desde DEBUG hasta ERROR
'level': 'ERROR', # captura todo desde DEBUG hasta ERROR
'class': 'logging.FileHandler',
'filename': os.path.join(BASE_DIR, 'logs', 'django.log'), # archivo único
'formatter': 'verbose',
},
},
'root': { # <-- logger global
'handlers': ['file'],
'level': 'DEBUG',
},
}
Pay attention to this:
Django does not automatically create the /logs folder. If you don't create it, you will get an exception like FileNotFoundError.
This will cause a log to be recorded that we can periodically review, which is located at:
djangoshopping\logs\django.log
You must create the logs folder inside the project as Django does not create it automatically and would give an error such as:
Exception in thread django-main-thread:
Traceback (most recent call last):
File "***Lib\logging\config.py", line 604, in configure
handler = self.configure_handler(handlers[name])
File "***Lib\logging\config.py", line 869, in configure_handler
result = factory(**kwargs)
File "***Lib\logging\__init__.py", line 1218, in __init__
StreamHandler.__init__(self, self._open())
As additional considerations, with this, we define the data we are going to record:
- verbose, is a name we want to give to the format.
- asctime: date and time.
- levelname: log level (DEBUG, INFO, ERROR, etc.).
- name: the name of the logger (example: store.views).
- message: the text you passed in logger.error("...").
- style: '{' indicates that we use curly braces ({}) instead of % for formatting (more modern).
The Handlers is where the logs are sent (file, console, email, etc.):
- level: 'ERROR', Only saves logs of level ERROR or more serious (CRITICAL). (If you change it to DEBUG, it will save everything (including debug and info)).
- class: 'logging.FileHandler', Writes to a flat file.
- filename, the file where it is saved (logs/django.log).
- formatter: 'verbose', uses the format we defined before.
With the last part:
'root': { # <-- logger global
'handlers': ['file'],
'level': 'DEBUG',
},
}
- Root logger, the global Python logger.
- handlers: ['file'], any log generated in your project will pass through the file handler.
- level: 'DEBUG', means: I accept logs from DEBUG level up. NOTE: Although the root accepts DEBUG, the handler has an ERROR level. This means that all are processed, but only errors are written, this was implemented this way for demonstration, but the correct thing is that, for our example, you put:
'root': { # <-- logger global
'handlers': ['file'],
'level': 'ERROR',
},
}
Saving logs to a file in Django
The key difference between console and file is that the file is persistent.
- In the console, logs are lost when the server restarts.
- In a file, they remain saved for consultation at any time.
With the FileHandler, you indicate exactly where you want to save them. My recommendation is to always use a dedicated folder (/logs) within the project to keep it organized.
Best practices for logging in Django
Some lessons learned:
- Use different levels depending on the environment: in development you can use DEBUG, in production ERROR or WARNING is better.
- Define a clear format with asctime, levelname, name, and message.
- Don't overuse logging: too much information can be as useless as none.
- Ensure that sensitive logs (e.g., user data) are not saved in plain text.
Advanced logging integration
Beyond a .log file, you can send logs to external services:
- Sentry → ideal for real-time error alerts.
- ELK Stack (Elasticsearch + Logstash + Kibana) → perfect for large projects.
- Datadog → complete observability (logs, metrics, and traces).
- This is achieved by configuring other handlers such as SMTPHandler, SysLogHandler, or custom integrations.
Frequent errors and how to solve them
- "No such file or directory: logs/django.log" → Manually create the /logs folder.
- Incorrectly configured Root logger → If you put DEBUG in root but ERROR in the handler, all are processed but only errors are saved. Adjust both to the same level.
- Logs are not written to file → Verify write permissions on the logs folder.
Frequently asked questions about logging in Django
- Where are Django logs saved?
Where you define it in filename within the handler, for example: BASE_DIR/logs/django.log. - How to register custom errors?
By creating your own logger:- logger = logging.getLogger("mi_aplicacion")
logger.warning("Advertencia personalizada")
- logger = logging.getLogger("mi_aplicacion")
- How to change the logging level in production?
Modify the level value in root and in the handler:- 'level': 'ERROR'
Conclusion
Correctly configuring logging in Django is one of those tasks that seems secondary, but when a critical failure occurs, you're thankful you did it. In my case, I've been able to detect errors in payment processes more than once thanks to having a detailed log in a file.
My recommendation: start with a simple FileHandler, add a clear format, and when your project grows, make the leap to external services like Sentry or ELK. This way, you'll have total control over what happens in your application.
I agree to receive announcements of interest about this Blog.
We see the steps to implement a Logging system, to record a log in Django.