pythonc++loggingpybind11spdlog

How to configure spdlog logger used in a pybind11 module


I'm using spdlog in various C++ libraries for which Python bindings are created with pybind11.

Now I'm wondering if there is a nice way how I can configure these loggers (mostly the level and format) from the Python side.

Currently I am creating a separate logger for each class in the constructor (logger_ is a class member):

MyClass::MyClass()
{
    logger_ = spdlog::get(LOGGER_NAME);
    if (!logger_)
    {
        logger_ = spdlog::stderr_color_mt(LOGGER_NAME);
    }
    logger_->debug("Logger is set up");
}

Now I can just use MyClass and it will create and use a logger with default settings. If I want to change this, I can do so by creating a logger with the proper name before creating the class:

auto logger = spdlog::stdout_color_st(MyClass::LOGGER_NAME);
logger->set_level(spdlog::level::debug);
logger->set_pattern("[%l] %n: %v");

MyClass c;

However, I can't find a good way to do this when using the Python bindings of MyClass. I saw that there is already a Python package binding spdlog, so I tried the following:

import spdlog
logger = spdlog.ConsoleLogger(MyClass.LOGGER_NAME)
logger.set_level(spdlog.LogLevel.DEBUG)
logger.set_pattern("[%l] %n: %v")

c = MyClass()

However, this does not affect the logger used inside MyClass (I assume there is simply no connection between the spdlog instance from the Python package and the one in my C++ code).

So long story short: Is there a way how I can configure the logger used in the C++ code from the Python side? If there is a way without having to manually pass a logger to the constructor of each class, that would be great. But I'm not sure if this is possible?


Solution

  • jwm's answer pointed me to a working solution. The logger can be created on the Python side and be registered with the module containing the C++ class by also binding spdlog::register_logger() in that module.

    1. Add a binding for spdlog::register_logger() in the module that binds MyClass. So the code for binding looks like this:
      PYBIND11_MODULE(mylib, m)
      {
          pybind11::class_<MyClass>(m, "MyClass")
              /* ... */;
      
          m.def("register_logger", &spdlog::register_logger);
      }
      
    2. In the Python code, use that binding to register the logger with the C++ module:
      import spdlog
      import mylib
      
      logger = spdlog.ConsoleLogger(mylib.MyClass.LOGGER_NAME)
      logger.set_level(spdlog.LogLevel.DEBUG)
      logger.set_pattern("[%l] %n: %v")
      # register logger with the function from mylib, so it is accessible inside MyClass
      mylib.register_logger(logger.get_underlying_logger())
      
      c = mylib.MyClass()