Unverified Commit dea2391b authored by Guolin Ke's avatar Guolin Ke Committed by GitHub
Browse files

redirect log to python console (#3090)



* redir log to python console

* fix pylint

* Apply suggestions from code review

* Update basic.py

* Apply suggestions from code review
Co-authored-by: default avatarNikita Titov <nekit94-08@mail.ru>

* Update c_api.h

* Apply suggestions from code review

* Apply suggestions from code review

* super-minor: better wording
Co-authored-by: default avatarNikita Titov <nekit94-08@mail.ru>
Co-authored-by: default avatarStrikerRUS <nekit94-12@hotmail.com>
parent f962744d
...@@ -39,6 +39,13 @@ typedef void* BoosterHandle; /*!< \brief Handle of booster. */ ...@@ -39,6 +39,13 @@ typedef void* BoosterHandle; /*!< \brief Handle of booster. */
*/ */
LIGHTGBM_C_EXPORT const char* LGBM_GetLastError(); LIGHTGBM_C_EXPORT const char* LGBM_GetLastError();
/*!
* \brief Register a callback function for log redirecting.
* \param callback The callback function to register
* \return 0 when succeed, -1 when failure happens
*/
LIGHTGBM_C_EXPORT int LGBM_RegisterLogCallback(void (*callback)(const char*));
// --- start Dataset interface // --- start Dataset interface
/*! /*!
......
/*! /*!
* Copyright (c) 2016 Microsoft Corporation. All rights reserved. * Copyright (c) 2016 Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE file in the project root for license information. * Licensed under the MIT License. See LICENSE file in the project root for
* license information.
*/ */
#ifndef LIGHTGBM_UTILS_LOG_H_ #ifndef LIGHTGBM_UTILS_LOG_H_
#define LIGHTGBM_UTILS_LOG_H_ #define LIGHTGBM_UTILS_LOG_H_
#include <string>
#include <cstdarg> #include <cstdarg>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
...@@ -13,12 +13,13 @@ ...@@ -13,12 +13,13 @@
#include <exception> #include <exception>
#include <iostream> #include <iostream>
#include <stdexcept> #include <stdexcept>
#include <string>
#ifdef LGB_R_BUILD #ifdef LGB_R_BUILD
#define R_NO_REMAP #define R_NO_REMAP
#define R_USE_C99_IN_CXX #define R_USE_C99_IN_CXX
#include <R_ext/Error.h> #include <R_ext/Error.h>
#include <R_ext/Print.h> #include <R_ext/Print.h>
#endif #endif
namespace LightGBM { namespace LightGBM {
...@@ -31,8 +32,9 @@ namespace LightGBM { ...@@ -31,8 +32,9 @@ namespace LightGBM {
#ifndef CHECK #ifndef CHECK
#define CHECK(condition) \ #define CHECK(condition) \
if (!(condition)) Log::Fatal("Check failed: " #condition \ if (!(condition)) \
" at %s, line %d .\n", __FILE__, __LINE__); Log::Fatal("Check failed: " #condition " at %s, line %d .\n", __FILE__, \
__LINE__);
#endif #endif
#ifndef CHECK_EQ #ifndef CHECK_EQ
...@@ -61,10 +63,12 @@ namespace LightGBM { ...@@ -61,10 +63,12 @@ namespace LightGBM {
#ifndef CHECK_NOTNULL #ifndef CHECK_NOTNULL
#define CHECK_NOTNULL(pointer) \ #define CHECK_NOTNULL(pointer) \
if ((pointer) == nullptr) LightGBM::Log::Fatal(#pointer " Can't be NULL at %s, line %d .\n", __FILE__, __LINE__); if ((pointer) == nullptr) \
LightGBM::Log::Fatal(#pointer " Can't be NULL at %s, line %d .\n", \
__FILE__, __LINE__);
#endif #endif
enum class LogLevel: int { enum class LogLevel : int {
Fatal = -1, Fatal = -1,
Warning = 0, Warning = 0,
Info = 1, Info = 1,
...@@ -72,17 +76,18 @@ enum class LogLevel: int { ...@@ -72,17 +76,18 @@ enum class LogLevel: int {
}; };
/*! /*!
* \brief A static Log class * \brief A static Log class
*/ */
class Log { class Log {
public: public:
using Callback = void (*)(const char *);
/*! /*!
* \brief Resets the minimal log level. It is INFO by default. * \brief Resets the minimal log level. It is INFO by default.
* \param level The new minimal log level. * \param level The new minimal log level.
*/ */
static void ResetLogLevel(LogLevel level) { static void ResetLogLevel(LogLevel level) { GetLevel() = level; }
GetLevel() = level;
} static void ResetCallBack(Callback callback) { GetLogCallBack() = callback; }
static void Debug(const char *format, ...) { static void Debug(const char *format, ...) {
va_list val; va_list val;
...@@ -113,38 +118,57 @@ class Log { ...@@ -113,38 +118,57 @@ class Log {
#endif #endif
va_end(val); va_end(val);
// R code should write back to R's error stream, // R code should write back to R's error stream,
// otherwise to stderr // otherwise to stderr
#ifndef LGB_R_BUILD #ifndef LGB_R_BUILD
fprintf(stderr, "[LightGBM] [Fatal] %s\n", str_buf); fprintf(stderr, "[LightGBM] [Fatal] %s\n", str_buf);
fflush(stderr); fflush(stderr);
throw std::runtime_error(std::string(str_buf)); throw std::runtime_error(std::string(str_buf));
#else #else
Rf_error("[LightGBM] [Fatal] %s\n", str_buf); Rf_error("[LightGBM] [Fatal] %s\n", str_buf);
#endif #endif
} }
private: private:
static void Write(LogLevel level, const char* level_str, const char *format, va_list val) { static void Write(LogLevel level, const char *level_str, const char *format,
va_list val) {
if (level <= GetLevel()) { // omit the message with low level if (level <= GetLevel()) { // omit the message with low level
// R code should write back to R's output stream, // R code should write back to R's output stream,
// otherwise to stdout // otherwise to stdout
#ifndef LGB_R_BUILD #ifndef LGB_R_BUILD
if (GetLogCallBack() == nullptr) {
printf("[LightGBM] [%s] ", level_str); printf("[LightGBM] [%s] ", level_str);
vprintf(format, val); vprintf(format, val);
printf("\n"); printf("\n");
fflush(stdout); fflush(stdout);
#else } else {
const size_t kBufSize = 512;
char buf[kBufSize];
snprintf(buf, kBufSize, "[LightGBM] [%s] ", level_str);
GetLogCallBack()(buf);
vsnprintf(buf, kBufSize, format, val);
GetLogCallBack()(buf);
GetLogCallBack()("\n");
}
#else
Rprintf("[LightGBM] [%s] ", level_str); Rprintf("[LightGBM] [%s] ", level_str);
Rvprintf(format, val); Rvprintf(format, val);
Rprintf("\n"); Rprintf("\n");
#endif #endif
} }
} }
// a trick to use static variable in header file. // a trick to use static variable in header file.
// May be not good, but avoid to use an additional cpp file // May be not good, but avoid to use an additional cpp file
static LogLevel& GetLevel() { static THREAD_LOCAL LogLevel level = LogLevel::Info; return level; } static LogLevel &GetLevel() {
static THREAD_LOCAL LogLevel level = LogLevel::Info;
return level;
}
static Callback &GetLogCallBack() {
static THREAD_LOCAL Callback callback = nullptr;
return callback;
}
}; };
} // namespace LightGBM } // namespace LightGBM
......
# coding: utf-8 # coding: utf-8
"""Wrapper for C API of LightGBM.""" """Wrapper for C API of LightGBM."""
from __future__ import absolute_import from __future__ import absolute_import, print_function
import copy import copy
import ctypes import ctypes
...@@ -21,6 +21,11 @@ from .compat import (PANDAS_INSTALLED, DataFrame, Series, is_dtype_sparse, ...@@ -21,6 +21,11 @@ from .compat import (PANDAS_INSTALLED, DataFrame, Series, is_dtype_sparse,
from .libpath import find_lib_path from .libpath import find_lib_path
def _log_callback(msg):
"""Redirect logs from native library into Python console."""
print("{0:s}".format(decode_string(msg)), end='')
def _load_lib(): def _load_lib():
"""Load LightGBM library.""" """Load LightGBM library."""
lib_path = find_lib_path() lib_path = find_lib_path()
...@@ -28,6 +33,10 @@ def _load_lib(): ...@@ -28,6 +33,10 @@ def _load_lib():
return None return None
lib = ctypes.cdll.LoadLibrary(lib_path[0]) lib = ctypes.cdll.LoadLibrary(lib_path[0])
lib.LGBM_GetLastError.restype = ctypes.c_char_p lib.LGBM_GetLastError.restype = ctypes.c_char_p
callback = ctypes.CFUNCTYPE(None, ctypes.c_char_p)
lib.callback = callback(_log_callback)
if lib.LGBM_RegisterLogCallback(lib.callback) != 0:
raise LightGBMError(decode_string(lib.LGBM_GetLastError()))
return lib return lib
......
...@@ -610,6 +610,12 @@ const char* LGBM_GetLastError() { ...@@ -610,6 +610,12 @@ const char* LGBM_GetLastError() {
return LastErrorMsg(); return LastErrorMsg();
} }
int LGBM_RegisterLogCallback(void (*callback)(const char*)) {
API_BEGIN();
Log::ResetCallBack(callback);
API_END();
}
int LGBM_DatasetCreateFromFile(const char* filename, int LGBM_DatasetCreateFromFile(const char* filename,
const char* parameters, const char* parameters,
const DatasetHandle reference, const DatasetHandle reference,
......
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