Advanced guide.

In this guide, we will cover more advanced uses of ObjLog, such as custom LogMessage types, Logging Python Exceptions, and catching logged errors.

Custom LogMessage types.

You can create custom LogMessage types by subclassing the LogMessage class.

they have two attributes that must be defined for them to work properly:

  • level: The level of the message. This is a string, and can be any value you want.

  • color: The color of the message. This is prefixed before the message, and is supposed to be an ansi color code.

Here is an example of a custom LogMessage type:

from objlog import LogMessage

class CustomLogMessage(LogMessage):
    level = "custom"
    color = "\033[35m"

it’s exactly the same as the built-in LogMessage types, but with a different level and color.

# extends the code from above.

from objlog import LogNode

log = LogNode("my logger")

log.log(CustomLogMessage("Hello, world!"))

Interacting with the LogNode

You can interact with the LogNode in a few ways.

Getting logged messages

You can get the messages that have been logged to the LogNode by using the get method.

from objlog import LogNode
from objlog.LogMessages import Info

log = LogNode("my logger")

log.log(Info("Hello, world!"))

print(log.get()) # prints: [Info("Hello, world!")]

you can also filter what types of messages you want to get by passing the specified types to the get method.

log.log(Info("Hello, world!"))
log.log(Debug("Hello, world!"))
log.log(Warn("Hello, world!"))

print(log.get(Info, Debug)) # prints: [Info("Hello, world!"), Debug("Hello, world!")]

Clearing logged messages

You can clear the messages that have been logged to the LogNode by using the wipe_messages method.

log.log(Info("Hello, world!"))

prints(log.get()) # prints: [Info("Hello, world!")]

log.wipe_messages()

prints(log.get()) # prints: []

keep in mind this will not clear any log files that are being logged to, to do that you can either set the parameter wipe_logfiles to True when calling the wipe_messages method, or you can call the clear_log method if you do not want to wipe the memory.

log.log(Info("Hello, world!"))

prints(log.get()) # prints: [Info("Hello, world!")]

log.wipe_messages(wipe_logfiles=True)

prints(log.get() # prints: []

# or

log.log(Info("Hello, world!"))

prints(log.get()) # prints: [Info("Hello, world!")]

log.clear_log()

prints(log.get()) # prints: [Info("Hello, world!")] as it did not wipe memory.

it also works with retrieving python exceptions of certain types (more on that later).

log.log(ImportError("Hello, world!"))

print(log.get(ImportError)) # prints: [PythonExceptionMessage("Hello, world!")]

checking for types of messages

You can check if a certain type of message has been logged to the LogNode by using the has method.

log.log(Info("Hello, world!"))

print(log.has(Info)) # prints: True

print(log.has(Debug)) # prints: False

if you want to find if you have a specific kind of python exception, you can just pass the exception type to the has method.

log.log(ImportError("Hello, world!"))

print(log.has(ImportError)) # prints: True

print(log.has(ValueError)) # prints: False

it even works with both combined.

log.log(Info("Hello, world!"))
log.log(ImportError("Hello, world!"))

print(log.has(Info, ImportError)) # prints: True

filtering messages (in place)

You can filter the messages that have been logged to the LogNode by using the filter method.

log.log(Info("Hello, world!"))
log.log(Debug("Hello, world!"))
log.log(Warn("Hello, world!"))

log.filter([Info, Debug])

print(log.get()) # prints: [Info("Hello, world!"), Debug("Hello, world!")]

optionally, you can filter logfiles as well by setting the filter_logfiles parameter to True.

log.log(Info("Hello, world!"))
log.log(Debug("Hello, world!"))
log.log(Warn("Hello, world!"))

log.filter([Info, Debug], filter_logfiles=True)

print(log.get()) # prints: [Info("Hello, world!"), Debug("Hello, world!")]

Logging Python Exceptions

You can log Python exceptions by using the log method with an exception instead of a LogMessage.

however, when getting the exception from the LogNode, it will be wrapped in a PythonExceptionMessage object. which is a subclass of LogMessage.

to get the original exception, you can use the .exception attribute of the PythonExceptionMessage object.

log.log(ImportError("Hello, world!"))

log.get() # returns: [PythonExceptionMessage("Hello, world!")]

log.get()[0].exception # returns: ImportError("Hello, world!")

Catching Real python exceptions

logging python exceptions is great, but what if you want to catch them when they happen?

you can do it in two ways, try/except, or by using the @monitor decorator.

from objlog import LogNode,
from objlog.utils import monitor

log = LogNode("my logger")

try:
    1 / 0
except ImportError as e:
    log.log(e) # logs the exception

@monitor(log)
def my_function():
    1 / 0

my_function() # logs the exception to LogNode 'log' when it occurs

@monitor decorator

The @monitor decorator is a decorator in the utils subpackage that logs any exceptions that occur in the function it is decorating.

it has a few parameters:

  • log: The LogNode to log the exceptions to. This is required.

  • raise_exceptions: Whether to raise the exception after logging it. This is optional, and defaults to False.

  • exit_on_exception: Whether to exit the program after logging the exception. This is optional, and defaults to False. It also completely ignores the raise_exceptions parameter, regardless of its value.

exit_on_exception

exit on exception is useful for when you want to log an exception and then exit the program in user-facing code.

however, it is not recommended to use it in library code, as it makes debugging harder.


@monitor(log, exit_on_exception=True)
def my_function():
    1 / 0

my_function() # logs the exception to LogNode 'log' when it occurs, and then exits the program.

exit on exception acts differently depending on where the lognode outputs to.

if the lognode outputs to a file and doesn’t print, it will log the exception and location to where the exception occurred, and then exit the program printing a message along the lines of “An exception occurred: (exception message) please check the log file for more information.”

however, if the lognode outputs to the console, it will not print any extra info, and you will see the exception message printed to the console (assuming it’s in the print list).

if the LogNode does not output to a file, it will print the whole traceback to the console.

raise_exceptions

raise exceptions is useful for when you want to log an exception and then raise it.


@monitor(log, raise_exceptions=True)
def my_function():
    1 / 0

my_function() # logs the exception to LogNode 'log' when it occurs, and then raises a ZeroDivisionError.

raise exceptions does not act differently depending on where the lognode outputs to.

it will always raise the exception after logging it.

it won’t do anything extra, it will just raise the exception.

Conclusion

That’s it for the advanced guide. You should now have a good understanding of how to use ObjLog in more advanced ways.

for the complete API reference, see the API reference.