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