Introduction
Writing code is about fixing issues, however not each drawback is predictable. In the true world, your software program will encounter surprising conditions: lacking recordsdata, invalid person inputs, community timeouts, and even {hardware} failures. For this reason dealing with errors isn’t only a nice-to-have; it’s a crucial a part of constructing strong and dependable purposes for manufacturing.
Think about an e-commerce web site. A buyer locations an order, however through the checkout course of, a database connection difficulty happens. With out correct Error Dealing with, this difficulty may trigger the appliance to crash, leaving the client annoyed and the transaction incomplete. Worse, it’d create inconsistent information, resulting in even greater issues down the road. Thus, error dealing with is a basic ability for any Python developer who needs to put in writing code for manufacturing.
Nonetheless, good error dealing with additionally goes hand in hand with logging system. It’s uncommon to have entry to the console when the code is working in manufacturing. So there’s no likelihood of your print being seen by anybody. To make sure you could monitor your software and examine any incidents, you must arrange a logging system. That is the place the loguru bundle comes into play, which I’ll introduce on this article.
I – How you can deal with Python errors?
On this half I current the very best practices of error dealing with in Python, from try-except blocks and the usage of elevate
to the lastly
assertion. These ideas will assist you write cleaner, extra maintainable code that’s appropriate for a manufacturing surroundings.
The try-expect blocks
The try-except block is the principle instrument for dealing with errors in Python. It permits you to catch potential errors throughout code execution and stop this system from crashing.
def divide(a, b):
strive:
return a / b
besides ZeroDivisionError:
print(f"Solely Chuck Norris can divide by 0!")
On this trivial perform, the try-except block permits the error attributable to a division by 0 to be intercepted. The code within the strive block is executed, and if an error happens, the besides block checks whether or not it’s a ZeroDivisionError
and print a message. However solely one of these error is caught. For instance, if b is a string, an error happens. To keep away from this, you’ll be able to add a TypeError
. So, you will need to check all potential errors.
The perform turns into:
def divide(a, b):
strive:
return a / b
besides ZeroDivisionError:
print(f"Solely Chuck Norris can divide by 0!")
besides TypeError:
print("Don't examine apples and orange!")
Elevate an exception
You should utilize the elevate assertion to manually elevate an exception. That is helpful if you wish to report a user-defined error or impose a particular restriction in your code.
def divide(a, b):
if b == 0:
elevate ValueError("Solely Chuck Norris can divide by 0!")
return a / b
strive:
consequence = divide(10, 0)
besides ValueError as e:
print(f"Error: {e}")
besides TypeError:
print("Don't examine apples and orange!")
On this instance, a ValueError
exception is triggered if the divisor is zero. On this approach, you’ll be able to explicitly management the error circumstances. Within the print perform, the message can be “Error: Solely Chuck Norris can divide by 0!“.
A few of the commonest exceptions
ValueError: The kind of a worth is right however its worth is invalid.
strive:
quantity = math.sqrt(-10)
besides ValueError:
print("It is too complicated to be actual!")
KeyError: Making an attempt to entry a key that doesn’t exist in a dictionary.
information = {"identify": "Alice"}
strive:
age = information["age"]
besides KeyError:
print("By no means ask a girl her age!")
IndexError: Making an attempt to entry a non-existent index in an inventory.
gadgets = [1, 2, 3]
strive:
print(gadgets[3])
besides IndexError:
print("You overlook that indexing begins at 0, do not you?")
TypeError: Performing an operation on incompatible varieties.
strive:
consequence = "textual content" + 5
besides TypeError:
print("Don't examine apples and orange!")
FileNotFoundError: Making an attempt to open a non-existing file.
strive:
with open("notexisting_file.txt", "r") as file:
content material = file.learn()
besides FileNotFoundError:
print("Are you certain of your path?")
Customized Error: You’ll be able to set off predefined exceptions or additionally outline your individual exception courses:
class CustomError(Exception):
move
strive:
elevate CustomError("It is a customized error")
besides CustomError as e:
print(f"Catched error: {e}")
Clear with the lastly assertion
The lastly
block is executed in each case, no matter whether or not the error has occurred or not. It’s typically used to carry out cleanup actions, equivalent to closing a connection to a database or releasing sources.
import sqlite3
strive:
conn = sqlite3.join("users_db.db") # Hook up with a database
cursor = conn.cursor()
cursor.execute("SELECT * FROM customers") # Execute a question
outcomes = cursor.fetchall() # Get results of the question
print(outcomes)
besides sqlite3.DatabaseError as e:
print("Database error:", e)
lastly:
print("Closing the database connection.")
if 'conn' in locals():
conn.shut() # Ensures the connection is closed
Finest practices for error dealing with
- Catch particular exceptions: Keep away from utilizing a generic besides block with out specifying an exception, as it might masks surprising errors. Want specifying the exception:
# Dangerous apply
strive:
consequence = 10 / 0
besides Exception as e:
print(f"Error: {e}")
# Good apply
strive:
consequence = 10 / 0
besides ZeroDivisionError as e:
print(f"Error: {e}")
- Present specific messages: Add clear and descriptive messages when elevating or dealing with exceptions.
- Keep away from silent failures: When you catch an exception, guarantee it’s logged or re-raised so it doesn’t go unnoticed.
import logging
logging.basicConfig(degree=logging.ERROR)
strive:
consequence = 10 / 0
besides ZeroDivisionError:
logging.error("Division by zero detected.")
- Use
else
andlastly
blocks: Theelse
block runs provided that no exception is raised within thestrive
block.
strive:
consequence = 10 / 2
besides ZeroDivisionError:
logging.error("Division by zero detected.")
else:
logging.information(f"Success: {consequence}")
lastly:
logging.information("Finish of processing.")
II – How you can deal with Python logs?
Good error-handling is one factor, but when nobody is aware of that an error has occurred, the entire level is misplaced. As defined within the introduction, the monitor isn’t consulted and even seen when a program is working in manufacturing. Nobody will see print. Due to this fact, good error dealing with should be accompanied by logging system.
What are logs?
Logs are information of messages generated by a program to trace the occasions that happen throughout its execution. These messages might include details about errors, warnings, profitable actions, course of milestones or different related occasions. Logs are important for debugging, monitoring efficiency and monitoring the well being of an software. They permit builders to know what’s going on in a program with out having to interrupt its execution, making it simpler to resolve issues and repeatedly enhance the software program.
The loguru bundle
Python already has a local logging bundle: logging. However we want the loguru bundle, which is way easier to make use of and simpler to configure. Actually, full output formatting is already preconfigured.
from loguru import logger
logger.debug("A fairly debug message!")

