log.cc 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #define PY_SSIZE_T_CLEAN
  15. #include <Python.h>
  16. #include <structmember.h>
  17. #include <config.h>
  18. #include <log/message_dictionary.h>
  19. #include <log/logger_manager.h>
  20. #include <log/logger_support.h>
  21. #include <log/logger.h>
  22. #include <config/ccsession.h>
  23. #include <string>
  24. #include <boost/bind.hpp>
  25. #include <util/python/pycppwrapper_util.h>
  26. #include <log/log_dbglevels.h>
  27. using namespace isc::log;
  28. using namespace isc::util::python;
  29. using std::string;
  30. using boost::bind;
  31. // We encountered a strange problem with Clang (clang version 2.8
  32. // (tags/RELEASE_28 115909)) on OSX, where unwinding the stack
  33. // segfaults the moment this exception was thrown and caught.
  34. //
  35. // Placing it in a named namespace instead of the originalRecommend
  36. // unnamed namespace appears to solve this, so as a temporary
  37. // workaround, we create a local randomly named namespace here
  38. // to solve this issue.
  39. namespace clang_unnamed_namespace_workaround {
  40. // To propagate python exceptions through our code
  41. // This exception is used to signal to the calling function that a
  42. // proper Python Exception has already been set, and the caller
  43. // should now return NULL.
  44. // Since it is only used internally, and should not pass any
  45. // information itself, is is not derived from std::exception
  46. class InternalError : public std::exception {};
  47. }
  48. using namespace clang_unnamed_namespace_workaround;
  49. namespace {
  50. // This is for testing only. The real module will have it always set as
  51. // NULL and will use the global dictionary.
  52. MessageDictionary* testDictionary = NULL;
  53. PyObject*
  54. setTestDictionary(PyObject*, PyObject* args) {
  55. PyObject* enableO;
  56. // The API doesn't seem to provide conversion to bool,
  57. // so we do it little bit manually
  58. if (!PyArg_ParseTuple(args, "O", &enableO)) {
  59. return (NULL);
  60. }
  61. int enableI(PyObject_IsTrue(enableO));
  62. if (enableI == -1) {
  63. return (NULL);
  64. }
  65. bool enable(enableI != 0);
  66. try {
  67. delete testDictionary;
  68. testDictionary = NULL;
  69. if (enable) {
  70. testDictionary = new MessageDictionary;
  71. }
  72. }
  73. catch (const std::exception& e) {
  74. PyErr_SetString(PyExc_RuntimeError, e.what());
  75. return (NULL);
  76. }
  77. catch (...) {
  78. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  79. return (NULL);
  80. }
  81. Py_RETURN_NONE;
  82. }
  83. PyObject*
  84. createMessage(PyObject*, PyObject* args) {
  85. const char* mid;
  86. const char* text;
  87. // We parse the strings
  88. if (!PyArg_ParseTuple(args, "ss", &mid, &text)) {
  89. return (NULL);
  90. }
  91. PyObject* origMid;
  92. // And extract the original representation of the message
  93. // ID, so we can return it instead of creating another instance.
  94. // This call shouldn't fail if the previous suceeded.
  95. if (!PyArg_ParseTuple(args, "Os", &origMid, &text)) {
  96. return (NULL);
  97. }
  98. try {
  99. MessageDictionary* dict = testDictionary ? testDictionary :
  100. &MessageDictionary::globalDictionary();
  101. // We ignore the result, they will be in some kind of dupe list
  102. // if there's a problem
  103. dict->add(mid, text);
  104. }
  105. catch (const std::exception& e) {
  106. PyErr_SetString(PyExc_RuntimeError, e.what());
  107. return (NULL);
  108. }
  109. catch (...) {
  110. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  111. return (NULL);
  112. }
  113. // Return the ID
  114. Py_INCREF(origMid);
  115. return (origMid);
  116. }
  117. PyObject*
  118. getMessage(PyObject*, PyObject* args) {
  119. const char* mid;
  120. if (!PyArg_ParseTuple(args, "s", &mid)) {
  121. return (NULL);
  122. }
  123. try {
  124. MessageDictionary* dict = testDictionary ? testDictionary :
  125. &MessageDictionary::globalDictionary();
  126. const std::string& result(dict->getText(mid));
  127. if (result.empty()) {
  128. Py_RETURN_NONE;
  129. } else {
  130. return (Py_BuildValue("s", result.c_str()));
  131. }
  132. }
  133. catch (const std::exception& e) {
  134. PyErr_SetString(PyExc_RuntimeError, e.what());
  135. return (NULL);
  136. }
  137. catch (...) {
  138. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  139. return (NULL);
  140. }
  141. }
  142. PyObject*
  143. reset(PyObject*, PyObject*) {
  144. LoggerManager::reset();
  145. Py_RETURN_NONE;
  146. }
  147. PyObject*
  148. init(PyObject*, PyObject* args, PyObject* arg_keywords) {
  149. const char* root;
  150. const char* file(NULL);
  151. const char* severity("INFO");
  152. bool buffer = false;
  153. int dbglevel(0);
  154. const char* const keywords[] = { "name", "severity", "debuglevel", "file",
  155. "buffer", NULL };
  156. if (!PyArg_ParseTupleAndKeywords(args, arg_keywords, "s|sizb",
  157. const_cast<char**>(keywords), &root,
  158. &severity, &dbglevel, &file, &buffer)) {
  159. return (NULL);
  160. }
  161. try {
  162. LoggerManager::init(root, getSeverity(severity), dbglevel, file,
  163. buffer);
  164. }
  165. catch (const std::exception& e) {
  166. PyErr_SetString(PyExc_RuntimeError, e.what());
  167. return (NULL);
  168. }
  169. catch (...) {
  170. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  171. return (NULL);
  172. }
  173. Py_RETURN_NONE;
  174. }
  175. // This initialization is for unit tests. It allows message settings to
  176. // be determined by a set of B10_xxx environment variables. (See the
  177. // description of initLogger() for more details.) The function has been named
  178. // resetUnitTestRootLogger() here as being more descriptive and
  179. // trying to avoid confusion.
  180. PyObject*
  181. resetUnitTestRootLogger(PyObject*, PyObject*) {
  182. try {
  183. isc::log::resetUnitTestRootLogger();
  184. }
  185. catch (const std::exception& e) {
  186. PyErr_SetString(PyExc_RuntimeError, e.what());
  187. return (NULL);
  188. }
  189. catch (...) {
  190. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  191. return (NULL);
  192. }
  193. Py_RETURN_NONE;
  194. }
  195. PyObject*
  196. logConfigUpdate(PyObject*, PyObject* args) {
  197. // we have no wrappers for ElementPtr and ConfigData,
  198. // So we expect JSON strings and convert them.
  199. // The new_config object is assumed to have been validated.
  200. const char* new_config_json;
  201. const char* mod_spec_json;
  202. if (!PyArg_ParseTuple(args, "ss",
  203. &new_config_json, &mod_spec_json)) {
  204. return (NULL);
  205. }
  206. try {
  207. isc::data::ConstElementPtr new_config =
  208. isc::data::Element::fromJSON(new_config_json);
  209. isc::data::ConstElementPtr mod_spec_e =
  210. isc::data::Element::fromJSON(mod_spec_json);
  211. isc::config::ModuleSpec mod_spec(mod_spec_e);
  212. isc::config::ConfigData config_data(mod_spec);
  213. isc::config::default_logconfig_handler("logging", new_config,
  214. config_data);
  215. Py_RETURN_NONE;
  216. } catch (const isc::data::JSONError& je) {
  217. std::string error_msg = std::string("JSON format error: ") + je.what();
  218. PyErr_SetString(PyExc_TypeError, error_msg.c_str());
  219. } catch (const isc::data::TypeError&) {
  220. PyErr_SetString(PyExc_TypeError, "argument 1 of log_config_update "
  221. "is not a map of config data");
  222. } catch (const isc::config::ModuleSpecError&) {
  223. PyErr_SetString(PyExc_TypeError, "argument 2 of log_config_update "
  224. "is not a correct module specification");
  225. } catch (const std::exception& e) {
  226. PyErr_SetString(PyExc_RuntimeError, e.what());
  227. } catch (...) {
  228. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  229. }
  230. return (NULL);
  231. }
  232. PyMethodDef methods[] = {
  233. {"set_test_dictionary", setTestDictionary, METH_VARARGS,
  234. "Set or unset testing mode for message dictionary. In testing, "
  235. "the create_message and get_message functions work on different "
  236. "than the logger-global dictionary, not polluting it."},
  237. {"create_message", createMessage, METH_VARARGS,
  238. "Creates a new message in the dictionary. You shouldn't need to "
  239. "call this directly, it should be called by the generated message "
  240. "file. Returns the identifier to be used in logging. The text "
  241. "shouldn't be empty."},
  242. {"get_message", getMessage, METH_VARARGS,
  243. "Get a message. This function is for testing purposes and you don't "
  244. "need to call it. It returns None if the message does not exist."},
  245. {"reset", reset, METH_NOARGS,
  246. "Reset all logging. For testing purposes only, do not use."},
  247. {"init", reinterpret_cast<PyCFunction>(init), METH_VARARGS | METH_KEYWORDS,
  248. "Run-time initialization. You need to call this before you do any "
  249. "logging, to configure the root logger name. You may also provide "
  250. "Arguments:\n"
  251. "name: root logger name\n"
  252. "severity (optional): one of 'DEBUG', 'INFO', 'WARN', 'ERROR' or "
  253. "'FATAL'\n"
  254. "debuglevel (optional): a debug level (integer in the range 0-99) "
  255. "file (optional): a file name of a dictionary with message text "
  256. "translations\n"
  257. "buffer (optional), boolean, when True, causes all log messages "
  258. "to be stored internally until log_config_update is called, at "
  259. "which point they shall be logged."},
  260. {"resetUnitTestRootLogger", resetUnitTestRootLogger, METH_VARARGS,
  261. "Resets the configuration of the root logger to that set by the "
  262. "B10_XXX environment variables. It is aimed at unit tests, where "
  263. "the logging is initialized by the code under test; called before "
  264. "the unit test starts, this function resets the logging configuration "
  265. "to that in use for the C++ unit tests."},
  266. {"log_config_update", logConfigUpdate, METH_VARARGS,
  267. "Update logger settings. This method is automatically used when "
  268. "ModuleCCSession is initialized with handle_logging_config set "
  269. "to True. When called, the first argument is the new logging "
  270. "configuration (in JSON format). The second argument is "
  271. "the raw specification (as returned from "
  272. "ConfigData.get_module_spec().get_full_spec(), and converted to "
  273. "JSON format).\n"
  274. "Raises a TypeError if either argument is not a (correct) JSON "
  275. "string, or if the spec is not a correct spec.\n"
  276. "If this call succeeds, the global logger settings have "
  277. "been updated."
  278. },
  279. {NULL, NULL, 0, NULL}
  280. };
  281. class LoggerWrapper : public PyObject {
  282. // Everything is public here, as it is accessible only inside this .cc file.
  283. public:
  284. Logger *logger_;
  285. };
  286. extern PyTypeObject logger_type;
  287. int
  288. Logger_init(PyObject* po_self, PyObject* args, PyObject*) {
  289. LoggerWrapper* self = static_cast<LoggerWrapper*>(po_self);
  290. const char* name;
  291. if (!PyArg_ParseTuple(args, "s", &name)) {
  292. return (-1);
  293. }
  294. try {
  295. self->logger_ = new Logger(name);
  296. return (0);
  297. }
  298. catch (const std::exception& e) {
  299. PyErr_SetString(PyExc_RuntimeError, e.what());
  300. return (-1);
  301. }
  302. catch (...) {
  303. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  304. return (-1);
  305. }
  306. }
  307. void
  308. //Logger_destroy(LoggerWrapper* const self) {
  309. Logger_destroy(PyObject* po_self) {
  310. LoggerWrapper* self = static_cast<LoggerWrapper*>(po_self);
  311. delete self->logger_;
  312. self->logger_ = NULL;
  313. Py_TYPE(self)->tp_free(self);
  314. }
  315. // The isc::log doesn't contain function to convert this way
  316. const char*
  317. severityToText(const Severity& severity) {
  318. switch (severity) {
  319. case DEFAULT:
  320. return ("DEFAULT");
  321. case DEBUG:
  322. return ("DEBUG");
  323. case INFO:
  324. return ("INFO");
  325. case WARN:
  326. return ("WARN");
  327. case ERROR:
  328. return ("ERROR");
  329. case FATAL:
  330. return ("FATAL");
  331. default:
  332. return (NULL);
  333. }
  334. }
  335. PyObject*
  336. Logger_getEffectiveSeverity(PyObject* po_self, PyObject*) {
  337. LoggerWrapper* self = static_cast<LoggerWrapper*>(po_self);
  338. try {
  339. return (Py_BuildValue("s",
  340. severityToText(
  341. self->logger_->getEffectiveSeverity())));
  342. }
  343. catch (const std::exception& e) {
  344. PyErr_SetString(PyExc_RuntimeError, e.what());
  345. return (NULL);
  346. }
  347. catch (...) {
  348. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  349. return (NULL);
  350. }
  351. }
  352. PyObject*
  353. Logger_getEffectiveDebugLevel(PyObject* po_self, PyObject*) {
  354. LoggerWrapper* self = static_cast<LoggerWrapper*>(po_self);
  355. try {
  356. return (Py_BuildValue("i", self->logger_->getEffectiveDebugLevel()));
  357. }
  358. catch (const std::exception& e) {
  359. PyErr_SetString(PyExc_RuntimeError, e.what());
  360. return (NULL);
  361. }
  362. catch (...) {
  363. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  364. return (NULL);
  365. }
  366. }
  367. PyObject*
  368. Logger_setSeverity(PyObject* po_self, PyObject* args) {
  369. LoggerWrapper* self = static_cast<LoggerWrapper*>(po_self);
  370. const char* severity;
  371. int dbgLevel = 0;
  372. if (!PyArg_ParseTuple(args, "z|i", &severity, &dbgLevel)) {
  373. return (NULL);
  374. }
  375. try {
  376. self->logger_->setSeverity((severity == NULL) ? DEFAULT :
  377. getSeverity(severity), dbgLevel);
  378. }
  379. catch (const std::exception& e) {
  380. PyErr_SetString(PyExc_RuntimeError, e.what());
  381. return (NULL);
  382. }
  383. catch (...) {
  384. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  385. return (NULL);
  386. }
  387. Py_RETURN_NONE;
  388. }
  389. template<class FPtr> // Who should remember the pointer-to-method syntax
  390. PyObject*
  391. Logger_isLevelEnabled(LoggerWrapper* self, FPtr function) {
  392. try {
  393. if ((self->logger_->*function)()) {
  394. Py_RETURN_TRUE;
  395. } else {
  396. Py_RETURN_FALSE;
  397. }
  398. }
  399. catch (const std::exception& e) {
  400. PyErr_SetString(PyExc_RuntimeError, e.what());
  401. return (NULL);
  402. }
  403. catch (...) {
  404. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  405. return (NULL);
  406. }
  407. }
  408. PyObject*
  409. Logger_isInfoEnabled(PyObject* po_self, PyObject*) {
  410. LoggerWrapper* self = static_cast<LoggerWrapper*>(po_self);
  411. return (Logger_isLevelEnabled(self, &Logger::isInfoEnabled));
  412. }
  413. PyObject*
  414. Logger_isWarnEnabled(PyObject* po_self, PyObject*) {
  415. LoggerWrapper* self = static_cast<LoggerWrapper*>(po_self);
  416. return (Logger_isLevelEnabled(self, &Logger::isWarnEnabled));
  417. }
  418. PyObject*
  419. Logger_isErrorEnabled(PyObject* po_self, PyObject*) {
  420. LoggerWrapper* self = static_cast<LoggerWrapper*>(po_self);
  421. return (Logger_isLevelEnabled(self, &Logger::isErrorEnabled));
  422. }
  423. PyObject*
  424. Logger_isFatalEnabled(PyObject* po_self, PyObject*) {
  425. LoggerWrapper* self = static_cast<LoggerWrapper*>(po_self);
  426. return (Logger_isLevelEnabled(self, &Logger::isFatalEnabled));
  427. }
  428. PyObject*
  429. Logger_isDebugEnabled(PyObject* po_self, PyObject* args) {
  430. LoggerWrapper* self = static_cast<LoggerWrapper*>(po_self);
  431. int level = MIN_DEBUG_LEVEL;
  432. if (!PyArg_ParseTuple(args, "|i", &level)) {
  433. return (NULL);
  434. }
  435. try {
  436. if (self->logger_->isDebugEnabled(level)) {
  437. Py_RETURN_TRUE;
  438. } else {
  439. Py_RETURN_FALSE;
  440. }
  441. }
  442. catch (const std::exception& e) {
  443. PyErr_SetString(PyExc_RuntimeError, e.what());
  444. return (NULL);
  445. }
  446. catch (...) {
  447. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  448. return (NULL);
  449. }
  450. }
  451. string
  452. objectToStr(PyObject* object, bool convert) {
  453. PyObjectContainer objstr_container;
  454. if (convert) {
  455. PyObject* text_obj = PyObject_Str(object);
  456. if (text_obj == NULL) {
  457. // PyObject_Str could fail for various reasons, including because
  458. // the object cannot be converted to a string. We exit with
  459. // InternalError to preserve the PyErr set in PyObject_Str.
  460. throw InternalError();
  461. }
  462. objstr_container.reset(text_obj);
  463. object = objstr_container.get();
  464. }
  465. PyObjectContainer tuple_container(Py_BuildValue("(O)", object));
  466. const char* value;
  467. if (!PyArg_ParseTuple(tuple_container.get(), "s", &value)) {
  468. throw InternalError();
  469. }
  470. return (string(value));
  471. }
  472. // Generic function to output the logging message. Called by the real functions.
  473. template <class Function>
  474. PyObject*
  475. Logger_performOutput(Function function, PyObject* args, bool dbgLevel) {
  476. try {
  477. const Py_ssize_t number(PyObject_Length(args));
  478. if (number < 0) {
  479. return (NULL);
  480. }
  481. // Which argument is the first to format?
  482. const size_t start = dbgLevel ? 2 : 1;
  483. if (number < start) {
  484. return (PyErr_Format(PyExc_TypeError, "Too few arguments to "
  485. "logging call, at least %zu needed and %zd "
  486. "given", start, number));
  487. }
  488. // Extract the fixed arguments
  489. long dbg(0);
  490. if (dbgLevel) {
  491. PyObjectContainer dbg_container(PySequence_GetItem(args, 0));
  492. dbg = PyLong_AsLong(dbg_container.get());
  493. if (PyErr_Occurred()) {
  494. return (NULL);
  495. }
  496. }
  497. // We create the logging message right now. If we fail to convert a
  498. // parameter to string, at least the part that we already did will
  499. // be output
  500. PyObjectContainer msgid_container(PySequence_GetItem(args, start - 1));
  501. const string mid(objectToStr(msgid_container.get(), false));
  502. Logger::Formatter formatter(function(dbg, mid.c_str()));
  503. // Now process the rest of parameters, convert each to string and put
  504. // into the formatter. It will print itself in the end.
  505. for (size_t i(start); i < number; ++ i) {
  506. PyObjectContainer param_container(PySequence_GetItem(args, i));
  507. try {
  508. formatter = formatter.arg(objectToStr(param_container.get(),
  509. true));
  510. }
  511. catch (...) {
  512. formatter.deactivate();
  513. throw;
  514. }
  515. }
  516. Py_RETURN_NONE;
  517. }
  518. catch (const InternalError&) {
  519. return (NULL);
  520. }
  521. catch (const std::exception& e) {
  522. PyErr_SetString(PyExc_RuntimeError, e.what());
  523. return (NULL);
  524. }
  525. catch (...) {
  526. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  527. return (NULL);
  528. }
  529. }
  530. // Now map the functions into the performOutput. I wish C++ could do
  531. // functional programming.
  532. PyObject*
  533. Logger_debug(PyObject* po_self, PyObject* args) {
  534. LoggerWrapper* self = static_cast<LoggerWrapper*>(po_self);
  535. return (Logger_performOutput(bind(&Logger::debug, self->logger_, _1, _2),
  536. args, true));
  537. }
  538. PyObject*
  539. Logger_info(PyObject* po_self, PyObject* args) {
  540. LoggerWrapper* self = static_cast<LoggerWrapper*>(po_self);
  541. return (Logger_performOutput(bind(&Logger::info, self->logger_, _2),
  542. args, false));
  543. }
  544. PyObject*
  545. Logger_warn(PyObject* po_self, PyObject* args) {
  546. LoggerWrapper* self = static_cast<LoggerWrapper*>(po_self);
  547. return (Logger_performOutput(bind(&Logger::warn, self->logger_, _2),
  548. args, false));
  549. }
  550. PyObject*
  551. Logger_error(PyObject* po_self, PyObject* args) {
  552. LoggerWrapper* self = static_cast<LoggerWrapper*>(po_self);
  553. return (Logger_performOutput(bind(&Logger::error, self->logger_, _2),
  554. args, false));
  555. }
  556. PyObject*
  557. Logger_fatal(PyObject* po_self, PyObject* args) {
  558. LoggerWrapper* self = static_cast<LoggerWrapper*>(po_self);
  559. return (Logger_performOutput(bind(&Logger::fatal, self->logger_, _2),
  560. args, false));
  561. }
  562. PyMethodDef loggerMethods[] = {
  563. { "get_effective_severity", Logger_getEffectiveSeverity, METH_NOARGS,
  564. "Returns the effective logging severity as string" },
  565. { "get_effective_debug_level", Logger_getEffectiveDebugLevel, METH_NOARGS,
  566. "Returns the current debug level." },
  567. { "set_severity", Logger_setSeverity, METH_VARARGS,
  568. "Sets the severity of a logger. The parameters are severity as a "
  569. "string and, optionally, a debug level (integer in range 0-99). "
  570. "The severity may be NULL, in which case an inherited value is taken."
  571. },
  572. { "is_debug_enabled", Logger_isDebugEnabled, METH_VARARGS,
  573. "Returns if the logger would log debug message now. "
  574. "You can provide a desired debug level." },
  575. { "is_info_enabled", Logger_isInfoEnabled, METH_NOARGS,
  576. "Returns if the logger would log info message now." },
  577. { "is_warn_enabled", Logger_isWarnEnabled, METH_NOARGS,
  578. "Returns if the logger would log warn message now." },
  579. { "is_error_enabled", Logger_isErrorEnabled, METH_NOARGS,
  580. "Returns if the logger would log error message now." },
  581. { "is_fatal_enabled", Logger_isFatalEnabled, METH_NOARGS,
  582. "Returns if the logger would log fatal message now." },
  583. { "debug", Logger_debug, METH_VARARGS,
  584. "Logs a debug-severity message. It takes the debug level, message ID "
  585. "and any number of stringifiable arguments to the message." },
  586. { "info", Logger_info, METH_VARARGS,
  587. "Logs a info-severity message. It taskes the message ID and any "
  588. "number of stringifiable arguments to the message." },
  589. { "warn", Logger_warn, METH_VARARGS,
  590. "Logs a warn-severity message. It taskes the message ID and any "
  591. "number of stringifiable arguments to the message." },
  592. { "error", Logger_error, METH_VARARGS,
  593. "Logs a error-severity message. It taskes the message ID and any "
  594. "number of stringifiable arguments to the message." },
  595. { "fatal", Logger_fatal, METH_VARARGS,
  596. "Logs a fatal-severity message. It taskes the message ID and any "
  597. "number of stringifiable arguments to the message." },
  598. { NULL, NULL, 0, NULL }
  599. };
  600. PyTypeObject logger_type = {
  601. PyVarObject_HEAD_INIT(NULL, 0)
  602. "isc.log.Logger",
  603. sizeof(LoggerWrapper), // tp_basicsize
  604. 0, // tp_itemsize
  605. Logger_destroy, // tp_dealloc
  606. NULL, // tp_print
  607. NULL, // tp_getattr
  608. NULL, // tp_setattr
  609. NULL, // tp_reserved
  610. NULL, // tp_repr
  611. NULL, // tp_as_number
  612. NULL, // tp_as_sequence
  613. NULL, // tp_as_mapping
  614. NULL, // tp_hash
  615. NULL, // tp_call
  616. NULL, // tp_str
  617. NULL, // tp_getattro
  618. NULL, // tp_setattro
  619. NULL, // tp_as_buffer
  620. Py_TPFLAGS_DEFAULT, // tp_flags
  621. "Wrapper around the C++ isc::log::Logger class."
  622. "It is not complete, but everything important should be here.",
  623. NULL, // tp_traverse
  624. NULL, // tp_clear
  625. NULL, // tp_richcompare
  626. 0, // tp_weaklistoffset
  627. NULL, // tp_iter
  628. NULL, // tp_iternext
  629. loggerMethods, // tp_methods
  630. NULL, // tp_members
  631. NULL, // tp_getset
  632. NULL, // tp_base
  633. NULL, // tp_dict
  634. NULL, // tp_descr_get
  635. NULL, // tp_descr_set
  636. 0, // tp_dictoffset
  637. Logger_init, // tp_init
  638. NULL, // tp_alloc
  639. PyType_GenericNew, // tp_new
  640. NULL, // tp_free
  641. NULL, // tp_is_gc
  642. NULL, // tp_bases
  643. NULL, // tp_mro
  644. NULL, // tp_cache
  645. NULL, // tp_subclasses
  646. NULL, // tp_weaklist
  647. NULL, // tp_del
  648. 0 // tp_version_tag
  649. };
  650. PyModuleDef iscLog = {
  651. { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
  652. "log",
  653. "Python bindings for the classes in the isc::log namespace.\n\n"
  654. "These bindings are close match to the C++ API, but they are not complete "
  655. "(some parts are not needed) and some are done in more python-like ways.",
  656. -1,
  657. methods,
  658. NULL,
  659. NULL,
  660. NULL,
  661. NULL
  662. };
  663. } // end anonymous namespace
  664. PyMODINIT_FUNC
  665. PyInit_log(void) {
  666. PyObject* mod = PyModule_Create(&iscLog);
  667. if (mod == NULL) {
  668. return (NULL);
  669. }
  670. // Finalize logger class and add in the definitions of the standard debug
  671. // levels. These can then be referred to in Python through the constants
  672. // log.DBGLVL_XXX.
  673. // N.B. These should be kept in sync with the constants defined in
  674. // log_dbglevels.h.
  675. try {
  676. if (PyType_Ready(&logger_type) < 0) {
  677. throw InternalError();
  678. }
  679. void* p = &logger_type;
  680. if (PyModule_AddObject(mod, "Logger",
  681. static_cast<PyObject*>(p)) < 0) {
  682. throw InternalError();
  683. }
  684. installClassVariable(logger_type, "DBGLVL_START_SHUT",
  685. Py_BuildValue("I", DBGLVL_START_SHUT));
  686. installClassVariable(logger_type, "DBGLVL_COMMAND",
  687. Py_BuildValue("I", DBGLVL_COMMAND));
  688. installClassVariable(logger_type, "DBGLVL_COMMAND_DATA",
  689. Py_BuildValue("I", DBGLVL_COMMAND_DATA));
  690. installClassVariable(logger_type, "DBGLVL_TRACE_BASIC",
  691. Py_BuildValue("I", DBGLVL_TRACE_BASIC));
  692. installClassVariable(logger_type, "DBGLVL_TRACE_BASIC_DATA",
  693. Py_BuildValue("I", DBGLVL_TRACE_BASIC_DATA));
  694. installClassVariable(logger_type, "DBGLVL_TRACE_DETAIL",
  695. Py_BuildValue("I", DBGLVL_TRACE_DETAIL));
  696. installClassVariable(logger_type, "DBGLVL_TRACE_DETAIL_DATA",
  697. Py_BuildValue("I", DBGLVL_TRACE_DETAIL_DATA));
  698. } catch (const InternalError&) {
  699. Py_DECREF(mod);
  700. return (NULL);
  701. } catch (const std::exception& ex) {
  702. const std::string ex_what =
  703. "Unexpected failure in Log initialization: " +
  704. std::string(ex.what());
  705. PyErr_SetString(PyExc_SystemError, ex_what.c_str());
  706. Py_DECREF(mod);
  707. return (NULL);
  708. } catch (...) {
  709. PyErr_SetString(PyExc_SystemError,
  710. "Unexpected failure in Log initialization");
  711. Py_DECREF(mod);
  712. return (NULL);
  713. }
  714. Py_INCREF(&logger_type);
  715. return (mod);
  716. }