log.cc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629
  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.h>
  21. #include <string>
  22. #include <boost/bind.hpp>
  23. using namespace isc::log;
  24. using std::string;
  25. using boost::bind;
  26. namespace {
  27. // This is for testing only. The real module will have it always set as
  28. // NULL and will use the global dictionary.
  29. MessageDictionary* testDictionary = NULL;
  30. PyObject*
  31. setTestDictionary(PyObject*, PyObject* args) {
  32. PyObject* enableO;
  33. // The API doesn't seem to provide conversion to bool,
  34. // so we do it little bit manually
  35. if (!PyArg_ParseTuple(args, "O", &enableO)) {
  36. return (NULL);
  37. }
  38. int enableI(PyObject_IsTrue(enableO));
  39. if (enableI == -1) {
  40. return (NULL);
  41. }
  42. bool enable(enableI != 0);
  43. try {
  44. delete testDictionary;
  45. testDictionary = NULL;
  46. if (enable) {
  47. testDictionary = new MessageDictionary;
  48. }
  49. }
  50. catch (const std::exception& e) {
  51. PyErr_SetString(PyExc_RuntimeError, e.what());
  52. return (NULL);
  53. }
  54. catch (...) {
  55. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  56. return (NULL);
  57. }
  58. Py_RETURN_NONE;
  59. }
  60. PyObject*
  61. createMessage(PyObject*, PyObject* args) {
  62. const char* mid;
  63. const char* text;
  64. // We parse the strings
  65. if (!PyArg_ParseTuple(args, "ss", &mid, &text)) {
  66. return (NULL);
  67. }
  68. PyObject* origMid;
  69. // And extract the original representation of the message
  70. // ID, so we can return it instead of creating another instance.
  71. // This call shouldn't fail if the previous suceeded.
  72. if (!PyArg_ParseTuple(args, "Os", &origMid, &text)) {
  73. return (NULL);
  74. }
  75. try {
  76. MessageDictionary* dict = testDictionary ? testDictionary :
  77. &MessageDictionary::globalDictionary();
  78. // We ignore the result, they will be in some kind of dupe list
  79. // if there's a problem
  80. dict->add(mid, text);
  81. }
  82. catch (const std::exception& e) {
  83. PyErr_SetString(PyExc_RuntimeError, e.what());
  84. return (NULL);
  85. }
  86. catch (...) {
  87. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  88. return (NULL);
  89. }
  90. // Return the ID
  91. Py_INCREF(origMid);
  92. return (origMid);
  93. }
  94. PyObject*
  95. getMessage(PyObject*, PyObject* args) {
  96. const char* mid;
  97. if (!PyArg_ParseTuple(args, "s", &mid)) {
  98. return (NULL);
  99. }
  100. try {
  101. MessageDictionary* dict = testDictionary ? testDictionary :
  102. &MessageDictionary::globalDictionary();
  103. const std::string& result(dict->getText(mid));
  104. if (result.empty()) {
  105. Py_RETURN_NONE;
  106. } else {
  107. return (Py_BuildValue("s", result.c_str()));
  108. }
  109. }
  110. catch (const std::exception& e) {
  111. PyErr_SetString(PyExc_RuntimeError, e.what());
  112. return (NULL);
  113. }
  114. catch (...) {
  115. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  116. return (NULL);
  117. }
  118. }
  119. PyObject*
  120. reset(PyObject*, PyObject*) {
  121. LoggerManager::reset();
  122. Py_RETURN_NONE;
  123. }
  124. PyObject*
  125. init(PyObject*, PyObject* args) {
  126. const char* root;
  127. const char* file(NULL);
  128. const char* severity("INFO");
  129. int dbglevel(0);
  130. if (!PyArg_ParseTuple(args, "s|siz", &root, &severity, &dbglevel, &file)) {
  131. return (NULL);
  132. }
  133. try {
  134. LoggerManager::init(root, getSeverity(severity), dbglevel, file);
  135. }
  136. catch (const std::exception& e) {
  137. PyErr_SetString(PyExc_RuntimeError, e.what());
  138. return (NULL);
  139. }
  140. catch (...) {
  141. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  142. return (NULL);
  143. }
  144. Py_RETURN_NONE;
  145. }
  146. PyMethodDef methods[] = {
  147. {"set_test_dictionary", setTestDictionary, METH_VARARGS,
  148. "Set or unset testing mode for message dictionary. In testing, "
  149. "the create_message and get_message functions work on different "
  150. "than the logger-global dictionary, not polluting it."},
  151. {"create_message", createMessage, METH_VARARGS,
  152. "Creates a new message in the dictionary. You shouldn't need to "
  153. "call this directly, it should be called by the generated message "
  154. "file. Returns the identifier to be used in logging. The text "
  155. "shouldn't be empty."},
  156. {"get_message", getMessage, METH_VARARGS,
  157. "Get a message. This function is for testing purposes and you don't "
  158. "need to call it. It returns None if the message does not exist."},
  159. {"reset", reset, METH_NOARGS,
  160. "Reset all logging. For testing purposes only, do not use."},
  161. {"init", init, METH_VARARGS,
  162. "Run-time initialization. You need to call this before you do any "
  163. "logging, to configure the root logger name. You may also provide "
  164. "logging severity (one of 'DEBUG', 'INFO', 'WARN', 'ERROR' or "
  165. "'FATAL'), a debug level (integer in the range 0-99) and a file name "
  166. "of a dictionary with message text translations."},
  167. {NULL, NULL, 0, NULL}
  168. };
  169. class LoggerWrapper : public PyObject {
  170. // Everything is public here, as it is accessible only inside this .cc file.
  171. public:
  172. Logger *logger_;
  173. };
  174. extern PyTypeObject logger_type;
  175. int
  176. Logger_init(LoggerWrapper* self, PyObject* args) {
  177. const char* name;
  178. if (!PyArg_ParseTuple(args, "s", &name)) {
  179. return (-1);
  180. }
  181. try {
  182. self->logger_ = new Logger(name);
  183. return (0);
  184. }
  185. catch (const std::exception& e) {
  186. PyErr_SetString(PyExc_RuntimeError, e.what());
  187. return (-1);
  188. }
  189. catch (...) {
  190. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  191. return (-1);
  192. }
  193. }
  194. void
  195. Logger_destroy(LoggerWrapper* const self) {
  196. delete self->logger_;
  197. self->logger_ = NULL;
  198. Py_TYPE(self)->tp_free(self);
  199. }
  200. // The isc::log doesn't contain function to convert this way
  201. const char*
  202. severityToText(const Severity& severity) {
  203. switch (severity) {
  204. case DEFAULT:
  205. return ("DEFAULT");
  206. case DEBUG:
  207. return ("DEBUG");
  208. case INFO:
  209. return ("INFO");
  210. case WARN:
  211. return ("WARN");
  212. case ERROR:
  213. return ("ERROR");
  214. case FATAL:
  215. return ("FATAL");
  216. default:
  217. return (NULL);
  218. }
  219. }
  220. PyObject*
  221. Logger_getEffectiveSeverity(LoggerWrapper* self, PyObject*) {
  222. try {
  223. return (Py_BuildValue("s",
  224. severityToText(
  225. self->logger_->getEffectiveSeverity())));
  226. }
  227. catch (const std::exception& e) {
  228. PyErr_SetString(PyExc_RuntimeError, e.what());
  229. return (NULL);
  230. }
  231. catch (...) {
  232. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  233. return (NULL);
  234. }
  235. }
  236. PyObject*
  237. Logger_getEffectiveDebugLevel(LoggerWrapper* self, PyObject*) {
  238. try {
  239. return (Py_BuildValue("i", self->logger_->getEffectiveDebugLevel()));
  240. }
  241. catch (const std::exception& e) {
  242. PyErr_SetString(PyExc_RuntimeError, e.what());
  243. return (NULL);
  244. }
  245. catch (...) {
  246. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  247. return (NULL);
  248. }
  249. }
  250. PyObject*
  251. Logger_setSeverity(LoggerWrapper* self, PyObject* args) {
  252. const char* severity;
  253. int dbgLevel = 0;
  254. if (!PyArg_ParseTuple(args, "z|i", &severity, &dbgLevel)) {
  255. return (NULL);
  256. }
  257. try {
  258. self->logger_->setSeverity((severity == NULL) ? DEFAULT :
  259. getSeverity(severity), dbgLevel);
  260. }
  261. catch (const std::exception& e) {
  262. PyErr_SetString(PyExc_RuntimeError, e.what());
  263. return (NULL);
  264. }
  265. catch (...) {
  266. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  267. return (NULL);
  268. }
  269. Py_RETURN_NONE;
  270. }
  271. template<class FPtr> // Who should remember the pointer-to-method syntax
  272. PyObject*
  273. Logger_isLevelEnabled(LoggerWrapper* self, FPtr function) {
  274. try {
  275. if ((self->logger_->*function)()) {
  276. Py_RETURN_TRUE;
  277. } else {
  278. Py_RETURN_FALSE;
  279. }
  280. }
  281. catch (const std::exception& e) {
  282. PyErr_SetString(PyExc_RuntimeError, e.what());
  283. return (NULL);
  284. }
  285. catch (...) {
  286. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  287. return (NULL);
  288. }
  289. }
  290. PyObject*
  291. Logger_isInfoEnabled(LoggerWrapper* self, PyObject*) {
  292. return (Logger_isLevelEnabled(self, &Logger::isInfoEnabled));
  293. }
  294. PyObject*
  295. Logger_isWarnEnabled(LoggerWrapper* self, PyObject*) {
  296. return (Logger_isLevelEnabled(self, &Logger::isWarnEnabled));
  297. }
  298. PyObject*
  299. Logger_isErrorEnabled(LoggerWrapper* self, PyObject*) {
  300. return (Logger_isLevelEnabled(self, &Logger::isErrorEnabled));
  301. }
  302. PyObject*
  303. Logger_isFatalEnabled(LoggerWrapper* self, PyObject*) {
  304. return (Logger_isLevelEnabled(self, &Logger::isFatalEnabled));
  305. }
  306. PyObject*
  307. Logger_isDebugEnabled(LoggerWrapper* self, PyObject* args) {
  308. int level = MIN_DEBUG_LEVEL;
  309. if (!PyArg_ParseTuple(args, "|i", &level)) {
  310. return (NULL);
  311. }
  312. try {
  313. if (self->logger_->isDebugEnabled(level)) {
  314. Py_RETURN_TRUE;
  315. } else {
  316. Py_RETURN_FALSE;
  317. }
  318. }
  319. catch (const std::exception& e) {
  320. PyErr_SetString(PyExc_RuntimeError, e.what());
  321. return (NULL);
  322. }
  323. catch (...) {
  324. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  325. return (NULL);
  326. }
  327. }
  328. // To propagate python exceptions trough our code
  329. class InternalError {};
  330. string
  331. objectToStr(PyObject* object, bool convert) {
  332. PyObject* cleanup(NULL);
  333. if (convert) {
  334. object = cleanup = PyObject_Str(object);
  335. if (object == NULL) {
  336. throw InternalError();
  337. }
  338. }
  339. const char* value;
  340. PyObject* tuple(Py_BuildValue("(O)", object));
  341. if (tuple == NULL) {
  342. if (cleanup != NULL) {
  343. Py_DECREF(cleanup);
  344. }
  345. throw InternalError();
  346. }
  347. if (!PyArg_ParseTuple(tuple, "s", &value)) {
  348. Py_DECREF(tuple);
  349. if (cleanup != NULL) {
  350. Py_DECREF(cleanup);
  351. }
  352. throw InternalError();
  353. }
  354. string result(value);
  355. Py_DECREF(tuple);
  356. if (cleanup != NULL) {
  357. Py_DECREF(cleanup);
  358. }
  359. return (result);
  360. }
  361. // Generic function to output the logging message. Called by the real functions.
  362. template<class Function>
  363. PyObject*
  364. Logger_performOutput(Function function, PyObject* args, bool dbgLevel) {
  365. try {
  366. Py_ssize_t number(PyObject_Length(args));
  367. if (number < 0) {
  368. return (NULL);
  369. }
  370. // Which argument is the first to format?
  371. size_t start(1);
  372. if (dbgLevel) {
  373. start ++;
  374. }
  375. if (number < start) {
  376. return (PyErr_Format(PyExc_TypeError, "Too few arguments to "
  377. "logging call, at least %zu needed and %zd "
  378. "given", start, number));
  379. }
  380. // Extract the fixed arguments
  381. PyObject *midO(PySequence_GetItem(args, start - 1));
  382. if (midO == NULL) {
  383. return (NULL);
  384. }
  385. string mid(objectToStr(midO, false));
  386. long dbg(0);
  387. if (dbgLevel) {
  388. PyObject *dbgO(PySequence_GetItem(args, 0));
  389. if (dbgO == NULL) {
  390. return (NULL);
  391. }
  392. dbg = PyLong_AsLong(dbgO);
  393. if (PyErr_Occurred()) {
  394. return (NULL);
  395. }
  396. }
  397. // We create the logging message right now. If we fail to convert a
  398. // parameter to string, at least the part that we already did will
  399. // be output
  400. Logger::Formatter formatter(function(dbg, mid.c_str()));
  401. // Now process the rest of parameters, convert each to string and put
  402. // into the formatter. It will print itself in the end.
  403. for (size_t i(start); i < number; ++ i) {
  404. PyObject* param(PySequence_GetItem(args, i));
  405. if (param == NULL) {
  406. return (NULL);
  407. }
  408. formatter = formatter.arg(objectToStr(param, true));
  409. }
  410. Py_RETURN_NONE;
  411. }
  412. catch (const InternalError&) {
  413. return (NULL);
  414. }
  415. catch (const std::exception& e) {
  416. PyErr_SetString(PyExc_RuntimeError, e.what());
  417. return (NULL);
  418. }
  419. catch (...) {
  420. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  421. return (NULL);
  422. }
  423. }
  424. // Now map the functions into the performOutput. I wish C++ could do
  425. // functional programming.
  426. PyObject*
  427. Logger_debug(LoggerWrapper* self, PyObject* args) {
  428. return (Logger_performOutput(bind(&Logger::debug, self->logger_, _1, _2),
  429. args, true));
  430. }
  431. PyObject*
  432. Logger_info(LoggerWrapper* self, PyObject* args) {
  433. return (Logger_performOutput(bind(&Logger::info, self->logger_, _2),
  434. args, false));
  435. }
  436. PyObject*
  437. Logger_warn(LoggerWrapper* self, PyObject* args) {
  438. return (Logger_performOutput(bind(&Logger::warn, self->logger_, _2),
  439. args, false));
  440. }
  441. PyObject*
  442. Logger_error(LoggerWrapper* self, PyObject* args) {
  443. return (Logger_performOutput(bind(&Logger::error, self->logger_, _2),
  444. args, false));
  445. }
  446. PyObject*
  447. Logger_fatal(LoggerWrapper* self, PyObject* args) {
  448. return (Logger_performOutput(bind(&Logger::fatal, self->logger_, _2),
  449. args, false));
  450. }
  451. PyMethodDef loggerMethods[] = {
  452. { "get_effective_severity",
  453. reinterpret_cast<PyCFunction>(Logger_getEffectiveSeverity),
  454. METH_NOARGS, "Returns the effective logging severity as string" },
  455. { "get_effective_debug_level",
  456. reinterpret_cast<PyCFunction>(Logger_getEffectiveDebugLevel),
  457. METH_NOARGS, "Returns the current debug level." },
  458. { "set_severity",
  459. reinterpret_cast<PyCFunction>(Logger_setSeverity), METH_VARARGS,
  460. "Sets the severity of a logger. The parameters are severity as a "
  461. "string and, optionally, a debug level (integer in range 0-99). "
  462. "The severity may be NULL, in which case an inherited value is taken."
  463. },
  464. { "is_debug_enabled", reinterpret_cast<PyCFunction>(Logger_isDebugEnabled),
  465. METH_VARARGS, "Returns if the logger would log debug message now. "
  466. "You can provide a desired debug level." },
  467. { "is_info_enabled", reinterpret_cast<PyCFunction>(Logger_isInfoEnabled),
  468. METH_NOARGS, "Returns if the logger would log info message now." },
  469. { "is_warn_enabled", reinterpret_cast<PyCFunction>(Logger_isWarnEnabled),
  470. METH_NOARGS, "Returns if the logger would log warn message now." },
  471. { "is_error_enabled", reinterpret_cast<PyCFunction>(Logger_isErrorEnabled),
  472. METH_NOARGS, "Returns if the logger would log error message now." },
  473. { "is_fatal_enabled", reinterpret_cast<PyCFunction>(Logger_isFatalEnabled),
  474. METH_NOARGS, "Returns if the logger would log fatal message now." },
  475. { "debug", reinterpret_cast<PyCFunction>(Logger_debug), METH_VARARGS,
  476. "Logs a debug-severity message. It takes the debug level, message ID "
  477. "and any number of stringifiable arguments to the message." },
  478. { "info", reinterpret_cast<PyCFunction>(Logger_info), METH_VARARGS,
  479. "Logs a info-severity message. It taskes the message ID and any "
  480. "number of stringifiable arguments to the message." },
  481. { "warn", reinterpret_cast<PyCFunction>(Logger_warn), METH_VARARGS,
  482. "Logs a warn-severity message. It taskes the message ID and any "
  483. "number of stringifiable arguments to the message." },
  484. { "error", reinterpret_cast<PyCFunction>(Logger_error), METH_VARARGS,
  485. "Logs a error-severity message. It taskes the message ID and any "
  486. "number of stringifiable arguments to the message." },
  487. { "fatal", reinterpret_cast<PyCFunction>(Logger_fatal), METH_VARARGS,
  488. "Logs a fatal-severity message. It taskes the message ID and any "
  489. "number of stringifiable arguments to the message." },
  490. { NULL, NULL, 0, NULL }
  491. };
  492. PyTypeObject logger_type = {
  493. PyVarObject_HEAD_INIT(NULL, 0)
  494. "isc.log.Logger",
  495. sizeof(LoggerWrapper), // tp_basicsize
  496. 0, // tp_itemsize
  497. reinterpret_cast<destructor>(Logger_destroy), // tp_dealloc
  498. NULL, // tp_print
  499. NULL, // tp_getattr
  500. NULL, // tp_setattr
  501. NULL, // tp_reserved
  502. NULL, // tp_repr
  503. NULL, // tp_as_number
  504. NULL, // tp_as_sequence
  505. NULL, // tp_as_mapping
  506. NULL, // tp_hash
  507. NULL, // tp_call
  508. NULL, // tp_str
  509. NULL, // tp_getattro
  510. NULL, // tp_setattro
  511. NULL, // tp_as_buffer
  512. Py_TPFLAGS_DEFAULT, // tp_flags
  513. "Wrapper around the C++ isc::log::Logger class."
  514. "It is not complete, but everything important should be here.",
  515. NULL, // tp_traverse
  516. NULL, // tp_clear
  517. NULL, // tp_richcompare
  518. 0, // tp_weaklistoffset
  519. NULL, // tp_iter
  520. NULL, // tp_iternext
  521. loggerMethods, // tp_methods
  522. NULL, // tp_members
  523. NULL, // tp_getset
  524. NULL, // tp_base
  525. NULL, // tp_dict
  526. NULL, // tp_descr_get
  527. NULL, // tp_descr_set
  528. 0, // tp_dictoffset
  529. reinterpret_cast<initproc>(Logger_init), // tp_init
  530. NULL, // tp_alloc
  531. PyType_GenericNew, // tp_new
  532. NULL, // tp_free
  533. NULL, // tp_is_gc
  534. NULL, // tp_bases
  535. NULL, // tp_mro
  536. NULL, // tp_cache
  537. NULL, // tp_subclasses
  538. NULL, // tp_weaklist
  539. NULL, // tp_del
  540. 0 // tp_version_tag
  541. };
  542. PyModuleDef iscLog = {
  543. { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
  544. "log",
  545. "Python bindings for the classes in the isc::log namespace.\n\n"
  546. "These bindings are close match to the C++ API, but they are not complete "
  547. "(some parts are not needed) and some are done in more python-like ways.",
  548. -1,
  549. methods,
  550. NULL,
  551. NULL,
  552. NULL,
  553. NULL
  554. };
  555. } // end anonymous namespace
  556. PyMODINIT_FUNC
  557. PyInit_log(void) {
  558. PyObject* mod = PyModule_Create(&iscLog);
  559. if (mod == NULL) {
  560. return (NULL);
  561. }
  562. if (PyType_Ready(&logger_type) < 0) {
  563. return (NULL);
  564. }
  565. if (PyModule_AddObject(mod, "Logger",
  566. static_cast<PyObject*>(static_cast<void*>(
  567. &logger_type))) < 0) {
  568. return (NULL);
  569. }
  570. Py_INCREF(&logger_type);
  571. return (mod);
  572. }