1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
|
/*************************************************************************
* 2006-2008 Václav Šmilauer *
* 2019 Janek Kozicki *
* *
* This program is free software; it is licensed under the terms of the *
* GNU General Public License v2 or later. See file LICENSE for details. *
*************************************************************************/
#pragma once
/*
* This file defines various useful logging-related macros. For full description see https://yade-dem.org/doc/prog.html#debugging
* Quick summary:
* - LOG_* for actual logging,
* - DECLARE_LOGGER; that should be used in class header to create separate logger for that class,
* - CREATE_LOGGER(ClassName); that must be used in class implementation file to create the static variable.
* - CREATE_CPP_LOCAL_LOGGER("filename.cpp"); use this inside a *.cpp file which has code that does not belong to any class, and needs logging. The name will be used for filtering logging.
* - TRVARn(…) for printing variables
* - TRACE; for quick tracing
*
* Yade has the logging config file by default in ~/.yade-$VERSION/logging.conf.
*
* Note: The Logging.[hc]pp files and Singleton.hpp are competely independent. They can be used in unrelated projects under license GNU GPL v2 or later,
* you only need to add -DBOOST_LOG_DYN_LINK -lboost_log -lboost_log_setup to your compilation options.
* Don't use -DBOOST_LOG_DYN_LINK if you use -static linking, which normally you shouldn't unless there is a really good reason.
*
*/
// boost::log inspired by git show 014b11496
#ifdef YADE_BOOST_LOG
// workaruond bug https://github.com/boostorg/multiprecision/issues/207, later add here ' and (BOOST_VERSION < 107?00)' to not include this after it is fixed.
#include <boost/version.hpp>
#if defined(YADE_MPFR) and (BOOST_VERSION >= 106800)
#include <lib/base/Math.hpp>
#endif
#include <lib/base/Singleton.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup.hpp>
#include <boost/preprocessor.hpp>
#include <map>
#include <string>
#include <vector>
// TODO: move this when fixing https://gitlab.com/yade-dev/trunk/issues/97
constexpr unsigned long hash(const char* str, int ha = 0) { return !str[ha] ? 5381 : (hash(str, ha + 1) * 33) ^ str[ha]; }
#define _LOG_HEAD \
":" << Logging::instance().colorLineNumber() << __LINE__ << Logging::instance().colorFunction() << " " << __PRETTY_FUNCTION__ \
<< Logging::instance().colorEnd() << ": "
// If you get "error: ‘logger’ was not declared in this scope" then you have to declare logger.
// Use DECLARE_LOGGER; inside class and CREATE_LOGGER(ClassName); inside .cpp file
// or use CREATE_CPP_LOCAL_LOGGER("filename.cpp") if you need logging outside some class.
#define LOG_TRACE(msg) \
{ \
BOOST_LOG_SEV(logger, Logging::SeverityLevel::eTRACE) << _LOG_HEAD << msg; \
}
#define LOG_DEBUG(msg) \
{ \
BOOST_LOG_SEV(logger, Logging::SeverityLevel::eDEBUG) << _LOG_HEAD << msg; \
}
#define LOG_INFO(msg) \
{ \
BOOST_LOG_SEV(logger, Logging::SeverityLevel::eINFO) << _LOG_HEAD << msg; \
}
#define LOG_WARN(msg) \
{ \
BOOST_LOG_SEV(logger, Logging::SeverityLevel::eWARN) << _LOG_HEAD << msg; \
}
#define LOG_ERROR(msg) \
{ \
BOOST_LOG_SEV(logger, Logging::SeverityLevel::eERROR) << _LOG_HEAD << msg; \
}
#define LOG_FATAL(msg) \
{ \
BOOST_LOG_SEV(logger, Logging::SeverityLevel::eFATAL) << _LOG_HEAD << msg; \
}
#define LOG_NOFILTER(msg) \
{ \
boost::log::sources::severity_logger<Logging::SeverityLevel> slg; \
BOOST_LOG_SEV(slg, Logging::eNOFILTER) << _LOG_HEAD << msg; \
}
class Logging : public Singleton<Logging> {
public:
enum SeverityLevel { eNOFILTER = 0, eFATAL = 1, eERROR = 2, eWARN = 3, eINFO = 4, eDEBUG = 5, eTRACE = 6 };
Logging();
void readConfigFile(const std::string&);
void saveConfigFile(const std::string&);
std::string defaultConfigFileName();
void setOutputStream(const std::string&, bool reset);
short int getDefaultLogLevel() const { return defaultLogLevel; };
short int getNamedLogLevel(const std::string&) const;
void setNamedLogLevel(const std::string&, short int);
void unsetNamedLogLevel(const std::string&);
boost::log::sources::severity_logger<Logging::SeverityLevel> createNamedLogger(std::string name);
const std::map<std::string, short int>& getClassLogLevels() const { return classLogLevels; };
static constexpr short int maxLogLevel { MAX_LOG_LEVEL };
void setUseColors(bool);
std::string colorSeverity(Logging::SeverityLevel);
std::string colorNameTag();
std::string colorLineNumber();
std::string colorFunction();
std::string colorEnd();
private:
void setDefaultLogLevel(short int);
typedef boost::log::sinks::synchronous_sink<boost::log::sinks::text_ostream_backend> TextSink;
const short int& findFilterName(const std::string&) const;
short int& findFilterName(const std::string&);
void updateFormatter();
short int defaultLogLevel {
(short int)(SeverityLevel::eWARN)
}; // see constructor in Logging.cpp, at later stages of initialization it is overwritten by core/main/main.py.in line 163
std::map<std::string, short int> classLogLevels { { "Default", defaultLogLevel } };
boost::shared_ptr<TextSink> sink { boost::make_shared<TextSink>() };
boost::shared_ptr<std::ostream> streamClog {}, streamCerr {}, streamCout {}, streamFile {};
std::vector<boost::shared_ptr<std::ostream>> streamOld {};
bool colors { false }; // can't be set here to avoid race condition, in later initialization it is overwritten by core/main/main.py.in line 163
const std::string esc { char { 27 } };
std::string lastOutputStream {};
boost::log::sources::severity_logger<Logging::SeverityLevel> logger {};
FRIEND_SINGLETON(Logging);
};
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", Logging::SeverityLevel)
BOOST_LOG_ATTRIBUTE_KEYWORD(class_name_tag, "NameTag", std::string)
inline std::ostream& operator<<(std::ostream& strm, Logging::SeverityLevel level) // necessary for formatting output.
{
static std::vector<std::string> names = { "NOFILTER", "FATAL ERROR", "ERROR", "WARNING", "INFO", "DEBUG", "TRACE" };
if ((int(level) < int(names.size())) and (int(level) >= 0))
strm << Logging::instance().colorSeverity(level) << names[level] << Logging::instance().colorEnd();
else
strm << int(level);
return strm;
}
// logger is local for every class, but if it is missing, we will use the parent's class logger automagically.
#define DECLARE_LOGGER \
public: \
static boost::log::sources::severity_logger<Logging::SeverityLevel> logger
#define CREATE_LOGGER(classname) \
boost::log::sources::severity_logger<Logging::SeverityLevel> classname::logger = Logging::instance().createNamedLogger(#classname)
#define TEMPLATE_CREATE_LOGGER(ClassName) template <> CREATE_LOGGER(ClassName) /* Use this when creating a logger for a templated ClassName<SomeOtherClass> */
#define CREATE_CPP_LOCAL_LOGGER(filtername) \
namespace { \
boost::log::sources::severity_logger<Logging::SeverityLevel> logger = Logging::instance().createNamedLogger(filtername); \
}
#else // #ifdef YADE_BOOST_LOG, without boost::log use the simplified logging method:
#include <iostream>
#define _POOR_MANS_LOG(level, msg) \
{ \
std::cerr << level " " << _LOG_HEAD << msg << std::endl; \
}
#define _LOG_HEAD __FILE__ ":" << __LINE__ << " " << __PRETTY_FUNCTION__ << ": "
#ifdef YADE_DEBUG
// when compiling with debug symbols and without boost::log it will print everything.
#define MAX_HARDCODED_LOG_LEVEL 6
#define LOG_TRACE(msg) _POOR_MANS_LOG("TRACE_6", msg)
#define LOG_DEBUG(msg) _POOR_MANS_LOG("DEBUG_5", msg)
#define LOG_INFO(msg) _POOR_MANS_LOG("INFO__4", msg)
#else
#define MAX_HARDCODED_LOG_LEVEL 3
#define LOG_TRACE(msg) \
{ \
_IGNORE_LOG(msg); \
}
#define LOG_DEBUG(msg) \
{ \
_IGNORE_LOG(msg); \
}
#define LOG_INFO(msg) \
{ \
_IGNORE_LOG(msg); \
}
#endif
#define LOG_WARN(msg) _POOR_MANS_LOG("WARN__3", msg)
#define LOG_ERROR(msg) _POOR_MANS_LOG("ERROR_2", msg)
#define LOG_FATAL(msg) _POOR_MANS_LOG("FATAL_1", msg)
#define LOG_NOFILTER(msg) _POOR_MANS_LOG("NOFILTER", msg)
#define DECLARE_LOGGER
#define CREATE_LOGGER(classname)
#define TEMPLATE_CREATE_LOGGER(classname)
#define CREATE_CPP_LOCAL_LOGGER(name)
#endif // END #ifdef YADE_BOOST_LOG
// macros for quick debugging
#define TRACE LOG_TRACE("Been here")
#define _TRV(x) #x "=" << x << "; "
#define TRVAR1(a) LOG_TRACE(_TRV(a))
#define TRVAR2(a, b) LOG_TRACE(_TRV(a) << _TRV(b))
#define TRVAR3(a, b, c) LOG_TRACE(_TRV(a) << _TRV(b) << _TRV(c))
#define TRVAR4(a, b, c, d) LOG_TRACE(_TRV(a) << _TRV(b) << _TRV(c) << _TRV(d))
#define TRVAR5(a, b, c, d, e) LOG_TRACE(_TRV(a) << _TRV(b) << _TRV(c) << _TRV(d) << _TRV(e))
#define TRVAR6(a, b, c, d, e, f) LOG_TRACE(_TRV(a) << _TRV(b) << _TRV(c) << _TRV(d) << _TRV(e) << _TRV(f))
#define TRVARn_PRINT_ONE(r, SKIP, VARn) TRVAR1(VARn)
// this one prints arbitrary number of variables, but they must be a boost preprocessor sequence like (var1)(var2)(var3), see py/_log.cpp for example usage.
#define TRVARn(ALL_VARS) BOOST_PP_SEQ_FOR_EACH(TRVARn_PRINT_ONE, ~, ALL_VARS)
// Logger aliases:
#define LOG_6_TRACE(msg) LOG_TRACE(msg)
#define LOG_5_DEBUG(msg) LOG_DEBUG(msg)
#define LOG_4_INFO(msg) LOG_INFO(msg)
#define LOG_3_WARN(msg) LOG_WARN(msg)
#define LOG_2_ERROR(msg) LOG_ERROR(msg)
#define LOG_1_FATAL(msg) LOG_FATAL(msg)
#define LOG_0_NOFILTER(msg) LOG_NOFILTER(msg)
#define LOG_6(msg) LOG_TRACE(msg)
#define LOG_5(msg) LOG_DEBUG(msg)
#define LOG_4(msg) LOG_INFO(msg)
#define LOG_3(msg) LOG_WARN(msg)
#define LOG_2(msg) LOG_ERROR(msg)
#define LOG_1(msg) LOG_FATAL(msg)
#define LOG_0(msg) LOG_NOFILTER(msg)
// honor MAX_LOG_LEVEL cmake option to disable selected macros
#if MAX_LOG_LEVEL < 6
#undef LOG_TRACE
#define LOG_TRACE(msg) \
{ \
_IGNORE_LOG(msg); \
}
#endif
#if MAX_LOG_LEVEL < 5
#undef LOG_DEBUG
#define LOG_DEBUG(msg) \
{ \
_IGNORE_LOG(msg); \
}
#endif
#if MAX_LOG_LEVEL < 4
#undef LOG_INFO
#define LOG_INFO(msg) \
{ \
_IGNORE_LOG(msg); \
}
#endif
#if MAX_LOG_LEVEL < 3
#undef LOG_WARN
#define LOG_WARN(msg) \
{ \
_IGNORE_LOG(msg); \
}
#endif
#if MAX_LOG_LEVEL < 2
#warning "MAX_LOG_LEVEL<2 means that all LOG_ERROR messages are ignored, be careful with this option."
#undef LOG_ERROR
#define LOG_ERROR(msg) \
{ \
_IGNORE_LOG(msg); \
}
#endif
#if MAX_LOG_LEVEL < 1
#warning "MAX_LOG_LEVEL<1 means that all LOG_ERROR, LOG_FATAL messages are ignored, be careful with this option."
#undef LOG_FATAL
#define LOG_FATAL(msg) \
{ \
_IGNORE_LOG(msg); \
}
#endif
#if MAX_LOG_LEVEL < 0
#warning "MAX_LOG_LEVEL<0 means that all LOG_ERROR, LOG_FATAL and LOG_NOFILTER messages are are ignored, be careful with this option."
#undef LOG_NOFILTER
#define LOG_NOFILTER(msg) \
{ \
_IGNORE_LOG(msg); \
}
#endif
// The msg must be used to avoid -Werror about unused variable. But the code is eliminated anyway.
// https://stackoverflow.com/questions/10809429/what-kind-of-dead-code-can-gcc-eliminate-from-the-final-output , added -fdce flag, to be sure.
#define _IGNORE_LOG(msg) \
{ \
if (false) { std::cerr << msg; } \
}
|