15#include <Poco/Message.h>
16#include <boost/python/errors.hpp>
17#include <boost/python/import.hpp>
18#include <boost/python/object.hpp>
28constexpr int PY_CRITICAL = 50;
29constexpr int PY_ERROR = 40;
30constexpr int PY_WARNING = 30;
31constexpr int PY_INFO = 20;
32constexpr int PY_DEBUG = 10;
33constexpr int PY_NOTSET = 0;
34constexpr size_t MAX_QUEUE_SIZE_DEFAULT = 10000;
35const std::string MAX_QUEUE_SIZE_KEY(
"logging.python.queueSize");
37auto pythonLevel(
const Message::Priority prio) {
39 case Message::Priority::PRIO_FATAL:
40 case Message::Priority::PRIO_CRITICAL:
42 case Message::Priority::PRIO_ERROR:
44 case Message::Priority::PRIO_WARNING:
46 case Message::Priority::PRIO_NOTICE:
47 case Message::Priority::PRIO_INFORMATION:
49 case Message::Priority::PRIO_DEBUG:
50 case Message::Priority::PRIO_TRACE:
60size_t configuredMaxQueueSize() {
61 auto &config = Mantid::Kernel::ConfigService::Instance();
63 if (!config.hasProperty(MAX_QUEUE_SIZE_KEY))
64 return MAX_QUEUE_SIZE_DEFAULT;
66 const auto value = config.getValue<
int>(MAX_QUEUE_SIZE_KEY);
68 return MAX_QUEUE_SIZE_DEFAULT;
70 return std::numeric_limits<size_t>::max();
71 return static_cast<size_t>(*value);
80 if (Py_IsInitialized()) {
94 std::unique_ptr<boost::python::object>
pyLogger;
104 auto logger = (boost::python::import(
"logging").attr(
"getLogger")(
"Mantid"));
105 m_state->pyLogger = std::make_unique<boost::python::object>(std::move(logger));
123 if (!Py_IsInitialized())
126 bool scheduleCallback{
false};
128 std::lock_guard lock(
m_state->mutex);
133 m_state->queue.emplace_back(std::move(msg));
138 m_state->callbackScheduled =
true;
139 scheduleCallback =
true;
143 if (!scheduleCallback)
147 auto stateHolder = std::make_unique<std::shared_ptr<State>>(
m_state);
149 auto *rawStateHolder = stateHolder.release();
152 stateHolder.reset(rawStateHolder);
153 std::lock_guard lock(
m_state->mutex);
154 m_state->callbackScheduled =
false;
160 const std::unique_ptr<std::shared_ptr<State>> holder(
static_cast<std::shared_ptr<State> *
>(stateHolder));
168 const auto maxQueueSize = configuredMaxQueueSize();
178 std::lock_guard lock(state.
mutex);
186 size_t droppedMessages{0};
188 std::lock_guard lock(state.
mutex);
190 if (state.
queue.empty()) {
196 message = std::move(state.
queue.front());
197 state.
queue.pop_front();
203 const auto logFn = state.
pyLogger->attr(
"log");
204 if (droppedMessages > 0) {
205 std::ostringstream warning;
206 warning <<
"PythonLoggingChannel dropped " << droppedMessages <<
" messages because its queue was full";
207 logFn(PY_WARNING, warning.str());
209 logFn(pythonLevel(message.getPriority()), message.getText());
210 }
catch (boost::python::error_already_set &) {
217 if (!Py_IsInitialized())
230 std::lock_guard lock(
m_state->mutex);
double value
The value of the point.
Defines a structure for acquiring/releasing the Python GIL using the RAII pattern.
const std::shared_ptr< State > m_state
Shared with any outstanding pending calls. It is set in the constructor and never reset.
void enqueue(Poco::Message &&msg)
static int pendingCallDrainQueue(void *stateHolder)
Callback with the signature required by Py_AddPendingCall.
~PythonLoggingChannel() override
void log(const Poco::Message &msg) override
static void drainQueue(State &state)
Deliver all queued messages to Python. The GIL must be held.
std::unique_ptr< boost::python::object > pyLogger
std::deque< Message > queue