first import
This commit is contained in:
commit
e30a6b3e85
5 changed files with 293 additions and 0 deletions
39
CMakeLists.txt
Normal file
39
CMakeLists.txt
Normal file
|
|
@ -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)
|
||||||
|
|
||||||
78
README.md
Normal file
78
README.md
Normal file
|
|
@ -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
|
||||||
|
```
|
||||||
|
|
||||||
128
clutchlog/clutchlog.h
Normal file
128
clutchlog/clutchlog.h
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
#ifndef __CLUTCHLOG_H__
|
||||||
|
#define __CLUTCHLOG_H__
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <string>
|
||||||
|
#include <limits>
|
||||||
|
#include <sstream>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
// #ifdef __unix__
|
||||||
|
#include <execinfo.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef WITH_THROWN_ASSERTS
|
||||||
|
// #undef assert
|
||||||
|
// #define assert( what ) { if(!(what)){CLUTCHLOG_RAISE(aion::Assert, "Failed assert: `" << #what << "`");} }
|
||||||
|
// #else
|
||||||
|
#include <cassert>
|
||||||
|
// #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<size_t>::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__
|
||||||
14
tests/CMakeLists.txt
Normal file
14
tests/CMakeLists.txt
Normal file
|
|
@ -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()
|
||||||
|
|
||||||
34
tests/t-core.cpp
Normal file
34
tests/t-core.cpp
Normal file
|
|
@ -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();
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue