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