All of the essential components are included straight within the message:
- Time stamp
- Log degree, indicating the seriousness of the message.
- File location, module and line quantity. On this instance, the file location is __main__ as a result of it was executed straight from the command line. The module is
resulting from the truth that the log will not be positioned in a category or perform. - The message.
The totally different logging ranges
There are a number of log ranges to take into consideration the significance of the message displayed (which is extra sophisticated in a print). Every degree has a reputation and an related quantity:
- TRACE (5): used to report detailed info on this system’s execution path for diagnostic functions.
- DEBUG (10): utilized by builders to report messages for debugging functions.
- INFO (20): used to report info messages describing regular program operation.
- SUCCESS (25): just like INFO, however used to point the success of an operation.
- WARNING (30): used to point an uncommon occasion which will require additional investigation.
- ERROR (40): used to report error circumstances which have affected a particular operation.
- CRITICAL (50): used to report error circumstances that stop a foremost perform from working.
The bundle naturally handles totally different formatting relying on the extent used
from loguru import logger
logger.hint("A hint message.")
logger.debug("A debug message.")
logger.information("An info message.")
logger.success("Successful message.")
logger.warning("A warning message.")
logger.error("An error message.")
logger.crucial("A crucial message.")

The hint message was not displayed as a result of the default minimal degree utilized by loguru is debug. It subsequently ignores all messages at decrease ranges.
It’s potential to outline new log ranges with the extent technique and is used with the log technique
logger.degree("FATAL", no=60, shade="", icon="!!!")
logger.log("FATAL", "A FATAL occasion has simply occurred.")
- identify : the identify of the log degree.
- no : the corresponding severity worth (should be an integer).
- shade : shade markup.
- icon : the extent icon.
The logger configuration
It’s potential to recreate a logger with a brand new configuration by deleting the outdated one with the take away
command and producing a brand new logger with a brand new configuration with the add
perform. This perform takes the next arguments:
- sink [mandatory]: specifies a goal for every information set created by the logger. By default, this worth is ready to
sys.stderr
(which corresponds to the usual error output). We are able to additionally retailer all output in a “.log” file (besides you probably have a log collector). - degree: Units the minimal logging degree for the recorder.
- format: is helpful to outline a customized format in your logs. To take care of the coloring of the logs within the terminal, this should be specified (see instance beneath).
- filter: is used to find out whether or not a log needs to be recorded or not.
- colorize: takes a boolean worth and determines whether or not the terminal coloring needs to be activated or not.
- serialize: causes the log to be displayed in JSON format whether it is set to
True
. - backtrace: determines whether or not the exception hint ought to transcend the purpose at which the error was recorded as a way to facilitate troubleshooting.
- diagnose: Determines whether or not variable values needs to be displayed within the exception hint. This selection should be set to
False
in manufacturing environments in order that no delicate info is leaked. - enqueue: If this selection is activated, the log information information are positioned in a queue to keep away from conflicts if a number of processes hook up with the identical goal.
- catch: If an surprising error happens when connecting to the server specified sink, you’ll be able to detect it by setting this selection to
True
. The error can be displayed in the usual error.
import sys
from loguru import logger
logger_format = (
"{time:YYYY-MM-DD HH:mm:ss.SSS} | "
"{degree: <8} | "
"{identify}:{perform}:{line}"
)
logger.take away()
logger.add(sys.stderr, format=logger_format)
Notice:
Colours disappear in a file. It is because there are particular characters (known as ansi codes) that show colours within the terminal, however this formatting doesn’t exist within the recordsdata.
Add context to logs
For complicated purposes, it may be helpful so as to add additional info to the logs to allow sorting and facilitate troubleshooting.
For instance, if a person adjustments a database, it may be helpful to have the person ID along with the change info.
Earlier than you begin recording context information, you must make it possible for the {further}
directive is included in your customized format. This variable is a Python dictionary that comprises context information for every log entry (if relevant).
Right here is an instance of a customization the place an additional user_id
is added. On this format, the colours.
import sys
from loguru import logger
logger_format = (
"{time:YYYY-MM-DD HH:mm:ss.SSS} | "
"{degree: <8} | "
"{identify} :{perform} :{line} | "
"Consumer ID: {further[user_id]} - {message} "
)
logger.configure(further={"user_id": ""}) # Default worth
logger.take away()
logger.add(sys.stderr, format=logger_format)
It’s now potential to make use of the bind technique to create a toddler logger inheriting all the information from the mother or father logger.
childLogger = logger.bind(user_id="001")
childLogger.information("Right here a message from the kid logger")
logger.information("Right here a message from the mother or father logger")

