Compare commits

..

6 Commits

Author SHA1 Message Date
Alexey Rybalchenko
aaacaf316e Update alfa ci 2019-01-17 16:32:21 +01:00
Alexey Rybalchenko
180acaae26 Allow use after static destruction took place 2019-01-17 14:12:14 +01:00
Alexey Rybalchenko
2d5dd004cb Expose color tools 2019-01-02 09:18:30 +01:00
Alexey Rybalchenko
4c2c238030 Format 2019-01-02 09:18:30 +01:00
Dennis Klein
3e1de0a17b Support user-defined verbosity formats
* Add `fair::Logger::DefineVerbosity(...)` API
* Add documentation to README
* Optionally support `BOOST_PRETTY_FUNCTION`
2019-01-02 09:18:30 +01:00
Giulio Eulisse
7d0411b939 Use quotes to include local headers
In general angular brackets are used for external headers, while quotes are used for internal ones. Is there any particular reason not to follow the conventions? While this is admittedly left to the compiler implementor by the standard, some implementations, like gcc, do have a peculiar behavior for the two kind of `include` and the current solution might end up including something unwanted, especially given the quite "common" name (Logger) used.
2019-01-01 15:50:00 +01:00
8 changed files with 621 additions and 253 deletions

1
.gitignore vendored
View File

@@ -1 +1,2 @@
build/
.vscode

View File

