diff --git a/.clang-tidy b/.clang-tidy index eff429960..6e94d11e4 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -17,7 +17,8 @@ Checks: '*, -google-runtime-references, -google-readability-todo, -google-readability-braces-around-statements, - -modernize-use-trailing-return-type' + -modernize-use-trailing-return-type, + -readability-isolate-declaration' HeaderFilterRegex: \.h AnalyzeTemporaryDtors: false diff --git a/sample/CMakeLists.txt b/sample/CMakeLists.txt index a1d2e21c1..dca5fd59b 100644 --- a/sample/CMakeLists.txt +++ b/sample/CMakeLists.txt @@ -1,36 +1,35 @@ -add_executable(a api.cpp) -target_link_libraries(a - slsDetectorShared +add_executable(using_logger using_logger.cpp) +target_link_libraries(using_logger slsSupportLib pthread rt ) -set_target_properties(a PROPERTIES +set_target_properties(using_logger PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin ) -add_executable(result useResult.cpp) -target_link_libraries(result - slsDetectorShared -) +# add_executable(result useResult.cpp) +# target_link_libraries(result +# slsDetectorShared +# ) -set_target_properties(result PROPERTIES - RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin -) +# set_target_properties(result PROPERTIES +# RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin +# ) -add_executable(udp udp.cpp) -target_link_libraries(udp - slsDetectorShared - slsSupportLib - pthread - rt - fmt -) +# add_executable(udp udp.cpp) +# target_link_libraries(udp +# slsDetectorShared +# slsSupportLib +# pthread +# rt +# fmt +# ) -set_target_properties(udp PROPERTIES - RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin -) +# set_target_properties(udp PROPERTIES +# RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin +# ) diff --git a/sample/using_logger.cpp b/sample/using_logger.cpp new file mode 100644 index 000000000..97574a642 --- /dev/null +++ b/sample/using_logger.cpp @@ -0,0 +1,47 @@ +#include "logger.h" +#include "logger2.h" + +#include +#include +int main() { + + //compare old and new + std::cout << "Compare output between old and new:\n"; + FILE_LOG(logINFO) << "Old message"; + LOG(logINFO) << "New message"; + FILE_LOG(logERROR) << "Old error"; + LOG(logERROR) << "New error"; + FILE_LOG(logWARNING) << "Old warning"; + LOG(logWARNING) << "New warning"; + + //Logging level can be configure at runtime + std::cout << "\n\n"; + std::cout << "The default macro controlled level is: " + << sls::Logger::ToString(LOG_MAX_REPORTING_LEVEL) << '\n'; + + sls::Logger::ReportingLevel() = logERROR; + LOG(logINFO) << "Now this is not written"; + LOG(logWARNING) << "and also not this"; + + sls::Logger::ReportingLevel() = logINFO; + LOG(logINFO) << "But now we can see it"; + + + //The output can be redirected to another buffer + std::ostringstream local; + auto clog_buff = std::clog.rdbuf(); + std::clog.rdbuf(local.rdbuf()); + + LOG(logINFOBLUE) << "A message"; + LOG(logWARNING) << "And another one"; + + std::clog.rdbuf(clog_buff); // restore + + std::cout << "local buf:\n" << local.str(); + + LOG(logINFO) << "After reset we should print directly"; + LOG(logINFOBLUE) << "some infoBLUE text"; + LOG(logINFOGREEN) << "some infoGREEN text"; + LOG(logINFORED) << "some infoRED text"; + +} \ No newline at end of file diff --git a/slsSupportLib/include/logger2.h b/slsSupportLib/include/logger2.h new file mode 100644 index 000000000..cbd0d31a3 --- /dev/null +++ b/slsSupportLib/include/logger2.h @@ -0,0 +1,82 @@ +#pragma once +/*Utility to log to console*/ + +#include "ansi.h" //Colors +#include "logger.h" //for enum, to be removed +#include +#include + +// Compiler should optimize away anything below this value +#ifndef LOG_MAX_REPORTING_LEVEL +#define LOG_MAX_REPORTING_LEVEL logINFO +#endif + +namespace sls { +class Logger { + std::ostringstream os; + TLogLevel level = LOG_MAX_REPORTING_LEVEL; + + public: + Logger() = default; + explicit Logger(TLogLevel level) : level(level){}; + ~Logger() { + // output in the destructor to allow for << syntax + os << RESET << '\n'; + std::clog << os.str() << std::flush; // Single write + } + + static TLogLevel &ReportingLevel() { // singelton eeh + static TLogLevel reportingLevel = logINFO; + return reportingLevel; + } + + // Danger this buffer need as many elements as TLogLevel + static const char *Color(TLogLevel level) noexcept { + static const char *const colors[] = { + RED BOLD, YELLOW BOLD, BLUE, GREEN, RED, RESET, + RESET, RESET, RESET, RESET, RESET, RESET}; + return colors[level]; + } + + // Danger this buffer need as many elements as TLogLevel + static std::string ToString(TLogLevel level) { + static const char *const buffer[] = { + "ERROR", "WARNING", "INFO", "INFO", "INFO", "INFO", + "DEBUG", "DEBUG1", "DEBUG2", "DEBUG3", "DEBUG4", "DEBUG5"}; + return buffer[level]; + } + + std::ostringstream &Get() { + os << Color(level) << "- " << Timestamp() << " " << ToString(level) + << ": "; + return os; + } + + std::string Timestamp() { + constexpr size_t buffer_len = 12; + char buffer[buffer_len]; + time_t t; + time(&t); + tm r; + strftime(buffer, buffer_len, "%X", localtime_r(&t, &r)); + buffer[buffer_len - 1] = '\0'; + struct timeval tv; + gettimeofday(&tv, nullptr); + constexpr size_t result_len = 100; + char result[result_len]; + snprintf(result, result_len, "%s.%03ld", buffer, + (long)tv.tv_usec / 1000); + result[result_len - 1] = '\0'; + return result; + } +}; + +#define LOG(level) \ + if (level > LOG_MAX_REPORTING_LEVEL) \ + ; \ + else if (level > sls::Logger::ReportingLevel()) \ + ; \ + else \ + sls::Logger(level).Get() + +} // namespace sls diff --git a/slsSupportLib/tests/CMakeLists.txt b/slsSupportLib/tests/CMakeLists.txt index bb84225d5..8832c3315 100755 --- a/slsSupportLib/tests/CMakeLists.txt +++ b/slsSupportLib/tests/CMakeLists.txt @@ -9,4 +9,5 @@ target_sources(tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/test-ToString.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test-TypeTraits.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test-UdpRxSocket.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/test-logger.cpp ) \ No newline at end of file diff --git a/slsSupportLib/tests/test-logger.cpp b/slsSupportLib/tests/test-logger.cpp new file mode 100644 index 000000000..d57b79866 --- /dev/null +++ b/slsSupportLib/tests/test-logger.cpp @@ -0,0 +1,53 @@ +#include "catch.hpp" +#include "logger.h" +#include "logger2.h" + +#include +#include +#include + +using sls::Logger; + +TEST_CASE("LogLevel to string") { + CHECK(Logger::ToString(logERROR) == "ERROR"); + CHECK(Logger::ToString(logWARNING) == "WARNING"); + CHECK(Logger::ToString(logINFOBLUE) == "INFO"); + CHECK(Logger::ToString(logINFOGREEN) == "INFO"); + CHECK(Logger::ToString(logINFORED) == "INFO"); + CHECK(Logger::ToString(logINFO) == "INFO"); + CHECK(Logger::ToString(logDEBUG) == "DEBUG"); + CHECK(Logger::ToString(logDEBUG1) == "DEBUG1"); + CHECK(Logger::ToString(logDEBUG2) == "DEBUG2"); + CHECK(Logger::ToString(logDEBUG3) == "DEBUG3"); + CHECK(Logger::ToString(logDEBUG4) == "DEBUG4"); + CHECK(Logger::ToString(logDEBUG5) == "DEBUG5"); +} + +TEST_CASE("Test output") { + + auto old_value = Logger::ReportingLevel(); + + Logger::ReportingLevel() = logERROR; + + //Redirect std::clog to local buffer + std::ostringstream local; + auto clog_buff = std::clog.rdbuf(); + std::clog.rdbuf(local.rdbuf()); + + //Try printing something with too low level + LOG(logDEBUG) << "This should not be printed"; + CHECK(local.str().empty()); + + //Try printing something with a higher level + LOG(logERROR) << "This should be printed"; + CHECK(!local.str().empty()); + std::clog.rdbuf(clog_buff); // restore + Logger::ReportingLevel() = old_value; //reset + + + //Check that the message is in the printed string + auto r = local.str(); + auto pos = r.find("This should be printed"); + CHECK(pos != std::string::npos); + +} \ No newline at end of file