scc 2025.09
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#include <functional>
38
40#define LEVELS(L) L(NONE) L(FATAL) L(ERR) L(WARN) L(INFO) L(DEBUG) L(TRACE) L(TRACEALL)
41#define DO_DESCRIPTION(e) #e,
42#define DO_ENUM(e) e,
43
49namespace logging {
51static const std::array<const char* const, 8> buffer = {{LEVELS(DO_DESCRIPTION)}};
53enum log_level { LEVELS(DO_ENUM) DBGTRACE = TRACEALL };
59inline log_level as_log_level(int logLevel) {
60 assert(logLevel >= NONE && logLevel <= TRACEALL);
61 std::array<const log_level, 8> m = {{NONE, FATAL, ERR, WARN, INFO, DEBUG, TRACE, TRACEALL}};
62 return m[logLevel];
63}
64
70inline std::istream& operator>>(std::istream& is, log_level& val) {
71 std::string buf;
72 is >> buf;
73 for(auto i = 0U; i <= log_level::TRACEALL; ++i) {
74 if(strcmp(buf.c_str(), buffer[i]) == 0) {
75 val = as_log_level(i);
76 return is;
77 }
78 }
79 return is;
80}
81
85inline std::string now_time();
86
88 using log_cb = std::function<void(log_level, std::string const&, std::string const&)>;
89 static void set_output_cb(log_cb cb) { _get_output_cb() = cb; }
90 static log_cb& get_output_cb() { return _get_output_cb(); }
91
92private:
93 static log_cb& _get_output_cb() {
94 static log_cb cb;
95 return cb;
96 }
97};
98
101template <typename T> class Log {
102public:
103 Log() = default;
104
105 Log(const Log&) = delete;
106
107 Log& operator=(const Log&) = delete;
113 virtual ~Log() noexcept(false) {
114 log_level lvl = get_last_log_level().load();
115 if(LoggerCallbacks::get_output_cb()) {
116 LoggerCallbacks::get_output_cb()(lvl, cat, os.str());
117 return;
118 }
119 os << std::endl;
120 T::output(os.str());
121 if(get_last_log_level() == FATAL && abort_on_fatal())
122 abort();
123 }
124
131 std::ostream& get(log_level level = INFO, const char* category = "") {
132 if(LoggerCallbacks::get_output_cb()) {
133 cat = category;
134 } else {
135 if(print_time())
136 os << "- " << now_time() << " ";
137 if(print_severity()) {
138 os << std::setw(7) << std::left << to_string(level);
139 if(strlen(category))
140 os << "[" << category << "]";
141 os << ": " << std::internal;
142 }
143 }
144 get_last_log_level() = level;
145 return os;
146 };
147
152 static log_level get_reporting_level() { return reporting_level().load(std::memory_order_relaxed); }
158 static void set_reporting_level(log_level lvl) { reporting_level().store(lvl, std::memory_order_seq_cst); }
164 static std::atomic<bool>& abort_on_fatal() {
165 static std::atomic<bool> flag{false};
166 return flag;
167 }
168
174 static std::string to_string(log_level level) { return std::string(get_log_level_cstr()[level]); };
181 static log_level from_string(const std::string& level) {
182 for(unsigned int i = NONE; i <= TRACE; i++)
183 if(!strncasecmp(level.c_str(), (const char*)(get_log_level_cstr() + i), strlen((const char*)get_log_level_cstr() + i)))
184 return static_cast<log_level>(i);
185 Log<T>().get(WARN) << "Unknown logging level '" << level << "'. Using INFO level as default.";
186 return INFO;
187 }
188
193 static std::atomic<bool>& print_time() {
194 static std::atomic<bool> flag{true};
195 return flag;
196 }
197
202 static std::atomic<bool>& print_severity() {
203 static std::atomic<bool> flag{true};
204 return flag;
205 }
206
207protected:
213 static std::atomic<log_level>& reporting_level() {
214 static std::atomic<log_level> reportingLevel{WARN};
215 return reportingLevel;
216 }
217 std::atomic<log_level>& get_last_log_level() {
218 static std::atomic<log_level> level{TRACE};
219 return level;
220 }
221 static const char* const* get_log_level_cstr() { return buffer.data(); };
222 std::string cat;
223 std::ostringstream os;
224};
225
228template <typename CATEGORY> class Output2FILE : CATEGORY {
229public:
235 static std::atomic<FILE*>& stream() {
236 static std::atomic<FILE*> pStream{stdout};
237 return pStream;
238 }
239
240 static std::atomic<std::ostream*>& ostream() {
241 static std::atomic<std::ostream*> oStream{nullptr};
242 return oStream;
243 }
249 static void output(const std::string& msg) {
250 static std::mutex mtx;
251 std::lock_guard<std::mutex> lock(mtx);
252 std::ostream* ostr = ostream();
253 if(ostr) {
254 *ostr << msg;
255 } else {
256 FILE* pStream = stream();
257 if(pStream) {
258 fprintf(pStream, "%s", msg.c_str());
259 fflush(pStream);
260 }
261 }
262 }
263};
264
265struct DEFAULT {
266 constexpr static char const* name = "default";
267};
268
269#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
270#if defined(BUILDING_FILELOG_DLL)
271#define FILELOG_DECLSPEC __declspec(dllexport)
272#elif defined(USING_FILELOG_DLL)
273#define FILELOG_DECLSPEC __declspec(dllimport)
274#else
275#define FILELOG_DECLSPEC
276#endif // BUILDING_DBSIMPLE_DLL
277#else
278#define FILELOG_DECLSPEC
279#endif // _WIN32
280
281#ifndef FILELOG_MAX_LEVEL
282#define FILELOG_MAX_LEVEL ::logging::TRACE
283#endif
284
285#define _LOGGER(CATEGORY) ::logging::Log<::logging::Output2FILE<CATEGORY>>
286#define LOGGER(CATEGORY) _LOGGER(::logging::CATEGORY)
287#define _LOG_OUTPUT(CATEGORY) ::logging::Output2FILE<CATEGORY>
288#define LOG_OUTPUT(CATEGORY) _LOG_OUTPUT(::logging::CATEGORY)
289
290#ifndef LOG
291#define LOG(LEVEL) \
292 if(::logging::LEVEL <= LOGGER(DEFAULT)::get_reporting_level() && LOG_OUTPUT(DEFAULT)::stream()) \
293 LOGGER(DEFAULT)().get(::logging::LEVEL)
294#endif
295
296// next few lines are tricky, its macro overloading
297// the one argument version
298#define CPPLOG1(LEVEL) \
299 if(::logging::LEVEL <= LOGGER(DEFAULT)::get_reporting_level() && LOG_OUTPUT(DEFAULT)::stream()) \
300 LOGGER(DEFAULT)().get(::logging::LEVEL, __PRETTY_FUNCTION__)
301// the two argument version
302#define CPPLOG2(LEVEL, TYPE) \
303 if(::logging::LEVEL <= LOGGER(DEFAULT)::get_reporting_level() && LOG_OUTPUT(DEFAULT)::stream()) \
304 LOGGER(DEFAULT)().get(::logging::LEVEL, TYPE)
305// This macro extracts the third argument (NAME) based on how many arguments are passed. It is used to route to the correct implementation.
306#define GET_CPPLOG_MACRO(_1, _2, NAME, ...) NAME
307// call the respective macro
308#define CPPLOG(...) GET_CPPLOG_MACRO(__VA_ARGS__, CPPLOG2, CPPLOG1)(__VA_ARGS__)
309
310#ifndef CLOG
311#define NSCLOG(LEVEL, CATEGORY) \
312 if(::logging::LEVEL <= _LOGGER(CATEGORY)::get_reporting_level() && _LOG_OUTPUT(CATEGORY)::stream()) \
313 _LOGGER(CATEGORY)().get(::logging::LEVEL, CATEGORY::name)
314#define CLOG(LEVEL, CATEGORY) NSCLOG(LEVEL, ::logging::CATEGORY)
315#endif
316#if defined(WIN32)
317inline std::string now_time() {
318 const int MAX_LEN = 200;
319 char buffer[MAX_LEN];
320 if(GetTimeFormatA(LOCALE_USER_DEFAULT, 0, 0, "HH':'mm':'ss", buffer, MAX_LEN) == 0)
321 return "Error in now_time()";
322 char result[100] = {0};
323 static DWORD first = GetTickCount();
324 std::sprintf(result, "%s.%03ld", buffer, (long)(GetTickCount() - first) % 1000);
325 return result;
326}
327#else
328inline std::string now_time() {
329 static std::mutex mtx;
330 static std::array<char, 11> buffer;
331 static std::array<char, 100> result;
332 std::lock_guard<std::mutex> lck(mtx);
333 time_t t;
334 time(&t);
335 tm r{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, nullptr};
336 strftime(buffer.data(), buffer.size(), "%X", localtime_r(&t, &r));
337 struct timeval tv;
338 gettimeofday(&tv, nullptr);
339 memset(result.data(), 100, 1);
340 sprintf(result.data(), "%s.%03ld", buffer.data(), (long)tv.tv_usec / 1000);
341 return result.data();
342}
343#endif // WIN32
345template <typename T> std::ostream& operator<<(std::ostream& stream, const std::vector<T>& vector) {
346 copy(vector.begin(), vector.end(), std::ostream_iterator<T>(stream, ","));
347 return stream;
348}
349} // namespace logging
350
351#undef LEVELS
352#undef CAT
353
354#ifndef NDEBUG
356#define SCCASSERT(condition, message) \
357 do { \
358 if(!(condition)) { \
359 logging::Logger().Get(logging::fatal) \
360 << "Assertion `" #condition "` failed in " << __FILE__ << " line " << __LINE__ << ": " << message << std::endl; \
361 std::terminate(); \
362 } \
363 } while(false)
364#else
365#define SCCASSERT(condition, message) \
366 do { \
367 } while(false)
368#endif
370#define SCCCHECK(condition, message) \
371 do { \
372 if(!(condition)) { \
373 logging::Logger().Get(logging::fatal) \
374 << "Check of `" #condition "` failed in " << __FILE__ << " line " << __LINE__ << ": " << message << std::endl; \
375 std::terminate(); \
376 } \
377 } while(false)
static void set_reporting_level(log_level lvl)
Definition logging.h:158
static std::atomic< log_level > & reporting_level()
Definition logging.h:213
static std::atomic< bool > & abort_on_fatal()
Definition logging.h:164
virtual ~Log() noexcept(false)
the destructor
Definition logging.h:113
static std::string to_string(log_level level)
Definition logging.h:174
static log_level get_reporting_level()
Definition logging.h:152
static std::atomic< bool > & print_severity()
Definition logging.h:202
static std::atomic< bool > & print_time()
Definition logging.h:193
std::ostream & get(log_level level=INFO, const char *category="")
Definition logging.h:131
static log_level from_string(const std::string &level)
Definition logging.h:181
static std::atomic< FILE * > & stream()
Definition logging.h:235
static void output(const std::string &msg)
Definition logging.h:249
SCC C++ logging.
Definition logging.h:49
log_level as_log_level(int logLevel)
Definition logging.h:59
std::istream & operator>>(std::istream &is, log_level &val)
Definition logging.h:70
log_level
enum defining the log levels
Definition logging.h:53
std::string now_time()
Definition logging.h:328
the default logging category
Definition logging.h:265