LogTargetWrapper, LogStateWrapper
This commit is contained in:
parent
3358c381f6
commit
0a1b5a9e42
1 changed files with 255 additions and 17 deletions
272
common/log.h
272
common/log.h
|
@ -5,7 +5,10 @@
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
#include <atomic>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
|
||||||
|
@ -47,6 +50,238 @@
|
||||||
//
|
//
|
||||||
// --------------------------------
|
// --------------------------------
|
||||||
|
|
||||||
|
class LogTargetWrapper
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
LogTargetWrapper( LogTargetWrapper& other ) = delete;
|
||||||
|
void operator=( const LogTargetWrapper& ) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
LogTargetWrapper( FILE* handle )
|
||||||
|
: _type(Type::Stream),
|
||||||
|
_opened(true),
|
||||||
|
_handle(handle)
|
||||||
|
{}
|
||||||
|
|
||||||
|
LogTargetWrapper( const std::string && filename )
|
||||||
|
: LogTargetWrapper(filename)
|
||||||
|
{}
|
||||||
|
LogTargetWrapper( const std::string & filename )
|
||||||
|
: _filename(filename)
|
||||||
|
{
|
||||||
|
if(_filename.empty())
|
||||||
|
{
|
||||||
|
_type = Type::Stream;
|
||||||
|
_handle = stderr;
|
||||||
|
_opened = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_type = Type::File;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~LogTargetWrapper()
|
||||||
|
{
|
||||||
|
if(_type == Type::File && _handle != nullptr) {std::fclose(_handle);}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Type{
|
||||||
|
Invalid,
|
||||||
|
File,
|
||||||
|
Stream
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
operator FILE*()
|
||||||
|
{
|
||||||
|
// Assigning lambda result to a static variable guarantees one time thread-safe execution
|
||||||
|
// while completly removing the need for conditional if(initialized).
|
||||||
|
//
|
||||||
|
// This noticeably reduces performance overhead of LOG macro invocations.
|
||||||
|
//
|
||||||
|
// Initializing log file here, prevents creating a file before first LOG use,
|
||||||
|
// simplifying initial conditions and overrides from command line arguments.
|
||||||
|
//
|
||||||
|
// TODO: MSVC
|
||||||
|
//
|
||||||
|
static const auto dummy_init __attribute__((unused)) = [&](){
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
if(!_opened && _type == Type::File)
|
||||||
|
{
|
||||||
|
auto result = std::fopen(_filename.c_str(), "w");
|
||||||
|
if( result )
|
||||||
|
{
|
||||||
|
_handle = result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::fprintf(stderr, "Failed to open logfile '%s' with error '%s'\n", _filename.c_str(), std::strerror(errno));
|
||||||
|
std::fflush(stderr);
|
||||||
|
_handle = stderr;
|
||||||
|
}
|
||||||
|
_opened = true;
|
||||||
|
}
|
||||||
|
return _opened;
|
||||||
|
}();
|
||||||
|
return _handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
if(_opened && _type != Type::Invalid && _handle)
|
||||||
|
{
|
||||||
|
std::fflush(_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex _mutex;
|
||||||
|
Type _type {Type::Invalid};
|
||||||
|
std::string _filename;
|
||||||
|
bool _opened {false};
|
||||||
|
std::atomic<FILE*> _handle {nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
class LogStateWrapper
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
LogStateWrapper(){};
|
||||||
|
|
||||||
|
virtual ~LogStateWrapper()
|
||||||
|
{
|
||||||
|
flush_all_targets();
|
||||||
|
for(auto t: _targets){delete t;}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
LogStateWrapper( LogStateWrapper& other ) = delete;
|
||||||
|
void operator=( const LogStateWrapper& ) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static LogStateWrapper& instance()
|
||||||
|
{
|
||||||
|
static LogStateWrapper inst;
|
||||||
|
return inst;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex _mutex;
|
||||||
|
bool _enabled{true};
|
||||||
|
bool _global_enabled{true};
|
||||||
|
std::set<LogTargetWrapper*> _targets;
|
||||||
|
LogTargetWrapper _null_target {nullptr};
|
||||||
|
LogTargetWrapper _stderr_target {stderr};
|
||||||
|
std::atomic<LogTargetWrapper*> _current_target {&_null_target};
|
||||||
|
std::atomic<LogTargetWrapper*> _current_tee_target {&_stderr_target};
|
||||||
|
std::atomic<LogTargetWrapper*> _stored_target {&_null_target};
|
||||||
|
std::atomic<LogTargetWrapper*> _stored_tee_target {&_null_target};
|
||||||
|
|
||||||
|
LogTargetWrapper* log_set_target_impl( const std::string && filename ) {return log_set_target_impl(filename);}
|
||||||
|
LogTargetWrapper* log_set_target_impl( const std::string & filename ) {return log_add_select_target(new LogTargetWrapper(filename));}
|
||||||
|
LogTargetWrapper* log_set_target_impl( FILE* handle ) {return log_add_select_target(new LogTargetWrapper(handle));}
|
||||||
|
LogTargetWrapper* log_set_target_impl( LogTargetWrapper * target )
|
||||||
|
{
|
||||||
|
flush_all_targets();
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
_current_target.store(target);
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogTargetWrapper* log_set_tee_target_impl( const std::string && filename ) {return log_set_tee_target_impl(filename);}
|
||||||
|
LogTargetWrapper* log_set_tee_target_impl( const std::string & filename ) {return log_add_select_tee_target(new LogTargetWrapper(filename));}
|
||||||
|
LogTargetWrapper* log_set_tee_target_impl( FILE* handle ) {return log_add_select_tee_target(new LogTargetWrapper(handle));}
|
||||||
|
LogTargetWrapper* log_set_tee_target_impl( LogTargetWrapper * target )
|
||||||
|
{
|
||||||
|
flush_all_targets();
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
_current_tee_target.store(target);
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogTargetWrapper* log_add_select_target(LogTargetWrapper* t)
|
||||||
|
{
|
||||||
|
flush_all_targets();
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
_current_target.store(t);
|
||||||
|
_targets.insert(t);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogTargetWrapper* log_add_select_tee_target(LogTargetWrapper* t)
|
||||||
|
{
|
||||||
|
flush_all_targets();
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
_current_tee_target.store(t);
|
||||||
|
_targets.insert(t);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_all_targets()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
for(auto t: _targets){ t->flush(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* log_handler_impl()
|
||||||
|
{
|
||||||
|
return *_current_target;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* log_tee_handler_impl()
|
||||||
|
{
|
||||||
|
return *_current_tee_target;
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_global_disable()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
_global_enabled = false;
|
||||||
|
}
|
||||||
|
log_disable_impl();
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_disable_impl()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
if(_enabled)
|
||||||
|
{
|
||||||
|
_stored_target.store (_current_target);
|
||||||
|
_stored_tee_target.store (_current_tee_target);
|
||||||
|
_current_target.store (&_null_target);
|
||||||
|
_current_tee_target.store (&_stderr_target);
|
||||||
|
_enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_enable_impl()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
if(!_enabled && _global_enabled)
|
||||||
|
{
|
||||||
|
_current_target.store (_stored_target);
|
||||||
|
_current_tee_target.store (_stored_tee_target);
|
||||||
|
_enabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static LogTargetWrapper* log_set_target( const std::string && filename ) {return log_set_target(filename);}
|
||||||
|
static LogTargetWrapper* log_set_target( const std::string & filename ) {return instance().log_set_target_impl(filename);}
|
||||||
|
static LogTargetWrapper* log_set_target( FILE* handle ) {return instance().log_set_target_impl(handle);}
|
||||||
|
static LogTargetWrapper* log_set_target( LogTargetWrapper * target ) {return instance().log_set_target_impl(target);}
|
||||||
|
static LogTargetWrapper* log_set_tee_target( const std::string && filename ) {return log_set_tee_target(filename);}
|
||||||
|
static LogTargetWrapper* log_set_tee_target( const std::string & filename ) {return instance().log_set_tee_target_impl(filename);}
|
||||||
|
static LogTargetWrapper* log_set_tee_target( FILE* handle ) {return instance().log_set_tee_target_impl(handle);}
|
||||||
|
static LogTargetWrapper* log_set_tee_target( LogTargetWrapper * target ) {return instance().log_set_tee_target_impl(target);}
|
||||||
|
static FILE* log_handler() {return instance().log_handler_impl();}
|
||||||
|
static FILE* log_tee_handler() {return instance().log_tee_handler_impl();}
|
||||||
|
static void log_disable() {instance().log_disable_impl();}
|
||||||
|
static void log_enable() {instance().log_enable_impl();}
|
||||||
|
};
|
||||||
|
|
||||||
// Specifies a log target.
|
// Specifies a log target.
|
||||||
// default uses log_handler() with "llama.log" log file
|
// default uses log_handler() with "llama.log" log file
|
||||||
// this can be changed, by defining LOG_TARGET
|
// this can be changed, by defining LOG_TARGET
|
||||||
|
@ -90,11 +325,11 @@
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
#ifndef LOG_TARGET
|
#ifndef LOG_TARGET
|
||||||
#define LOG_TARGET log_handler()
|
#define LOG_TARGET LogStateWrapper::log_handler()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef LOG_TEE_TARGET
|
#ifndef LOG_TEE_TARGET
|
||||||
#define LOG_TEE_TARGET stderr
|
#define LOG_TEE_TARGET LogStateWrapper::log_tee_handler()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Utility to obtain "pid" like unique process id and use it when creating log files.
|
// Utility to obtain "pid" like unique process id and use it when creating log files.
|
||||||
|
@ -225,7 +460,7 @@ enum LogTriState
|
||||||
// USE LOG() INSTEAD
|
// USE LOG() INSTEAD
|
||||||
//
|
//
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
#define LOG_IMPL(str, ...) \
|
#define LOG_IMPL(str, ...) \
|
||||||
{ \
|
{ \
|
||||||
if (LOG_TARGET != nullptr) \
|
if (LOG_TARGET != nullptr) \
|
||||||
{ \
|
{ \
|
||||||
|
@ -234,7 +469,7 @@ enum LogTriState
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#define LOG_IMPL(str, ...) \
|
#define LOG_IMPL(str, ...) \
|
||||||
{ \
|
{ \
|
||||||
if (LOG_TARGET != nullptr) \
|
if (LOG_TARGET != nullptr) \
|
||||||
{ \
|
{ \
|
||||||
|
@ -248,7 +483,7 @@ enum LogTriState
|
||||||
// USE LOG_TEE() INSTEAD
|
// USE LOG_TEE() INSTEAD
|
||||||
//
|
//
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
#define LOG_TEE_IMPL(str, ...) \
|
#define LOG_TEE_IMPL(str, ...) \
|
||||||
{ \
|
{ \
|
||||||
if (LOG_TARGET != nullptr) \
|
if (LOG_TARGET != nullptr) \
|
||||||
{ \
|
{ \
|
||||||
|
@ -262,7 +497,7 @@ enum LogTriState
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#define LOG_TEE_IMPL(str, ...) \
|
#define LOG_TEE_IMPL(str, ...) \
|
||||||
{ \
|
{ \
|
||||||
if (LOG_TARGET != nullptr) \
|
if (LOG_TARGET != nullptr) \
|
||||||
{ \
|
{ \
|
||||||
|
@ -405,7 +640,8 @@ inline FILE *log_handler2_impl(bool change = false, LogTriState disable = LogTri
|
||||||
// Disables logs entirely at runtime.
|
// Disables logs entirely at runtime.
|
||||||
// Makes LOG() and LOG_TEE() produce no output,
|
// Makes LOG() and LOG_TEE() produce no output,
|
||||||
// untill enabled back.
|
// untill enabled back.
|
||||||
#define log_disable() log_disable_impl()
|
//#define log_disable() log_disable_impl()
|
||||||
|
#define log_disable() LogStateWrapper::log_disable()
|
||||||
|
|
||||||
// INTERNAL, DO NOT USE
|
// INTERNAL, DO NOT USE
|
||||||
inline FILE *log_disable_impl()
|
inline FILE *log_disable_impl()
|
||||||
|
@ -414,7 +650,8 @@ inline FILE *log_disable_impl()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enables logs at runtime.
|
// Enables logs at runtime.
|
||||||
#define log_enable() log_enable_impl()
|
//#define log_enable() log_enable_impl()
|
||||||
|
#define log_enable() LogStateWrapper::log_enable()
|
||||||
|
|
||||||
// INTERNAL, DO NOT USE
|
// INTERNAL, DO NOT USE
|
||||||
inline FILE *log_enable_impl()
|
inline FILE *log_enable_impl()
|
||||||
|
@ -423,7 +660,8 @@ inline FILE *log_enable_impl()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets target fir logs, either by a file name or FILE* pointer (stdout, stderr, or any valid FILE*)
|
// Sets target fir logs, either by a file name or FILE* pointer (stdout, stderr, or any valid FILE*)
|
||||||
#define log_set_target(target) log_set_target_impl(target)
|
//#define log_set_target(target) log_set_target_impl(target)
|
||||||
|
#define log_set_target(target) LogStateWrapper::log_set_target(target)
|
||||||
|
|
||||||
// INTERNAL, DO NOT USE
|
// INTERNAL, DO NOT USE
|
||||||
inline FILE *log_set_target_impl(const std::string & filename) { return log_handler1_impl(true, LogTriStateSame, filename); }
|
inline FILE *log_set_target_impl(const std::string & filename) { return log_handler1_impl(true, LogTriStateSame, filename); }
|
||||||
|
@ -513,16 +751,16 @@ inline bool log_param_pair_parse(bool check_but_dont_parse, const std::string &
|
||||||
|
|
||||||
inline void log_print_usage()
|
inline void log_print_usage()
|
||||||
{
|
{
|
||||||
fprintf(stdout, "log options:\n");
|
printf("log options:\n");
|
||||||
/* format
|
/* format
|
||||||
fprintf(stdout, " -h, --help show this help message and exit\n");*/
|
printf(" -h, --help show this help message and exit\n");*/
|
||||||
/* spacing
|
/* spacing
|
||||||
fprintf(stdout, "__-param----------------Description\n");*/
|
printf("__-param----------------Description\n");*/
|
||||||
fprintf(stdout, " --log-test Run simple logging test\n");
|
printf(" --log-test Run simple logging test\n");
|
||||||
fprintf(stdout, " --log-disable Disable trace logs\n");
|
printf(" --log-disable Disable trace logs\n");
|
||||||
fprintf(stdout, " --log-enable Enable trace logs\n");
|
printf(" --log-enable Enable trace logs\n");
|
||||||
fprintf(stdout, " --log-file Specify a log filename (without extension)\n");
|
printf(" --log-file Specify a log filename (without extension)\n");
|
||||||
fprintf(stdout, " Log file will be tagged with unique ID and written as \"<name>.<ID>.log\"\n"); /* */
|
printf(" Log file will be tagged with unique ID and written as \"<name>.<ID>.log\"\n"); /* */
|
||||||
}
|
}
|
||||||
|
|
||||||
#define log_dump_cmdline(argc, argv) log_dump_cmdline_impl(argc, argv)
|
#define log_dump_cmdline(argc, argv) log_dump_cmdline_impl(argc, argv)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue