feat: full featured dump
This commit is contained in:
parent
7ef6459c35
commit
93402d0c4b
4 changed files with 156 additions and 50 deletions
|
|
@ -19,6 +19,7 @@ mark_as_advanced(VERSION_MAJOR VERSION_MINOR VERSION_PATCH)
|
||||||
# There's no point building clutchlog tests in Release
|
# There's no point building clutchlog tests in Release
|
||||||
set(CMAKE_BUILD_TYPE Debug)
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD_LIBRARIES -lstdc++fs)
|
||||||
|
|
||||||
######################################################################################
|
######################################################################################
|
||||||
# Configurable user settings
|
# Configurable user settings
|
||||||
|
|
|
||||||
95
README.md
95
README.md
|
|
@ -62,6 +62,9 @@ allowing for the fast tracking of a bug across the execution.
|
||||||
API documentation
|
API documentation
|
||||||
=================
|
=================
|
||||||
|
|
||||||
|
Calls
|
||||||
|
-----
|
||||||
|
|
||||||
The main entrypoint is the `CLUTCHLOG` macro, which takes the desired log level and message.
|
The main entrypoint is the `CLUTCHLOG` macro, which takes the desired log level and message.
|
||||||
The message can be anything which can be output in an `ostringstream`.
|
The message can be anything which can be output in an `ostringstream`.
|
||||||
```cpp
|
```cpp
|
||||||
|
|
@ -76,11 +79,61 @@ CLUTCHLOG(error, value);
|
||||||
CLUTCHLOG(debug, "hello " << value << " world");
|
CLUTCHLOG(debug, "hello " << value << " world");
|
||||||
```
|
```
|
||||||
|
|
||||||
|
There is also a macro to dump the content of an iterable within a separate file: `CLUTCHDUMP`.
|
||||||
|
This function takes care of incrementing a numeric suffix in the file name,
|
||||||
|
if an existing file exists.
|
||||||
|
```cpp
|
||||||
|
std::vector<int> v(10);
|
||||||
|
std::generate(v.begin(), v.end(), std::rand);
|
||||||
|
CLUTCHLOG(debug, vec, "test_{n}.dat");
|
||||||
|
/* Will output in cat "rand_0.dat"
|
||||||
|
* # [t-dump] Info in main (at depth 5) @ /home/nojhan/code/clutchlog/tests/t-dump.cpp:22
|
||||||
|
* 1804289383
|
||||||
|
* 846930886
|
||||||
|
* 1681692777
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
Note that if you pass a file name without the `{n}` tag, the file will be overwritten as is.
|
||||||
|
|
||||||
|
|
||||||
|
Location filtering
|
||||||
|
------------------
|
||||||
|
|
||||||
To configure the global behaviour of the logger, you must first get a reference on its instance:
|
To configure the global behaviour of the logger, you must first get a reference on its instance:
|
||||||
```cpp
|
```cpp
|
||||||
auto& log = clutchlog::logger();
|
auto& log = clutchlog::logger();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
One can configure the location(s) at which messages should actually be logged:
|
||||||
|
```cpp
|
||||||
|
log.depth(3); // Depth of the call stack, defaults to the maximum possible value.
|
||||||
|
log.threshold(clutchlog::level::error); // Log level, defaults to error.
|
||||||
|
```
|
||||||
|
Current levels are defined in an enumeration as `clutchlog::level`:
|
||||||
|
```cpp
|
||||||
|
enum level {quiet=0, error=1, warning=2, progress=3, info=4, debug=5, xdebug=6};
|
||||||
|
```
|
||||||
|
|
||||||
|
File, function and line are indicated using regular expression:
|
||||||
|
```cpp
|
||||||
|
log.file(".*"); // File location, defaults to any.
|
||||||
|
log.func(".*"); // Function location, defaults to any.
|
||||||
|
log.line(".*"); // Line location, defaults to any.
|
||||||
|
```
|
||||||
|
A shortcut function can be used to indicates file, function and line regular expression at once:
|
||||||
|
```cpp
|
||||||
|
log.location(file, func, line); // Defaults to any, second and last parameters being optional.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Output Configuration
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
The output stream can be configured using the `out` method:
|
||||||
|
```cpp
|
||||||
|
log.out(std::clog); // Defaults to clog.
|
||||||
|
```
|
||||||
|
|
||||||
The format of the messages can be defined with the `format` method, passing a string with standardized tags surrounded by `{}`:
|
The format of the messages can be defined with the `format` method, passing a string with standardized tags surrounded by `{}`:
|
||||||
```cpp
|
```cpp
|
||||||
log.format("{msg}");
|
log.format("{msg}");
|
||||||
|
|
@ -97,29 +150,16 @@ Available tags are:
|
||||||
- `{depth}`: the current depth of the call stack,
|
- `{depth}`: the current depth of the call stack,
|
||||||
- `{depth_marks}`: as many chevrons `>` as there is calls in the stack.
|
- `{depth_marks}`: as many chevrons `>` as there is calls in the stack.
|
||||||
|
|
||||||
The default format is `"[{name}] {level_letter}:{depth_marks} {msg}\t\t\t\t\t{func} @ {file}:{line}\n"`,
|
The default log format is `"[{name}] {level_letter}:{depth_marks} {msg}\t\t\t\t\t{func} @ {file}:{line}\n"`,
|
||||||
it can be overriden at compile time by defining the `CLUTCHLOG_DEFAULT_FORMAT` macro.
|
it can be overriden at compile time by defining the `CLUTCHLOG_DEFAULT_FORMAT` macro.
|
||||||
|
|
||||||
The output stream can be configured using the `out` method:
|
The default format of the comment added with the dump macro is
|
||||||
```cpp
|
`"# [{name}] {level} in {func} (at depth {depth}) @ {file}:{line}"`.
|
||||||
log.out(std::clog); // Defaults to clog.
|
It can be modified at compile time with `CLUTCHDUMP_DEFAULT_FORMAT`.
|
||||||
```
|
If it is set to an empty string, then no comment line is added.
|
||||||
|
By default, the separator between items in the container is a new line.
|
||||||
One can configure the location(s) at which messages should actually be logged:
|
To change this behaviour, you can change `CLUTCHDUMP_DEFAULT_SEP` or
|
||||||
```cpp
|
call the low-level `dump` method.
|
||||||
log.depth(3); // Depth of the call stack, defaults to the maximum possible value.
|
|
||||||
log.threshold(clutchlog::level::error); // Log level, defaults to error.
|
|
||||||
```
|
|
||||||
File, function and line are indicated using regular expression:
|
|
||||||
```cpp
|
|
||||||
log.file(".*"); // File location, defaults to any.
|
|
||||||
log.func(".*"); // Function location, defaults to any.
|
|
||||||
log.line(".*"); // Line location, defaults to any.
|
|
||||||
```
|
|
||||||
A shortcut function can be used to indicates file, function and line regular expression at once:
|
|
||||||
```cpp
|
|
||||||
log.location(file, func, line); // Defaults to any, second and last parameters being optional.
|
|
||||||
```
|
|
||||||
|
|
||||||
The mark used with the `{depth_marks}` tag can be configured with the `depth_mark` method,
|
The mark used with the `{depth_marks}` tag can be configured with the `depth_mark` method,
|
||||||
and its default with the `CLUTCHLOG_DEFAULT_DEPTH_MARK` macro:
|
and its default with the `CLUTCHLOG_DEFAULT_DEPTH_MARK` macro:
|
||||||
|
|
@ -127,6 +167,9 @@ and its default with the `CLUTCHLOG_DEFAULT_DEPTH_MARK` macro:
|
||||||
log.depth_mark(CLUTCHLOG_DEFAULT_DEPTH_MARK); // Defaults to ">".
|
log.depth_mark(CLUTCHLOG_DEFAULT_DEPTH_MARK); // Defaults to ">".
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Low-level API
|
||||||
|
-------------
|
||||||
|
|
||||||
All configuration setters have a getters counterpart, with the same name but taking no parameter,
|
All configuration setters have a getters counterpart, with the same name but taking no parameter,
|
||||||
for example:
|
for example:
|
||||||
```cpp
|
```cpp
|
||||||
|
|
@ -141,6 +184,11 @@ A helper macro can helps to fill in the location with the actual one, as seen by
|
||||||
```cpp
|
```cpp
|
||||||
log.log(clutchlog::level::xdebug, "hello world", CLUTCHLOC);
|
log.log(clutchlog::level::xdebug, "hello world", CLUTCHLOC);
|
||||||
```
|
```
|
||||||
|
A similar `dump` method exists:
|
||||||
|
```cpp
|
||||||
|
log.dump(clutchlog::level::xdebug, cont.begin(), cont.end(), CLUTCHLOC, "dumped_{n}.dat", "\n");
|
||||||
|
log.dump(clutchlog::level::xdebug, cont.begin(), cont.end(), "main.cpp", "main", 122, "dumped.dat", "\n\n");
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Limitations
|
Limitations
|
||||||
|
|
@ -153,11 +201,14 @@ Clutchlog is only implemented for Linux at the moment.
|
||||||
Build and tests
|
Build and tests
|
||||||
===============
|
===============
|
||||||
|
|
||||||
|
To use clutchlog, just include its header in your code.
|
||||||
|
|
||||||
To build and run the tests, just use a classical CMake workflow:
|
To build and run the tests, just use a classical CMake workflow:
|
||||||
```sh
|
```sh
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake ..
|
# There's no point building in Release mode, at clutchlog is declutched.
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Debug ..
|
||||||
make
|
make
|
||||||
ctest
|
ctest
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
#ifndef __CLUTCHLOG_H__
|
#ifndef __CLUTCHLOG_H__
|
||||||
#define __CLUTCHLOG_H__
|
#define __CLUTCHLOG_H__
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <fstream>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <iomanip>
|
// #include <iomanip>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
@ -36,6 +38,16 @@
|
||||||
#define CLUTCHLOG_DEFAULT_FORMAT "[{name}] {level_letter}:{depth_marks} {msg}\t\t\t\t\t{func} @ {file}:{line}\n"
|
#define CLUTCHLOG_DEFAULT_FORMAT "[{name}] {level_letter}:{depth_marks} {msg}\t\t\t\t\t{func} @ {file}:{line}\n"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLUTCHDUMP_DEFAULT_FORMAT
|
||||||
|
//! Default format of the comment line in file dump.
|
||||||
|
#define CLUTCHDUMP_DEFAULT_FORMAT "# [{name}] {level} in {func} (at depth {depth}) @ {file}:{line}"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLUTCHDUMP_DEFAULT_SEP
|
||||||
|
//! Default item separator for dump.
|
||||||
|
#define CLUTCHDUMP_DEFAULT_SEP "\n"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef CLUTCHLOG_DEFAULT_DEPTH_MARK
|
#ifndef CLUTCHLOG_DEFAULT_DEPTH_MARK
|
||||||
#define CLUTCHLOG_DEFAULT_DEPTH_MARK ">"
|
#define CLUTCHLOG_DEFAULT_DEPTH_MARK ">"
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -51,15 +63,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Dump the given container.
|
//! Dump the given container.
|
||||||
#define CLUTCHDUMP( LEVEL, CONTAINER ) { \
|
#define CLUTCHDUMP( LEVEL, CONTAINER, FILENAME ) { \
|
||||||
auto& logger = clutchlog::logger(); \
|
auto& logger = clutchlog::logger(); \
|
||||||
logger.dump(clutchlog::level::LEVEL, std::begin(CONTAINER), std::end(CONTAINER), CLUTCHLOC, "\n"); \
|
logger.dump(clutchlog::level::LEVEL, std::begin(CONTAINER), std::end(CONTAINER), CLUTCHLOC, FILENAME, CLUTCHDUMP_DEFAULT_SEP); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
// 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, WHAT ) { do {/*nothing*/} while(false); }
|
#define CLUTCHDUMP ( LEVEL, CONTAINER, FILENAME ) { do {/*nothing*/} while(false); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
|
|
@ -85,7 +97,7 @@ class clutchlog
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Available log levels.
|
//! Available log levels.
|
||||||
enum level {quiet=0, error=1, warning=2, info=3, debug=4, xdebug=5};
|
enum level {quiet=0, error=1, warning=2, progress=3, info=4, debug=5, xdebug=6};
|
||||||
|
|
||||||
/** }@ High-level API */
|
/** }@ High-level API */
|
||||||
|
|
||||||
|
|
@ -103,11 +115,13 @@ class clutchlog
|
||||||
{level::quiet,"Quiet"},
|
{level::quiet,"Quiet"},
|
||||||
{level::error,"Error"},
|
{level::error,"Error"},
|
||||||
{level::warning,"Warning"},
|
{level::warning,"Warning"},
|
||||||
|
{level::progress,"Progress"},
|
||||||
{level::info,"Info"},
|
{level::info,"Info"},
|
||||||
{level::debug,"Debug"},
|
{level::debug,"Debug"},
|
||||||
{level::xdebug,"XDebug"}
|
{level::xdebug,"XDebug"}
|
||||||
}),
|
}),
|
||||||
_format(CLUTCHLOG_DEFAULT_FORMAT),
|
_format_log(CLUTCHLOG_DEFAULT_FORMAT),
|
||||||
|
_format_dump(CLUTCHDUMP_DEFAULT_FORMAT),
|
||||||
_out(&std::clog),
|
_out(&std::clog),
|
||||||
_depth(std::numeric_limits<size_t>::max() - _strip_calls),
|
_depth(std::numeric_limits<size_t>::max() - _strip_calls),
|
||||||
_stage(level::error),
|
_stage(level::error),
|
||||||
|
|
@ -120,7 +134,8 @@ class clutchlog
|
||||||
protected:
|
protected:
|
||||||
const size_t _strip_calls;
|
const size_t _strip_calls;
|
||||||
const std::map<level,std::string> _level_words;
|
const std::map<level,std::string> _level_words;
|
||||||
std::string _format;
|
std::string _format_log;
|
||||||
|
std::string _format_dump;
|
||||||
std::ostream* _out;
|
std::ostream* _out;
|
||||||
size_t _depth;
|
size_t _depth;
|
||||||
level _stage;
|
level _stage;
|
||||||
|
|
@ -172,8 +187,11 @@ class clutchlog
|
||||||
|
|
||||||
/** Configuration accessors @{ */
|
/** Configuration accessors @{ */
|
||||||
|
|
||||||
void format(const std::string& format) {_format = format;}
|
void format(const std::string& format) {_format_log = format;}
|
||||||
std::string format() const {return _format;}
|
std::string format() const {return _format_log;}
|
||||||
|
|
||||||
|
void format_comment(const std::string& format) {_format_dump = format;}
|
||||||
|
std::string format_comment() const {return _format_dump;}
|
||||||
|
|
||||||
void out(std::ostream& out) {_out = &out;}
|
void out(std::ostream& out) {_out = &out;}
|
||||||
std::ostream& out() {return *_out;}
|
std::ostream& out() {return *_out;}
|
||||||
|
|
@ -268,10 +286,20 @@ class clutchlog
|
||||||
// throw;
|
// throw;
|
||||||
// } // catch
|
// } // catch
|
||||||
|
|
||||||
std::regex re(mark);
|
const std::regex re(mark);
|
||||||
return std::regex_replace(form, re, tag);
|
return std::regex_replace(form, re, tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string replace(
|
||||||
|
const std::string& form,
|
||||||
|
const std::string& mark,
|
||||||
|
const size_t tag
|
||||||
|
) const
|
||||||
|
{
|
||||||
|
std::ostringstream stag; stag << tag;
|
||||||
|
return replace(form, mark, stag.str());
|
||||||
|
}
|
||||||
|
|
||||||
std::string format(
|
std::string format(
|
||||||
std::string format,
|
std::string format,
|
||||||
const std::string& what,
|
const std::string& what,
|
||||||
|
|
@ -288,19 +316,16 @@ class clutchlog
|
||||||
format = replace(format, "\\{file\\}", file);
|
format = replace(format, "\\{file\\}", file);
|
||||||
format = replace(format, "\\{func\\}", func);
|
format = replace(format, "\\{func\\}", func);
|
||||||
format = replace(format, "\\{level\\}", _level_words.at(stage));
|
format = replace(format, "\\{level\\}", _level_words.at(stage));
|
||||||
|
format = replace(format, "\\{line\\}", line);
|
||||||
|
format = replace(format, "\\{depth\\}", depth);
|
||||||
|
|
||||||
std::ostringstream sline; sline << line;
|
std::string letter(1, _level_words.at(stage).at(0)); // char -> string
|
||||||
format = replace(format, "\\{line\\}", sline.str());
|
|
||||||
|
|
||||||
std::string letter(1, _level_words.at(stage).at(0));
|
|
||||||
format = replace(format, "\\{level_letter\\}", letter);
|
format = replace(format, "\\{level_letter\\}", letter);
|
||||||
|
|
||||||
std::ostringstream sdepth; sdepth << depth;
|
|
||||||
format = replace(format, "\\{depth\\}", sdepth.str());
|
|
||||||
|
|
||||||
std::ostringstream chevrons;
|
std::ostringstream chevrons;
|
||||||
for(size_t i = _strip_calls; i < depth; ++i) {
|
for(size_t i = _strip_calls; i < depth; ++i) {
|
||||||
chevrons << ">"; }
|
chevrons << ">";
|
||||||
|
}
|
||||||
format = replace(format, "\\{depth_marks\\}", chevrons.str());
|
format = replace(format, "\\{depth_marks\\}", chevrons.str());
|
||||||
|
|
||||||
return format;
|
return format;
|
||||||
|
|
@ -315,7 +340,7 @@ class clutchlog
|
||||||
scope_t scope = locate(stage, file, func, line);
|
scope_t scope = locate(stage, file, func, line);
|
||||||
|
|
||||||
if(scope.matches) {
|
if(scope.matches) {
|
||||||
*_out << format(_format, what, basename(getenv("_")),
|
*_out << format(_format_log, what, basename(getenv("_")),
|
||||||
stage, file, func,
|
stage, file, func,
|
||||||
line, scope.depth );
|
line, scope.depth );
|
||||||
_out->flush();
|
_out->flush();
|
||||||
|
|
@ -327,21 +352,44 @@ class clutchlog
|
||||||
const level& stage,
|
const level& stage,
|
||||||
const In container_begin, const In container_end,
|
const In container_begin, const In container_end,
|
||||||
const std::string& file, const std::string& func, size_t line,
|
const std::string& file, const std::string& func, size_t line,
|
||||||
const std::string sep="\n"
|
const std::string& filename_template="dump_{n}.dat",
|
||||||
|
const std::string sep=CLUTCHDUMP_DEFAULT_SEP
|
||||||
) const
|
) const
|
||||||
// FIXME use a file name template as input
|
|
||||||
{
|
{
|
||||||
scope_t scope = locate(stage, file, func, line);
|
scope_t scope = locate(stage, file, func, line);
|
||||||
|
|
||||||
if(scope.matches) {
|
if(scope.matches) {
|
||||||
*_out << "#" // FIXME add a _format_dump parameter?
|
const std::string tag = "\\{n\\}";
|
||||||
<< format(_format, "", basename(getenv("_")),
|
const std::regex re(tag);
|
||||||
stage, file, func,
|
std::string outfile = "";
|
||||||
line, scope.depth );
|
|
||||||
|
// If the file name template has the {n} tag.
|
||||||
|
if(std::regex_search(filename_template, re)) {
|
||||||
|
// Increment n until a free one is found.
|
||||||
|
size_t n = 0;
|
||||||
|
do {
|
||||||
|
outfile = replace(filename_template, tag, n);
|
||||||
|
n++;
|
||||||
|
} while( std::filesystem::exists( outfile ) );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Use the parameter as is.
|
||||||
|
outfile = filename_template;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ofstream fd(outfile);
|
||||||
|
|
||||||
|
if(_format_dump.size() > 0) {
|
||||||
|
fd << format(_format_dump, "", basename(getenv("_")),
|
||||||
|
stage, file, func,
|
||||||
|
line, scope.depth );
|
||||||
|
fd << sep; // sep after comment line.
|
||||||
|
}
|
||||||
|
|
||||||
std::copy(container_begin, container_end,
|
std::copy(container_begin, container_end,
|
||||||
std::ostream_iterator<typename In::value_type>(*_out, sep.c_str()));
|
std::ostream_iterator<typename In::value_type>(fd, sep.c_str()));
|
||||||
// No flush
|
|
||||||
|
fd.close();
|
||||||
} // if scopes.matches
|
} // if scopes.matches
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
#include "../clutchlog/clutchlog.h"
|
#include "../clutchlog/clutchlog.h"
|
||||||
|
|
||||||
|
|
@ -13,6 +15,10 @@ int main(/*const int argc, char* argv[]*/)
|
||||||
|
|
||||||
std::vector<std::string> msg = {"hello", "world", "!"};
|
std::vector<std::string> msg = {"hello", "world", "!"};
|
||||||
|
|
||||||
CLUTCHDUMP(xdebug, msg);
|
CLUTCHDUMP(xdebug, msg, "test_{n}.dat");
|
||||||
|
|
||||||
|
std::vector<int> v(3);
|
||||||
|
std::generate(v.begin(), v.end(), std::rand);
|
||||||
|
CLUTCHDUMP(info, v, "rand_{n}.dat");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue