scc  2024.06
SystemC components library
logging.h
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 
17 #pragma once
18 
19 #include <array>
20 #include <atomic>
21 #include <cassert>
22 #include <cstdio>
23 #include <cstring>
24 #include <iomanip>
25 #include <iostream>
26 #include <iterator>
27 #include <mutex>
28 #include <sstream>
29 #include <string>
30 #include <vector>
31 #if defined(WIN32)
32 #include <array>
33 #include <windows.h>
34 #else
35 #include <sys/time.h>
36 #endif
37 
39 #define LEVELS(L) L(NONE) L(FATAL) L(ERR) L(WARN) L(INFO) L(DEBUG) L(TRACE) L(TRACEALL)
40 #define DO_DESCRIPTION(e) #e,
41 #define DO_ENUM(e) e,
42 
48 namespace logging {
50 static const std::array<const char* const, 8> buffer = {{LEVELS(DO_DESCRIPTION)}};
52 enum log_level { LEVELS(DO_ENUM) DBGTRACE = TRACEALL };
58 inline log_level as_log_level(int logLevel) {
59  assert(logLevel >= NONE && logLevel <= TRACEALL);
60  std::array<const log_level, 8> m = {{NONE, FATAL, ERR, WARN, INFO, DEBUG, TRACE, TRACEALL}};
61  return m[logLevel];
62 }
69 inline std::istream& operator>>(std::istream& is, log_level& val) {
70  std::string buf;
71  is >> buf;
72  for(auto i = 0U; i <= log_level::TRACEALL; ++i) {
73  if(strcmp(buf.c_str(), buffer[i]) == 0) {
74  val = as_log_level(i);
75  return is;
76  }
77  }
78  return is;
79 }
84 inline std::string now_time();
88 template <typename T> class Log {
89 public:
90  Log() = default;
91 
92  Log(const Log&) = delete;
93 
94  Log& operator=(const Log&) = delete;
100  virtual ~Log() {
101  os << std::endl;
102  T::output(os.str());
103  // TODO: use a more specific exception
104  if(get_last_log_level() == FATAL && abort_on_fatal())
105  abort();
106  }
114  std::ostream& get(log_level level = INFO, const char* category = "") {
115  if(print_time())
116  os << "- " << now_time() << " ";
117  if(print_severity()) {
118  os << std::setw(7) << std::left << to_string(level);
119  if(strlen(category))
120  os << "[" << category << "]";
121  os << ": " << std::internal;
122  }
123  get_last_log_level() = level;
124  return os;
125  };
131  static std::atomic<log_level>& reporting_level() {
132  static std::atomic<log_level> reportingLevel{WARN};
133  return reportingLevel;
134  }
140  static log_level get_reporting_level() { return reporting_level().load(std::memory_order_relaxed); }
146  static std::atomic<bool>& abort_on_fatal() {
147  static std::atomic<bool> flag{false};
148  return flag;
149  }
156  static std::string to_string(log_level level) { return std::string(get_log_level_cstr()[level]); };
163  static log_level from_string(const std::string& level) {
164  for(unsigned int i = NONE; i <= TRACE; i++)
165  if(!strncasecmp(level.c_str(), (const char*)(get_log_level_cstr() + i), strlen((const char*)get_log_level_cstr() + i)))
166  return static_cast<log_level>(i);
167  Log<T>().Get(WARN) << "Unknown logging level '" << level << "'. Using INFO level as default.";
168  return INFO;
169  }
175  static std::atomic<bool>& print_time() {
176  static std::atomic<bool> flag{true};
177  return flag;
178  }
184  static std::atomic<bool>& print_severity() {
185  static std::atomic<bool> flag{true};
186  return flag;
187  }
188 
189 protected:
190  std::atomic<log_level>& get_last_log_level() {
191  static std::atomic<log_level> level{TRACE};
192  return level;
193  }
194  static const char* const* get_log_level_cstr() { return buffer.data(); };
195  std::ostringstream os;
196 };
200 template <typename CATEGORY> class Output2FILE : CATEGORY {
201 public:
207  static std::atomic<FILE*>& stream() {
208  static std::atomic<FILE*> pStream{stdout};
209  return pStream;
210  }
211 
212  static std::atomic<std::ostream*>& ostream() {
213  static std::atomic<std::ostream*> oStream{nullptr};
214  return oStream;
215  }
221  static void output(const std::string& msg) {
222  static std::mutex mtx;
223  std::lock_guard<std::mutex> lock(mtx);
224  std::ostream* ostr = ostream();
225  if(ostr) {
226  *ostr << msg;
227  } else {
228  FILE* pStream = stream();
229  if(pStream) {
230  fprintf(pStream, "%s", msg.c_str());
231  fflush(pStream);
232  }
233  }
234  }
235 };
237 struct DEFAULT {
238  constexpr static char const* name = "default";
239 };
240 
241 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
242 #if defined(BUILDING_FILELOG_DLL)
243 #define FILELOG_DECLSPEC __declspec(dllexport)
244 #elif defined(USING_FILELOG_DLL)
245 #define FILELOG_DECLSPEC __declspec(dllimport)
246 #else
247 #define FILELOG_DECLSPEC
248 #endif // BUILDING_DBSIMPLE_DLL
249 #else
250 #define FILELOG_DECLSPEC
251 #endif // _WIN32
252 
253 #ifndef FILELOG_MAX_LEVEL
254 #define FILELOG_MAX_LEVEL ::logging::TRACE
255 #endif
256 
257 #define _LOGGER(CATEGORY) ::logging::Log<::logging::Output2FILE<CATEGORY>>
258 #define LOGGER(CATEGORY) _LOGGER(::logging::CATEGORY)
259 #define _LOG_OUTPUT(CATEGORY) ::logging::Output2FILE<CATEGORY>
260 #define LOG_OUTPUT(CATEGORY) _LOG_OUTPUT(::logging::CATEGORY)
261 
262 #ifndef LOG
263 #define LOG(LEVEL) \
264  if(::logging::LEVEL <= LOGGER(DEFAULT)::get_reporting_level() && LOG_OUTPUT(DEFAULT)::stream()) \
265  LOGGER(DEFAULT)().get(::logging::LEVEL)
266 #endif
267 #define CPPLOG(LEVEL) \
268  if(::logging::LEVEL <= LOGGER(DEFAULT)::get_reporting_level() && LOG_OUTPUT(DEFAULT)::stream()) \
269  LOGGER(DEFAULT)().get(::logging::LEVEL)
270 #ifndef CLOG
271 #define NSCLOG(LEVEL, CATEGORY) \
272  if(::logging::LEVEL <= _LOGGER(CATEGORY)::get_reporting_level() && _LOG_OUTPUT(CATEGORY)::stream()) \
273  _LOGGER(CATEGORY)().get(::logging::LEVEL, CATEGORY::name)
274 #define CLOG(LEVEL, CATEGORY) NSCLOG(LEVEL, ::logging::CATEGORY)
275 #endif
276 #if defined(WIN32)
277 inline std::string now_time() {
278  const int MAX_LEN = 200;
279  char buffer[MAX_LEN];
280  if(GetTimeFormatA(LOCALE_USER_DEFAULT, 0, 0, "HH':'mm':'ss", buffer, MAX_LEN) == 0)
281  return "Error in now_time()";
282  char result[100] = {0};
283  static DWORD first = GetTickCount();
284  std::sprintf(result, "%s.%03ld", buffer, (long)(GetTickCount() - first) % 1000);
285  return result;
286 }
287 #else
288 inline std::string now_time() {
289  static std::mutex mtx;
290  static std::array<char, 11> buffer;
291  static std::array<char, 100> result;
292  std::lock_guard<std::mutex> lck(mtx);
293  time_t t;
294  time(&t);
295  tm r{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, nullptr};
296  strftime(buffer.data(), buffer.size(), "%X", localtime_r(&t, &r));
297  struct timeval tv;
298  gettimeofday(&tv, nullptr);
299  memset(result.data(), 100, 1);
300  sprintf(result.data(), "%s.%03ld", buffer.data(), (long)tv.tv_usec / 1000);
301  return result.data();
302 }
303 #endif // WIN32
305 template <typename T> std::ostream& operator<<(std::ostream& stream, const std::vector<T>& vector) {
306  copy(vector.begin(), vector.end(), std::ostream_iterator<T>(stream, ","));
307  return stream;
308 }
309 } // namespace logging
311 #undef LEVELS
312 #undef CAT
313 
314 #ifndef NDEBUG
316 #define SCCASSERT(condition, message) \
317  do { \
318  if(!(condition)) { \
319  logging::Logger().Get(logging::fatal) \
320  << "Assertion `" #condition "` failed in " << __FILE__ << " line " << __LINE__ << ": " << message << std::endl; \
321  std::terminate(); \
322  } \
323  } while(false)
324 #else
325 #define SCCASSERT(condition, message) \
326  do { \
327  } while(false)
328 #endif
330 #define SCCCHECK(condition, message) \
331  do { \
332  if(!(condition)) { \
333  logging::Logger().Get(logging::fatal) \
334  << "Check of `" #condition "` failed in " << __FILE__ << " line " << __LINE__ << ": " << message << std::endl; \
335  std::terminate(); \
336  } \
337  } while(false)
static std::atomic< log_level > & reporting_level()
Definition: logging.h:131
virtual ~Log()
the destructor
Definition: logging.h:100
static std::string to_string(log_level level)
Definition: logging.h:156
static std::atomic< bool > & print_time()
Definition: logging.h:175
std::ostream & get(log_level level=INFO, const char *category="")
Definition: logging.h:114
static log_level get_reporting_level()
Definition: logging.h:140
static std::atomic< bool > & abort_on_fatal()
Definition: logging.h:146
static std::atomic< bool > & print_severity()
Definition: logging.h:184
static log_level from_string(const std::string &level)
Definition: logging.h:163
static void output(const std::string &msg)
Definition: logging.h:221
static std::atomic< FILE * > & stream()
Definition: logging.h:207
SCC C++ logging.
Definition: logging.h:48
log_level as_log_level(int logLevel)
Definition: logging.h:58
log_level
enum defining the log levels
Definition: logging.h:52
std::istream & operator>>(std::istream &is, log_level &val)
Definition: logging.h:69
std::string now_time()
Definition: logging.h:288
the default logging category
Definition: logging.h:237