29 #include "log4cxx/patternlayout.h"
30 #include "log4cxx/helpers/stringhelper.h"
37 using namespace log4cxx::helpers;
45 GilGuard() : gil_state(PyGILState_Ensure()) {}
47 ~GilGuard() { PyGILState_Release(gil_state); }
50 PyGILState_STATE gil_state;
58 PyErr_Fetch(&ptype.
get(), &pvalue.
get(), &ptraceback.
get());
61 if (pvalue !=
nullptr or ptype !=
nullptr) {
64 exc_msg += PyUnicode_AsUTF8(exc_str);
71 unsigned const MAX_LRU_CACHE_SIZE = 32;
77 PyLogAppender::PyLogAppender() {
81 PyObjectPtr logging(PyImport_ImportModule(
"logging"));
82 if (logging ==
nullptr) {
83 ::reraise(
"ImportError: Failed to import Python logging module");
85 _getLogger = PyObject_GetAttrString(logging,
"getLogger");
86 if (_getLogger ==
nullptr) {
87 ::reraise(
"AttributeError: logging.getLogger method does not exist");
90 PyObjectPtr lsstlog_module(PyImport_ImportModule(
"lsst.log"));
91 if (lsstlog_module ==
nullptr) {
92 ::reraise(
"ImportError: Failed to import lsst.log module");
94 _mdc_class = PyObject_GetAttrString(lsstlog_module,
"MDCDict");
95 if (_mdc_class ==
nullptr) {
96 ::reraise(
"AttributeError: lsst.log.MDCDict class does not exist");
104 log4cxx::helpers::Transcoder::encodeUTF8(event->getLoggerName(), logger_name);
105 int const level =
event->getLevel()->toInt();
106 int const pyLevel = level / 1000;
113 auto cache_iter = _cache.find(logger_name);
114 if (cache_iter != _cache.end()) {
116 logger = cache_iter->second.logger;
125 if (logger_name ==
"root") {
126 logger = PyObject_CallFunction(_getLogger,
nullptr);
128 logger = PyObject_CallFunction(_getLogger,
"s", logger_name.
c_str());
132 ::reraise(
"Failed to retrieve Python logger \"" + logger_name +
"\"");
136 _cache.emplace(logger_name, LRUEntry({
logger, _lru_age ++}));
137 while (_cache.size() > ::MAX_LRU_CACHE_SIZE) {
140 _cache.begin(), _cache.end(),
141 [](
const LRUCache::value_type& lhs,
const LRUCache::value_type& rhs) {
142 return lhs.second.age < rhs.second.age;
150 for (
auto& cache_value: _cache) {
152 cache_value.second.age = _lru_age ++;
158 PyObjectPtr py_is_enabled(PyObject_CallMethod(
logger,
"isEnabledFor",
"i", pyLevel));
159 if (py_is_enabled ==
nullptr) {
160 ::reraise(
"Failure when calling logger.isEnabledFor() method");
162 if (not PyObject_IsTrue(py_is_enabled)) {
167 auto& loc =
event->getLocationInformation();
169 if (loc.getFileName() !=
nullptr) {
170 file_name = loc.getFileName();
172 int const lineno = loc.getLineNumber();
178 this->layout->format(msg, event, p);
180 if (not msg.empty() and msg.back() ==
'\n') {
183 log4cxx::helpers::Transcoder::encodeUTF8(msg, message);
185 log4cxx::helpers::Transcoder::encodeUTF8(event->getMessage(), message);
200 if (record ==
nullptr) {
201 ::reraise(
"Failed to create LogRecord instance");
211 PyObjectPtr mdc(PyObject_GetAttrString(record,
"MDC"));
212 if (mdc ==
nullptr) {
215 mdc = PyObject_CallObject(_mdc_class,
nullptr);
216 if (mdc ==
nullptr) {
217 ::reraise(
"Failed to make MDCDict instance");
220 if (PyObject_SetAttrString(record,
"MDC", mdc) == -1) {
221 ::reraise(
"Failed to set LogRecord MDC attribute");
226 for (
auto& mdc_key: event->getMDCKeySet()) {
228 log4cxx::helpers::Transcoder::encodeUTF8(mdc_key, key);
229 log4cxx::LogString mdc_value;
230 event->getMDC(mdc_key, mdc_value);
231 log4cxx::helpers::Transcoder::encodeUTF8(mdc_value, value);
234 if (PyObject_SetItem(mdc, py_key, py_value) == -1) {
236 ::reraise(
"Failed to update MDC dictionary");
242 if (res ==
nullptr) {
243 ::reraise(
"Logger failed to handle LogRecord");
247 void PyLogAppender::close() {
250 bool PyLogAppender::requiresLayout()
const {
254 void PyLogAppender::setOption(
const LogString &option,
const LogString &value) {
256 if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR(
"MESSAGEPATTERN"),
257 LOG4CXX_STR(
"messagepattern"))) {
258 setLayout(LayoutPtr(
new PatternLayout(value)));
260 AppenderSkeleton::setOption(option, value);
This class defines special log4cxx appender which "appends" log messages to Python logging.
Smart pointer class for PyObject instances.
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.