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 information, invalid consumer inputs, community timeouts, and even {hardware} failures. Because of this dealing with errors isn’t only a nice-to-have; it’s a essential a part of constructing strong and dependable functions for manufacturing.
Think about an e-commerce web site. A buyer locations an order, however throughout the checkout course of, a database connection problem happens. With out correct Error Dealing with, this problem might trigger the appliance to crash, leaving the shopper annoyed and the transaction incomplete. Worse, it would create inconsistent information, resulting in even larger issues down the road. Thus, error dealing with is a basic talent for any Python developer who needs to write down code for manufacturing.
Nonetheless, good error dealing with additionally goes hand in hand with an excellent logging system. It’s uncommon to have entry to the console when the code is working in manufacturing. So there’s no probability of your print being seen by anybody. To make sure that you may monitor your software and examine any incidents, you want to arrange a logging system. That is the place the loguru bundle comes into play, which I’ll introduce on this article.
I – The best way to deal with Python errors?
On this half I current the perfect practices of error dealing with in Python, from try-except blocks and using elevate
to the lastly
assertion. These ideas will make it easier to write cleaner, extra maintainable code that’s appropriate for a manufacturing surroundings.
The try-expect blocks
The try-except block is the principle software for dealing with errors in Python. It lets you catch potential errors throughout code execution and forestall this system from crashing.
def divide(a, b):
attempt:
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 attempt 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 such a error is caught. For instance, if b is a string, an error happens. To keep away from this, you may add a TypeError
. So, you will need to check all attainable errors.
The perform turns into:
def divide(a, b):
attempt:
return a / b
besides ZeroDivisionError:
print(f"Solely Chuck Norris can divide by 0!")
besides TypeError:
print("Don't examine apples and orange!")
Increase 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 selected restriction in your code.
def divide(a, b):
if b == 0:
elevate ValueError("Solely Chuck Norris can divide by 0!")
return a / b
attempt:
outcome = 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 method, you may explicitly management the error circumstances. Within the print perform, the message shall be “Error: Solely Chuck Norris can divide by 0!“.
A number of the commonest exceptions
ValueError: The kind of a price is right however its worth is invalid.
attempt:
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"}
attempt:
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.
objects = [1, 2, 3]
attempt:
print(objects[3])
besides IndexError:
print("You overlook that indexing begins at 0, do not you?")
TypeError: Performing an operation on incompatible varieties.
attempt:
outcome = "textual content" + 5
besides TypeError:
print("Don't examine apples and orange!")
FileNotFoundError: Making an attempt to open a non-existing file.
attempt:
with open("notexisting_file.txt", "r") as file:
content material = file.learn()
besides FileNotFoundError:
print("Are you positive of your path?")
Customized Error: You possibly can set off predefined exceptions or additionally outline your personal exception courses:
class CustomError(Exception):
go
attempt:
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, similar to closing a connection to a database or releasing sources.
import sqlite3
attempt:
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. Choose specifying the exception:
# Unhealthy apply
attempt:
outcome = 10 / 0
besides Exception as e:
print(f"Error: {e}")
# Good apply
attempt:
outcome = 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: In case you catch an exception, guarantee it’s logged or re-raised so it doesn’t go unnoticed.
import logging
logging.basicConfig(degree=logging.ERROR)
attempt:
outcome = 10 / 0
besides ZeroDivisionError:
logging.error("Division by zero detected.")
- Use
else
andlastly
blocks: Theelse
block runs provided that no exception is raised within theattempt
block.
attempt:
outcome = 10 / 2
besides ZeroDivisionError:
logging.error("Division by zero detected.")
else:
logging.data(f"Success: {outcome}")
lastly:
logging.data("Finish of processing.")
II – The best way to 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. Subsequently, good error dealing with should be accompanied by an excellent logging system.
What are logs?
Logs are data 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 choose the loguru bundle, which is far easier to make use of and simpler to configure. In actual fact, full output formatting is already preconfigured.
from loguru import logger
logger.debug("A fairly debug message!")

All of the vital parts 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 <module> resulting from the truth that the log shouldn’t be positioned in a category or perform.
- The message.
The completely 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 file detailed info on this system’s execution path for diagnostic functions.
- DEBUG (10): utilized by builders to file messages for debugging functions.
- INFO (20): used to file 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 file error circumstances which have affected a selected operation.
- CRITICAL (50): used to file error circumstances that forestall a major perform from working.
The bundle naturally handles completely different formatting relying on the extent used
from loguru import logger
logger.hint("A hint message.")
logger.debug("A debug message.")
logger.data("An info message.")
logger.success("Successful message.")
logger.warning("A warning message.")
logger.error("An error message.")
logger.essential("A essential 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 attainable to outline new log ranges with the extent technique and is used with the log technique
logger.degree("FATAL", no=60, colour="<crimson>", 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).
- colour : colour markup.
- icon : the extent icon.
The logger configuration
It’s attainable 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 when you have a log collector). - degree: Units the minimal logging degree for the recorder.
- format: is beneficial 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 must be recorded or not.
- colorize: takes a boolean worth and determines whether or not the terminal coloring must 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 with a view to facilitate troubleshooting.
- diagnose: Determines whether or not variable values must be displayed within the exception hint. This feature 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 data are positioned in a queue to keep away from conflicts if a number of processes connect with the identical goal.
- catch: If an surprising error happens when connecting to the server specified sink, you may detect it by setting this selection to
True
. The error shall 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)
Be aware:
Colours disappear in a file. It’s because there are particular characters (known as ansi codes) that show colours within the terminal, however this formatting doesn’t exist within the information.
Add context to logs
For complicated functions, it may be helpful so as to add additional info to the logs to allow sorting and facilitate troubleshooting.
For instance, if a consumer modifications a database, it may be helpful to have the consumer ID along with the change info.
Earlier than you begin recording context information, you want to guarantee that the {additional}
directive is included in your customized format. This variable is a Python dictionary that incorporates 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 = (
"<inexperienced>{time:YYYY-MM-DD HH:mm:ss.SSS}</inexperienced> | "
"<degree>{degree: <8}</degree> | "
"<cyan>{identify}</cyan>:<cyan>{perform}</cyan>:<cyan>{line}</cyan> | "
"Consumer ID: {additional[user_id]} - <degree>{message}</degree>"
)
logger.configure(additional={"user_id": ""}) # Default worth
logger.take away()
logger.add(sys.stderr, format=logger_format)
It’s now attainable to make use of the bind technique to create a toddler logger inheriting all the info from the mum or dad logger.
childLogger = logger.bind(user_id="001")
childLogger.data("Right here a message from the kid logger")
logger.data("Right here a message from the mum or dad logger")

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

As a substitute of the with block, you should use a decorator. The previous code then turns into
@logger.contextualize(user_id="001")
def child_logger():
logger.data("Right here a message from the logger with user_id 001")
child_logger()
logger.data("Right here a message from the logger with out user_id")
The catch technique
Errors could 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 instances, 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 completely different circumstances below which a file could be modified:
- rotation: specifies a situation below which the present log file is closed and a brand new file is created. This situation could be an int, a datetime or a str. Str is advisable as it’s simpler to learn.
- retention: specifies how lengthy every log file must 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 information.
Be aware:
Normally, within the case of a manufacturing software, a log collector shall 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 may deal with errors predictably whereas sustaining readable and maintainable code.
Furthermore, an excellent logging system improves the flexibility to observe and debug your software. Loguru offers 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 functions.
References
1 – Error dealing with in Python: official Python documentation on exceptions
2 – The loguru documentation: https://loguru.readthedocs.io/en/steady/
3 – Information about loguru: https://betterstack.com/group/guides/logging/loguru/