Rohi
Rohi

Reputation: 814

Custom exception with functionality

I've encountered a pretty standard issue and was wondering if my solution is the right way to go.

Every time an exception occures I want it to be caught and logged by the caller, and then re-raised.

Since I dont want to repeat logging messages every time, I created a custom exception which saves the message data and also logs.

class LoggingException(Exception):
    def __init__(self, message, package_id):
        # Get caller informat
        caller = getframeinfo(stack()[2][0])
        self.filename = caller.filename
        self.function = caller.function

        # Set message info and log
        self.message = message
        if (LogManager.log_handler is None):
            print(message)
        else:
            LogManager.l(package_id, LogLevelEnum.ERROR, message)

Use case:

def main_func():
    try:
        secondary_func()
    except Exception as ex:
        raise LoggingException("Some log") from ex

def secondary_func():
    raise LoggingException("Exception info")

The problem is im not completley sure having an exception do any operations is a good idea, and this to generic for it to not have a standard python solution.

Note: I am not using the python logging module due to product constraints.

Upvotes: 2

Views: 132

Answers (1)

Mad Physicist
Mad Physicist

Reputation: 114219

Trying to get caller information like that is going to be unreliable. Also, there will be cases where it's not the immediate caller that you are interested in.

The idea of the exception logging itself seems sensible. To compromise, I would move the logging functionality into a separate method that you could trigger explicitly. Exceptions are, after all, mostly regular objects:

class LoggingException(Exception):
    def __init__(self, message, package_id):
        # Set message info
        super.__init__(message)
        self.package_id = package_id

    def log(self, manager=None):
        if manager.log_handler is None:
            print(super().__str__())
        else:
            manager.l(self.package_id, LogLevelEnum.ERROR, super()..__str__())

Now you can trigger the logging operation whenever you want, without having to reconstruct the message:

try:
    ...
except LoggingException as e:
    e.log(some_manager)
    raise

This gives you the option of really re-raising the error, as shown here, or chaining it as in your example. I highly recommend against chaining unless you have a really good reason to do it.

Upvotes: 2

Related Questions