Belle II Software  release-08-01-10
LogPythonInterface.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 #include <boost/python.hpp>
10 
11 #include <framework/pybasf2/LogPythonInterface.h>
12 
13 #include <framework/logging/LogConnectionFilter.h>
14 #include <framework/logging/LogConnectionTxtFile.h>
15 #include <framework/logging/LogConnectionJSON.h>
16 #include <framework/logging/LogConnectionUDP.h>
17 #include <framework/logging/LogConnectionConsole.h>
18 #include <framework/logging/LogVariableStream.h>
19 #include <framework/logging/LogSystem.h>
20 
21 #include <framework/core/Environment.h>
22 
23 #include <iostream>
24 #include <string>
25 #include <map>
26 #include <utility>
27 
28 using namespace std;
29 using namespace Belle2;
30 using namespace boost::python;
31 
32 void LogPythonInterface::setLogLevel(LogConfig::ELogLevel level)
33 {
34  auto overrideLevel = (LogConfig::ELogLevel)Environment::Instance().getLogLevelOverride();
35  if (overrideLevel != LogConfig::c_Default)
36  level = overrideLevel;
37 
38  LogSystem::Instance().getLogConfig()->setLogLevel(level);
39 }
40 
41 void LogPythonInterface::setAbortLevel(LogConfig::ELogLevel level)
42 {
43  LogSystem::Instance().getLogConfig()->setAbortLevel(level);
44 }
45 
46 void LogPythonInterface::setDebugLevel(int level)
47 {
48  LogSystem::Instance().getLogConfig()->setDebugLevel(level);
49 }
50 
51 void LogPythonInterface::setLogInfo(LogConfig::ELogLevel level, int info)
52 {
53  LogSystem::Instance().getLogConfig()->setLogInfo(level, info);
54 }
55 
56 void LogPythonInterface::setPackageLogConfig(const std::string& package, const LogConfig& config)
57 {
58  LogSystem::Instance().addPackageLogConfig(package, config);
59 }
60 
61 void LogPythonInterface::setMaxMessageRepetitions(unsigned repetitions)
62 {
63  LogSystem::Instance().setMaxMessageRepetitions(repetitions);
64 }
65 
66 LogConfig::ELogLevel LogPythonInterface::getLogLevel()
67 {
68  return LogSystem::Instance().getLogConfig()->getLogLevel();
69 }
70 
71 LogConfig::ELogLevel LogPythonInterface::getAbortLevel()
72 {
73  return LogSystem::Instance().getLogConfig()->getAbortLevel();
74 }
75 
76 int LogPythonInterface::getDebugLevel()
77 {
78  return LogSystem::Instance().getLogConfig()->getDebugLevel();
79 }
80 
81 int LogPythonInterface::getLogInfo(LogConfig::ELogLevel level)
82 {
83  return LogSystem::Instance().getLogConfig()->getLogInfo(level);
84 }
85 
86 LogConfig& LogPythonInterface::getPackageLogConfig(const std::string& package)
87 {
88  return LogSystem::Instance().getPackageLogConfig(package);
89 }
90 
91 unsigned LogPythonInterface::getMaxMessageRepetitions() const
92 {
93  return LogSystem::Instance().getMaxMessageRepetitions();
94 }
95 
96 void LogPythonInterface::addLogJSON(bool complete)
97 {
98  LogSystem::Instance().addLogConnection(new LogConnectionJSON(complete));
99 }
100 
101 void LogPythonInterface::addLogUDP(const std::string& hostname, unsigned short port)
102 {
103  LogSystem::Instance().addLogConnection(new LogConnectionUDP(hostname, port));
104 }
105 
106 void LogPythonInterface::addLogFile(const std::string& filename, bool append)
107 {
108  LogSystem::Instance().addLogConnection(new LogConnectionFilter(new LogConnectionTxtFile(filename, append)));
109 }
110 
111 void LogPythonInterface::addLogConsole()
112 {
113  LogSystem::Instance().addLogConnection(new LogConnectionFilter(new LogConnectionConsole(STDOUT_FILENO)));
114 }
115 
116 void LogPythonInterface::addLogConsole(bool color)
117 {
118  LogSystem::Instance().addLogConnection(new LogConnectionFilter(new LogConnectionConsole(STDOUT_FILENO, color)));
119 }
120 
121 void LogPythonInterface::reset()
122 {
123  LogSystem::Instance().resetLogConnections();
124 }
125 
126 void LogPythonInterface::zeroCounters()
127 {
128  LogSystem::Instance().resetMessageCounter();
129 }
130 
131 void LogPythonInterface::enableErrorSummary(bool on)
132 {
133  LogSystem::Instance().enableErrorSummary(on);
134 }
135 
136 void LogPythonInterface::setPythonLoggingEnabled(bool enabled) const
137 {
138  LogConnectionConsole::setPythonLoggingEnabled(enabled);
139 }
140 
141 bool LogPythonInterface::getPythonLoggingEnabled() const
142 {
143  return LogConnectionConsole::getPythonLoggingEnabled();
144 }
145 
146 void LogPythonInterface::setEscapeNewlinesEnabled(bool enabled) const
147 {
148  LogConnectionConsole::setEscapeNewlinesEnabled(enabled);
149 }
150 
151 bool LogPythonInterface::getEscapeNewlinesEnabled() const
152 {
153  return LogConnectionConsole::getEscapeNewlinesEnabled();
154 }
155 
157 dict LogPythonInterface::getLogStatistics()
158 {
159  dict returnDict;
160  const LogSystem& logSys = LogSystem::Instance();
161  for (int iLevel = 0; iLevel < LogConfig::c_Default; ++iLevel) {
162  auto logLevel = static_cast<LogConfig::ELogLevel>(iLevel);
163  returnDict[logLevel] = logSys.getMessageCounter(logLevel);
164  }
165  return returnDict;
166 }
167 
168 namespace {
169 #if !defined(__GNUG__) || defined(__ICC)
170 #else
171 #pragma GCC diagnostic push
172 #pragma GCC diagnostic ignored "-Wunused-local-typedefs"
173 #endif
175  BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(addLogConsole_overloads, addLogConsole, 0, 1)
176 #if !defined(__GNUG__) || defined(__ICC)
177 #else
178 #pragma GCC diagnostic pop
179 #endif
180 
181  bool terminalSupportsColors()
182  {
183  return LogConnectionConsole::terminalSupportsColors(STDOUT_FILENO);
184  }
185 }
186 
188 void LogPythonInterface::exposePythonAPI()
189 {
190  // to avoid confusion between std::arg and boost::python::arg we want a shorthand namespace as well
191  namespace bp = boost::python;
192  scope global;
193  docstring_options options(true, true, false); //userdef, py sigs, c++ sigs
194 
195  //Interface LogLevel enum
196  enum_<LogConfig::ELogLevel>("LogLevel", R"DOCSTRING(Class for all possible log levels
197 
198 .. attribute:: DEBUG
199 
200  The lowest possible severity meant for expert only information and disabled
201  by default. In contrast to all other log levels DEBUG messages have an
202  additional numeric indication of their priority called the ``debug_level`` to
203  allow for different levels of verbosity.
204 
205  The agreed values for ``debug_level`` are
206 
207  * **0-9** for user code. These numbers are reserved for user analysis code and
208  may not be used by any part of basf2.
209  * **10-19** for analysis package code. The use case is that a user wants to debug
210  problems in analysis jobs with the help of experts.
211 
212  * **20-29** for simulation/reconstruction code.
213  * **30-39** for core framework code.
214 
215  .. note:: The default maximum debug level which will be shown when
216  running ``basf2 --debug`` without any argument for ``--debug`` is **10**
217 
218 
219 .. attribute:: INFO
220 
221  Used for informational messages which are of use for the average user but not
222  very important. Should be used very sparsely, everything which is of no
223  interest to the average user should be a debug message.
224 
225 .. attribute:: RESULT
226 
227  Informational message which don't indicate an error condition but are more
228  important than a mere information. For example the calculated cross section
229  or the output file name.
230 
231  .. deprecated:: release-01-00-00
232  use `INFO <basf2.LogLevel.INFO>` messages instead
233 
234 .. attribute:: WARNING
235 
236  For messages which indicate something which is not correct but not fatal to
237  the processing. This should **not** be used to make informational messages
238  more prominent and they should not be ignored by the user but they are not
239  critical.
240 
241 .. attribute:: ERROR
242 
243  For messages which indicate a clear error condition which needs to be
244  recovered. If error messages are produced before event processing is started
245  the processing will be aborted. During processing errors don't lead to a stop
246  of the processing but still indicate a problem.
247 
248 .. attribute:: FATAL
249 
250  For errors so severe that no recovery is possible. Emitting a fatal error
251  will always stop the processing and the `B2FATAL` function is guaranteed to
252  not return.
253 )DOCSTRING")
254  .value(LogConfig::logLevelToString(LogConfig::c_Debug), LogConfig::c_Debug)
255  .value(LogConfig::logLevelToString(LogConfig::c_Info), LogConfig::c_Info)
256  .value(LogConfig::logLevelToString(LogConfig::c_Result), LogConfig::c_Result)
257  .value(LogConfig::logLevelToString(LogConfig::c_Warning), LogConfig::c_Warning)
258  .value(LogConfig::logLevelToString(LogConfig::c_Error), LogConfig::c_Error)
259  .value(LogConfig::logLevelToString(LogConfig::c_Fatal), LogConfig::c_Fatal)
260  .value(LogConfig::logLevelToString(LogConfig::c_Default), LogConfig::c_Default)
261  ;
262 
263  //Interface LogInfo enum
264  enum_<LogConfig::ELogInfo>("LogInfo", R"DOCSTRING(The different fields of a log message.
265 
266 These fields can be used as a bitmask to configure the appearance of log messages.
267 
268 .. attribute:: LEVEL
269 
270  The severity of the log message, one of `basf2.LogLevel`
271 
272 .. attribute:: MESSAGE
273 
274  The actual log message
275 
276 .. attribute:: MODULE
277 
278  The name of the module active when the message was emitted. Can be empty if
279  no module was active (before/after processing or outside of the normal event
280  loop)
281 
282 .. attribute:: PACKAGE
283 
284  The package the code that emitted the message belongs to. This is empty for
285  messages emitted by python scripts
286 
287 .. attribute:: FUNCTION
288 
289  The function name that emitted the message
290 
291 .. attribute:: FILE
292 
293  The filename containing the code emitting the message
294 
295 .. attribute:: LINE
296 
297  The line number in the file emitting the message
298 )DOCSTRING")
299  .value("LEVEL", LogConfig::c_Level)
300  .value("MESSAGE", LogConfig::c_Message)
301  .value("MODULE", LogConfig::c_Module)
302  .value("PACKAGE", LogConfig::c_Package)
303  .value("FUNCTION", LogConfig::c_Function)
304  .value("FILE", LogConfig::c_File)
305  .value("LINE", LogConfig::c_Line)
306  .value("TIMESTAMP", LogConfig::c_Timestamp)
307  ;
308 
309  //Interface LogConfig class
310  class_<LogConfig>("LogConfig",
311  R"(Defines logging settings (log levels and items included in each message) for a certain context, e.g. a module or package.
312 
313 .. seealso:: `logging.package(str) <basf2.LogPythonInterface.package>`)")
314  .def(init<bp::optional<LogConfig::ELogLevel, int> >())
315  .add_property("log_level", &LogConfig::getLogLevel, &LogConfig::setLogLevel, "set or get the current log level")
316  .add_property("debug_level", &LogConfig::getDebugLevel, &LogConfig::setDebugLevel, "set or get the current debug level")
317  .add_property("abort_level", &LogConfig::getAbortLevel, &LogConfig::setAbortLevel,
318  "set or get the severity which causes program abort")
319  .def("set_log_level", &LogConfig::setLogLevel, args("log_level"), R"DOC(
320 Set the minimum log level to be shown. Messages with a log level below this value will not be shown at all.
321 
322 .. warning: Message with a level of `ERROR <LogLevel.ERROR>` or higher will always be shown and cannot be silenced.
323 )DOC")
324  .def("set_debug_level", &LogConfig::setDebugLevel, args("debug_level"), R"DOC(
325 Set the maximum debug level to be shown. Any messages with log level `DEBUG <LogLevel.DEBUG>` and a larger debug level will not be shown.
326 
327 .. seealso: the documentation of `DEBUG <LogLevel.DEBUG>` for suitable values
328 )DOC")
329  .def("set_abort_level", &LogConfig::setAbortLevel, args("abort_level"), R"DOC(
330 Set the severity which causes program abort.
331 
332 This can be set to a `LogLevel` which will cause the processing to be aborted if
333 a message with the given level or higher is encountered. The default is
334 `FATAL <LogLevel.FATAL>`. It cannot be set any higher but can be lowered.
335 )DOC")
336  .def("set_info", &LogConfig::setLogInfo, args("log_level", "log_info"),
337  "set the bitmask of LogInfo members to show when printing messages for a given log level")
338  .def("get_info", &LogConfig::getLogInfo, args("log_level"),
339  "get the current bitmask of which parts of the log message will be printed for a given log level")
340  ;
341 
342  void (LogPythonInterface::*addLogConsole)(bool) = &LogPythonInterface::addLogConsole;
343 
344  //Interface the Interface class :)
345  class_<LogPythonInterface, std::shared_ptr<LogPythonInterface>, boost::noncopyable>("LogPythonInterface", R"(
346 Logging configuration (for messages generated from C++ or Python), available as a global `basf2.logging` object in Python. See also `basf2.set_log_level()` and `basf2.set_debug_level()`.
347 
348 This class exposes a object called `logging <basf2.logging>` to the python interface. With
349 this object it is possible to set all properties of the logging system
350 directly in the steering file in a consistent manner This class also
351 exposes the `LogConfig` class as well as the `LogLevel`
352 and `LogInfo` enums to make setting of properties more transparent
353 by using the names and not just the values. To set or get the log level,
354 one can simply do:
355 
356 >>> logging.log_level = LogLevel.FATAL
357 >>> print("Logging level set to", logging.log_level)
358 FATAL
359 
360 This module also allows to send log messages directly from python to ease
361 consistent error reporting throughout the framework
362 
363 >>> B2WARNING("This is a warning message")
364 
365 .. seealso::
366 
367  For all features, see :download:`b2logging.py </framework/examples/b2logging.py>`)")
368  .add_property("log_level", &LogPythonInterface::getLogLevel, &LogPythonInterface::setLogLevel, R"DOC(
369 Attribute for setting/getting the current `log level <basf2.LogLevel>`.
370 Messages with a lower level are ignored.
371 
372 .. warning: Message with a level of `ERROR <LogLevel.ERROR>` or higher will always be shown and cannot be silenced.
373 )DOC")
374  .add_property("debug_level", &LogPythonInterface::getDebugLevel, &LogPythonInterface::setDebugLevel,
375  "Attribute for getting/setting the debug level. If debug messages are enabled, their level needs to be at least this high to be printed. Defaults to 100.")
376  .add_property("abort_level", &LogPythonInterface::getAbortLevel, &LogPythonInterface::setAbortLevel,
377  "Attribute for setting/getting the `log level <basf2.LogLevel>` at which to abort processing. Defaults to `FATAL <LogLevel.FATAL>` but can be set to a lower level in rare cases.")
378  .add_property("max_repetitions", &LogPythonInterface::getMaxMessageRepetitions, &LogPythonInterface::setMaxMessageRepetitions, R"DOC(
379 Set the maximum amount of times log messages with the same level and message text
380 (excluding variables) will be repeated before it is suppressed. Suppressed messages
381 will still be counted but not shown for the remainder of the processing.
382 
383 This affects messages with the same text but different ref:`logging_logvariables`.
384 If the same log message is repeated frequently with different variables all of
385 these will be suppressed after the given amount of repetitions.
386 
387 .. versionadded:: release-05-00-00
388 )DOC")
389 
390  .def("set_package", &LogPythonInterface::setPackageLogConfig, args("package", "config"),
391  "Set `basf2.LogConfig` for given package, see also `package() <basf2.LogPythonInterface.package>`.")
392  .def("package", &LogPythonInterface::getPackageLogConfig, return_value_policy<reference_existing_object>(), args("package"),
393  R"(Get the `LogConfig` for given package to set detailed logging pararameters for this package.
394 
395  >>> logging.package('svd').debug_level = 10
396  >>> logging.package('svd').set_info(LogLevel.INFO, LogInfo.LEVEL | LogInfo.MESSAGE | LogInfo.FILE)
397  )")
398  .def("set_info", &LogPythonInterface::setLogInfo, args("log_level", "log_info"),
399  R"DOCSTRING(Set info to print for given log level. Should be an OR combination of `basf2.LogInfo` constants.
400 As an example, to show only the level and text for all debug messages one could use
401 
402 >>> basf2.logging.set_info(basf2.LogLevel.DEBUG, basf2.LogInfo.LEVEL | basf2.LogInfo.MESSAGE)
403 
404 Parameters:
405  log_level (LogLevel): log level for which to set the display info
406  log_info (int): Bitmask of `basf2.LogInfo` constants.)DOCSTRING")
407  .def("get_info", &LogPythonInterface::getLogInfo, args("log_level"), "Get info to print for given log level.\n\n"
408  "Parameters:\n log_level (basf2.LogLevel): Log level for which to get the display info")
409  .def("add_file", &LogPythonInterface::addLogFile, (bp::arg("filename"), bp::arg("append") = false),
410  R"DOCSTRING(Write log output to given file. (In addition to existing outputs)\n\n"
411 
412 Parameters:
413  filename (str): Filename to to write log messages into
414  append (bool): If set to True the file will be truncated before writing new messages.)DOCSTRING")
415  .def("add_console", addLogConsole,
416  addLogConsole_overloads(args("enable_color"), "Write log output to console. (In addition to existing outputs). "
417  "If ``enable_color`` is not specified color will be enabled if supported"))
418  .def("add_json", &LogPythonInterface::addLogJSON, (bp::arg("complete_info") = false), R"DOCSTRING(
419 Write log output to console, but format log messages as json objects for
420 simplified parsing by other tools. Each log message will be printed as a one
421 line JSON object.
422 
423 .. versionadded:: release-03-00-00
424 
425 Parameters:
426  complete_info (bool): If this is set to True the complete log information is printed regardless of the `LogInfo` setting.
427 
428 See Also:
429  `add_console()`, `set_info()`
430 )DOCSTRING")
431  .def("add_udp", &LogPythonInterface::addLogUDP, (bp::arg("hostname"), bp::arg("port")), R"DOCSTRING(
432  Send the log output as a JSON object to the given hostname and port via UDP.
433 
434 .. versionadded:: release-04-00-00
435 
436 Parameters:
437  hostname (str): The hostname to send the message to. If it can not be resolved, an exception will be thrown.
438  port (int): The port on the host to send the message via UDP.
439 
440 See Also:
441  `add_json()`
442 )DOCSTRING")
443  .def("terminal_supports_colors", &terminalSupportsColors, "Returns true if the terminal supports colored output")
444  .staticmethod("terminal_supports_colors")
445  .def("reset", &LogPythonInterface::reset, "Remove all configured logging outputs. "
446  "You can then configure your own via `add_file() <basf2.LogPythonInterface.add_file>` "
447  "or `add_console() <basf2.LogPythonInterface.add_console>`")
448  .def("zero_counters", &LogPythonInterface::zeroCounters, "Reset the per-level message counters.")
449  .def_readonly("log_stats", &LogPythonInterface::getLogStatistics, "Returns dictionary with message counters.")
450  .def("enable_summary", &LogPythonInterface::enableErrorSummary, args("on"),
451  "Enable or disable the error summary printed at the end of processing. "
452  "Expects one argument whether or not the summary should be shown")
453  .add_property("enable_python_logging", &LogPythonInterface::getPythonLoggingEnabled,
454  &LogPythonInterface::setPythonLoggingEnabled, R"DOCSTRING(
455 Enable or disable logging via python. If this is set to true than log messages
456 will be sent via `sys.stdout`. This is probably slightly slower but is useful
457 when running in jupyter notebooks or when trying to redirect stdout in python
458 to a buffer. This setting affects all log connections to the
459 console.
460 
461 .. versionadded:: release-03-00-00)DOCSTRING")
462  .add_property("enable_escape_newlines", &LogPythonInterface::getEscapeNewlinesEnabled,
463  &LogPythonInterface::setEscapeNewlinesEnabled, R"DOCSTRING(
464 Enable or disable escaping of newlines in log messages to the console. If this
465 is set to true than any newline character in log messages printed to the console
466 will be replaced by a "\n" to ensure that every log messages fits exactly on one line.
467 
468 .. versionadded:: release-04-02-00)DOCSTRING")
469  ;
470 
471  //Expose Logging object
472  std::shared_ptr<LogPythonInterface> initguard{new LogPythonInterface()};
473  scope().attr("logging") = initguard;
474 
475  //Add all the logging functions. To handle arbitrary keyword arguments we add
476  //them as raw functions. However it seems setting the docstring needs to be
477  //done manually in this case. So create function objects, add to namespace,
478  //set docstring ...
479 
480  const std::string common_doc = R"DOCSTRING(
481 All additional positional arguments are converted to strings and concatenated
482 to the log message. All keyword arguments are added to the function as
483 :ref:`logging_logvariables`.)DOCSTRING";
484 
485  auto logDebug = raw_function(&LogPythonInterface::logDebug);
486  def("B2DEBUG", logDebug);
487  setattr(logDebug, "__doc__", "B2DEBUG(debugLevel, message, *args, **kwargs)\n\n"
488  "Print a `DEBUG <basf2.LogLevel.DEBUG>` message. "
489  "The first argument is the `debug_level <basf2.LogLevel.DEBUG>`. " +
490  common_doc);
491 
492  auto logInfo = raw_function(&LogPythonInterface::logInfo);
493  def("B2INFO", logInfo);
494  setattr(logInfo, "__doc__", "B2INFO(message, *args, **kwargs)\n\n"
495  "Print a `INFO <basf2.LogLevel.INFO>` message. " + common_doc);
496 
497  auto logResult = raw_function(&LogPythonInterface::logResult);
498  def("B2RESULT", logResult);
499  setattr(logResult, "__doc__", "B2RESULT(message, *args, **kwargs)\n\n"
500  "Print a `RESULT <basf2.LogLevel.RESULT>` message. " + common_doc
501  + "\n\n.. deprecated:: release-01-00-00\n use `B2INFO()` instead");
502 
503  auto logWarning = raw_function(&LogPythonInterface::logWarning);
504  def("B2WARNING", logWarning);
505  setattr(logWarning, "__doc__", "B2WARNING(message, *args, **kwargs)\n\n"
506  "Print a `WARNING <basf2.LogLevel.WARNING>` message. " + common_doc);
507 
508  auto logError = raw_function(&LogPythonInterface::logError);
509  def("B2ERROR", logError);
510  setattr(logError, "__doc__", "B2ERROR(message, *args, **kwargs)\n\n"
511  "Print a `ERROR <basf2.LogLevel.ERROR>` message. " + common_doc);
512 
513  auto logFatal = raw_function(&LogPythonInterface::logFatal);
514  def("B2FATAL", logFatal);
515  setattr(logFatal, "__doc__", "B2FATAL(message, *args, **kwargs)\n\n"
516  "Print a `FATAL <basf2.LogLevel.FATAL>` message. " + common_doc +
517  "\n\n.. note:: This also exits the programm with an error and is "
518  "guaranteed to not return.");
519 }
520 
521 namespace {
523  std::string pythonObjectToString(const boost::python::object& obj)
524  {
525  return boost::python::extract<std::string>(obj.attr("__str__")());
526  }
527 
532  auto pythonDictToMap(const dict& d)
533  {
534  std::map<std::string, std::string> result;
535  if (d.is_none()) return result;
536  const auto items = d.items();
537  const int size = len(d);
538  for (int i = 0; i < size; ++i) {
539  const auto key = pythonObjectToString(items[i][0]);
540  const auto val = pythonObjectToString(items[i][1]);
541  result.emplace(std::make_pair(key, val));
542  }
543  return result;
544  }
545 
551  void dispatchMessage(LogConfig::ELogLevel logLevel, boost::python::tuple args, const boost::python::dict& kwargs)
552  {
553  int debugLevel = 0;
554  const int firstArg = logLevel == LogConfig::c_Debug ? 1 : 0;
555  const int argSize = len(args);
556  if (argSize - firstArg <= 0) {
557  PyErr_SetString(PyExc_TypeError, ("At least " + std::to_string(firstArg + 1) + " positional arguments required").c_str());
558  boost::python::throw_error_already_set();
559  }
560  if (logLevel == LogConfig::c_Debug) {
561  boost::python::extract<int> proxy(args[0]);
562  if (!proxy.check()) {
563  PyErr_SetString(PyExc_TypeError, "First argument `debugLevel` must be an integer");
564  boost::python::throw_error_already_set();
565  }
566  debugLevel = proxy;
567  }
568  if (logLevel >= LogConfig::c_Error || Belle2::LogSystem::Instance().isLevelEnabled(logLevel, debugLevel, "steering")) {
569  //Finally we know we actually will send the message: concatenate all
570  //positional arguments and convert the keyword arguments to a python dict
571  stringstream message;
572  int size = len(args);
573  for (int i = firstArg; i < size; ++i) {
574  message << pythonObjectToString(args[i]);
575  }
576  const auto cppKwArgs = pythonDictToMap(kwargs);
577  LogVariableStream lvs(message.str(), cppKwArgs);
578 
579  // Now we also need to find out where the message came from: use the
580  // inspect module to get the filename/linenumbers
581  object inspect = import("inspect");
582  auto frame = inspect.attr("currentframe")();
583  const std::string function = extract<std::string>(frame.attr("f_code").attr("co_name"));
584  const std::string file = extract<std::string>(frame.attr("f_code").attr("co_filename"));
585  int line = extract<int>(frame.attr("f_lineno"));
586 
587  // Everything done, send it away
588  Belle2::LogSystem::Instance().sendMessage(Belle2::LogMessage(logLevel, std::move(lvs), "steering",
589  function, file, line, debugLevel));
590  }
591  }
592 }
593 
594 boost::python::object LogPythonInterface::logDebug(boost::python::tuple args, const boost::python::dict& kwargs)
595 {
596 #ifndef LOG_NO_B2DEBUG
597  dispatchMessage(LogConfig::c_Debug, std::move(args), kwargs);
598 #endif
599  return boost::python::object();
600 }
601 
602 boost::python::object LogPythonInterface::logInfo(boost::python::tuple args, const boost::python::dict& kwargs)
603 {
604 #ifndef LOG_NO_B2INFO
605  dispatchMessage(LogConfig::c_Info, std::move(args), kwargs);
606 #endif
607  return boost::python::object();
608 }
609 
610 boost::python::object LogPythonInterface::logResult(boost::python::tuple args, const boost::python::dict& kwargs)
611 {
612 #ifndef LOG_NO_B2RESULT
613  dispatchMessage(LogConfig::c_Result, std::move(args), kwargs);
614 #endif
615  return boost::python::object();
616 }
617 
618 boost::python::object LogPythonInterface::logWarning(boost::python::tuple args, const boost::python::dict& kwargs)
619 {
620 #ifndef LOG_NO_B2WARNING
621  dispatchMessage(LogConfig::c_Warning, std::move(args), kwargs);
622 #endif
623  return boost::python::object();
624 }
625 
626 boost::python::object LogPythonInterface::logError(boost::python::tuple args, const boost::python::dict& kwargs)
627 {
628  dispatchMessage(LogConfig::c_Error, std::move(args), kwargs);
629  return boost::python::object();
630 }
631 
632 boost::python::object LogPythonInterface::logFatal(boost::python::tuple args, const boost::python::dict& kwargs)
633 {
634  dispatchMessage(LogConfig::c_Fatal, std::move(args), kwargs);
635  std::exit(1);
636  return boost::python::object();
637 }
The LogConfig class.
Definition: LogConfig.h:22
ELogLevel
Definition of the supported log levels.
Definition: LogConfig.h:26
Implements a log connection to an IO Stream.
Implements a log connection that filters repeated messages.
Implements a log connection to stdout but with messages formatted as json objects to allow easy parsi...
Implements a log connection to a text file.
Log Connection to send the log message as JSON to a UDP server.
The LogMessage class.
Definition: LogMessage.h:29
Thin wrapper to expose a usable interface to the logging framework in python.
Class for logging debug, info and error messages.
Definition: LogSystem.h:46
bool sendMessage(LogMessage &&message)
Sends a log message using the log connection object.
Definition: LogSystem.cc:69
int getMessageCounter(LogConfig::ELogLevel logLevel) const
Returns the number of logging calls per log level.
Definition: LogSystem.cc:161
static LogSystem & Instance()
Static method to get a reference to the LogSystem instance.
Definition: LogSystem.cc:31
Specialized implementation of an ostream-like class where the << operator can be used to insert value...
Abstract base class for different kinds of events.