LCOV - code coverage report
Current view: top level - python/isc/log - log.cc (source / functions) Hit Total Coverage
Test: report.info Lines: 169 239 70.7 %
Date: 2012-05-15 Functions: 29 32 90.6 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 151 400 37.8 %

           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 : }

Generated by: LCOV version 1.9