diff --git a/fims.log b/fims.log new file mode 100644 index 0000000..32960f8 --- /dev/null +++ b/fims.log @@ -0,0 +1,2 @@ +[ +] \ No newline at end of file diff --git a/inst/include/common/def.hpp b/inst/include/common/def.hpp index 9a1a50b..f13d511 100644 --- a/inst/include/common/def.hpp +++ b/inst/include/common/def.hpp @@ -13,6 +13,9 @@ #ifndef DEF_HPP #define DEF_HPP +#include "fims_log.hpp" + + #define TMB_MODEL diff --git a/inst/include/common/fims_log.hpp b/inst/include/common/fims_log.hpp new file mode 100644 index 0000000..bd42b41 --- /dev/null +++ b/inst/include/common/fims_log.hpp @@ -0,0 +1,347 @@ +#ifndef FIMS_COMMON_FIMS_LOG +#define FIMS_COMMON_FIMS_LOG + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if defined(linux) || defined(__linux) || defined(__linux__) +#define FIMS_LINUX +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) +#define FIMS_BSD +#elif defined(sun) || defined(__sun) +#define FIMS_SOLARIS +#elif defined(__sgi) +#define FIMS_IRIX +#elif defined(__hpux) +#define FIMS_HPUX +#elif defined(__CYGWIN__) +#define FIMS_CYGWIN +#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) +#define FIMS_WIN32 +#elif defined(_WIN64) || defined(__WIN64__) || defined(WIN64) +#define FIMS_WIN64 +#elif defined(__BEOS__) +#define FIMS_BEOS +#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) +#define FIMS_MACOS +#elif defined(__IBMCPP__) || defined(_AIX) +#define FIMS_AIX +#elif defined(__amigaos__) +#define FIMS_AMIGAOS +#elif defined(__QNXNTO__) +#define FIMS_QNXNTO +#endif + +#if defined(FIMS_WIN32) || defined(FIMS_WIN64) +#define FIMS_WINDOWS +#endif + +#ifdef FIMS_WINDOWS +#include +#define __PRETTY_FUNCTION__ __FUNCSIG__ +#endif + +struct LogEntry { + std::string timestamp; + std::string message; + std::string level; + size_t rank; + std::string user; + std::string wd; + std::string file; + std::string func; + size_t line; + + std::string to_string() { + std::stringstream ss; + ss << "\"timestamp\" : " << "\"" << this->timestamp << "\"" << ",\n"; + ss << "\"level\" : " << "\"" << this->level << "\",\n"; + ss << "\"message\" : " << "\"" << this->message << "\",\n"; + ss << "\"id\" : " << "\"" << this->rank << "\",\n"; + ss << "\"user\" : " << "\"" << this->user << "\",\n"; + ss << "\"wd\" : " << "\"" << this->wd << "\",\n"; + ss << "\"file\" : " << "\"" << this->file << "\",\n"; + ss << "\"function\" : " << "\"" << this->func << "\",\n"; + ss << "\"line\" : " << "\"" << this->line << "\"\n"; + return ss.str(); + } + +}; + +class FIMSLog { + std::vector entries; + std::vector log_entries; + size_t entry_number = 0; + std::string path = "fims.log"; + + std::string get_user() { + char * user; +#ifdef FIMS_WINDOWS + user = getenv("username"); +#endif +#ifdef FIMS_LINUX + user = getenv("USERNAME"); +#endif + +#ifdef FIMS_MACOS + user = getenv("USER"); +#endif + + return std::string(user); + } +public: + bool write_on_exit = true; + static std::shared_ptr fims_log; + + FIMSLog() { + + } + + ~FIMSLog() { + if (this->write_on_exit) { + std::ofstream log(this->path); + log << this->get_log(); + log.close(); + } + } + + void set_path(std::string path) { + this->path = path; + } + + std::string get_path() { + return this->path; + } + + void message(std::string str, int line, const char* file, const char* func ) { + + std::__fs::filesystem::path cwd = std::__fs::filesystem::current_path(); + std::stringstream ss; + auto now = std::chrono::system_clock::now(); + std::time_t now_time = std::chrono::system_clock::to_time_t(now); + std::string ctime_no_newline = strtok(ctime(&now_time), "\n"); + + LogEntry l; + l.timestamp = ctime_no_newline; + l.message = str; + l.level = "info"; + l.rank = this->log_entries.size(); + l.user = this->get_user(); + l.wd = cwd.string(); + l.file = file; + l.line = line; + l.func = func; + this->log_entries.push_back(l); + + } + + void error_message(std::string str, int line, const char* file, const char* func ) { + std::__fs::filesystem::path cwd = std::__fs::filesystem::current_path(); + std::stringstream ss; + auto now = std::chrono::system_clock::now(); + std::time_t now_time = std::chrono::system_clock::to_time_t(now); + std::string ctime_no_newline = strtok(ctime(&now_time), "\n"); + + LogEntry l; + l.timestamp = ctime_no_newline; + l.message = str; + l.level = "error"; + l.rank = this->log_entries.size(); + l.user = this->get_user(); + l.wd = cwd.string(); + l.file = file; + l.line = line; + l.func = func; + this->log_entries.push_back(l); + + } + + void warning_message(std::string str, int line, const char* file, const char* func ) { + std::__fs::filesystem::path cwd = std::__fs::filesystem::current_path(); + std::stringstream ss; + auto now = std::chrono::system_clock::now(); + std::time_t now_time = std::chrono::system_clock::to_time_t(now); + std::string ctime_no_newline = strtok(ctime(&now_time), "\n"); + + LogEntry l; + l.timestamp = ctime_no_newline; + l.message = str; + l.level = "warning"; + l.rank = this->log_entries.size(); + l.user = this->get_user(); + l.wd = cwd.string(); + l.file = file; + l.line = line; + l.func = func; + this->log_entries.push_back(l); + + } + + std::string get_log() { + std::stringstream ss; + if (log_entries.size() == 0) { + ss << "[\n]"; + } else { + ss << "[\n"; + for (size_t i = 0; i < log_entries.size() - 1; i++) { + ss << "{\n" << this->log_entries[i].to_string() << "},\n"; + + } + ss << "{\n" << this->log_entries[log_entries.size() - 1].to_string() << "}\n]"; + } + return ss.str(); + } + + std::string get_errors() { + std::stringstream ss; + std::vector errors; + for (size_t i = 0; i < log_entries.size(); i++) { + if (log_entries[i].level == "error") { + errors.push_back(this->log_entries[i]); + } + } + + if (errors.size() == 0) { + ss << "[\n]"; + } else { + ss << "[\n"; + for (size_t i = 0; i < errors.size() - 1; i++) { + + ss << "{\n" << errors[i].to_string() << "},\n"; + + } + + ss << "{\n" << errors[errors.size() - 1].to_string() << "}\n]"; + + } + return ss.str(); + } + + std::string get_warnings() { + std::stringstream ss; + std::vector warnings; + for (size_t i = 0; i < log_entries.size(); i++) { + if (log_entries[i].level == "warning") { + warnings.push_back(this->log_entries[i]); + } + } + + if (warnings.size() == 0) { + ss << "[\n]"; + } else { + ss << "[\n"; + for (size_t i = 0; i < warnings.size() - 1; i++) { + + ss << "{\n" << warnings[i].to_string() << "},\n"; + + } + + ss << "{\n" << warnings[warnings.size() - 1].to_string() << "}\n]"; + + } + return ss.str(); + } + + std::string get_info() { + std::stringstream ss; + std::vector info; + for (size_t i = 0; i < log_entries.size(); i++) { + if (log_entries[i].level == "info") { + info.push_back(this->log_entries[i]); + } + } + + if (info.size() == 0) { + ss << "[\n]"; + } else { + ss << "[\n"; + for (size_t i = 0; i < info.size() - 1; i++) { + + ss << "{\n" << info[i].to_string() << "},\n"; + + } + + ss << "{\n" << info[info.size() - 1].to_string() << "}\n]"; + + } + return ss.str(); + } + + std::string get_module(const std::string& module) { + std::stringstream ss; + std::vector info; + for (size_t i = 0; i < log_entries.size(); i++) { + if (log_entries[i].file == module) { + info.push_back(this->log_entries[i]); + } + } + + if (info.size() == 0) { + ss << "[\n]"; + } else { + ss << "[\n"; + for (size_t i = 0; i < info.size() - 1; i++) { + + ss << "{\n" << info[i].to_string() << "},\n"; + + } + + ss << "{\n" << info[info.size() - 1].to_string() << "}\n]"; + + } + return ss.str(); + } + + + + +}; + +std::shared_ptr FIMSLog::fims_log = std::make_shared(); + + + + +#define FIMS_LOG(MESSAGE) FIMSLog::fims_log->message(MESSAGE, __LINE__, __FILE__, __PRETTY_FUNCTION__); +#define FIMS_WARNING(MESSAGE) FIMSLog::fims_log->warning_message(MESSAGE, __LINE__, __FILE__, __PRETTY_FUNCTION__); +#define FIMS_ERROR(MESSAGE) FIMSLog::fims_log->error_message(MESSAGE, __LINE__, __FILE__, __PRETTY_FUNCTION__); + +void WriteAtExit(int sig) { + + + if (FIMSLog::fims_log->write_on_exit) { + std::ofstream log(FIMSLog::fims_log->get_path()); + log << FIMSLog::fims_log->get_log(); + log.close(); + } + std::signal(sig, SIG_DFL); + raise(sig); +} + + + +namespace fims { + +template +std::string to_string(T v) { + std::stringstream ss; + ss << v; + return ss.str(); +} + +} + + + +#endif diff --git a/inst/include/common/model.hpp b/inst/include/common/model.hpp index af1a3a7..7b84e25 100644 --- a/inst/include/common/model.hpp +++ b/inst/include/common/model.hpp @@ -39,6 +39,12 @@ class Model{ * of observed and predicted length. */ Type evaluate(){ + if (obsdata -> ages.size() == 0) { + FIMS_WARNING("Model::obsdata -> ages.size() == 0.") + } + + + Type norm2 = 0.0; for(int i =0; i < obsdata -> ages.size(); i++){ Type pred = vb -> evaluate(obsdata -> ages[i]); @@ -60,4 +66,4 @@ class Model{ template std::shared_ptr > Model::instance = std::make_shared >(); -#endif \ No newline at end of file +#endif diff --git a/inst/include/interface/rcpp/rcpp_interface.hpp b/inst/include/interface/rcpp/rcpp_interface.hpp index 11b73fb..af6746a 100644 --- a/inst/include/interface/rcpp/rcpp_interface.hpp +++ b/inst/include/interface/rcpp/rcpp_interface.hpp @@ -7,6 +7,8 @@ #include "rcpp_objects/rcpp_data.hpp" bool CreateModel(){ + + FIMS_LOG("CreateModel: "+ fims::to_string(RcppInterfaceBase::interface_objects.size())+ " instantiated model objects.") for (size_t i = 0; i < RcppInterfaceBase::interface_objects.size(); i++) { RcppInterfaceBase::interface_objects[i]->prepare(); @@ -42,6 +44,45 @@ void clear(){ Variable::parameters.clear(); } +std::string GetLog() { + return FIMSLog::fims_log->get_log(); +} + +std::string GetLogErrors() { + return FIMSLog::fims_log->get_errors(); +} + +std::string GetLogWarnings() { + return FIMSLog::fims_log->get_warnings(); +} + +std::string GetLogInfo() { + return FIMSLog::fims_log->get_info(); +} + +std::string GetLogModule(const std::string& module) { + return FIMSLog::fims_log->get_module(module); +} + +void WriteLog(bool write) { + FIMS_LOG("Setting FIMS write log: "+ fims::to_string(write)) + FIMSLog::fims_log->write_on_exit = write; +} + +void SetLogPath(const std::string& path) { + FIMS_LOG("Setting FIMS log path: "+ path) + FIMSLog::fims_log->set_path(path); +} + +void InitLogging(){ + FIMS_LOG("Initializing FIMS logging system.") + std::signal(SIGSEGV, &WriteAtExit); + std::signal(SIGINT, &WriteAtExit); + std::signal(SIGABRT, &WriteAtExit); + std::signal(SIGFPE, &WriteAtExit); + std::signal(SIGILL, &WriteAtExit); + std::signal(SIGTERM, &WriteAtExit); +} /** * Define the Rcpp module. @@ -67,6 +108,14 @@ RCPP_MODULE(growth) { Rcpp::function("get_parameter_vector", get_parameter_vector); Rcpp::function("clear", clear); Rcpp::function("CreateModel", CreateModel); + Rcpp::function("GetLog", GetLog); + Rcpp::function("GetLogErrors", GetLogErrors); + Rcpp::function("GetLogWarnings", GetLogWarnings); + Rcpp::function("GetLogInfo", GetLogInfo); + Rcpp::function("GetLogModule", GetLogModule); + Rcpp::function("WriteLog", WriteLog); + Rcpp::function("SetLogPath", SetLogPath); + Rcpp::function("InitLogging", InitLogging); }; -#endif \ No newline at end of file +#endif diff --git a/inst/include/interface/rcpp/rcpp_objects/rcpp_growth.hpp b/inst/include/interface/rcpp/rcpp_objects/rcpp_growth.hpp index 82bac2d..bcea2f3 100644 --- a/inst/include/interface/rcpp/rcpp_objects/rcpp_growth.hpp +++ b/inst/include/interface/rcpp/rcpp_objects/rcpp_growth.hpp @@ -51,7 +51,8 @@ class vonBertalanffyInterface : public GrowthInterfaceBase{ template bool prepare_local() { - + FIMS_LOG("vonBertalanffyInterface::prepare_local.") + std::shared_ptr > model = Model::getInstance(); std::shared_ptr< VonBertalanffy > vb = std::make_shared >(); @@ -102,7 +103,7 @@ class vonBertalanffyInterface : public GrowthInterfaceBase{ * Prepares the model to work with TMB. */ virtual bool prepare() { - + FIMS_LOG("vonBertalanffyInterface::prepare.") #ifdef TMB_MODEL this->prepare_local(); this->prepare_local(); @@ -119,6 +120,7 @@ class vonBertalanffyInterface : public GrowthInterfaceBase{ * portable model once and transfers values back to the Rcpp interface. */ void finalize(Rcpp::NumericVector v) { + FIMS_LOG("vonBertalanffyInterface::finalize.") std::shared_ptr< Model > model = Model::getInstance(); //std::shared_ptr< VonBertalanffy > vb; //vb = std::make_shared >(); @@ -141,6 +143,7 @@ class vonBertalanffyInterface : public GrowthInterfaceBase{ * Print model values. */ void show_() { + FIMS_LOG("vonBertalanffyInterface::show.") Rcpp::Rcout << "vonBertalanffy:\n"; Rcpp::Rcout << "k = " << this->k.value << "\n"; Rcpp::Rcout << "a_min = " << this->a_min.value << "\n"; @@ -149,4 +152,4 @@ class vonBertalanffyInterface : public GrowthInterfaceBase{ }; -#endif \ No newline at end of file +#endif diff --git a/test/fims.log b/test/fims.log new file mode 100644 index 0000000..f3501d9 --- /dev/null +++ b/test/fims.log @@ -0,0 +1,90 @@ +[ +{ +"timestamp" : "Thu May 2 10:37:45 2024", +"level" : "info", +"message" : "Initializing FIMS logging system.", +"id" : "0", +"user" : "mattadmin", +"wd" : "/Users/mattadmin/ModularTMBExample/logging-refactor/ModularTMBExample/test", +"file" : "./../inst/include/interface/rcpp/rcpp_interface.hpp", +"function" : "void InitLogging()", +"line" : "78" +}, +{ +"timestamp" : "Thu May 2 10:37:45 2024", +"level" : "info", +"message" : "CreateModel: 2 instantiated model objects.", +"id" : "1", +"user" : "mattadmin", +"wd" : "/Users/mattadmin/ModularTMBExample/logging-refactor/ModularTMBExample/test", +"file" : "./../inst/include/interface/rcpp/rcpp_interface.hpp", +"function" : "bool CreateModel()", +"line" : "11" +}, +{ +"timestamp" : "Thu May 2 10:37:45 2024", +"level" : "info", +"message" : "vonBertalanffyInterface::prepare.", +"id" : "2", +"user" : "mattadmin", +"wd" : "/Users/mattadmin/ModularTMBExample/logging-refactor/ModularTMBExample/test", +"file" : "./../inst/include/interface/rcpp/rcpp_objects/rcpp_growth.hpp", +"function" : "virtual bool vonBertalanffyInterface::prepare()", +"line" : "106" +}, +{ +"timestamp" : "Thu May 2 10:37:45 2024", +"level" : "info", +"message" : "vonBertalanffyInterface::prepare_local.", +"id" : "3", +"user" : "mattadmin", +"wd" : "/Users/mattadmin/ModularTMBExample/logging-refactor/ModularTMBExample/test", +"file" : "./../inst/include/interface/rcpp/rcpp_objects/rcpp_growth.hpp", +"function" : "bool vonBertalanffyInterface::prepare_local() [Type = double]", +"line" : "54" +}, +{ +"timestamp" : "Thu May 2 10:37:45 2024", +"level" : "info", +"message" : "vonBertalanffyInterface::prepare_local.", +"id" : "4", +"user" : "mattadmin", +"wd" : "/Users/mattadmin/ModularTMBExample/logging-refactor/ModularTMBExample/test", +"file" : "./../inst/include/interface/rcpp/rcpp_objects/rcpp_growth.hpp", +"function" : "bool vonBertalanffyInterface::prepare_local() [Type = CppAD::AD]", +"line" : "54" +}, +{ +"timestamp" : "Thu May 2 10:37:45 2024", +"level" : "info", +"message" : "vonBertalanffyInterface::prepare_local.", +"id" : "5", +"user" : "mattadmin", +"wd" : "/Users/mattadmin/ModularTMBExample/logging-refactor/ModularTMBExample/test", +"file" : "./../inst/include/interface/rcpp/rcpp_objects/rcpp_growth.hpp", +"function" : "bool vonBertalanffyInterface::prepare_local() [Type = CppAD::AD>]", +"line" : "54" +}, +{ +"timestamp" : "Thu May 2 10:37:45 2024", +"level" : "info", +"message" : "vonBertalanffyInterface::prepare_local.", +"id" : "6", +"user" : "mattadmin", +"wd" : "/Users/mattadmin/ModularTMBExample/logging-refactor/ModularTMBExample/test", +"file" : "./../inst/include/interface/rcpp/rcpp_objects/rcpp_growth.hpp", +"function" : "bool vonBertalanffyInterface::prepare_local() [Type = CppAD::AD>>]", +"line" : "54" +}, +{ +"timestamp" : "Thu May 2 10:37:45 2024", +"level" : "info", +"message" : "vonBertalanffyInterface::finalize.", +"id" : "7", +"user" : "mattadmin", +"wd" : "/Users/mattadmin/ModularTMBExample/logging-refactor/ModularTMBExample/test", +"file" : "./../inst/include/interface/rcpp/rcpp_objects/rcpp_growth.hpp", +"function" : "void vonBertalanffyInterface::finalize(Rcpp::NumericVector)", +"line" : "123" +} +] \ No newline at end of file diff --git a/test/test.R b/test/test.R index 0203e46..0cc244a 100644 --- a/test/test.R +++ b/test/test.R @@ -20,6 +20,9 @@ for(i in 1:length(ages)){ data[i] = (l_inf * (1.0 - exp(-k * (ages[i] - a_min))))* runif(1,.90,1.5) } + +g$InitLogging() + #clear the parameter list, if there already is one g$clear(); @@ -78,3 +81,4 @@ obj$report() #show final gradient print("final gradient:") print(rep$gradient.fixed) +cat(g$GetLog())