From e30a6b3e854f07af9c93c4d374be29b0fa7feb69 Mon Sep 17 00:00:00 2001 From: nojhan Date: Sun, 23 Aug 2020 11:11:15 +0200 Subject: [PATCH] first import --- CMakeLists.txt | 39 +++++++++++++ README.md | 78 +++++++++++++++++++++++++ clutchlog/clutchlog.h | 128 ++++++++++++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 14 +++++ tests/t-core.cpp | 34 +++++++++++ 5 files changed, 293 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 clutchlog/clutchlog.h create mode 100644 tests/CMakeLists.txt create mode 100644 tests/t-core.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e1d6843 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,39 @@ + +###################################################################################### +# Project settings +###################################################################################### + +cmake_minimum_required(VERSION 3.10 FATAL_ERROR) + +project("clutchlog") + +enable_language(CXX) # C++ +set(CMAKE_CXX_STANDARD 17) + +## Current version +set(VERSION_MAJOR 0 CACHE STRING "Major version number" ) +set(VERSION_MINOR 1 CACHE STRING "Minor version number" ) +set(VERSION_PATCH 0 CACHE STRING "Patch version number" ) +mark_as_advanced(VERSION_MAJOR VERSION_MINOR VERSION_PATCH) + + +###################################################################################### +# Configurable user settings +###################################################################################### + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic") + +# put binaries in the build directory +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + +# Dump used compiler flags. +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + + +###################################################################################### +# Start building +###################################################################################### + +enable_testing() +add_subdirectory(tests) + diff --git a/README.md b/README.md new file mode 100644 index 0000000..c128209 --- /dev/null +++ b/README.md @@ -0,0 +1,78 @@ +Clutchlog is a logging system whith targets versatile debugging. +It allows to (de)clutch messages either for a given: log level, source code location or call stack depth. + +Features +======== + +Clutchlog allows to select which log messages will be displayed, based on their locations: + +- classical log levels: each message has a given detail level and it is displayed if you ask for a at least the same one + (current ones: quiet, error, warning, info, debug, xdebug). +- call stack depth: you can ask to display messages within functions which are called up to a given stack depth. +- source code location: you can ask to display messages called from given files, functions and line number, all based on + regular expressions. + +Appart from those, Clutchlog have classical logging system features: output selection, default to unbuffered mode, etc. + +Of course, Clutchlog is disabled by default if `NDEBUG` is not defined. + + +Example +======= + +Adding a message is a simple as calling a macro (which is declutched in Debug build type): +```cpp +CLUTCHLOG(info, "matrix size: " << m << "x" << n); +``` + +To configure the display, you indicate the three types of locations, for example in your `main` function: +```cpp + auto& log = clutchlog::logger(); + log.depth(2); // Log functions called from "main" but not below. + log.threshold(clutchlog::level::info); // Log "info", "warning", "error" or "quiet" messages. + log.file("algebra/.*"); // Will match any file in the "algebra" directory. + log.func("(mul|add|sub|div)"); // Will match "multiply", for instance. +``` + +For more detailled examples, see the `tests` directory. + + +Rationale +========= + +Most of existing logging systems targets service events storage, like fast queuing of transactions in a round-robin +database. +Their aim is to provide a simple interface to efficiently store messages somewhere, which is appropriated when you have +a well known service running and you want to be able to trace complex users interactions across its states. + +Clutchlog, however, targets the debugging of a (single-run) program. +While you develop your software, it's common practice to output several detailled informations on the internal states +around the feature you are currently programming. +However, once the feature is up and running, those detailled informations are only useful if you encounter a bug +traversing this specific part. + +While tracing a bug, it is tedious to uncomment old debugging code (and go on the build-test cycle) +or to set up a full debugger session which displays all appropriate data (with ad-hoc fancy hooks). + +To solve this problem, Clutchlog allows to disengage your debug log messages in various parts of the program, +allowing for the fast tracking of a bug across the execution. + + +Limitations +=========== + +Call stack depth is only implemented for Linux. + + +Build and tests +=============== + +To build and run the tests, just use a classical CMake workflow: +```sh +mkdir build +cd build +cmake .. +make +ctest +``` + diff --git a/clutchlog/clutchlog.h b/clutchlog/clutchlog.h new file mode 100644 index 0000000..03474c1 --- /dev/null +++ b/clutchlog/clutchlog.h @@ -0,0 +1,128 @@ +#ifndef __CLUTCHLOG_H__ +#define __CLUTCHLOG_H__ + +#include +#include +#include +#include +#include +#include +#include + +// #ifdef __unix__ +#include +#include +#include +// #endif + +// #ifdef WITH_THROWN_ASSERTS +// #undef assert +// #define assert( what ) { if(!(what)){CLUTCHLOG_RAISE(aion::Assert, "Failed assert: `" << #what << "`");} } +// #else +#include +// #endif + +#ifndef WITH_CLUTCHLOG +#ifndef NDEBUG +#define WITH_CLUTCHLOG +#endif +#endif + +class clutchlog +{ + public: + static clutchlog& logger() + { + static clutchlog instance; + return instance; + } + + enum level {quiet, error, warning, info, debug, xdebug}; + + public: + clutchlog(clutchlog const&) = delete; + void operator=(clutchlog const&) = delete; + + private: + clutchlog() : + _out(&std::clog), + _depth(std::numeric_limits::max()), + _level(level::error), + _in_file(".*"), + _in_func(".*"), + _in_line(".*") + {} + + protected: + // system, main, log + const size_t _strip_calls = 3; + std::ostream* _out; + size_t _depth; + level _level; + std::regex _in_file; + std::regex _in_func; + std::regex _in_line; + + public: + void out(std::ostream& out) {_out = &out;} + std::ostream& out() {return *_out;} + + void depth(size_t d) {_depth = d;} + size_t depth() const {return _depth;} + + void threshold(level l) {_level = l;} + level threshold() const {return _level;} + + void file(std::string file) {_in_file = file;} + void func(std::string func) {_in_func = func;} + void line(std::string line) {_in_line = line;} + + void location(std::string in_file, std::string in_function=".*", std::string in_line=".*") + { + file(in_file); + func(in_function); + line(in_line); + } + + void log(std::string what, level log_level, std::string file, std::string func, size_t line, bool newline) + { + const size_t max_buffer = 1024; + size_t stack_depth; + void *buffer[max_buffer]; + stack_depth = backtrace(buffer, max_buffer); + if(log_level <= _level and stack_depth <= _depth + _strip_calls) { + + std::ostringstream sline; sline << line; + if( std::regex_search(file, _in_file) + and std::regex_search(func, _in_func) + and std::regex_search(sline.str(), _in_line)) { + + *_out << "[" << basename(getenv("_")) << "] "; + for(size_t i = _strip_calls; i < stack_depth; ++i) { + *_out << ">"; + } + if(stack_depth > _strip_calls) { + *_out << " "; + } + *_out << what; + *_out << "\t\t\t\t\t" << file << ":" << line << " (" << func << ")"; + if(newline) { + *_out << std::endl; + } + } // regex location + } // log level and stack depth + } +}; + +#ifdef WITH_CLUTCHLOG +#define CLUTCHLOG( LEVEL, WHAT ) { \ + auto& logger = clutchlog::logger(); \ + std::ostringstream msg ; msg << WHAT; \ + logger.log(msg.str(), clutchlog::level::LEVEL, __FILE__, __FUNCTION__, __LINE__, true); \ +} + +#else +#define CLUTCHLOG ( LEVEL, WHAT ) { do {/*nothing*/} while(false); } +#endif + +#endif // __CLUTCHLOG_H__ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..02865f2 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,14 @@ + +function(add_simple_test tname) + add_executable(${tname} ${tname}.cpp) + add_test(NAME ${tname} COMMAND ${tname}) +endfunction() + +file(GLOB sources "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") + +foreach(filename ${sources}) + # File name without directory or longest extension + get_filename_component(name ${filename} NAME_WE) + add_simple_test(${name}) +endforeach() + diff --git a/tests/t-core.cpp b/tests/t-core.cpp new file mode 100644 index 0000000..45123dd --- /dev/null +++ b/tests/t-core.cpp @@ -0,0 +1,34 @@ +#include "../clutchlog/clutchlog.h" + +void g() +{ + CLUTCHLOG(info, "!"); +} + +void f() +{ + CLUTCHLOG(warning, "world"); + g(); +} + +int main(const int argc, char* argv[]) +{ +#ifdef WITH_CLUTCHLOG + auto& log = clutchlog::logger(); + + log.out(std::clog); + + size_t below = 2; + if(argc >= 2) { below = atoi(argv[1]); } + log.depth(below); + + log.threshold(clutchlog::level::warning); + + log.file("core"); + log.func("(main|f)"); +#endif + + CLUTCHLOG(error, "hello " << argc); + + f(); +}