feat add the CLUTCHFUNC macro
- Allow to (de)clutch any function call, like asserts. - fix make max_buffer static const. - feat make locate and scope_t public.
This commit is contained in:
parent
81e3f6fd03
commit
978d3d9b66
2 changed files with 124 additions and 72 deletions
48
README.md
48
README.md
|
|
@ -1,5 +1,5 @@
|
||||||
**Clutchlog is a logging system which targets versatile debugging.**
|
***Clutchlog is a logging system which targets versatile debugging.***
|
||||||
**It allows to (de)clutch messages for a given: log level, source code location or call stack depth.**
|
***It allows to (de)clutch messages for a given: log level, source code location or call stack depth.***
|
||||||
|
|
||||||
[TOC]
|
[TOC]
|
||||||
|
|
||||||
|
|
@ -8,15 +8,22 @@ Features
|
||||||
|
|
||||||
Clutchlog allows to select which log messages will be displayed, based on their locations:
|
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
|
- **Classical log levels**: each message has a given detail level and it is displayed if you ask for a at least the same
|
||||||
one.
|
one.
|
||||||
- *call stack depth*: you can ask to display messages within functions which are called up to a given stack depth.
|
- **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
|
- **Source code location**: you can ask to display messages called from given files, functions and line number, all based on
|
||||||
regular expressions.
|
regular expressions.
|
||||||
|
|
||||||
Additionally, Clutchlog will do its best to allow the compiler to optimize out calls,
|
Additionally, Clutchlog will do its best to allow the compiler to optimize out calls,
|
||||||
for instance debug messages in "Release" builds.
|
for instance debug messages in "Release" builds.
|
||||||
|
|
||||||
|
Additional features:
|
||||||
|
|
||||||
|
- **Templated log format**, to easily design your own format.
|
||||||
|
- **Colored log**. By default only important ones are colored (critical and error in red, warning in magenta).
|
||||||
|
- **Macro to dump the content of a container in a file** with automatic naming (yes, it is useful for fast debugging).
|
||||||
|
- **Generic clutching wrapper**, to wrap any function call. Useful to (de)clutch *asserts* for example.
|
||||||
|
|
||||||
|
|
||||||
Example
|
Example
|
||||||
=======
|
=======
|
||||||
|
|
@ -291,6 +298,21 @@ log.dump(clutchlog::level::xdebug, cont.begin(), cont.end(), CLUTCHLOC, "dumped_
|
||||||
log.dump(clutchlog::level::xdebug, cont.begin(), cont.end(), "main.cpp", "main", 122, "dumped.dat", "\n\n");
|
log.dump(clutchlog::level::xdebug, cont.begin(), cont.end(), "main.cpp", "main", 122, "dumped.dat", "\n\n");
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
(De)clutch any function call
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
The `CLUTHFUNC` macro allows to wrap any function within the current logger.
|
||||||
|
|
||||||
|
For instance, this can be useful if you want to (de)clutch calls to `assert`s.
|
||||||
|
To do that, just declare your own macro:
|
||||||
|
```cpp
|
||||||
|
#define ASSERT(LEVEL, ...) { CLUTCHFUNC(LEVEL, assert, __VA_ARGS__) }
|
||||||
|
```
|
||||||
|
Thus, any call like `ASSERT(error, x > 3);` will be declutchable
|
||||||
|
with the same configuration than a call to `CLUTCHLOG`.
|
||||||
|
|
||||||
|
|
||||||
Log level semantics
|
Log level semantics
|
||||||
===================
|
===================
|
||||||
|
|
||||||
|
|
@ -319,7 +341,21 @@ are only available for operating systems having the following headers:
|
||||||
Some colors/styles may not be supported by some exotic terminal emulators.
|
Some colors/styles may not be supported by some exotic terminal emulators.
|
||||||
|
|
||||||
Clutchlog needs `C++-17` with the `filesystem` feature.
|
Clutchlog needs `C++-17` with the `filesystem` feature.
|
||||||
You may need to indicate `-std=c++17 -lstdc++fs` to your compiler.
|
You may need to indicate `-std=c++17 -lstdc++fs` to some compilers.
|
||||||
|
|
||||||
|
What Clutchlog do not provide at the moment (but may in a near future):
|
||||||
|
|
||||||
|
- Super fast log writing.
|
||||||
|
- Thread safety.
|
||||||
|
|
||||||
|
What Clutchlog will most certainly never provide:
|
||||||
|
|
||||||
|
- Round-robin log managers.
|
||||||
|
- Duplicated messages management.
|
||||||
|
- External output systems (only allow output stream, you can still do the proxy yourself).
|
||||||
|
- External error handlers (not my job, come on).
|
||||||
|
- Automatic argument parser (please, use a dedicated lib).
|
||||||
|
- Signal handling (WTF would you do that, anyway?).
|
||||||
|
|
||||||
|
|
||||||
Build and tests
|
Build and tests
|
||||||
|
|
|
||||||
|
|
@ -65,10 +65,12 @@
|
||||||
#endif // CLUTCHDUMP_DEFAULT_SEP
|
#endif // CLUTCHDUMP_DEFAULT_SEP
|
||||||
|
|
||||||
#ifndef CLUTCHLOG_DEFAULT_DEPTH_MARK
|
#ifndef CLUTCHLOG_DEFAULT_DEPTH_MARK
|
||||||
|
//! Default mark for stack depth.
|
||||||
#define CLUTCHLOG_DEFAULT_DEPTH_MARK ">"
|
#define CLUTCHLOG_DEFAULT_DEPTH_MARK ">"
|
||||||
#endif // CLUTCHLOG_DEFAULT_DEPTH_MARK
|
#endif // CLUTCHLOG_DEFAULT_DEPTH_MARK
|
||||||
|
|
||||||
#ifndef CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG
|
#ifndef CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG
|
||||||
|
//! Default level over which calls to the logger are optimized out when NDEBUG is defined.
|
||||||
#define CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG clutchlog::level::progress
|
#define CLUTCHLOG_DEFAULT_DEPTH_BUILT_NODEBUG clutchlog::level::progress
|
||||||
#endif // CLUTCHLOG_DEFAULT_DEPTH_BUILT
|
#endif // CLUTCHLOG_DEFAULT_DEPTH_BUILT
|
||||||
|
|
||||||
|
|
@ -114,12 +116,26 @@
|
||||||
}
|
}
|
||||||
#endif // NDEBUG
|
#endif // NDEBUG
|
||||||
|
|
||||||
|
//! Call an assert at the given level.
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#define CLUTCHFUNC( LEVEL, FUNC, ... ) { \
|
||||||
|
auto& logger = clutchlog::logger(); \
|
||||||
|
clutchlog::scope_t scope = logger.locate(clutchlog::level::LEVEL, CLUTCHLOC); \
|
||||||
|
if(scope.matches) { \
|
||||||
|
FUNC(__VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
#else // not Debug build.
|
||||||
|
#define CLUTCHFUNC( LEVEL, FUNC, ... ) { do {/*nothing*/} while(false); }
|
||||||
|
#endif // NDEBUG
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
#else // not WITH_CLUTCHLOG
|
#else // not WITH_CLUTCHLOG
|
||||||
// Disabled macros can still be used in Release builds.
|
// Disabled macros can still be used in Release builds.
|
||||||
#define CLUTCHLOG( LEVEL, WHAT ) { do {/*nothing*/} while(false); }
|
#define CLUTCHLOG( LEVEL, WHAT ) { do {/*nothing*/} while(false); }
|
||||||
#define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) { do {/*nothing*/} while(false); }
|
#define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) { do {/*nothing*/} while(false); }
|
||||||
|
#define CLUTCHFUNC( LEVEL, FUNC, ... ) { do {/*nothing*/} while(false); }
|
||||||
#endif // WITH_CLUTCHLOG
|
#endif // WITH_CLUTCHLOG
|
||||||
|
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
|
|
@ -344,71 +360,7 @@ class clutchlog
|
||||||
std::regex _in_func;
|
std::regex _in_func;
|
||||||
std::regex _in_line;
|
std::regex _in_line;
|
||||||
|
|
||||||
//! Structure holding a location matching.
|
static const size_t max_buffer = 4096;
|
||||||
struct scope_t {
|
|
||||||
bool matches; // everything is compatible
|
|
||||||
level stage; // current log level
|
|
||||||
#if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
|
|
||||||
size_t depth; // current depth
|
|
||||||
#endif
|
|
||||||
bool there; // location is compatible
|
|
||||||
scope_t() :
|
|
||||||
matches(false),
|
|
||||||
stage(level::xdebug),
|
|
||||||
#if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
|
|
||||||
depth(0),
|
|
||||||
#endif
|
|
||||||
there(false)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
//! Gather information on the current location of the call.
|
|
||||||
scope_t locate(
|
|
||||||
const level& stage,
|
|
||||||
const std::string& file,
|
|
||||||
const std::string& func,
|
|
||||||
const size_t line
|
|
||||||
) const
|
|
||||||
{
|
|
||||||
scope_t scope; // False scope by default.
|
|
||||||
|
|
||||||
/***** Log level stage *****/
|
|
||||||
// Test stage first, because it's fastest.
|
|
||||||
scope.stage = stage;
|
|
||||||
if(not (scope.stage <= _stage)) {
|
|
||||||
// Bypass useless computations if no match
|
|
||||||
// because of the stage.
|
|
||||||
return scope;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
|
|
||||||
/***** Stack depth *****/
|
|
||||||
// Backtrace in second, quite fast.
|
|
||||||
const size_t max_buffer = 4096;
|
|
||||||
size_t stack_depth;
|
|
||||||
void *buffer[max_buffer];
|
|
||||||
stack_depth = backtrace(buffer, max_buffer);
|
|
||||||
scope.depth = stack_depth;
|
|
||||||
if(not (scope.depth <= _depth + _strip_calls)) {
|
|
||||||
// Bypass if no match.
|
|
||||||
return scope;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/***** Location *****/
|
|
||||||
// Location last, slowest.
|
|
||||||
std::ostringstream sline; sline << line;
|
|
||||||
scope.there =
|
|
||||||
std::regex_search(file, _in_file)
|
|
||||||
and std::regex_search(func, _in_func)
|
|
||||||
and std::regex_search(sline.str(), _in_line);
|
|
||||||
|
|
||||||
// No need to retest stage and depth, which are true here.
|
|
||||||
scope.matches = scope.there;
|
|
||||||
|
|
||||||
return scope;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @}*/
|
/** @}*/
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
@ -499,6 +451,70 @@ class clutchlog
|
||||||
/** @name Low-level API
|
/** @name Low-level API
|
||||||
* @{ */
|
* @{ */
|
||||||
|
|
||||||
|
//! Structure holding a location matching.
|
||||||
|
struct scope_t {
|
||||||
|
bool matches; // everything is compatible
|
||||||
|
level stage; // current log level
|
||||||
|
#if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
|
||||||
|
size_t depth; // current depth
|
||||||
|
#endif
|
||||||
|
bool there; // location is compatible
|
||||||
|
scope_t() :
|
||||||
|
matches(false),
|
||||||
|
stage(level::xdebug),
|
||||||
|
#if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
|
||||||
|
depth(0),
|
||||||
|
#endif
|
||||||
|
there(false)
|
||||||
|
{}
|
||||||
|
}; // scope_t
|
||||||
|
|
||||||
|
|
||||||
|
//! Gather information on the current location of the call.
|
||||||
|
scope_t locate(
|
||||||
|
const level& stage,
|
||||||
|
const std::string& file,
|
||||||
|
const std::string& func,
|
||||||
|
const size_t line
|
||||||
|
) const
|
||||||
|
{
|
||||||
|
scope_t scope; // False scope by default.
|
||||||
|
|
||||||
|
/***** Log level stage *****/
|
||||||
|
// Test stage first, because it's fastest.
|
||||||
|
scope.stage = stage;
|
||||||
|
if(not (scope.stage <= _stage)) {
|
||||||
|
// Bypass useless computations if no match
|
||||||
|
// because of the stage.
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
#if CLUTCHLOG_HAVE_UNIX_SYSINFO == 1
|
||||||
|
/***** Stack depth *****/
|
||||||
|
// Backtrace in second, quite fast.
|
||||||
|
size_t stack_depth;
|
||||||
|
void *buffer[max_buffer];
|
||||||
|
stack_depth = backtrace(buffer, max_buffer);
|
||||||
|
scope.depth = stack_depth;
|
||||||
|
if(not (scope.depth <= _depth + _strip_calls)) {
|
||||||
|
// Bypass if no match.
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/***** Location *****/
|
||||||
|
// Location last, slowest.
|
||||||
|
std::ostringstream sline; sline << line;
|
||||||
|
scope.there =
|
||||||
|
std::regex_search(file, _in_file)
|
||||||
|
and std::regex_search(func, _in_func)
|
||||||
|
and std::regex_search(sline.str(), _in_line);
|
||||||
|
|
||||||
|
// No need to retest stage and depth, which are true here.
|
||||||
|
scope.matches = scope.there;
|
||||||
|
|
||||||
|
return scope;
|
||||||
|
} // locate
|
||||||
|
|
||||||
/** Replace `mark` by `tag` in `form`.
|
/** Replace `mark` by `tag` in `form`.
|
||||||
*
|
*
|
||||||
* @code
|
* @code
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue