A SystemC productivity library for virtual platform development utilizing SCV and TLM2.0 https://www.minres.com/#opensource

logging.h 9.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. /*******************************************************************************
  2. * Copyright 2017, 2018 MINRES Technologies GmbH
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *******************************************************************************/
  16. #ifndef _UTIL_LOGGING_H_
  17. #define _UTIL_LOGGING_H_
  18. #include <cassert>
  19. #include <cstdio>
  20. #include <cstring>
  21. #include <iomanip>
  22. #include <iterator>
  23. #include <mutex>
  24. #include <sstream>
  25. #include <string>
  26. #include <sys/time.h>
  27. #include <vector>
  28. //! log level definitions
  29. #define LEVELS(L) L(NONE) L(FATAL) L(ERROR) L(WARNING) L(INFO) L(DEBUG) L(TRACE)
  30. #define DO_DESCRIPTION(e) #e,
  31. #define DO_ENUM(e) e,
  32. namespace logging {
  33. //! array holding string representations of log levels
  34. static std::array<const char *const, 7> buffer = {{LEVELS(DO_DESCRIPTION)}};
  35. //! enum defining the log levels
  36. enum log_level { LEVELS(DO_ENUM) };
  37. /**
  38. * safely convert an integer into a log level
  39. * @param logLevel the integer
  40. * @return the log level
  41. */
  42. inline log_level as_log_level(int logLevel) {
  43. assert(logLevel >= NONE && logLevel <= TRACE);
  44. std::array<const log_level, 7> m = {{NONE, FATAL, ERROR, WARNING, INFO, DEBUG, TRACE}};
  45. return m[logLevel];
  46. }
  47. /**
  48. * get the current host time as string
  49. * @return
  50. */
  51. inline std::string now_time();
  52. /**
  53. * the logger class
  54. */
  55. template <typename T> class Log {
  56. public:
  57. /**
  58. * default constructor
  59. */
  60. Log() = default;
  61. /**
  62. * no copy constructor
  63. *
  64. * @param
  65. */
  66. Log(const Log &) = delete;
  67. /**
  68. * no copy assignment constructor
  69. *
  70. * @param
  71. * @return
  72. */
  73. Log &operator=(const Log &) = delete;
  74. /**
  75. * the destructor
  76. */
  77. virtual ~Log() {
  78. os << std::endl;
  79. T::output(os.str());
  80. // TODO: use a more specific exception
  81. if (get_last_log_level() == FATAL) abort();
  82. }
  83. /**
  84. * get the underlying ostringstream for a certain log level and category
  85. *
  86. * @param level the log level
  87. * @param category the category string
  88. * @return the underlying output stream
  89. */
  90. std::ostream &get(log_level level = INFO, const char *category = "") {
  91. if (print_time()) os << "- " << now_time() << " ";
  92. if (print_severity()) {
  93. os << std::setw(7) << std::left << to_string(level);
  94. if (strlen(category)) os << "[" << category << "]";
  95. os << ": ";
  96. }
  97. get_last_log_level() = level;
  98. return os;
  99. };
  100. /**
  101. * get a reference to the configured logging level
  102. *
  103. * @return the logging level
  104. */
  105. static log_level &reporting_level() {
  106. static log_level reportingLevel = WARNING;
  107. return reportingLevel;
  108. }
  109. /**
  110. * translate a lg level to a string
  111. *
  112. * @param level the log level
  113. * @return the string representing the log level
  114. */
  115. static std::string to_string(log_level level) { return std::string(get_log_level_cstr()[level]); };
  116. /**
  117. * parse a log level from a string
  118. *
  119. * @param level the string representing the log level
  120. * @return the log level
  121. */
  122. static log_level from_string(const std::string &level) {
  123. for (unsigned int i = NONE; i <= TRACE; i++)
  124. if (!strncasecmp(level.c_str(), (const char *)(get_log_level_cstr() + i),
  125. strlen((const char *)get_log_level_cstr() + i)))
  126. return static_cast<log_level>(i);
  127. Log<T>().Get(WARNING) << "Unknown logging level '" << level << "'. Using INFO level as default.";
  128. return INFO;
  129. }
  130. /**
  131. * get the reference to the flag indicating if the current host time should be part of the log
  132. *
  133. * @return the print time flag
  134. */
  135. static bool &print_time() {
  136. static bool flag = true;
  137. return flag;
  138. }
  139. /**
  140. * get the reference to the flag indicating if severity should be part of the log
  141. *
  142. * @return the print severity flag
  143. */
  144. static bool &print_severity() {
  145. static bool flag = true;
  146. return flag;
  147. }
  148. protected:
  149. log_level &get_last_log_level() {
  150. static log_level level = TRACE;
  151. return level;
  152. }
  153. static const char *const *get_log_level_cstr() { return buffer.data(); };
  154. std::ostringstream os;
  155. };
  156. /**
  157. * the output writer
  158. */
  159. template <typename CATEGORY> class Output2FILE : CATEGORY {
  160. public:
  161. /**
  162. * get the file handle of the underlying output file (or stdout)
  163. *
  164. * @return the file handle
  165. */
  166. static FILE *&stream() {
  167. static FILE *pStream = stdout;
  168. return pStream;
  169. }
  170. /**
  171. * write an output string to the file
  172. *
  173. * @param msg the string to write
  174. */
  175. static void output(const std::string &msg) {
  176. static std::mutex mtx;
  177. std::lock_guard<std::mutex> lock(mtx);
  178. FILE *pStream = stream();
  179. if (!pStream) return;
  180. fprintf(pStream, "%s", msg.c_str());
  181. fflush(pStream);
  182. }
  183. };
  184. //! the default logging category
  185. class DEFAULT {};
  186. #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
  187. #if defined(BUILDING_FILELOG_DLL)
  188. #define FILELOG_DECLSPEC __declspec(dllexport)
  189. #elif defined(USING_FILELOG_DLL)
  190. #define FILELOG_DECLSPEC __declspec(dllimport)
  191. #else
  192. #define FILELOG_DECLSPEC
  193. #endif // BUILDING_DBSIMPLE_DLL
  194. #else
  195. #define FILELOG_DECLSPEC
  196. #endif // _WIN32
  197. #ifndef FILELOG_MAX_LEVEL
  198. #define FILELOG_MAX_LEVEL logging::TRACE
  199. #endif
  200. #define LOGGER(CATEGORY) logging::Log<logging::Output2FILE<logging::CATEGORY>>
  201. #define LOG_OUTPUT(CATEGORY) logging::Output2FILE<logging::CATEGORY>
  202. #ifndef LOG
  203. #define LOG(LEVEL) \
  204. if (logging::LEVEL <= LOGGER(DEFAULT)::reporting_level() && LOG_OUTPUT(DEFAULT)::stream()) \
  205. LOGGER(DEFAULT)().get(logging::LEVEL)
  206. #endif
  207. #ifndef CLOG
  208. #define CLOG(LEVEL, CATEGORY) \
  209. if (logging::LEVEL <= LOGGER(CATEGORY)::reporting_level() && LOG_OUTPUT(CATEGORY)::stream()) \
  210. LOGGER(CATEGORY)().get(logging::LEVEL, #CATEGORY)
  211. #endif
  212. #if defined(WIN32)
  213. #include <array>
  214. #include <windows.h>
  215. inline std::string now_time() {
  216. const int MAX_LEN = 200;
  217. char buffer[MAX_LEN];
  218. if (GetTimeFormatA(LOCALE_USER_DEFAULT, 0, 0, "HH':'mm':'ss", buffer, MAX_LEN) == 0) return "Error in now_time()";
  219. char result[100] = {0};
  220. static DWORD first = GetTickCount();
  221. std::sprintf(result, "%s.%03ld", buffer, (long)(GetTickCount() - first) % 1000);
  222. return result;
  223. }
  224. #else
  225. inline std::string now_time() {
  226. static std::array<char, 11> buffer;
  227. static std::array<char, 100> result;
  228. time_t t;
  229. time(&t);
  230. tm r = {0};
  231. strftime(buffer.data(), buffer.size(), "%X", localtime_r(&t, &r));
  232. struct timeval tv;
  233. gettimeofday(&tv, nullptr);
  234. memset(result.data(), 100, 1);
  235. sprintf(result.data(), "%s.%03ld", buffer.data(), (long)tv.tv_usec / 1000);
  236. return result.data();
  237. }
  238. #endif // WIN32
  239. //! a print function for a vector
  240. template <typename T> std::ostream &operator<<(std::ostream &stream, const std::vector<T> &vector) {
  241. copy(vector.begin(), vector.end(), std::ostream_iterator<T>(stream, ","));
  242. return stream;
  243. }
  244. } // namespace
  245. #undef LEVELS
  246. #undef CAT
  247. #ifndef NDEBUG
  248. //! check only in debug mode
  249. #define ASSERT(condition, message) \
  250. do { \
  251. if (!(condition)) { \
  252. logging::Logger().Get(logging::fatal) << "Assertion `" #condition "` failed in " << __FILE__ << " line " \
  253. << __LINE__ << ": " << message << std::endl; \
  254. std::terminate(); \
  255. } \
  256. } while (false)
  257. #else
  258. #define ASSERT(condition, message) \
  259. do { \
  260. } while (false)
  261. #endif
  262. //! check always
  263. #define CHECK(condition, message) \
  264. do { \
  265. if (!(condition)) { \
  266. logging::Logger().Get(logging::fatal) << "Check of `" #condition "` failed in " << __FILE__ << " line " \
  267. << __LINE__ << ": " << message << std::endl; \
  268. std::terminate(); \
  269. } \
  270. } while (false)
  271. #endif /* _UTIL_LOGGING_H_ */