@@ -22,6 +22,15 @@ message(STATUS "${BWhite}${PROJECT_NAME}${CR} ${FairLogger_GIT_VERSION} from ${F
set_fairlogger_defaults()
include(CTest)
option(USE_BOOST_PRETTY_FUNCTION "Use Boost BOOST_PRETTY_FUNCTION macro" OFF)
################################################################################
# Dependency ###################################################################
if(USE_BOOST_PRETTY_FUNCTION)
find_package(Boost REQUIRED)
endif()
################################################################################
@@ -37,6 +46,11 @@ add_library(FairLogger
logger/Logger.h
)
if(USE_BOOST_PRETTY_FUNCTION)
target_link_libraries(FairLogger PUBLIC Boost::boost)
target_compile_definitions(FairLogger PUBLIC FAIRLOGGER_USE_BOOST_PRETTY_FUNCTION)
endif()
target_include_directories(FairLogger
PUBLIC
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/logger>

53
Jenkinsfile vendored
View File

@@ -4,24 +4,53 @@ def specToLabel(Map spec) {
return "${spec.os}-${spec.arch}-${spec.compiler}-FairSoft_${spec.fairsoft}"
}
def buildMatrix(List specs, Closure callback) {
def jobMatrix(String prefix, List specs, Closure callback) {
def nodes = [:]
for (spec in specs) {
def label = specToLabel(spec)
nodes[label] = {
def fairsoft = spec.fairsoft
def os = spec.os
def compiler = spec.compiler
nodes["${prefix}/${label}"] = {
node(label) {
githubNotify(context: "alfa-ci/${label}", description: 'Building ...', status: 'PENDING')
githubNotify(context: "${prefix}/${label}", description: 'Building ...', status: 'PENDING')
try {
deleteDir()
checkout scm
sh """\
echo "export SIMPATH=\${SIMPATH_PREFIX}${fairsoft}" >> Dart.cfg
echo "export FAIRSOFT_VERSION=${fairsoft}" >> Dart.cfg
"""
if (os =~ /Debian/ && compiler =~ /gcc8/) {
sh '''\
echo "source /etc/profile.d/modules.sh" >> Dart.cfg
echo "module use /cvmfs/it.gsi.de/modulefiles" >> Dart.cfg
echo "module load compiler/gcc/8" >> Dart.cfg
'''
}
if (os =~ /MacOS/) {
sh "echo \"export EXTRA_FLAGS='-DCMAKE_CXX_COMPILER=clang++'\" >> Dart.cfg"
} else {
sh "echo \"export EXTRA_FLAGS='-DCMAKE_CXX_COMPILER=g++'\" >> Dart.cfg"
}
sh '''\
echo "export BUILDDIR=$PWD/build" >> Dart.cfg
echo "export SOURCEDIR=$PWD" >> Dart.cfg
echo "export PATH=\\\$SIMPATH/bin:\\\$PATH" >> Dart.cfg
echo "export GIT_BRANCH=$JOB_BASE_NAME" >> Dart.cfg
echo "echo \\\$PATH" >> Dart.cfg
'''
sh 'cat Dart.cfg'
callback.call(spec, label)
deleteDir()
githubNotify(context: "alfa-ci/${label}", description: 'Success', status: 'SUCCESS')
githubNotify(context: "${prefix}/${label}", description: 'Success', status: 'SUCCESS')
} catch (e) {
deleteDir()
githubNotify(context: "alfa-ci/${label}", description: 'Error', status: 'ERROR')
githubNotify(context: "${prefix}/${label}", description: 'Error', status: 'ERROR')
throw e
}
}
@@ -33,19 +62,13 @@ def buildMatrix(List specs, Closure callback) {
pipeline{
agent none
stages {
stage("Run Build/Test Matrix") {
stage("Run CI Matrix") {
steps{
script {
parallel(buildMatrix([
[os: 'Debian8', arch: 'x86_64', compiler: 'gcc4.9', fairsoft: 'may18'],
[os: 'MacOS10.13', arch: 'x86_64', compiler: 'AppleLLVM9.0.0', fairsoft: 'may18'],
parallel(jobMatrix('alfa-ci/build', [
[os: 'Debian8', arch: 'x86_64', compiler: 'gcc8.1.0', fairsoft: 'fairmq_dev'],
[os: 'MacOS10.13', arch: 'x86_64', compiler: 'AppleLLVM10.0.0', fairsoft: 'fairmq_dev'],
]) { spec, label ->
sh '''\
echo "export BUILDDIR=$PWD/build" >> Dart.cfg
echo "export SOURCEDIR=$PWD" >> Dart.cfg
echo "export PATH=$SIMPATH/bin:$PATH" >> Dart.cfg
echo "export GIT_BRANCH=$JOB_BASE_NAME" >> Dart.cfg
'''
sh './Dart.sh alfa_ci Dart.cfg'
})
}

View File

@@ -8,12 +8,40 @@ def buildMatrix(List specs, Closure callback) {
def nodes = [:]
for (spec in specs) {
def label = specToLabel(spec)
def fairsoft = spec.fairsoft
def os = spec.os
def compiler = spec.compiler
nodes[label] = {
node(label) {
try {
deleteDir()
checkout scm
sh """\
echo "export SIMPATH=\${SIMPATH_PREFIX}${fairsoft}" >> Dart.cfg
echo "export FAIRSOFT_VERSION=${fairsoft}" >> Dart.cfg
"""
if (os =~ /Debian/ && compiler =~ /gcc8/) {
sh '''\
echo "source /etc/profile.d/modules.sh" >> Dart.cfg
echo "module use /cvmfs/it.gsi.de/modulefiles" >> Dart.cfg
echo "module load compiler/gcc/8" >> Dart.cfg
'''
}
if (os =~ /MacOS/) {
sh "echo \"export EXTRA_FLAGS='-DCMAKE_CXX_COMPILER=clang++'\" >> Dart.cfg"
} else {
sh "echo \"export EXTRA_FLAGS='-DCMAKE_CXX_COMPILER=g++'\" >> Dart.cfg"
}
sh '''\
echo "export BUILDDIR=$PWD/build" >> Dart.cfg
echo "export SOURCEDIR=$PWD" >> Dart.cfg
echo "export PATH=\\\$SIMPATH/bin:\\\$PATH" >> Dart.cfg
echo "export GIT_BRANCH=dev" >> Dart.cfg
echo "echo \\\$PATH" >> Dart.cfg
'''
sh 'cat Dart.cfg'
callback.call(spec, label)
deleteDir()
@@ -35,16 +63,9 @@ pipeline{
steps{
script {
parallel(buildMatrix([
[os: 'Debian8', arch: 'x86_64', compiler: 'gcc4.9', fairsoft: 'may18'],
[os: 'MacOS10.11', arch: 'x86_64', compiler: 'AppleLLVM8.0.0', fairsoft: 'may18'],
[os: 'MacOS10.13', arch: 'x86_64', compiler: 'AppleLLVM9.0.0', fairsoft: 'may18'],
[os: 'Debian8', arch: 'x86_64', compiler: 'gcc8.1.0', fairsoft: 'fairmq_dev'],
[os: 'MacOS10.13', arch: 'x86_64', compiler: 'AppleLLVM10.0.0', fairsoft: 'fairmq_dev'],
]) { spec, label ->
sh '''\
echo "export BUILDDIR=$PWD/build" >> Dart.cfg
echo "export SOURCEDIR=$PWD" >> Dart.cfg
echo "export PATH=$SIMPATH/bin:$PATH" >> Dart.cfg
echo "export GIT_BRANCH=dev" >> Dart.cfg
'''
sh './Dart.sh Nightly Dart.cfg'
sh './Dart.sh Profile Dart.cfg'
})

138
README.md
View File

@@ -41,6 +41,144 @@ On command line:
* `-DDISABLE_COLOR=ON` disables coloured console output.
* `-DBUILD_TESTING=OFF` disables building of unit tests.
* `-DUSE_BOOST_PRETTY_FUNCTION=ON` enables usage of `BOOST_PRETTY_FUNCTION` macro.
## Documentation
## 1. General
All log calls go through the provided LOG(severity) macro. Output through this macro is thread-safe. Logging is done to cout, file output and/or custom sinks.
## 2. Severity
The log severity is controlled via:
```C++
fair::Logger::SetConsoleSeverity("<severity level>");
// and/or
fair::Logger::SetFileSeverity("<severity level>");
// and/or
fair::Logger::SetCustomSeverity("<customSinkName>", "<severity level>");
```
where severity level is one of the following:
```C++
"nolog",
"fatal",
"error",
"warn",
"state",
"info",
"debug",
"debug1",
"debug2",
"debug3",
"debug4",
"trace",
```
Logger will log the chosen severity and all above it (except "nolog", which deactivates logging for that sink completely). Fatal severity is always logged.
## 3. Verbosity
The log verbosity is controlled via:
```C++
fair::Logger::SetVerbosity("<verbosity level>");
```
it is same for all sinks, and is one of the following values: `verylow`, `low`, `medium`, `high`, `veryhigh`, which translates to following output:
```
verylow: message
low: [severity] message
medium: [HH:MM:SS][severity] message
high: [process name][HH:MM:SS:µS][severity] message
veryhigh: [process name][HH:MM:SS:µS][severity][file:line:function] message
```
When running a FairMQ device, the log severity can be simply provided via `--verbosity <level>` cmd option.
The user may customize the existing verbosities or any of `user1`, `user2`, `user3`, `user4` verbosities via:
```C++
void fair::Logger::DefineVerbosity(fair::Verbosity, fair::VerbositySpec);
void fair::Logger::DefineVerbosity("<verbosity level>", fair::VerbositySpec);
```
The `fair::Logger::VerbositySpec` object can e.g. be created like this:
```C++
auto spec = fair::VerbositySpec::Make(VerbositySpec::Info::timestamp_s,
VerbositySpec::Info::process_name);
// results in [HH:MM:SS][process name] message
```
| **Argument** | **Result** |
| --- | --- |
| `fair::VerbositySpec::Info::process_name` | `[process name]` |
| `fair::VerbositySpec::Info::timestamp_s` | `[HH:MM:SS]` |
| `fair::VerbositySpec::Info::timestamp_us` | `[HH:MM:SS:µS]` |
| `fair::VerbositySpec::Info::severity` | `[severity]` |
| `fair::VerbositySpec::Info::file` | `[file]` |
| `fair::VerbositySpec::Info::file_line` | `[file:line]` |
| `fair::VerbositySpec::Info::file_line_function` | `[file:line:function]` |
### 3.1 `BOOST_PRETTY_FUNCTION` support
By default, the `veryhigh` verbosity prints the function name from which the `LOG` macro was invoked. If you desire a more verbose function signature including the full namespace, return value and function arguments, you can enable support for `BOOST_PRETTY_FUNCTION`
* **globally** by compiling FairLogger with the CMake option `-DUSE_BOOST_PRETTY_FUNCTION=ON`, or
* **per translation unit** by defining `FAIRLOGGER_USE_BOOST_PRETTY_FUNCTION` before including the FairLogger header, e.g.
```C++
#define FAIRLOGGER_USE_BOOST_PRETTY_FUNCTION
#include <Logger.h>
```
In the latter case, the user needs to take care of adding the boost include path to the compiler search path manually (e.g. `-I/path/to/boost/include`).
## 4. Color
Colored output on console can be activated with:
```C++
Logger::SetConsoleColor(true);
```
When running a FairMQ device, the log color (console) can be simply provided via `--color <true/false>` cmd option (default is true).
## 5. File output
Output to file can be enabled via:
```C++
Logger::InitFileSink("<severity level>", "test_log", true);
```
which will add output to "test_log" filename (if third parameter is `true` it will add timestamp to the file name) with `<severity level>` severity.
When running a FairMQ device, the log file can be simply provided via `--log-to-file <filename_prefix>` cmd option (this will also turn off console output).
## 5.5 Custom sinks
Custom sinks can be added via `Logger::AddCustomSink("sink name", "<severity>", callback)` method.
Here is an example adding a custom sink for all severities ("trace" and above). It has access to the log content and meta data. Custom log calls are also thread-safe.
```C++
Logger::AddCustomSink("MyCustomSink", "trace", [](const string& content, const LogMetaData& metadata)
{
cout << "content: " << content << endl;
cout << "available metadata: " << endl;
cout << "std::time_t timestamp: " << metadata.timestamp << endl;
cout << "std::chrono::microseconds us: " << metadata.us.count() << endl;
cout << "std::string process_name: " << metadata.process_name << endl;
cout << "std::string file: " << metadata.file << endl;
cout << "std::string line: " << metadata.line << endl;
cout << "std::string func: " << metadata.func << endl;
cout << "std::string severity_name: " << metadata.severity_name << endl;
cout << "fair::Severity severity: " << static_cast<int>(metadata.severity) << endl;
});
```
If only output from custom sinks is desirable, console/file sinks must be deactivated by setting their severity to `"nolog"`.
## License

View File

@@ -5,7 +5,7 @@
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#include <Logger.h>
#include "Logger.h"
#include <iostream>
#include <ostream>
@@ -13,59 +13,13 @@
#include <chrono>
#include <ctime> // strftime
#include <iomanip> // setw, setfill
#include <cstdio> // printf
using namespace std;
namespace fair
{
enum class Color : int
{
fgBlack = 30,
fgRed = 31,
fgGreen = 32,
fgYellow = 33,
fgBlue = 34,
fgMagenta = 35,
fgCyan = 36,
fgWhite = 37,
fgDefault = 39,
bgRed = 41,
bgGreen = 42,
bgBlue = 44,
bgDefault = 49
};
string startColor(Color color)
{
ostringstream os;
os << "\033[01;" << static_cast<int>(color) << "m";
return os.str();
}
string endColor()
{
return "\033[0m";
}
class ColorOut
{
public:
ColorOut(Color color, const string& str)
: fColor(color)
, fStr(str)
{}
friend ostream& operator<<(ostream& os, const ColorOut& w)
{
return os << "\033[01;" << static_cast<int>(w.fColor) << "m" << w.fStr << "\033[0m";
}
private:
Color fColor;
const string& fStr;
};
class ColoredSeverityWriter
{
public:
@@ -75,43 +29,42 @@ class ColoredSeverityWriter
friend ostream& operator<<(ostream& os, const ColoredSeverityWriter& w)
{
switch (w.fSeverity)
{
switch (w.fSeverity) {
case Severity::nolog:
return os << "\033[01;" << static_cast<int>(Color::fgDefault) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
return os << "\033[01;" << static_cast<int>(Logger::Color::fgDefault) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
break;
case Severity::fatal:
return os << "\033[01;" << static_cast<int>(Color::bgRed) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
return os << "\033[01;" << static_cast<int>(Logger::Color::bgRed) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
break;
case Severity::error:
return os << "\033[01;" << static_cast<int>(Color::fgRed) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
return os << "\033[01;" << static_cast<int>(Logger::Color::fgRed) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
break;
case Severity::warn:
return os << "\033[01;" << static_cast<int>(Color::fgYellow) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
return os << "\033[01;" << static_cast<int>(Logger::Color::fgYellow) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
break;
case Severity::state:
return os << "\033[01;" << static_cast<int>(Color::fgMagenta) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
return os << "\033[01;" << static_cast<int>(Logger::Color::fgMagenta) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
break;
case Severity::info:
return os << "\033[01;" << static_cast<int>(Color::fgGreen) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
return os << "\033[01;" << static_cast<int>(Logger::Color::fgGreen) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
break;
case Severity::debug:
return os << "\033[01;" << static_cast<int>(Color::fgBlue) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
return os << "\033[01;" << static_cast<int>(Logger::Color::fgBlue) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
break;
case Severity::debug1:
return os << "\033[01;" << static_cast<int>(Color::fgBlue) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
return os << "\033[01;" << static_cast<int>(Logger::Color::fgBlue) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
break;
case Severity::debug2:
return os << "\033[01;" << static_cast<int>(Color::fgBlue) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
return os << "\033[01;" << static_cast<int>(Logger::Color::fgBlue) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
break;
case Severity::debug3:
return os << "\033[01;" << static_cast<int>(Color::fgBlue) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
return os << "\033[01;" << static_cast<int>(Logger::Color::fgBlue) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
break;
case Severity::debug4:
return os << "\033[01;" << static_cast<int>(Color::fgBlue) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
return os << "\033[01;" << static_cast<int>(Logger::Color::fgBlue) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
break;
case Severity::trace:
return os << "\033[01;" << static_cast<int>(Color::fgCyan) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
return os << "\033[01;" << static_cast<int>(Logger::Color::fgCyan) << "m" << Logger::SeverityName(w.fSeverity) << "\033[0m";
break;
default:
return os << "UNKNOWN";
@@ -132,6 +85,8 @@ Severity Logger::fMinSeverity = Severity::info;
function<void()> Logger::fFatalCallback;
unordered_map<string, pair<Severity, function<void(const string& content, const LogMetaData& metadata)>>> Logger::fCustomSinks;
mutex Logger::fMtx;
bool Logger::fIsDestructed = false;
Logger::DestructionHelper fDestructionHelper;
#if defined(__APPLE__) || defined(__FreeBSD__)
const string Logger::fProcessName = getprogname();
@@ -143,16 +98,20 @@ const string Logger::fProcessName = "?";
const unordered_map<string, Verbosity> Logger::fVerbosityMap =
{
{ "veryhigh", Verbosity::veryhigh },
{ "high", Verbosity::high },
{ "medium", Verbosity::medium },
{ "low", Verbosity::low },
{ "verylow", Verbosity::verylow },
{ "VERYHIGH", Verbosity::veryhigh },
{ "HIGH", Verbosity::high },
{ "MEDIUM", Verbosity::medium },
{ "LOW", Verbosity::low },
{ "VERYLOW", Verbosity::verylow }
{ "veryhigh", Verbosity::veryhigh },
{ "high", Verbosity::high },
{ "medium", Verbosity::medium },
{ "low", Verbosity::low },
{ "verylow", Verbosity::verylow },
{ "VERYHIGH", Verbosity::veryhigh },
{ "HIGH", Verbosity::high },
{ "MEDIUM", Verbosity::medium },
{ "LOW", Verbosity::low },
{ "VERYLOW", Verbosity::verylow },
{ "user1", Verbosity::user1 },
{ "user2", Verbosity::user2 },
{ "user3", Verbosity::user3 },
{ "user4", Verbosity::user4 }
};
const unordered_map<string, Severity> Logger::fSeverityMap =
@@ -212,6 +171,25 @@ const array<string, 5> Logger::fVerbosityNames =
}
};
std::map<Verbosity, VerbositySpec> Logger::fVerbosities =
{
{ Verbosity::verylow, VerbositySpec::Make() },
{ Verbosity::low, VerbositySpec::Make(VerbositySpec::Info::severity) },
{ Verbosity::medium, VerbositySpec::Make(VerbositySpec::Info::timestamp_s,
VerbositySpec::Info::severity) },
{ Verbosity::high, VerbositySpec::Make(VerbositySpec::Info::process_name,
VerbositySpec::Info::timestamp_s,
VerbositySpec::Info::severity) },
{ Verbosity::veryhigh, VerbositySpec::Make(VerbositySpec::Info::process_name,
VerbositySpec::Info::timestamp_s,
VerbositySpec::Info::severity,
VerbositySpec::Info::file_line_function) },
{ Verbosity::user1, VerbositySpec::Make(VerbositySpec::Info::severity) },
{ Verbosity::user2, VerbositySpec::Make(VerbositySpec::Info::severity) },
{ Verbosity::user3, VerbositySpec::Make(VerbositySpec::Info::severity) },
{ Verbosity::user4, VerbositySpec::Make(VerbositySpec::Info::severity) }
};
string Logger::SeverityName(Severity severity)
{
return fSeverityNames.at(static_cast<size_t>(severity));
@@ -224,17 +202,19 @@ string Logger::VerbosityName(Verbosity verbosity)
Logger::Logger(Severity severity, const string& file, const string& line, const string& func)
{
chrono::time_point<chrono::system_clock> now = chrono::system_clock::now();
size_t pos = file.rfind("/");
if (!fIsDestructed) {
chrono::time_point<chrono::system_clock> now = chrono::system_clock::now();
size_t pos = file.rfind("/");
fMetaData.timestamp = chrono::system_clock::to_time_t(now);
fMetaData.us = chrono::duration_cast<chrono::microseconds>(now.time_since_epoch()) % 1000000;
fMetaData.process_name = fProcessName;
fMetaData.file = file.substr(pos + 1);
fMetaData.line = line;
fMetaData.func = func;
fMetaData.severity_name = fSeverityNames.at(static_cast<size_t>(severity));
fMetaData.severity = severity;
fMetaData.timestamp = chrono::system_clock::to_time_t(now);
fMetaData.us = chrono::duration_cast<chrono::microseconds>(now.time_since_epoch()) % 1000000;
fMetaData.process_name = fProcessName;
fMetaData.file = file.substr(pos + 1);
fMetaData.line = line;
fMetaData.func = func;
fMetaData.severity_name = fSeverityNames.at(static_cast<size_t>(severity));
fMetaData.severity = severity;
}
}
void Logger::SetConsoleSeverity(const Severity severity)
@@ -245,12 +225,9 @@ void Logger::SetConsoleSeverity(const Severity severity)
void Logger::SetConsoleSeverity(const string& severityStr)
{
if (fSeverityMap.count(severityStr))
{
if (fSeverityMap.count(severityStr)) {
SetConsoleSeverity(fSeverityMap.at(severityStr));
}
else
{
} else {
LOG(error) << "Unknown severity setting: '" << severityStr << "', setting to default 'info'.";
SetConsoleSeverity(Severity::info);
}
@@ -269,12 +246,9 @@ void Logger::SetFileSeverity(const Severity severity)
void Logger::SetFileSeverity(const string& severityStr)
{
if (fSeverityMap.count(severityStr))
{
if (fSeverityMap.count(severityStr)) {
SetFileSeverity(fSeverityMap.at(severityStr));
}
else
{
} else {
LOG(error) << "Unknown severity setting: '" << severityStr << "', setting to default 'info'.";
SetFileSeverity(Severity::info);
}
@@ -288,12 +262,9 @@ void Logger::SetCustomSeverity(const string& key, const Severity severity)
void Logger::SetCustomSeverity(const string& key, const string& severityStr)
{
if (fSeverityMap.count(severityStr))
{
if (fSeverityMap.count(severityStr)) {
SetCustomSeverity(key, fSeverityMap.at(severityStr));
}
else
{
} else {
LOG(error) << "Unknown severity setting: '" << severityStr << "', setting to default 'info'.";
SetCustomSeverity(key, Severity::info);
}
@@ -379,10 +350,8 @@ void Logger::UpdateMinSeverity()
{
fMinSeverity = (fConsoleSeverity <= fFileSeverity) ? fFileSeverity : fConsoleSeverity;
for (auto& it : fCustomSinks)
{
if (fMinSeverity <= it.second.first)
{
for (auto& it : fCustomSinks) {
if (fMinSeverity <= it.second.first) {
fMinSeverity = it.second.first;
}
}
@@ -390,28 +359,21 @@ void Logger::UpdateMinSeverity()
bool Logger::Logging(Severity severity)
{
if (Severity::fatal == severity)
{
if (Severity::fatal == severity) {
return true;
}
if (severity <= fMinSeverity && severity > Severity::nolog)
{
if (severity <= fMinSeverity && severity > Severity::nolog) {
return true;
}
else
{
} else {
return false;
}
}
bool Logger::Logging(const std::string& severityStr)
{
if (fSeverityMap.count(severityStr))
{
if (fSeverityMap.count(severityStr)) {
return Logging(fSeverityMap.at(severityStr));
}
else
{
} else {
LOG(error) << "Unknown severity setting: '" << severityStr;
return false;
}
@@ -424,12 +386,9 @@ void Logger::SetVerbosity(const Verbosity verbosity)
void Logger::SetVerbosity(const string& verbosityStr)
{
if (fVerbosityMap.count(verbosityStr))
{
if (fVerbosityMap.count(verbosityStr)) {
fVerbosity = fVerbosityMap.at(verbosityStr);
}
else
{
} else {
LOG(error) << "Unknown verbosity setting: '" << verbosityStr << "', setting to default 'low'.";
fVerbosity = Verbosity::low;
}
@@ -440,6 +399,20 @@ Verbosity Logger::GetVerbosity()
return fVerbosity;
}
void Logger::DefineVerbosity(const Verbosity verbosity, const VerbositySpec spec)
{
fVerbosities[verbosity] = spec;
}
void Logger::DefineVerbosity(const std::string& verbosityStr, const VerbositySpec spec)
{
if (fVerbosityMap.count(verbosityStr)) {
DefineVerbosity(fVerbosityMap.at(verbosityStr), spec);
} else {
LOG(error) << "Unknown verbosity: '" << verbosityStr;
}
}
void Logger::SetConsoleColor(const bool colored)
{
fColored = colored;
@@ -448,15 +421,13 @@ void Logger::SetConsoleColor(const bool colored)
void Logger::InitFileSink(const Severity severity, const string& filename, bool customizeName)
{
lock_guard<mutex> lock(fMtx);
if (fFileStream.is_open())
{
if (fFileStream.is_open()) {
fFileStream.close();
}
string fullName = filename;
if (customizeName)
{
if (customizeName) {
// TODO: customize file name
auto now = chrono::system_clock::to_time_t(chrono::system_clock::now());
stringstream ss;
@@ -472,13 +443,10 @@ void Logger::InitFileSink(const Severity severity, const string& filename, bool
fFileStream.open(fullName, fstream::out | fstream::app);
if (fFileStream.is_open())
{
if (fFileStream.is_open()) {
fFileSeverity = severity;
UpdateMinSeverity();
}
else
{
} else {
cout << "Error opening file: " << fullName;
}
@@ -486,12 +454,9 @@ void Logger::InitFileSink(const Severity severity, const string& filename, bool
void Logger::InitFileSink(const string& severityStr, const string& filename, bool customizeName)
{
if (fSeverityMap.count(severityStr))
{
if (fSeverityMap.count(severityStr)) {
InitFileSink(fSeverityMap.at(severityStr), filename, customizeName);
}
else
{
} else {
LOG(error) << "Unknown severity setting: '" << severityStr << "', setting to default 'info'.";
InitFileSink(Severity::info, filename);
}
@@ -500,8 +465,7 @@ void Logger::InitFileSink(const string& severityStr, const string& filename, boo
void Logger::RemoveFileSink()
{
lock_guard<mutex> lock(fMtx);
if (fFileStream.is_open())
{
if (fFileStream.is_open()) {
fFileStream.close();
}
}
@@ -535,25 +499,19 @@ void Logger::OnFatal(function<void()> func)
void Logger::AddCustomSink(const string& key, Severity severity, function<void(const string& content, const LogMetaData& metadata)> func)
{
lock_guard<mutex> lock(fMtx);
if (fCustomSinks.count(key) == 0)
{
if (fCustomSinks.count(key) == 0) {
fCustomSinks.insert(make_pair(key, make_pair(severity, func)));
UpdateMinSeverity();
}
else
{
} else {
cout << "Logger::AddCustomSink: sink '" << key << "' already exists, will not add again. Remove first with Logger::RemoveCustomSink(const string& key)" << endl;
}
}
void Logger::AddCustomSink(const string& key, const string& severityStr, function<void(const string& content, const LogMetaData& metadata)> func)
{
if (fSeverityMap.count(severityStr))
{
if (fSeverityMap.count(severityStr)) {
AddCustomSink(key, fSeverityMap.at(severityStr), func);
}
else
{
} else {
LOG(error) << "Unknown severity setting: '" << severityStr << "', setting to default 'info'.";
AddCustomSink(key, Severity::info, func);
}
@@ -561,80 +519,114 @@ void Logger::AddCustomSink(const string& key, const string& severityStr, functio
void Logger::RemoveCustomSink(const string& key)
{
if (fCustomSinks.count(key) > 0)
{
if (fCustomSinks.count(key) > 0) {
fCustomSinks.erase(key);
UpdateMinSeverity();
}
else
{
} else {
cout << "Logger::RemoveCustomSink: sink '" << key << "' doesn't exists, will not remove." << endl;
}
}
Logger& Logger::Log()
{
if (fIsDestructed) {
return *this;
}
char tsstr[32];
{
lock_guard<mutex> lock(fMtx); // localtime is not threadsafe, guard it
if (!strftime(tsstr, sizeof(tsstr), "%H:%M:%S", localtime(&(fMetaData.timestamp))))
{
if (!strftime(tsstr, sizeof(tsstr), "%H:%M:%S", localtime(&(fMetaData.timestamp)))) {
tsstr[0] = 'u';
}
}
if ((!fColored && LoggingToConsole()) || LoggingToFile())
{
if (fVerbosity >= Verbosity::high)
{
fBWOut << "[" << fMetaData.process_name << "]"
<< "[" << tsstr << "." << setw(6) << setfill('0') << fMetaData.us.count() << "]";
}
else if (fVerbosity == Verbosity::medium)
{
fBWOut << "[" << tsstr << "]";
auto spec = fVerbosities[fVerbosity];
if ((!fColored && LoggingToConsole()) || LoggingToFile()) {
bool appendSpace = false;
for (const auto info : spec.fOrder) {
switch (info) {
case VerbositySpec::Info::process_name:
fBWOut << "[" << fMetaData.process_name << "]";
appendSpace = true;
break;
case VerbositySpec::Info::timestamp_us:
fBWOut << "[" << tsstr << "." << setw(6) << setfill('0') << fMetaData.us.count() << "]";
appendSpace = true;
break;
case VerbositySpec::Info::timestamp_s:
fBWOut << "[" << tsstr << "]";
appendSpace = true;
break;
case VerbositySpec::Info::severity:
fBWOut << "[" << fMetaData.severity_name << "]";
appendSpace = true;
break;
case VerbositySpec::Info::file_line_function:
fBWOut << "[" << fMetaData.file << ":" << fMetaData.line << ":" << fMetaData.func << "]";
appendSpace = true;
break;
case VerbositySpec::Info::file_line:
fBWOut << "[" << fMetaData.file << ":" << fMetaData.line << "]";
appendSpace = true;
break;
case VerbositySpec::Info::file:
fBWOut << "[" << fMetaData.file << "]";
appendSpace = true;
break;
default:
break;
}
}
if (fVerbosity > Verbosity::verylow)
{
fBWOut << "[" << fMetaData.severity_name << "]";
}
if (fVerbosity == Verbosity::veryhigh)
{
fBWOut << "[" << fMetaData.file << ":" << fMetaData.line << ":" << fMetaData.func << "]";
}
if (fVerbosity != Verbosity::verylow)
{
if (appendSpace) {
fBWOut << " ";
}
}
if (fColored && (LoggingToConsole()))
{
if (fVerbosity >= Verbosity::high)
{
fColorOut << "[" << ColorOut(Color::fgBlue, fMetaData.process_name) << "]"
<< "[" << startColor(Color::fgCyan) << tsstr << "." << setw(6) << setfill('0') << fMetaData.us.count() << endColor() << "]";
}
else if (fVerbosity == Verbosity::medium)
{
fColorOut << "[" << startColor(Color::fgCyan) << tsstr << endColor() << "]";
if (fColored && LoggingToConsole()) {
bool appendSpace = false;
for (const auto info : spec.fOrder) {
switch (info) {
case VerbositySpec::Info::process_name:
fColorOut << "[" << ColorOut(Color::fgBlue, fMetaData.process_name) << "]";
appendSpace = true;
break;
case VerbositySpec::Info::timestamp_us:
fColorOut << "[" << startColor(Color::fgCyan) << tsstr << "."
<< setw(6) << setfill('0') << fMetaData.us.count() << endColor() << "]";
appendSpace = true;
break;
case VerbositySpec::Info::timestamp_s:
fColorOut << "[" << startColor(Color::fgCyan) << tsstr << endColor() << "]";
appendSpace = true;
break;
case VerbositySpec::Info::severity:
fColorOut << "[" << ColoredSeverityWriter(fMetaData.severity) << "]";
appendSpace = true;
break;
case VerbositySpec::Info::file_line_function:
fColorOut << "[" << ColorOut(Color::fgBlue, fMetaData.file) << ":"
<< ColorOut(Color::fgYellow, fMetaData.line) << ":"
<< ColorOut(Color::fgBlue, fMetaData.func) << "]";
appendSpace = true;
break;
case VerbositySpec::Info::file_line:
fColorOut << "[" << ColorOut(Color::fgBlue, fMetaData.file) << ":"
<< ColorOut(Color::fgYellow, fMetaData.line) << "]";
appendSpace = true;
break;
case VerbositySpec::Info::file:
fColorOut << "[" << ColorOut(Color::fgBlue, fMetaData.file) << "]";
appendSpace = true;
break;
default:
break;
}
}
if (fVerbosity > Verbosity::verylow)
{
fColorOut << "[" << ColoredSeverityWriter(fMetaData.severity) << "]";
}
if (fVerbosity == Verbosity::veryhigh)
{
fColorOut << "[" << ColorOut(Color::fgBlue, fMetaData.file) << ":" << ColorOut(Color::fgYellow, fMetaData.line) << ":" << ColorOut(Color::fgBlue, fMetaData.func) << "]";
}
if (fVerbosity != Verbosity::verylow)
{
if (appendSpace) {
fColorOut << " ";
}
}
@@ -656,10 +648,13 @@ Logger& Logger::operator<<(ostream& (*manip) (ostream&))
Logger::~Logger() noexcept(false)
{
for (auto& it : fCustomSinks)
{
if (LoggingCustom(it.second.first))
{
if (fIsDestructed) {
printf("post-static destruction output: %s\n", fContent.str().c_str());
return;
}
for (auto& it : fCustomSinks) {
if (LoggingCustom(it.second.first)) {
lock_guard<mutex> lock(fMtx);
it.second.second(fContent.str(), fMetaData);
}
@@ -669,32 +664,24 @@ Logger::~Logger() noexcept(false)
fBWOut << fContent.str();
if (LoggingToConsole())
{
if (fColored)
{
if (LoggingToConsole()) {
if (fColored) {
fColorOut << fContent.str();
cout << fColorOut.str() << flush;
}
else
{
} else {
cout << fBWOut.str() << flush;
}
}
if (LoggingToFile())
{
if (LoggingToFile()) {
lock_guard<mutex> lock(fMtx);
if (fFileStream.is_open())
{
if (fFileStream.is_open()) {
fFileStream << fBWOut.str() << flush;
}
}
if (fMetaData.severity == Severity::fatal)
{
if (fFatalCallback)
{
if (fMetaData.severity == Severity::fatal) {
if (fFatalCallback) {
fFatalCallback();
}
}

View File

@@ -18,11 +18,19 @@
#include <string>
#include <unordered_map>
#include <functional>
#include <unordered_map>
#include <map>
#include <chrono>
#include <mutex>
#include <utility> // pair
#include <time.h> // time_t
#include <array>
#include <type_traits>
#include <cassert>
#include <algorithm>
#ifdef FAIRLOGGER_USE_BOOST_PRETTY_FUNCTION
#include <boost/current_function.hpp>
#endif
namespace fair
{
@@ -76,7 +84,64 @@ enum class Verbosity : int
LOW = low,
MEDIUM = medium,
HIGH = high,
VERYHIGH = veryhigh
VERYHIGH = veryhigh,
// extra slots for user-defined verbosities:
user1,
user2,
user3,
user4,
};
struct VerbositySpec
{
enum class Info : int
{
__empty__ = 0, // used to initialize order array
process_name, // [process name]
timestamp_s, // [HH:MM:SS]
timestamp_us, // [HH:MM:SS:µS]
severity, // [severity]
file, // [file]
file_line, // [file:line]
file_line_function, // [file:line:function]
__max__ // needs to be last in enum
};
std::array<Info, static_cast<int>(Info::__max__)> fOrder;
VerbositySpec() : fOrder({Info::__empty__}) {}
template<typename ... Ts>
static VerbositySpec Make(Ts ... options)
{
static_assert(sizeof...(Ts) < static_cast<int>(Info::__max__),
"Maximum number of VerbositySpec::Info parameters exceeded.");
return Make(VerbositySpec(), 0, options...);
}
private:
template<typename T, typename ... Ts>
static VerbositySpec Make(VerbositySpec spec, int i, T option, Ts ... options)
{
static_assert(std::is_same<T, Info>::value,
"Only arguments of type VerbositySpec::Info are allowed.");
assert(option > Info::__empty__);
assert(option < Info::__max__);
if (std::find(spec.fOrder.begin(), spec.fOrder.end(), option) == spec.fOrder.end()) {
spec.fOrder[i] = option;
++i;
}
return Make(spec, i, options ...);
}
static VerbositySpec Make(VerbositySpec spec, int)
{
return spec;
}
};
// non-std exception to avoid undesirable catches - fatal should exit in a way we want.
@@ -117,6 +182,82 @@ class Logger
public:
Logger(Severity severity, const std::string& file, const std::string& line, const std::string& func);
enum class Color : int
{
bold = 1,
dim = 2,
underline = 4,
blink = 5,
reverse = 7,
hidden = 8,
fgDefault = 39,
fgBlack = 30,
fgRed = 31,
fgGreen = 32,
fgYellow = 33,
fgBlue = 34,
fgMagenta = 35,
fgCyan = 36,
fgLightGray = 37,
fgDarkGray = 90,
fgLightRed = 91,
fgLightGreen = 92,
fgLightYellow = 93,
fgLightBlue = 94,
fgLightMagenta = 95,
fgLightCyan = 96,
fgWhite = 97,
bgDefault = 49,
bgBlack = 40,
bgRed = 41,
bgGreen = 42,
bgYellow = 43,
bgBlue = 44,
bgMagenta = 45,
bgCyan = 46,
bgLightGray = 47,
bgDarkGray = 100,
bgLightRed = 101,
bgLightGreen = 102,
bgLightYellow = 103,
bgLightBlue = 104,
bgLightMagenta = 105,
bgLightCyan = 106,
bgWhite = 107
};
static std::string startColor(Color color)
{
std::ostringstream os;
os << "\033[01;" << static_cast<int>(color) << "m";
return os.str();
}
static std::string endColor()
{
return "\033[0m";
}
class ColorOut
{
public:
ColorOut(Color color, const std::string& str)
: fColor(color)
, fStr(str)
{}
friend std::ostream& operator<<(std::ostream& os, const ColorOut& w)
{
return os << "\033[01;" << static_cast<int>(w.fColor) << "m" << w.fStr << "\033[0m";
}
private:
Color fColor;
const std::string& fStr;
};
static void SetConsoleSeverity(const Severity severity);
static void SetConsoleSeverity(const std::string& severityStr);
static Severity GetConsoleSeverity();
@@ -138,6 +279,8 @@ class Logger
static void SetVerbosity(const Verbosity verbosity);
static void SetVerbosity(const std::string& verbosityStr);
static Verbosity GetVerbosity();
static void DefineVerbosity(const Verbosity, VerbositySpec);
static void DefineVerbosity(const std::string& verbosityStr, VerbositySpec);
static void SetConsoleColor(const bool colored = true);
@@ -192,6 +335,10 @@ class Logger
virtual ~Logger() noexcept(false);
// protection for use after static destruction took place
static bool fIsDestructed;
static struct DestructionHelper { ~DestructionHelper() { Logger::fIsDestructed = true; }} fDestructionHelper;
private:
LogMetaData fMetaData;
@@ -217,6 +364,8 @@ class Logger
bool LoggingCustom(const Severity) const;
static void UpdateMinSeverity();
static std::map<Verbosity, VerbositySpec> fVerbosities;
};
} // namespace fair
@@ -224,9 +373,15 @@ class Logger
#define IMP_CONVERTTOSTRING(s) # s
#define CONVERTTOSTRING(s) IMP_CONVERTTOSTRING(s)
#ifdef FAIRLOGGER_USE_BOOST_PRETTY_FUNCTION
#define LOG(severity) \
for (bool fairLOggerunLikelyvariable = false; fair::Logger::Logging(fair::Severity::severity) && !fairLOggerunLikelyvariable; fairLOggerunLikelyvariable = true) \
fair::Logger(fair::Severity::severity, __FILE__, CONVERTTOSTRING(__LINE__), BOOST_CURRENT_FUNCTION).Log()
#else
#define LOG(severity) \
for (bool fairLOggerunLikelyvariable = false; fair::Logger::Logging(fair::Severity::severity) && !fairLOggerunLikelyvariable; fairLOggerunLikelyvariable = true) \
fair::Logger(fair::Severity::severity, __FILE__, CONVERTTOSTRING(__LINE__), __FUNCTION__).Log()
#endif
// with custom file, line, function
#define LOGD(severity, file, line, function) \

View File

@@ -18,6 +18,9 @@
using namespace std;
using namespace fair;
namespace test
{
void printEverySeverity()
{
static int i = 1;
@@ -35,25 +38,39 @@ void printEverySeverity()
LOG(trace) << "trace message " << i++;
}
}
void printAllVerbositiesWithSeverity(Severity sev)
{
Logger::SetConsoleSeverity(sev);
cout << endl << "cout: >>> testing severity '" << Logger::SeverityName(sev) << "' with 'verylow' verbosity..." << endl;
Logger::SetVerbosity(Verbosity::verylow);
printEverySeverity();
test::printEverySeverity();
cout << endl << "cout: >>> testing severity '" << Logger::SeverityName(sev) << "' with 'low' verbosity..." << endl;
Logger::SetVerbosity(Verbosity::low);
printEverySeverity();
test::printEverySeverity();
cout << endl << "cout: >>> testing severity '" << Logger::SeverityName(sev) << "' with 'medium' verbosity..." << endl;
Logger::SetVerbosity(Verbosity::medium);
printEverySeverity();
test::printEverySeverity();
cout << endl << "cout: >>> testing severity '" << Logger::SeverityName(sev) << "' with 'high' verbosity..." << endl;
Logger::SetVerbosity(Verbosity::high);
printEverySeverity();
test::printEverySeverity();
cout << endl << "cout: >>> testing severity '" << Logger::SeverityName(sev) << "' with 'veryhigh' verbosity..." << endl;
Logger::SetVerbosity(Verbosity::veryhigh);
printEverySeverity();
test::printEverySeverity();
cout << endl << "cout: >>> testing severity '" << Logger::SeverityName(sev) << "' with 'user1' verbosity..." << endl;
Logger::SetVerbosity(Verbosity::user1);
test::printEverySeverity();
cout << endl << "cout: >>> testing severity '" << Logger::SeverityName(sev) << "' with 'user2' verbosity..." << endl;
Logger::SetVerbosity(Verbosity::user2);
test::printEverySeverity();
cout << endl << "cout: >>> testing severity '" << Logger::SeverityName(sev) << "' with 'user3' verbosity..." << endl;
Logger::SetVerbosity(Verbosity::user3);
test::printEverySeverity();
cout << endl << "cout: >>> testing severity '" << Logger::SeverityName(sev) << "' with 'user4' verbosity..." << endl;
Logger::SetVerbosity(Verbosity::user4);
test::printEverySeverity();
}
void silentlyPrintAllVerbositiesWithSeverity(Severity sev)
@@ -61,21 +78,34 @@ void silentlyPrintAllVerbositiesWithSeverity(Severity sev)
Logger::SetConsoleSeverity(sev);
Logger::SetVerbosity(Verbosity::verylow);
printEverySeverity();
test::printEverySeverity();
Logger::SetVerbosity(Verbosity::low);
printEverySeverity();
test::printEverySeverity();
Logger::SetVerbosity(Verbosity::medium);
printEverySeverity();
test::printEverySeverity();
Logger::SetVerbosity(Verbosity::high);
printEverySeverity();
test::printEverySeverity();
Logger::SetVerbosity(Verbosity::veryhigh);
printEverySeverity();
test::printEverySeverity();
Logger::SetVerbosity(Verbosity::user1);
test::printEverySeverity();
Logger::SetVerbosity(Verbosity::user2);
test::printEverySeverity();
Logger::SetVerbosity(Verbosity::user3);
test::printEverySeverity();
Logger::SetVerbosity(Verbosity::user4);
test::printEverySeverity();
}
int main()
{
Logger::SetConsoleColor(true);
auto spec = VerbositySpec::Make(VerbositySpec::Info::file_line_function,
VerbositySpec::Info::process_name,VerbositySpec::Info::process_name);
cout << "Defining custom verbosity \"user2\"" << endl;
Logger::DefineVerbosity(Verbosity::user2, spec);
cout << "cout: testing severities..." << endl;
printAllVerbositiesWithSeverity(Severity::trace);
@@ -133,7 +163,7 @@ int main()
cout << "cout: ----------------------------" << endl;
cout << "cout: open log file with severity 'error'" << endl;
Logger::InitFileSink(Severity::error, "test_log", true);
printEverySeverity();
test::printEverySeverity();
cout << "cout: closing log file" << endl;
Logger::RemoveFileSink();
@@ -158,7 +188,7 @@ int main()
cout << "CustomSink: \tfair::Severity severity: " << static_cast<int>(metadata.severity) << endl;
});
printEverySeverity();
test::printEverySeverity();
cout << endl << "cout: removing custom sink with info severity" << endl;
@@ -166,6 +196,5 @@ int main()
Logger::RemoveCustomSink("CustomSink");
Logger::RemoveCustomSink("bla");
return 0;
}