Commit 12d884ff authored by Guolin Ke's avatar Guolin Ke
Browse files

simplify logger

parent 4e15aed6
......@@ -93,7 +93,7 @@ public:
std::string output_result = "LightGBM_predict_result.txt";
std::string input_model = "";
std::string input_init_score = "";
int verbosity = 0;
int verbosity = 1;
std::string log_file = "";
int num_model_predict = -1;
bool is_pre_partition = false;
......
......@@ -23,88 +23,13 @@ namespace LightGBM {
// A enumeration type of log message levels. The values are ordered:
// Debug < Info < Error < Fatal.
enum class LogLevel : int {
Debug = 0,
enum class LogLevel: int {
Fatal = -1,
Error = 0,
Info = 1,
Error = 2,
Fatal = 3
Debug = 2,
};
/*!
* \brief The Logger class is responsible for writing log messages into
* standard output or log file.
*/
class Logger {
// Enable the static Log class to call the private method.
friend class Log;
public:
/*!
* \brief Creates an instance of Logger class. By default, the log
* messages will be written to standard output with minimal
* level of INFO. Users are able to further set the log file or
* log level with corresponding methods.
* \param level Minimal log level, Info by default.
*/
explicit Logger(LogLevel level = LogLevel::Info);
/*!
* \brief Creates an instance of Logger class by specifying log file
* and log level. The log message will be written to both STDOUT
* and file (if created successfully).
* \param filename Log file name
* \param level Minimal log level
*/
explicit Logger(std::string filename, LogLevel level = LogLevel::Info);
~Logger();
/*!
* \brief Resets the log file.
* \param filename The new log filename. If it is empty, the Logger
* will close current log file (if it exists).
* \return Returns -1 if the filename is not empty but failed on
* creating the log file, or 0 will be returned otherwise.
*/
int ResetLogFile(std::string filename);
/*!
* \brief Resets the log level.
* \param level The new log level.
*/
void ResetLogLevel(LogLevel level) { level_ = level; }
/*!
* \brief Resets the option of whether kill the process when fatal
* error occurs. By defualt the option is false.
*/
void ResetKillFatal(bool is_kill_fatal) { is_kill_fatal_ = is_kill_fatal; }
/*!
* \brief C style formatted method for writing log messages. A message
* is with the following format: [LEVEL] [TIME] message
* \param level The log level of this message.
* \param format The C format string.
* \param ... Output items.
*/
void Write(LogLevel level, const char *format, ...);
void Debug(const char *format, ...);
void Info(const char *format, ...);
void Error(const char *format, ...);
void Fatal(const char *format, ...);
private:
void Write(LogLevel level, const char* format, va_list* val);
void CloseLogFile();
// Returns current system time as a string.
std::string GetSystemTime();
// Returns the string of a log level.
std::string GetLevelStr(LogLevel level);
std::FILE *file_; // A file pointer to the log file.
LogLevel level_; // Only the message not less than level_ will be outputed.
bool is_kill_fatal_; // If kill the process when fatal error occurs.
// No copying allowed
Logger(const Logger&);
void operator=(const Logger&);
};
/*!
* \brief The Log class is a static wrapper of a global Logger instance in
......@@ -113,35 +38,61 @@ private:
*/
class Log {
public:
/*!
* \brief Resets the log file. The logger will write messages to the
* log file if it exists in addition to the STDOUT by default.
* \param filename The log filename. If it is empty, the logger will
* close the current log file (if it exists) and only output to
* STDOUT.
* \return -1 if fail on creating the log file, or 0 otherwise.
*/
static int ResetLogFile(std::string filename);
/*!
* \brief Resets the minimal log level. It is INFO by default.
* \param level The new minimal log level.
*/
static void ResetLogLevel(LogLevel level);
/*!
* \brief Resets the option of whether kill the process when fatal
* error occurs. By defualt the option is false.
*/
static void ResetKillFatal(bool is_kill_fatal);
/*! \brief The C formatted methods of writing the messages. */
static void Write(LogLevel level, const char* format, ...);
static void Debug(const char *format, ...);
static void Info(const char *format, ...);
static void Error(const char *format, ...);
static void Fatal(const char *format, ...);
static void ResetLogLevel(LogLevel level) {
GetLevel() = level;
}
static void Debug(const char *format, ...) {
va_list val;
va_start(val, format);
Write(LogLevel::Debug, "Debug", format, val);
va_end(val);
}
static void Info(const char *format, ...) {
va_list val;
va_start(val, format);
Write(LogLevel::Info, "Info", format, val);
va_end(val);
}
static void Error(const char *format, ...) {
va_list val;
va_start(val, format);
Write(LogLevel::Error, "Error", format, val);
va_end(val);
}
static void Fatal(const char *format, ...) {
va_list val;
va_start(val, format);
Write(LogLevel::Fatal, "Fatal", format, val);
va_end(val);
}
private:
static Logger logger_;
static void Write(LogLevel level, const char* level_str, const char *format, va_list val) {
if (level <= GetLevel()) { // omit the message with low level
// write to STDOUT
printf("[LightGBM] [%s] ", level_str);
vprintf(format, val);
fflush(stdout);
if (level == LogLevel::Fatal) {
exit(1);
}
}
}
// a trick to use static variable in header file.
// May be not good, but avoid to use an additional cpp file
static LogLevel& GetLevel() {
static LogLevel level;
return level;
};
};
} // namespace LightGBM
......
......@@ -11,9 +11,8 @@ AUX_SOURCE_DIRECTORY(./metric/ METRIC_SRC)
AUX_SOURCE_DIRECTORY(./objective/ OBJECTIVE_SRC)
AUX_SOURCE_DIRECTORY(./network/ NETWORK_SRC)
AUX_SOURCE_DIRECTORY(./treelearner/ TREELEARNER_SRC)
AUX_SOURCE_DIRECTORY(./util/ UTIL_SRC)
add_executable(lightgbm main.cpp ${APPLICATION_SRC} ${BOOSTING_SRC} ${IO_SRC} ${METRIC_SRC} ${OBJECTIVE_SRC} ${NETWORK_SRC} ${TREELEARNER_SRC} ${UTIL_SRC})
add_executable(lightgbm main.cpp ${APPLICATION_SRC} ${BOOSTING_SRC} ${IO_SRC} ${METRIC_SRC} ${OBJECTIVE_SRC} ${NETWORK_SRC} ${TREELEARNER_SRC})
if(USE_MPI)
TARGET_LINK_LIBRARIES(lightgbm ${MPI_CXX_LIBRARIES})
......
......@@ -35,14 +35,18 @@ void OverallConfig::Set(const std::unordered_map<std::string, std::string>& para
// check for conflicts
CheckParamConflict();
if (io_config.verbosity == 1)
if (io_config.verbosity == 1) {
LightGBM::Log::ResetLogLevel(LightGBM::LogLevel::Info);
else if (io_config.verbosity == 0)
}
else if (io_config.verbosity == 0) {
LightGBM::Log::ResetLogLevel(LightGBM::LogLevel::Error);
else if (io_config.verbosity >= 2)
}
else if (io_config.verbosity >= 2) {
LightGBM::Log::ResetLogLevel(LightGBM::LogLevel::Debug);
else
}
else {
LightGBM::Log::ResetLogLevel(LightGBM::LogLevel::Fatal);
}
}
void OverallConfig::GetBoostingType(const std::unordered_map<std::string, std::string>& params) {
......
#include "LightGBM/utils/log.h"
#include <time.h>
#include <stdarg.h>
#include <string>
namespace LightGBM {
// Creates a Logger intance writing messages into STDOUT.
Logger::Logger(LogLevel level) {
level_ = level;
file_ = nullptr;
is_kill_fatal_ = true;
}
// Creates a Logger instance writing messages into both STDOUT and log file.
Logger::Logger(std::string filename, LogLevel level) {
level_ = level;
file_ = nullptr;
ResetLogFile(filename);
}
Logger::~Logger() {
CloseLogFile();
}
int Logger::ResetLogFile(std::string filename) {
CloseLogFile();
if (filename.size() > 0) { // try to open the log file if it is specified
#ifdef _MSC_VER
fopen_s(&file_, filename.c_str(), "w");
#else
file_ = fopen(filename.c_str(), "w");
#endif
if (file_ == nullptr) {
Error("Cannot create log file %s\n", filename.c_str());
return -1;
}
}
return 0;
}
void Logger::Write(LogLevel level, const char *format, ...) {
va_list val;
va_start(val, format);
Write(level, format, val);
va_end(val);
}
void Logger::Debug(const char *format, ...) {
va_list val;
va_start(val, format);
Write(LogLevel::Debug, format, val);
va_end(val);
}
void Logger::Info(const char *format, ...) {
va_list val;
va_start(val, format);
Write(LogLevel::Info, format, val);
va_end(val);
}
void Logger::Error(const char *format, ...) {
va_list val;
va_start(val, format);
Write(LogLevel::Error, format, val);
va_end(val);
}
void Logger::Fatal(const char *format, ...) {
va_list val;
va_start(val, format);
Write(LogLevel::Fatal, format, val);
va_end(val);
}
inline void Logger::Write(LogLevel level, const char *format, va_list* val) {
if (level >= level_) { // omit the message with low level
std::string level_str = GetLevelStr(level);
std::string time_str = GetSystemTime();
va_list val_copy;
va_copy(val_copy, *val);
// write to STDOUT
printf("[%s] [%s] ", level_str.c_str(), time_str.c_str());
vprintf(format, *val);
fflush(stdout);
// write to log file
if (file_ != nullptr) {
fprintf(file_, "[%s] [%s] ", level_str.c_str(), time_str.c_str());
vfprintf(file_, format, val_copy);
fflush(file_);
}
va_end(val_copy);
if (is_kill_fatal_ && level == LogLevel::Fatal) {
CloseLogFile();
exit(1);
}
}
}
// Closes the log file if it it not null.
void Logger::CloseLogFile() {
if (file_ != nullptr) {
fclose(file_);
file_ = nullptr;
}
}
std::string Logger::GetSystemTime() {
time_t t = time(0);
char str[64];
#ifdef _MSC_VER
tm time;
localtime_s(&time, &t);
strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S", &time);
#else
strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S", localtime(&t));
#endif
return str;
}
std::string Logger::GetLevelStr(LogLevel level) {
switch (level) {
case LogLevel::Debug: return "DEBUG";
case LogLevel::Info: return "INFO";
case LogLevel::Error: return "ERROR";
case LogLevel::Fatal: return "FATAL";
default: return "UNKNOW";
}
}
//-- End of Logger rountine ----------------------------------------------/
Logger Log::logger_; // global (in process) static Logger instance
int Log::ResetLogFile(std::string filename) {
return logger_.ResetLogFile(filename);
}
void Log::ResetLogLevel(LogLevel level) {
logger_.ResetLogLevel(level);
}
void Log::ResetKillFatal(bool is_kill_fatal) {
logger_.ResetKillFatal(is_kill_fatal);
}
void Log::Write(LogLevel level, const char *format, ...) {
va_list val;
va_start(val, format);
logger_.Write(level, format, &val);
va_end(val);
}
void Log::Debug(const char *format, ...) {
va_list val;
va_start(val, format);
logger_.Write(LogLevel::Debug, format, &val);
va_end(val);
}
void Log::Info(const char *format, ...) {
va_list val;
va_start(val, format);
logger_.Write(LogLevel::Info, format, &val);
va_end(val);
}
void Log::Error(const char *format, ...) {
va_list val;
va_start(val, format);
logger_.Write(LogLevel::Error, format, &val);
va_end(val);
}
void Log::Fatal(const char *format, ...) {
va_list val;
va_start(val, format);
logger_.Write(LogLevel::Fatal, format, &val);
va_end(val);
}
} // namespace lightGBM
......@@ -218,7 +218,6 @@
<ClCompile Include="..\src\treelearner\feature_parallel_tree_learner.cpp" />
<ClCompile Include="..\src\treelearner\serial_tree_learner.cpp" />
<ClCompile Include="..\src\treelearner\tree_learner.cpp" />
<ClCompile Include="..\src\util\log.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
......
......@@ -221,8 +221,5 @@
<ClCompile Include="..\src\main.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\src\util\log.cpp">
<Filter>include\LightGBM\utils</Filter>
</ClCompile>
</ItemGroup>
</Project>
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment