log_enable/disable, LOG_TEE, basic usage doc

This commit is contained in:
staviq 2023-08-24 02:00:16 +02:00
parent 71d05b9ae4
commit 47b9f2d36f
2 changed files with 193 additions and 31 deletions

View file

@ -9,6 +9,41 @@
#include <sstream>
#include <iostream>
// --------------------------------
//
// Basic usage:
//
// --------
//
// The LOG() and LOG_TEE() macros are ready to go by default
// they do not require any initialization.
//
// LOG() behaves exactly like printf, by default writing to a logfile.
// LOG_TEE() additionally, prints to the screen too ( mimics Unix tee command ).
//
// Default logfile is named
// "llama.<threadID>.log"
// Default LOG_TEE() secondary output target is
// stderr
//
// Logs can be dynamically disabled or enabled using functions:
// log_disable()
// and
// log_enable()
//
// A log target can be changed with:
// log_set_target( string )
// creating and opening, or re-opening a file by string filename
// or
// log_set_target( FILE* )
// allowing to point at stderr, stdout, or any valid FILE* file handler.
//
// --------
//
// End of Basic usage.
//
// --------------------------------
// Specifies a log target.
// default uses log_handler() with "llama.log" log file
// this can be changed, by defining LOG_TARGET
@ -55,6 +90,24 @@
#define LOG_TARGET log_handler()
#endif
#ifndef LOG_TEE_TARGET
#define LOG_TEE_TARGET stderr
#endif
#ifndef LOG_DEFAULT_FILE_NAME
// TODO: This should get a wrapper
// to let others have custom log names with automatic unique id
#define LOG_DEFAULT_FILE_NAME std::string().append("llama.").append(log_get_pid()).append(".log")
#endif
// Utility for turning #define values into string literals
// so we can have a define for stderr and
// we can print "stderr" instead of literal stderr, etc.
#define _LOG_STRINGIZE(s) #s
#define LOG_STRINGIZE(s) _LOG_STRINGIZE(s)
#define LOG_TEE_TARGET_STRING LOG_STRINGIZE(LOG_TEE_TARGET)
// Allows disabling timestamps.
// in order to disable, define LOG_NO_TIMESTAMPS
// like so:
@ -85,18 +138,66 @@
#define LOG_FLF_VAL
#endif
#define _LOG(str, ...) \
{ \
/*fprintf(stderr, "DBG:" str, ##__VA_ARGS__);*/ \
fprintf(LOG_TARGET, LOG_TIMESTAMP_FMT LOG_FLF_FMT str "%c" LOG_TIMESTAMP_VAL LOG_FLF_VAL, ##__VA_ARGS__); \
fflush(LOG_TARGET); /*fprintf(stderr, "DBGEND\n");*/ \
// Utility for synchronizing log configuration state
// since std::optional was introduced only in c++17
enum LogTriState
{
LogTriStateSame,
LogTriStateFalse,
LogTriStateTrue
};
// INTERNAL, DO NOT USE
// USE LOG() INSTEAD
//
#define _LOG(str, ...) \
{ \
/*fprintf(stderr, "DBG:" str, ##__VA_ARGS__);*/ \
if (LOG_TARGET != nullptr) \
{ \
fprintf(LOG_TARGET, LOG_TIMESTAMP_FMT LOG_FLF_FMT str "%c" LOG_TIMESTAMP_VAL LOG_FLF_VAL, ##__VA_ARGS__); \
fflush(LOG_TARGET); /*fprintf(stderr, "DBGEND\n");*/ \
} \
}
// This is a trick to bypass the silly
// "warning: ISO C++11 requires at least one argument for the "..." in a variadic macro"
// so we can gave a single macro which can be called just like printf.
// INTERNAL, DO NOT USE
// USE LOG_TEE() INSTEAD
//
#define _LOG_TEE(str, ...) \
{ \
/*fprintf(stderr, "DBG:" str, ##__VA_ARGS__);*/ \
if (LOG_TARGET != nullptr) \
{ \
fprintf(LOG_TARGET, LOG_TIMESTAMP_FMT LOG_FLF_FMT str "%c" LOG_TIMESTAMP_VAL LOG_FLF_VAL, ##__VA_ARGS__); \
fflush(LOG_TARGET); /*fprintf(stderr, "DBGEND\n");*/ \
} \
if (LOG_TARGET != nullptr && LOG_TARGET != stdout && LOG_TARGET != stderr && LOG_TEE_TARGET != nullptr) \
{ \
fprintf(LOG_TEE_TARGET, LOG_TIMESTAMP_FMT LOG_FLF_FMT str "%c" LOG_TIMESTAMP_VAL LOG_FLF_VAL, ##__VA_ARGS__); \
fflush(LOG_TEE_TARGET); /*fprintf(stderr, "DBGEND\n");*/ \
} \
}
// The '\0' as a last argument, is a trick to bypass the silly
// "warning: ISO C++11 requires at least one argument for the "..." in a variadic macro"
// so we can have a single macro which can be called just like printf.
// Main LOG macro.
// behaves like printf, and supports arguments the exact same way.
//
#define LOG(...) _LOG(__VA_ARGS__, '\0')
// Main TEE macro.
// does the same as LOG
// and
// simultaneously writes stderr.
//
// Secondary target can be changed just like LOG_TARGET
// by defining LOG_TEE_TARGET
//
#define LOG_TEE(...) _LOG_TEE(__VA_ARGS__, '\0')
// Utility to obtain "pid" like unique process id and use it when creating log files.
inline std::string log_get_pid()
{
static std::string pid;
@ -113,36 +214,59 @@ inline std::string log_get_pid()
return pid;
}
#define LOG_DEFAULT_FILE_NAME std::string().append("llama.").append(log_get_pid()).append(".log")
inline FILE *_log_handler1(bool change = false, std::string filename = LOG_DEFAULT_FILE_NAME, FILE *target = nullptr)
// INTERNAL, DO NOT USE
inline FILE *_log_handler1(bool change = false, LogTriState disable = LogTriStateSame, std::string filename = LOG_DEFAULT_FILE_NAME, FILE *target = nullptr)
{
// std::cerr << "\tFNM:" << filename << "TGT:" << (uint64_t)target << std::endl;
static bool _initialized{false};
static bool _disabled{(filename.empty() && target == nullptr)};
static std::string log_current_filename{filename};
static FILE *log_current_target{target};
static FILE *logfile = nullptr;
if (change && log_current_filename.compare(filename) != 0) [[unlikely]]
if (change) [[unlikely]]
{
// std::cerr << "\t\tFNM changed, deinit" << std::endl;
_initialized = false;
}
if (disable == LogTriStateTrue)
{
// Disable primary target
_disabled = true;
}
// If previously disabled, only enable, and keep previous target
else if (disable == LogTriStateFalse)
{
_disabled = false;
}
// Otherwise, process the arguments
else
{
if (log_current_filename.compare(filename) != 0)
{
// std::cerr << "\t\tFNM changed, deinit" << std::endl;
_initialized = false;
}
if (change && log_current_target != target) [[unlikely]]
{
// std::cerr << "\t\tTGT changed, deinit" << std::endl;
_initialized = false;
if (log_current_target != target)
{
// std::cerr << "\t\tTGT changed, deinit" << std::endl;
_initialized = false;
}
}
}
// std::cerr << "\tINIT:" << (_initialized ? "true" : "false") << std::endl;
if (_initialized) [[likely]]
{
// std::cerr << "\t\tIS Inited" << std::endl;
// with fallback in case something went wrong
// std::cerr << "\t\tEarly Done" << std::endl;
return logfile ? logfile : stderr;
if (_disabled)
{
// Log is disabled
return nullptr;
}
else
{
return logfile ? logfile : stderr;
}
}
else
{
@ -197,17 +321,38 @@ inline FILE *_log_handler1(bool change = false, std::string filename = LOG_DEFAU
fflush(stderr);
}
// At this point we set init flag to true, and let the target fallback to stderr
// otherwise we would repeatedly fopen() which was already unsuccessful
_initialized = true;
}
// std::cerr << "\tDone" << std::endl;
return logfile ? logfile : stderr;
}
inline FILE *_log_handler2(bool change = false, FILE *target = nullptr, std::string filename = LOG_DEFAULT_FILE_NAME)
// INTERNAL, DO NOT USE
inline FILE *_log_handler2(bool change = false, LogTriState disable = LogTriStateSame, FILE *target = nullptr, std::string filename = LOG_DEFAULT_FILE_NAME)
{
return _log_handler1(change, filename, target);
return _log_handler1(change, disable, filename, target);
}
inline FILE *log_set_target(std::string filename) { return _log_handler1(true, filename); }
inline FILE *log_set_target(FILE *target) { return _log_handler2(true, target); }
// Disables logs entirely at runtime.
// Makes LOG() and LOG_TEE() produce no output,
// untill enabled back.
inline FILE *log_disable()
{
return _log_handler1(true, LogTriStateTrue);
}
// Enables logs at runtime.
inline FILE *log_enable()
{
return _log_handler1(true, LogTriStateFalse);
}
// Sets target fir logs, either by a file name or FILE* pointer (stdout, stderr, or any valid FILE*)
inline FILE *log_set_target(std::string filename) { return _log_handler1(true, LogTriStateSame, filename); }
inline FILE *log_set_target(FILE *target) { return _log_handler2(true, LogTriStateSame, target); }
// INTERNAL, DO NOT USE
inline FILE *log_handler() { return _log_handler1(); }

View file

@ -61,15 +61,32 @@ void sigint_handler(int signo) {
int main(int argc, char ** argv) {
gpt_params params;
LOG("Hello World to default output!\n")
log_disable();
LOG("01 Hello World to nobody, because logs are disabled!\n")
log_enable();
LOG("02 Hello World to default output, which is \"%s\" ( Yaaay, arguments! )!\n", LOG_STRINGIZE(LOG_TARGET))
LOG_TEE("03 Hello World to **both** default output and " LOG_TEE_TARGET_STRING "!\n")
log_set_target(stderr);
LOG("Hello World to stderr!\n")
LOG("04 Hello World to stderr!\n")
LOG_TEE("05 Hello World TEE with double printing to stderr prevented!\n")
log_set_target(LOG_DEFAULT_FILE_NAME);
LOG("Hello World to default log file!\n")
LOG("06 Hello World to default log file!\n")
log_set_target(stdout);
LOG("Hello World to stdout!\n")
LOG("07 Hello World to stdout!\n")
log_set_target(LOG_DEFAULT_FILE_NAME);
LOG("Hello World to default log file again!\n")
LOG("08 Hello World to default log file again!\n")
log_disable();
LOG("09 Hello World _1_ into the void!\n")
log_enable();
LOG("10 Hello World back from the void ( you should not see _1_ in the log or the output )!\n")
log_disable();
log_set_target("llama.anotherlog.log");
LOG("11 Hello World _2_ to nobody, new target was selected but logs are still disabled!\n")
log_enable();
LOG("12 Hello World this time in a new file ( you should not see _2_ in the log or the output )?\n")
log_set_target("llama.yetanotherlog.log");
LOG("13 Hello World this time in yet new file?\n")
if (gpt_params_parse(argc, argv, params) == false) {
return 1;