One other approach to do that is to make use of the contextualize technique in a with block.
with logger.contextualize(user_id="001"):
logger.information("Right here a message from the logger with user_id 001")
logger.information("Right here a message from the logger with out user_id")

As an alternative of the with block, you should utilize a decorator. The previous code then turns into
@logger.contextualize(user_id="001")
def child_logger():
logger.information("Right here a message from the logger with user_id 001")
child_logger()
logger.information("Right here a message from the logger with out user_id")
The catch technique
Errors might be routinely logged after they happen utilizing the catch technique.
def check(x):
50/x
with logger.catch():
check(0)

However it’s easier to make use of this technique as a decorator. This leads to the next code
@logger.catch()
def check(x):
50/x
check(0)
The log file
A manufacturing software is designed to run repeatedly and uninterrupted. In some circumstances, you will need to predict the conduct of the file, in any other case you’ll have to seek the advice of pages of logs within the occasion of an error.
Listed here are the totally different circumstances underneath which a file might be modified:
- rotation: specifies a situation underneath which the present log file is closed and a brand new file is created. This situation might be an int, a datetime or a str. Str is beneficial as it’s simpler to learn.
- retention: specifies how lengthy every log file needs to be saved earlier than it’s deleted from the file system.
- compression: The log file is transformed to the desired compression format if this selection is activated.
- delay: If this selection is ready to True, the creation of a brand new log file is delayed till the primary log message has been pushed.
- mode, buffering, encoding : Parameters which are handed to the Python perform open and decide how Python opens log recordsdata.
Notice:
Normally, within the case of a manufacturing software, a log collector can be set as much as retrieve the app’s outputs straight. It’s subsequently not essential to create a log file.
Conclusion
Error dealing with in Python is a vital step in writing skilled and dependable code. By combining try-except blocks, the elevate assertion, and the lastly block, you’ll be able to deal with errors predictably whereas sustaining readable and maintainable code.
Furthermore, logging system improves the power to observe and debug your software. Loguru gives a easy and versatile bundle for logging messages and may subsequently be simply built-in into your codebase.
In abstract, combining efficient error dealing with with a complete logging system can considerably enhance the reliability, maintainability, and debugging functionality of your Python purposes.
References
1 – Error dealing with in Python: official Python documentation on exceptions
2 – The loguru documentation: https://loguru.readthedocs.io/en/secure/
3 – Information about loguru: https://betterstack.com/group/guides/logging/loguru/