Support pluggable backend log lib with glog (#2695)

* [WIP] Support different backend log lib

* Refine code, unify level, address comment

* Address comment and change formatter

* Fix linux building failure.

* Fix lint

* Remove log4cplus.

* Add log init to raylet main and add test to travis.

* Address comment and refine.

* Update logging_test.cc
This commit is contained in:
Yuhong Guo
2018-08-24 00:43:38 +08:00
committed by Philipp Moritz
parent 4be324efc3
commit eec1a3eb89
10 changed files with 339 additions and 67 deletions
+1
View File
@@ -182,6 +182,7 @@ install:
- ./src/ray/raylet/lineage_cache_test
- ./src/ray/raylet/task_dependency_manager_test
- ./src/ray/raylet/reconstruction_policy_test
- ./src/ray/util/logging_test --gtest_filter=PrintLogTest*
- bash ../src/common/test/run_tests.sh
- bash ../src/plasma/test/run_tests.sh
+4
View File
@@ -34,6 +34,10 @@ option(RAY_USE_NEW_GCS
"Use the new GCS implementation"
OFF)
option(RAY_USE_GLOG
"Build the logging system using glog"
ON)
if (RAY_USE_NEW_GCS)
add_definitions(-DRAY_USE_NEW_GCS)
endif()
+36
View File
@@ -94,3 +94,39 @@ endif()
set(Boost_USE_STATIC_LIBS ON)
find_package(Boost COMPONENTS system filesystem REQUIRED)
include_directories(${Boost_INCLUDE_DIR})
if(RAY_USE_GLOG)
message(STATUS "Starting to build glog")
set(GLOG_VERSION "0.3.5")
set(GLOG_CMAKE_CXX_FLAGS ${EP_CXX_FLAGS} "-fPIC")
set(GLOG_URL "https://github.com/google/glog/archive/v${GLOG_VERSION}.tar.gz")
set(GLOG_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/glog_ep-prefix/src/glog_ep")
set(GLOG_HOME "${GLOG_PREFIX}")
set(GLOG_INCLUDE_DIR "${GLOG_PREFIX}/include")
set(GLOG_STATIC_LIB "${GLOG_PREFIX}/lib/libglog.a")
set(GLOG_CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_INSTALL_PREFIX=${GLOG_PREFIX}
-DBUILD_SHARED_LIBS=OFF
-DBUILD_TESTING=OFF
-DWITH_GFLAGS=OFF
-DCMAKE_CXX_FLAGS_${UPPERCASE_BUILD_TYPE}=${GLOG_CMAKE_CXX_FLAGS}
-DCMAKE_C_FLAGS_${UPPERCASE_BUILD_TYPE}=${EP_C_FLAGS}
-DCMAKE_CXX_FLAGS=${GLOG_CMAKE_CXX_FLAGS})
ExternalProject_Add(glog_ep
URL ${GLOG_URL}
${EP_LOG_OPTIONS}
BUILD_IN_SOURCE 1
BUILD_BYPRODUCTS "${GLOG_STATIC_LIB}"
CMAKE_ARGS ${GLOG_CMAKE_ARGS})
message(STATUS "GLog include dir: ${GLOG_INCLUDE_DIR}")
message(STATUS "GLog static library: ${GLOG_STATIC_LIB}")
include_directories(SYSTEM ${GLOG_INCLUDE_DIR})
ADD_THIRDPARTY_LIB(glog
STATIC_LIB ${GLOG_STATIC_LIB})
add_dependencies(glog glog_ep)
endif()
+4 -1
View File
@@ -14,4 +14,7 @@ endif()
add_library(ray_redis_module SHARED ray_redis_module.cc)
target_compile_options(ray_redis_module PUBLIC ${REDIS_MODULE_CFLAGS} -fPIC)
target_link_libraries(ray_redis_module ${REDIS_MODULE_LDFLAGS})
target_link_libraries(ray_redis_module
# RAY_CHECK needs this lib.
LINK_PUBLIC ray_static
${REDIS_MODULE_LDFLAGS})
+12 -2
View File
@@ -36,6 +36,7 @@ set(RAY_SRCS
gcs/task_table.cc
gcs/redis_context.cc
gcs/asio.cc
util/logging.cc
common/client_connection.cc
object_manager/object_manager_client_connection.cc
object_manager/connection_pool.cc
@@ -61,6 +62,15 @@ set(RAY_SRCS
raylet/raylet.cc
)
set(RAY_LIB_STATIC_LINK_LIBS ${PLASMA_STATIC_LIB} ${ARROW_STATIC_LIB})
set(RAY_LIB_DEPENDENCIES gen_gcs_fbs gen_object_manager_fbs gen_node_manager_fbs gen_local_scheduler_fbs ${COMMON_FBS_OUTPUT_FILES})
if(RAY_USE_GLOG)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DRAY_USE_GLOG")
set(RAY_LIB_STATIC_LINK_LIBS ${RAY_LIB_STATIC_LINK_LIBS} ${GLOG_STATIC_LIB})
set(RAY_LIB_DEPENDENCIES ${RAY_LIB_DEPENDENCIES} glog)
endif()
install(FILES
api.h
id.h
@@ -79,6 +89,6 @@ ADD_RAY_LIB(ray
SOURCES ${RAY_SRCS} ${AE_SRCS} ${HIREDIS_SRCS} ${UTIL_SRCS}
# TODO: When raylet replaces the old backend, please remove the dependency gen_local_scheduler_fbs.
# TODO: When remove the included Task.h from table.h, please remove the dependency gen_common_python_fbs.
DEPENDENCIES gen_gcs_fbs gen_object_manager_fbs gen_node_manager_fbs gen_local_scheduler_fbs ${COMMON_FBS_OUTPUT_FILES}
DEPENDENCIES ${RAY_LIB_DEPENDENCIES}
SHARED_LINK_LIBS ""
STATIC_LINK_LIBS ${PLASMA_STATIC_LIB} ${ARROW_STATIC_LIB})
STATIC_LINK_LIBS ${RAY_LIB_STATIC_LINK_LIBS})
+2
View File
@@ -6,6 +6,7 @@
#ifndef RAYLET_TEST
int main(int argc, char *argv[]) {
RayLog::StartRayLog(argv[0], RAY_INFO);
RAY_CHECK(argc == 10);
const std::string raylet_socket_name = std::string(argv[1]);
@@ -104,5 +105,6 @@ int main(int argc, char *argv[]) {
signals.async_wait(handler);
main_service.run();
RayLog::ShutDownRayLog();
}
#endif
+2
View File
@@ -5,3 +5,5 @@ install(FILES
visibility.h
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ray/util"
)
ADD_RAY_TEST(logging_test STATIC_LINK_LIBS ray_static gtest pthread)
+140
View File
@@ -0,0 +1,140 @@
#include <cstdlib>
#include <iostream>
#include "ray/util/logging.h"
#ifdef RAY_USE_GLOG
#include "glog/logging.h"
#endif
namespace ray {
// This is the default implementation of ray log,
// which is independent of any libs.
class CerrLog {
public:
CerrLog(int severity) : severity_(severity), has_logged_(false) {}
virtual ~CerrLog() {
if (has_logged_) {
std::cerr << std::endl;
}
if (severity_ == RAY_FATAL) {
PrintBackTrace();
std::abort();
}
}
std::ostream &Stream() {
has_logged_ = true;
return std::cerr;
}
template <class T>
CerrLog &operator<<(const T &t) {
if (severity_ != RAY_DEBUG) {
has_logged_ = true;
std::cerr << t;
}
return *this;
}
protected:
const int severity_;
bool has_logged_;
void PrintBackTrace() {
#if defined(_EXECINFO_H) || !defined(_WIN32)
void *buffer[255];
const int calls = backtrace(buffer, sizeof(buffer) / sizeof(void *));
backtrace_symbols_fd(buffer, calls, 1);
#endif
}
};
int RayLog::severity_threshold_ = RAY_INFO;
#ifdef RAY_USE_GLOG
using namespace google;
// Glog's severity map.
static int GetMappedSeverity(int severity) {
switch (severity) {
case RAY_DEBUG:
return GLOG_INFO;
case RAY_INFO:
return GLOG_INFO;
case RAY_WARNING:
return GLOG_WARNING;
case RAY_ERROR:
return GLOG_ERROR;
case RAY_FATAL:
return GLOG_FATAL;
default:
RAY_LOG(FATAL) << "Unsupported logging level: " << severity;
// This return won't be hit but compiler needs it.
return GLOG_FATAL;
}
}
#endif
void RayLog::StartRayLog(const std::string &app_name, int severity_threshold,
const std::string &log_dir) {
#ifdef RAY_USE_GLOG
severity_threshold_ = severity_threshold;
int mapped_severity_threshold = GetMappedSeverity(severity_threshold_);
google::InitGoogleLogging(app_name.c_str());
google::SetStderrLogging(mapped_severity_threshold);
// Enble log file if log_dir is not empty.
if (!log_dir.empty()) {
auto dir_ends_with_slash = log_dir;
if (log_dir[log_dir.length() - 1] != '/') {
dir_ends_with_slash += "/";
}
auto app_name_without_path = app_name;
if (app_name.empty()) {
app_name_without_path = "DefaultApp";
} else {
// Find the app name without the path.
size_t pos = app_name.rfind('/');
if (pos != app_name.npos && pos + 1 < app_name.length()) {
app_name_without_path = app_name.substr(pos + 1);
}
}
google::SetLogFilenameExtension(app_name_without_path.c_str());
google::SetLogDestination(mapped_severity_threshold, log_dir.c_str());
}
#endif
}
void RayLog::ShutDownRayLog() {
#ifdef RAY_USE_GLOG
google::ShutdownGoogleLogging();
#endif
}
RayLog::RayLog(const char *file_name, int line_number, int severity) {
#ifdef RAY_USE_GLOG
// glog does not have DEBUG level, we can handle it here.
if (severity >= severity_threshold_) {
logging_provider_.reset(
new google::LogMessage(file_name, line_number, GetMappedSeverity(severity)));
}
#else
logging_provider_.reset(new CerrLog(severity));
*logging_provider_ << file_name << ":" << line_number << ": ";
#endif
}
std::ostream &RayLog::Stream() {
#ifdef RAY_USE_GLOG
return logging_provider_->stream();
#else
return logging_provider_->Stream();
#endif
}
RayLog::~RayLog() { logging_provider_.reset(); }
} // namespace ray
+59 -64
View File
@@ -7,16 +7,24 @@
#include <cstdlib>
#include <iostream>
#include <memory>
#include "ray/util/macros.h"
// Forward declaration for the log provider.
#ifdef RAY_USE_GLOG
namespace google {
class LogMessage;
} // namespace google
typedef google::LogMessage LoggingProvider;
#else
namespace ray {
class CerrLog;
} // namespace ray
typedef ray::CerrLog LoggingProvider;
#endif
// Stubbed versions of macros defined in glog/logging.h, intended for
// environments where glog headers aren't available.
//
// Add more as needed.
namespace ray {
// Log levels. LOG ignores them, so their values are abitrary.
#define RAY_DEBUG (-1)
@@ -25,22 +33,21 @@ namespace ray {
#define RAY_ERROR 2
#define RAY_FATAL 3
#define RAY_LOG_INTERNAL(level) \
::ray::internal::CerrLog(level) << __FILE__ << ":" << __LINE__ << ": "
#define RAY_LOG_INTERNAL(level) ::ray::RayLog(__FILE__, __LINE__, level)
#define RAY_LOG(level) RAY_LOG_INTERNAL(RAY_##level)
#define RAY_IGNORE_EXPR(expr) ((void) (expr));
#define RAY_IGNORE_EXPR(expr) ((void)(expr))
#define RAY_CHECK(condition) \
(condition) ? 0 : ::ray::internal::FatalLog(RAY_FATAL) \
<< __FILE__ << ":" << __LINE__ \
<< " Check failed: " #condition " "
#define RAY_CHECK(condition) \
(condition) ? RAY_IGNORE_EXPR(0) : ::ray::Voidify() & \
::ray::RayLog(__FILE__, __LINE__, RAY_FATAL) \
<< " Check failed: " #condition " "
#ifdef NDEBUG
#define RAY_DCHECK(condition) \
RAY_IGNORE_EXPR(condition) \
while (false) ::ray::internal::NullLog()
RAY_IGNORE_EXPR(condition); \
while (false) ::ray::RayLogBase()
#else
@@ -48,74 +55,62 @@ namespace ray {
#endif // NDEBUG
namespace internal {
// To make the logging lib plugable with other logging libs and make
// the implementation unawared by the user, RayLog is only a declaration
// which hide the implementation into logging.cc file.
// In logging.cc, we can choose different log libs using different macros.
class NullLog {
// This is also a null log which does not output anything.
class RayLogBase {
public:
template <class T>
NullLog &operator<<(const T &t) {
virtual ~RayLogBase(){};
template <typename T>
RayLogBase &operator<<(const T &t) {
RAY_IGNORE_EXPR(t);
return *this;
}
};
class CerrLog {
class RayLog : public RayLogBase {
public:
CerrLog(int severity) // NOLINT(runtime/explicit)
: severity_(severity),
has_logged_(false) {}
RayLog(const char *file_name, int line_number, int severity);
virtual ~RayLog();
virtual ~CerrLog() {
if (has_logged_) {
std::cerr << std::endl;
}
// This code is duplicated in the FatalLog class.
if (severity_ == RAY_FATAL) {
#if defined(_EXECINFO_H) || !defined(_WIN32)
void *buffer[255];
const int calls = backtrace(buffer, sizeof(buffer) / sizeof(void *));
backtrace_symbols_fd(buffer, calls, 1);
#endif
std::abort();
}
}
template <class T>
CerrLog &operator<<(const T &t) {
if (severity_ != RAY_DEBUG) {
has_logged_ = true;
std::cerr << t;
template <typename T>
RayLogBase &operator<<(const T &t) {
if (logging_provider_ == nullptr) {
// This means the logging level is lower than the threshold.
RAY_IGNORE_EXPR(t);
} else {
this->Stream() << t;
}
return *this;
}
protected:
const int severity_;
bool has_logged_;
// The init function of ray log for a program which should be called only once.
// If logDir is empty, the log won't output to file.
static void StartRayLog(const std::string &appName, int severity_threshold = RAY_ERROR,
const std::string &logDir = "");
// The shutdown function of ray log which should be used with StartRayLog as a pair.
static void ShutDownRayLog();
private:
std::ostream &Stream();
std::unique_ptr<LoggingProvider> logging_provider_;
static int severity_threshold_;
};
// Clang-tidy isn't smart enough to determine that DCHECK using CerrLog doesn't
// return so we create a new class to give it a hint.
class FatalLog : public CerrLog {
// This class make RAY_CHECK compilation pass to change the << operator to void.
// This class is copied from glog.
class Voidify {
public:
explicit FatalLog(int /* severity */) // NOLINT
: CerrLog(RAY_FATAL) {} // NOLINT
RAY_NORETURN ~FatalLog() {
if (has_logged_) {
std::cerr << std::endl;
#if defined(_EXECINFO_H) || !defined(_WIN32)
void *buffer[255];
const int calls = backtrace(buffer, sizeof(buffer) / sizeof(void *));
backtrace_symbols_fd(buffer, calls, 1);
#endif
}
std::abort();
}
Voidify() {}
// This has to be an operator with a precedence lower than << but
// higher than ?:
void operator&(RayLogBase &) {}
};
} // namespace internal
} // namespace ray
#endif // RAY_UTIL_LOGGING_H
+79
View File
@@ -0,0 +1,79 @@
#include <chrono>
#include <cstdlib>
#include <iostream>
#include "gtest/gtest.h"
#include "ray/util/logging.h"
namespace ray {
int64_t current_time_ms() {
std::chrono::milliseconds ms_since_epoch =
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch());
return ms_since_epoch.count();
}
// This is not really test.
// This file just print some information using the logging macro.
void PrintLog() {
RAY_LOG(DEBUG) << "This is the DEBUG message";
RAY_LOG(INFO) << "This is the INFO message";
RAY_LOG(WARNING) << "This is the WARNING message";
RAY_LOG(ERROR) << "This is the ERROR message";
RAY_CHECK(true) << "This is a RAY_CHECK message but it won't show up";
// The following 2 lines should not run since it will cause program failure.
// RAY_LOG(FATAL) << "This is the FATAL message";
// RAY_CHECK(false) << "This is a RAY_CHECK message but it won't show up";
}
TEST(PrintLogTest, LogTestWithoutInit) {
// Without RayLog::StartRayLog, this should also work.
PrintLog();
}
TEST(PrintLogTest, LogTestWithInit) {
// Test empty app name.
RayLog::StartRayLog("", RAY_DEBUG);
PrintLog();
RayLog::ShutDownRayLog();
}
// This test will output large amount of logs to stderr, should be disabled in travis.
TEST(LogPerfTest, PerfTest) {
RayLog::StartRayLog("/fake/path/to/appdire/LogPerfTest", RAY_ERROR, "/tmp/");
int rounds = 100000;
int64_t start_time = current_time_ms();
for (int i = 0; i < rounds; ++i) {
RAY_LOG(DEBUG) << "This is the RAY_DEBUG message";
}
int64_t elapsed = current_time_ms() - start_time;
std::cout << "Testing DEBUG log for " << rounds << " rounds takes " << elapsed << " ms."
<< std::endl;
start_time = current_time_ms();
for (int i = 0; i < rounds; ++i) {
RAY_LOG(ERROR) << "This is the RAY_ERROR message";
}
elapsed = current_time_ms() - start_time;
std::cout << "Testing RAY_ERROR log for " << rounds << " rounds takes " << elapsed
<< " ms." << std::endl;
start_time = current_time_ms();
for (int i = 0; i < rounds; ++i) {
RAY_CHECK(i >= 0) << "This is a RAY_CHECK message but it won't show up";
}
elapsed = current_time_ms() - start_time;
std::cout << "Testing RAY_CHECK(true) for " << rounds << " rounds takes " << elapsed
<< " ms." << std::endl;
RayLog::ShutDownRayLog();
}
} // namespace ray
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}