diff --git a/src/log.cc b/src/log.cc index 9dc05f7..2a3002f 100644 --- a/src/log.cc +++ b/src/log.cc @@ -6,33 +6,51 @@ #include #include #include +#include +#include //------------------------------------------------------------------------------------------------- -// Private functions Prototypes +// Internal functions Prototypes +// Global State //------------------------------------------------------------------------------------------------- #ifdef __cplusplus extern "C" { #endif const char* _orca_log_level_to_string(const ORCA_LOG_LEVEL* level); + bool _orca_logfile_open(void); + void _orca_logfile_close(void); #ifdef __cplusplus } #endif namespace Orca { namespace Utils { + std::atomic termsize_x = 120; std::string _to_termcol(const ORCA_LOG_COLOR& col); std::string _decorate_three_lines(std::string_view msg, char decoration = '-'); std::string _decorate_centered(std::string_view msg, char decoration = '-'); } // namespace Utils namespace Log { - std::mutex _mtx_log{}; std::atomic _log_level{ ORCA_LOG_LEVEL_NONE }; std::atomic _log_color{ ORCA_LOG_COLOR_WHITE }; namespace Backend { - void _log_stderr(std::string_view msg, ORCA_LOG_COLOR col = ORCA_LOG_COLOR_DEFAULT); - void _log_file(std::string_view msg); - } - } // namespace Log + void _dispatcher(std::string_view msg, ORCA_LOG_COLOR col = ORCA_LOG_COLOR_DEFAULT); + namespace Console { + std::mutex _mtx_stdout{}; + void _write_stdout(std::string_view msg, ORCA_LOG_COLOR col = ORCA_LOG_COLOR_DEFAULT); + std::mutex _mtx_stderr{}; + void _write_stderr(std::string_view msg, ORCA_LOG_COLOR col = ORCA_LOG_COLOR_DEFAULT); + } // namespace Console + namespace Logfile { + std::recursive_mutex _mtx_path{}; + std::filesystem::path _path{ "orca.log" }; + std::recursive_mutex _mtx_stream{}; + std::ofstream _stream{}; + std::atomic _clear_on_open{ true }; + void _write(std::string_view msg); + } // namespace Logfile + } // namespace Backend + } // namespace Log } // namespace Orca //------------------------------------------------------------------------------------------------- @@ -41,55 +59,81 @@ namespace Orca { #ifdef __cplusplus extern "C" { #endif + void orca_log_level_set(ORCA_LOG_LEVEL level) + { + Orca::Log::_log_level = level; + } + + ORCA_LOG_LEVEL orca_log_level_get(void) + { + return Orca::Log::_log_level; + } - void orca_log_set_level(ORCA_LOG_LEVEL level) + void orca_log_logfile_path_set(const char* path) { - Orca::Log::_log_level.store(level); + std::lock_guard l{ Orca::Log::Backend::Logfile::_mtx_path }; + std::lock_guard l2{ Orca::Log::Backend::Logfile::_mtx_stream }; + Orca::Log::Backend::Logfile::_path = path; + if (Orca::Log::Backend::Logfile::_stream.is_open()) { + Orca::Log::Backend::Logfile::_stream.close(); + } } - ORCA_LOG_LEVEL orca_log_get_level() + void orca_log_logfile_path_get(const char** path) { - return Orca::Log::_log_level.load(); + std::lock_guard l{ Orca::Log::Backend::Logfile::_mtx_path }; + *path = Orca::Log::Backend::Logfile::_path.c_str(); } - void orca_log_set_color(ORCA_LOG_COLOR col) + void orca_log_color_set(ORCA_LOG_COLOR col) { if (col == ORCA_LOG_COLOR_DEFAULT) { col = ORCA_LOG_COLOR_RESET; } - Orca::Log::_log_color.store(col); + Orca::Log::_log_color = col; } - ORCA_LOG_COLOR orca_log_get_color() + ORCA_LOG_COLOR orca_log_color_get(void) { - return Orca::Log::_log_color.load(); + return Orca::Log::_log_color; + } + + void orca_log_logfile_clear_on_open_set(bool clear) + { + Orca::Log::Backend::Logfile::_clear_on_open = clear; + } + + bool orca_log_logfile_clear_on_open_get(void) + { + return Orca::Log::Backend::Logfile::_clear_on_open; } void orca_log(const char* msg) { - Orca::Log::Backend::_log_stderr(msg, Orca::Log::_log_color.load()); + Orca::Log::Backend::_dispatcher(msg, Orca::Log::_log_color); } void orca_log_h1(const char* msg) { - Orca::Log::Backend::_log_stderr( + Orca::Log::Backend::_dispatcher( Orca::Utils::_decorate_three_lines(msg, '='), - Orca::Log::_log_color.load()); + Orca::Log::_log_color); } void orca_log_h2(const char* msg) { - Orca::Log::Backend::_log_stderr( + Orca::Log::Backend::_dispatcher( "\n" + Orca::Utils::_decorate_centered(msg, '='), - Orca::Log::_log_color.load()); + Orca::Log::_log_color); } void orca_log_h3(const char* msg) { - Orca::Log::Backend::_log_stderr(Orca::Utils::_decorate_centered(msg, '-'), Orca::Log::_log_color.load()); + Orca::Log::Backend::_dispatcher( + Orca::Utils::_decorate_centered(msg, '-'), + Orca::Log::_log_color); } - void _orca_log_for_macro( ORCA_LOG_LEVEL level, const char* file, @@ -98,7 +142,6 @@ extern "C" { const char* fmt, ...) { - std::lock_guard l{ Orca::Log::_mtx_log }; if (level > Orca::Log::_log_level) { return; } @@ -124,7 +167,7 @@ extern "C" { } } free(msg_fmtd); - Orca::Log::log(msg); + Orca::Log::Backend::_dispatcher(msg); va_end(var_args); } @@ -145,6 +188,38 @@ extern "C" { return "invalid log level"; } } + + bool _orca_logfile_open(void) + { + std::lock_guard l{ Orca::Log::Backend::Logfile::_mtx_stream }; + std::lock_guard l2{ Orca::Log::Backend::Logfile::_mtx_path }; + + if (Orca::Log::Backend::Logfile::_stream.is_open()) { + return true; + } + + // Append or clear + unsigned int openmode = std::ios::app; + if (Orca::Log::Backend::Logfile::_clear_on_open) { + openmode = std::ios::out | std::ios::trunc; + } + + Orca::Log::Backend::Logfile::_stream.open(Orca::Log::Backend::Logfile::_path, openmode); + if (Orca::Log::Backend::Logfile::_stream.fail()) { + // TODO: Where to log error + std::cerr << "LOGFILE ERROR" << std::endl; + Orca::Log::Backend::Logfile::_stream.clear(); + return false; + } + return true; + } + + void _orca_logfile_close(void) + { + std::lock_guard l{ Orca::Log::Backend::Logfile::_mtx_stream }; + Orca::Log::Backend::Logfile::_stream.close(); + } + #ifdef __cplusplus } #endif @@ -158,25 +233,27 @@ namespace Orca { // for C++ some functions are just wrappers for the // C linkage functions // to be nicely available in the namespace + // C linkage functions are mutexed and therefore + // wrapped here even if otherwise trivial void set_level(ORCA_LOG_LEVEL level) { - ::orca_log_set_level(level); + ::orca_log_level_set(level); } ORCA_LOG_LEVEL get_level() { - return ::orca_log_get_level(); + return ::orca_log_level_get(); } void set_color(ORCA_LOG_COLOR col) { - ::orca_log_set_color(col); + ::orca_log_color_set(col); } ORCA_LOG_COLOR get_color() { - return ::orca_log_get_color(); + return ::orca_log_color_get(); } void _log_for_macro( @@ -191,54 +268,96 @@ namespace Orca { void log(std::string_view msg, ORCA_LOG_COLOR col) { - Backend::_log_stderr(msg, col); + Backend::_dispatcher(msg, col); } void logH1(std::string_view msg, ORCA_LOG_COLOR col) { - Backend::_log_stderr(Utils::_decorate_three_lines(msg, '='), col); + Backend::_dispatcher(Utils::_decorate_three_lines(msg, '='), col); } void logH2(std::string_view msg, ORCA_LOG_COLOR col) { - Backend::_log_stderr("\n" + Utils::_decorate_centered(msg, '='), col); + Backend::_dispatcher("\n" + Utils::_decorate_centered(msg, '='), col); } void logH3(std::string_view msg, ORCA_LOG_COLOR col) { - Backend::_log_stderr(Utils::_decorate_centered(msg, '-'), col); + Backend::_dispatcher(Utils::_decorate_centered(msg, '-'), col); } namespace Backend { - // TODO: add file backend - void _log_stderr(std::string_view msg, ORCA_LOG_COLOR col) + void _dispatcher(std::string_view msg, ORCA_LOG_COLOR col) { - std::lock_guard l{ _mtx_raw_log }; - std::cerr << Utils::_to_termcol(col) << msg - << Utils::_to_termcol(ORCA_LOG_COLOR_RESET) - << std::endl; //endl also flushes, but cerr is unbuffered anyways - _log_file(msg); + Console::_write_stderr(msg, col); + Logfile::_write(msg); } - void _log_file(std::string_view msg) { - std::lock_guard l{ _mtx_backend_file }; - std::filesystem::path path{ "logfile.log" }; - - std::ofstream ofs(path); - ofs << msg << std::endl; - ofs.close(); - } - } // namespace Backend - } // namespace Log + namespace Console { + void _write_stdout(std::string_view msg, ORCA_LOG_COLOR col) + { + std::lock_guard l{ _mtx_stdout }; + std::cerr << Utils::_to_termcol(col) << msg + << Utils::_to_termcol(ORCA_LOG_COLOR_RESET) + << std::endl; //endl also flushes, but cerr is unbuffered anyways + } + void _write_stderr(std::string_view msg, ORCA_LOG_COLOR col) + { + std::lock_guard l{ _mtx_stderr }; + std::cerr << Utils::_to_termcol(col) << msg + << Utils::_to_termcol(ORCA_LOG_COLOR_RESET) + << std::endl; //endl also flushes, but cerr is unbuffered anyways + } + } // namespace Console + + namespace Logfile { + void _write(std::string_view msg) + { + std::lock_guard l{ _mtx_stream }; + if (!_orca_logfile_open()) { + std::cerr << "ERROR: Logfile open failed" << std::endl; + } else { + _stream << msg << std::endl; + if (_stream.fail()) { + std::cerr << "ERROR: Logfile Write failed" << std::endl; + _stream.clear(); + } + } + } + + void set_path(const std::filesystem::path& path) + { + ::orca_log_logfile_path_set(path.c_str()); + } + + const std::filesystem::path& get_path() + { + std::lock_guard l{ Orca::Log::Backend::Logfile::_mtx_path }; + return Orca::Log::Backend::Logfile::_path; + } + + void set_clear_on_open(bool clear) + { + ::orca_log_logfile_clear_on_open_set(clear); + } + + bool get_clear_on_open() + { + return ::orca_log_logfile_clear_on_open_get(); + } + + } // namespace Logfile + } // namespace Backend + } // namespace Log namespace Utils { - int termsize_x = 120; - std::string _to_termcol(const ORCA_LOG_COLOR& col) { switch (col) { case ORCA_LOG_COLOR_DEFAULT: + // Caution: Make sure Orca::Log::_log_color can + // NEVER be set to ORCA_LOG_COLOR_DEFAULT return _to_termcol(Orca::Log::_log_color); case ORCA_LOG_COLOR_RESET: return "\033[0m"; @@ -298,7 +417,7 @@ namespace Orca { //------------------------------------------------------------------------------------------------- std::ostream& operator<<(std::ostream& o, const ORCA_LOG_LEVEL* level) { - return o << std::string_view (_orca_log_level_to_string(level)); + return o << std::string_view(_orca_log_level_to_string(level)); } // Stuff like that would be fucking handy diff --git a/src/log.h b/src/log.h index 8ee0808..c042760 100644 --- a/src/log.h +++ b/src/log.h @@ -15,6 +15,11 @@ #define ORCA_LOG_MACRO_FUNCNAME __func__ #endif +typedef enum { + ORCA_LOG_BACKEND_STDOUT, + ORCA_LOG_BACKEND_STDERR, + ORCA_LOG_BACKEND_FILE +} ORCA_LOG_BACKEND; typedef enum { ORCA_LOG_LEVEL_NONE = 0, // Logging Disabled @@ -50,16 +55,23 @@ typedef enum { #define ORCA_LOG_INFO(...) ORCA_LOG(ORCA_LOG_LEVEL_INFO, __VA_ARGS__) - #define ORCA_LOG(level, ...) \ - do { \ + #define ORCA_LOG(level, ...) \ + do { \ _orca_log_for_macro(level, __FILE__, __LINE__, ORCA_LOG_MACRO_FUNCNAME, "" __VA_ARGS__); \ } while (0) // config/settings -void orca_log_set_level(ORCA_LOG_LEVEL level); -ORCA_LOG_LEVEL orca_log_get_level(); -void orca_log_set_color(ORCA_LOG_COLOR col); -ORCA_LOG_COLOR orca_log_get_color(); +void orca_log_level_set(ORCA_LOG_LEVEL level); +ORCA_LOG_LEVEL orca_log_level_get(void); + +void orca_log_logfile_path_set(const char* path); +void orca_log_logfile_path_get(const char** path); + +void orca_log_color_set(ORCA_LOG_COLOR col); +ORCA_LOG_COLOR orca_log_color_get(void); + +void orca_log_logfile_clear_on_open_set(bool clear); +bool orca_log_logfile_clear_on_open_get(void); // logging void orca_log(const char* msg); @@ -84,6 +96,7 @@ void _orca_log_for_macro( #ifdef __cplusplus #include #include + #include #define ORCA_LOG_ERR(msg) ORCA_LOG(ORCA_LOG_LEVEL_ERROR, msg) @@ -106,11 +119,20 @@ namespace Orca { void set_color(ORCA_LOG_COLOR col); ORCA_LOG_COLOR get_color(); + namespace Backend { + namespace Logfile { + void set_path(const std::filesystem::path& path); + const std::filesystem::path& get_path(); + void set_clear_on_open(bool clear); + bool get_clear_on_open(); + } // namespace Logfile + } // namespace Backend + // logging - void log(const std::string& msg, ORCA_LOG_COLOR col = ORCA_LOG_COLOR_DEFAULT); - void logH1(const std::string& msg, ORCA_LOG_COLOR col = ORCA_LOG_COLOR_DEFAULT); - void logH2(const std::string& msg, ORCA_LOG_COLOR col = ORCA_LOG_COLOR_DEFAULT); - void logH3(const std::string& msg, ORCA_LOG_COLOR col = ORCA_LOG_COLOR_DEFAULT); + void log(std::string_view msg, ORCA_LOG_COLOR col = ORCA_LOG_COLOR_DEFAULT); + void logH1(std::string_view msg, ORCA_LOG_COLOR col = ORCA_LOG_COLOR_DEFAULT); + void logH2(std::string_view msg, ORCA_LOG_COLOR col = ORCA_LOG_COLOR_DEFAULT); + void logH3(std::string_view msg, ORCA_LOG_COLOR col = ORCA_LOG_COLOR_DEFAULT); // helper function for macro use only void _log_for_macro( diff --git a/test/test_log.c b/test/test_log.c index 3629b60..18abfc9 100644 --- a/test/test_log.c +++ b/test/test_log.c @@ -7,26 +7,34 @@ // ORCA LOG C usage -int main() +int main(void) { // Test Levels { ORCA_LOG_ERR("SHOULD NOT SEE THIS"); - orca_log_set_level(ORCA_LOG_LEVEL_ALL); + orca_log_level_set(ORCA_LOG_LEVEL_ALL); ORCA_LOG_ERR(); ORCA_LOG_WARN(); ORCA_LOG_INFO(); - orca_log_set_level(ORCA_LOG_LEVEL_ERROR); + orca_log_level_set(ORCA_LOG_LEVEL_ERROR); ORCA_LOG_ERR(); ORCA_LOG_WARN("SHOULD NOT SEE THIS"); ORCA_LOG_INFO("SHOULD NOT SEE THIS"); } + // Logfile + { + const char *path = NULL; + orca_log_logfile_path_get(&path); + printf("LOGFILE PATH: %s\n", path); + orca_log_logfile_path_set("newlogfile.log"); + } + // BASIC USAGE MACRO { - orca_log_set_level(ORCA_LOG_LEVEL_INFO); + orca_log_level_set(ORCA_LOG_LEVEL_INFO); // no message ORCA_LOG_INFO(); @@ -44,7 +52,7 @@ int main() // BASIC USAGE FUNC { - orca_log_set_level(ORCA_LOG_LEVEL_INFO); + orca_log_level_set(ORCA_LOG_LEVEL_INFO); // literal orca_log("Literal RAW"); @@ -62,12 +70,12 @@ int main() { orca_log_h1("Colors functions only"); orca_log("DEFAULT COLOR"); - orca_log_set_color(ORCA_LOG_COLOR_GREEN); + orca_log_color_set(ORCA_LOG_COLOR_GREEN); orca_log("DEFAULT COLOR AFTER SET GREEN"); - orca_log_set_color(ORCA_LOG_COLOR_YELLOW); + orca_log_color_set(ORCA_LOG_COLOR_YELLOW); strdup("YELLOW ONE OFF"); orca_log(strdup("YELLOW ONE OFF")); - orca_log_set_color(ORCA_LOG_COLOR_RESET); + orca_log_color_set(ORCA_LOG_COLOR_RESET); orca_log("COLOR RESET"); } @@ -75,11 +83,13 @@ int main() { orca_log_h1("Colors using Macros"); orca_log("DEFAULT COLOR"); - orca_log_set_color(ORCA_LOG_COLOR_GREEN); + orca_log_color_set(ORCA_LOG_COLOR_GREEN); ORCA_LOG_ERR("DEFAULT COLOR AFTER SET GREEN"); - orca_log_set_color(ORCA_LOG_COLOR_YELLOW); + orca_log_color_set(ORCA_LOG_COLOR_YELLOW); ORCA_LOG_ERR("YELLOW ONE OFF"); - orca_log_set_color(ORCA_LOG_COLOR_RESET); + orca_log_color_set(ORCA_LOG_COLOR_RESET); ORCA_LOG_ERR("COLOR RESET"); } + + orca_log("ALL TEST SUCCESSFUL"); } diff --git a/test/test_log_cxx.cc b/test/test_log_cxx.cc index b5b7f68..4333d7f 100644 --- a/test/test_log_cxx.cc +++ b/test/test_log_cxx.cc @@ -25,6 +25,12 @@ int main() ORCA_LOG_INFO("SHOULD NOT SEE THIS"); } + // Logfile + { + std::cout << " LOGFILE PATH: " << Orca::Log::Backend::Logfile::get_path(); + Orca::Log::Backend::Logfile::set_path("newlogfile.log"); + } + // BASIC USAGE MACRO { Orca::Log::set_level(ORCA_LOG_LEVEL_INFO); @@ -89,6 +95,5 @@ int main() ORCA_LOG_ERR("DEFAULT COLOR GREEN"); } - - // FUNCTIONS + Orca::Log::log("ALL TEST SUCCESSFUL"); }