1
$\begingroup$

I'm working on an addon and I'd like to be able to use Python's logging module to be able to log to an output file. While my initial purpose was to use it only for debugging the addon, I'd eventually like it to create a .log file in the same directory as the .blend file that's open so that users of the addon can use it in their error reports.

However, I can't figure out how to configure the logging module. I can import logging and call logging.warning() to print out a message to the system console, but if I make a call like logging.debug() in an operator, it doesn't print anything (it apparently doesn't take into account the logging.basicConfig(level=logging.DEBUG) that I set at the beginning of the file as soon as I import the logging module).

I would eventually also like to use logging.basicConfig() to set the log file output location, but when I do this, it doesn't write the log file to where I specify. Any ideas of how to tackle this?

Thanks!

$\endgroup$

1 Answer 1

4
+50
$\begingroup$

When you do logging.debug() or logging.basicConfig() you are using the root logger. But in Blender where your addon is just one addon of many, you shouldn't try to "take control" of the root logger like this. The logging modules provides a nested hierarchy of loggers, so you can use a logger unique to your addon.

This may be why basicConfig isn't working for you, it "does nothing if the root logger already has handlers configured".

The usual pattern for this is

import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logger.addHandler(logging.StreamHandler())

def my_fun():
    logger.debug("this is a debug message")

(See Configuring Logging for how to configure formatting, etc.)


If you want to write to a file in the .blend's directory, you can use a FileHandler. But because the directory could change, for example if the file is saved to a new location, we can attach the handler for the duration of a single operator execution only.

class MyOperator(Operator):
    bl_idname = "wm.my_op"
    bl_label = "Test Operator"

    def execute(self, context):
        log_path = bpy.path.abspath("//my-operator-log.txt")
        log_handler = logging.FileHandler(log_path)

        try:
            logger.addHandler(log_handler)
            do_my_operation(context)
            return {'FINISHED'}

        except e:
            logger.error(e)
            return {'CANCELLED'}

        finally:
            logger.removeHandler(log_handler)

If you want to further customize this, you can write a custom handler. For example, Godot reports warnings and errors through the GUI with op.report().


Btw grepping for logging in the addon directory is a good way to find out how other people are using it.

$\endgroup$
6
  • $\begingroup$ Hey, thanks for the thoughtful answer! I feel it's a little cumbersome having to configure the logger in each operator. Could I avoid that if I wanted to only write to the temp directory of the OS? I tried configuring that with Python's tempfile module, but I didn't have any luck getting it to write out a file. $\endgroup$ Jul 31, 2022 at 4:01
  • $\begingroup$ Yes, just do logger.addHandler(logger.FileHandler(path_to_tmpfile)) once when you configure the logger. You can also write a custom handler to send it basically wherever you want. $\endgroup$
    – scurest
    Jul 31, 2022 at 4:23
  • 1
    $\begingroup$ @AnsonSavage -- don't forget to mark this as "Answer Accepted" if you agree with it. This gives credit to the person who spent time answering, increases their reputation on the site, and drives more people with similar questions to read this answer. $\endgroup$
    – james_t
    Jul 31, 2022 at 16:17
  • $\begingroup$ @scurest Okay, neat! Is there any way to do that in a single place for the entire addon? Or does it have to be in each module? $\endgroup$ Aug 1, 2022 at 19:04
  • 1
    $\begingroup$ If you are putting logger = logging.getLogger(__name__) in each module, you should only have to configure the top-level logger in the __init__.py file because the child loggers will propagate to their parent. $\endgroup$
    – scurest
    Aug 2, 2022 at 2:23

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .