blob: d3e7e05c2910d2d2949138e8955060bc481cbfc8 [file] [log] [blame]
/**
* Copyright (c) 2016-present, Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "caffe2/core/logging.h"
#include "caffe2/core/flags.h"
#include <algorithm>
#include <cstring>
#include <numeric>
// Common code that we use regardless of whether we use glog or not.
CAFFE2_DEFINE_bool(caffe2_use_fatal_for_enforce, false,
"If set true, when CAFFE_ENFORCE is not met, abort instead "
"of throwing an exception.");
namespace caffe2 {
std::string StripBasename(const std::string &full_path) {
const char kSeparator = '/';
size_t pos = full_path.rfind(kSeparator);
if (pos != std::string::npos) {
return full_path.substr(pos + 1, std::string::npos);
} else {
return full_path;
}
}
size_t ReplaceAll(string& s, const char* from, const char* to) {
CAFFE_ENFORCE(from && *from);
CAFFE_ENFORCE(to);
size_t numReplaced = 0;
string::size_type lenFrom = std::strlen(from);
string::size_type lenTo = std::strlen(to);
for (string::size_type pos = s.find(from); pos != string::npos;
pos = s.find(from, pos + lenTo)) {
s.replace(pos, lenFrom, to);
numReplaced++;
}
return numReplaced;
}
static std::function<string(void)> FetchStackTrace = []() { return ""; };
void SetStackTraceFetcher(std::function<string(void)> fetcher) {
FetchStackTrace = fetcher;
}
static std::function<void(const OperatorDef&)> OperatorLogger =
[](const OperatorDef&) { return; };
void SetOperatorLogger(std::function<void(const OperatorDef&)> tracer) {
OperatorLogger = tracer;
}
std::function<void(const OperatorDef&)> GetOperatorLogger() {
return OperatorLogger;
}
EnforceNotMet::EnforceNotMet(
const char* file,
const int line,
const char* condition,
const string& msg,
const void* caller)
: msg_stack_{MakeString(
"[enforce fail at ",
StripBasename(std::string(file)),
":",
line,
"] ",
condition,
". ",
msg,
" ")},
stack_trace_(FetchStackTrace()) {
if (FLAGS_caffe2_use_fatal_for_enforce) {
LOG(FATAL) << msg_stack_[0];
}
caller_ = caller;
full_msg_ = this->msg();
}
void EnforceNotMet::AppendMessage(const string& msg) {
msg_stack_.push_back(msg);
full_msg_ = this->msg();
}
string EnforceNotMet::msg() const {
return std::accumulate(msg_stack_.begin(), msg_stack_.end(), string("")) +
stack_trace_;
}
const char* EnforceNotMet::what() const noexcept {
return full_msg_.c_str();
}
const void* EnforceNotMet::caller() const noexcept {
return caller_;
}
} // namespace caffe2
#ifdef CAFFE2_USE_GOOGLE_GLOG
#ifdef CAFFE2_USE_GFLAGS
// GLOG's minloglevel
CAFFE2_DECLARE_int(minloglevel);
// GLOG's verbose log value.
CAFFE2_DECLARE_int(v);
// GLOG's logtostderr value
CAFFE2_DECLARE_bool(logtostderr);
#else
using fLI::FLAGS_minloglevel;
using fLI::FLAGS_v;
using fLB::FLAGS_logtostderr;
#endif // CAFFE2_USE_GFLAGS
CAFFE2_DEFINE_int(caffe2_log_level, google::ERROR,
"The minimum log level that caffe2 will output.");
// Google glog's api does not have an external function that allows one to check
// if glog is initialized or not. It does have an internal function - so we are
// declaring it here. This is a hack but has been used by a bunch of others too
// (e.g. Torch).
namespace google {
namespace glog_internal_namespace_ {
bool IsGoogleLoggingInitialized();
} // namespace glog_internal_namespace_
} // namespace google
namespace caffe2 {
bool InitCaffeLogging(int* argc, char** argv) {
if (*argc == 0) return true;
if (!::google::glog_internal_namespace_::IsGoogleLoggingInitialized()) {
::google::InitGoogleLogging(argv[0]);
::google::InstallFailureSignalHandler();
}
// If caffe2_log_level is set and is lower than the min log level by glog,
// we will transfer the caffe2_log_level setting to glog to override that.
FLAGS_minloglevel = std::min(FLAGS_caffe2_log_level, FLAGS_minloglevel);
// If caffe2_log_level is explicitly set, let's also turn on logtostderr.
if (FLAGS_caffe2_log_level < google::ERROR) {
FLAGS_logtostderr = 1;
}
// Also, transfer the caffe2_log_level verbose setting to glog.
if (FLAGS_caffe2_log_level < 0) {
FLAGS_v = std::min(FLAGS_v, -FLAGS_caffe2_log_level);
}
return true;
}
void ShowLogInfoToStderr() {
FLAGS_logtostderr = 1;
FLAGS_minloglevel = std::min(FLAGS_minloglevel, google::INFO);
}
} // namespace caffe2
#else // !CAFFE2_USE_GOOGLE_GLOG
#ifdef ANDROID
#include <android/log.h>
#endif // ANDROID
CAFFE2_DEFINE_int(caffe2_log_level, ERROR,
"The minimum log level that caffe2 will output.");
namespace caffe2 {
bool InitCaffeLogging(int* argc, char** argv) {
// When doing InitCaffeLogging, we will assume that caffe's flag paser has
// already finished.
if (*argc == 0) return true;
if (!CommandLineFlagsHasBeenParsed()) {
std::cerr << "InitCaffeLogging() has to be called after "
"ParseCaffeCommandLineFlags. Modify your program to make sure "
"of this." << std::endl;
return false;
}
if (FLAGS_caffe2_log_level > FATAL) {
std::cerr << "The log level of Caffe2 has to be no larger than FATAL("
<< FATAL << "). Capping it to FATAL." << std::endl;
FLAGS_caffe2_log_level = FATAL;
}
return true;
}
void ShowLogInfoToStderr() {
FLAGS_caffe2_log_level = INFO;
}
MessageLogger::MessageLogger(const char *file, int line, int severity)
: severity_(severity) {
if (severity_ < FLAGS_caffe2_log_level) {
// Nothing needs to be logged.
return;
}
#ifdef ANDROID
tag_ = "native";
#else // !ANDROID
tag_ = "";
#endif // ANDROID
/*
time_t rawtime;
struct tm * timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
std::chrono::nanoseconds ns =
std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch());
*/
stream_ << "[" << CAFFE2_SEVERITY_PREFIX[std::min(4, FATAL - severity_)]
//<< (timeinfo->tm_mon + 1) * 100 + timeinfo->tm_mday
//<< std::setfill('0')
//<< " " << std::setw(2) << timeinfo->tm_hour
//<< ":" << std::setw(2) << timeinfo->tm_min
//<< ":" << std::setw(2) << timeinfo->tm_sec
//<< "." << std::setw(9) << ns.count() % 1000000000
<< " " << StripBasename(std::string(file)) << ":" << line << "] ";
}
// Output the contents of the stream to the proper channel on destruction.
MessageLogger::~MessageLogger() {
if (severity_ < FLAGS_caffe2_log_level) {
// Nothing needs to be logged.
return;
}
stream_ << "\n";
#ifdef ANDROID
static const int android_log_levels[] = {
ANDROID_LOG_FATAL, // LOG_FATAL
ANDROID_LOG_ERROR, // LOG_ERROR
ANDROID_LOG_WARN, // LOG_WARNING
ANDROID_LOG_INFO, // LOG_INFO
ANDROID_LOG_DEBUG, // VLOG(1)
ANDROID_LOG_VERBOSE, // VLOG(2) .. VLOG(N)
};
int android_level_index = FATAL - std::min(FATAL, severity_);
int level = android_log_levels[std::min(android_level_index, 5)];
// Output the log string the Android log at the appropriate level.
__android_log_print(level, tag_, "%s", stream_.str().c_str());
// Indicate termination if needed.
if (severity_ == FATAL) {
__android_log_print(ANDROID_LOG_FATAL, tag_, "terminating.\n");
}
#else // !ANDROID
if (severity_ >= FLAGS_caffe2_log_level) {
// If not building on Android, log all output to std::cerr.
std::cerr << stream_.str();
}
#endif // ANDROID
if (severity_ == FATAL) {
DealWithFatal();
}
}
} // namespace caffe2
#endif // !CAFFE2_USE_GOOGLE_GLOG