diff options
author | 2016-11-22 21:58:50 +0000 | |
---|---|---|
committer | 2016-11-23 08:51:00 +0000 | |
commit | 78f1c853e17909b001ac5d61469fd436d50fab34 (patch) | |
tree | e3c3f1595aad1c2014066ae05673237f548f0b4f | |
parent | c944c4d9fc5cbad830b69383a76ffff463088c7e (diff) |
Adds logging functionality to the bazel client, which will be activated in a later change.
--
MOS_MIGRATED_REVID=139951184
-rw-r--r-- | src/main/cpp/BUILD | 1 | ||||
-rw-r--r-- | src/main/cpp/blaze.cc | 7 | ||||
-rw-r--r-- | src/main/cpp/blaze.h | 4 | ||||
-rw-r--r-- | src/main/cpp/main.cc | 3 | ||||
-rw-r--r-- | src/main/cpp/option_processor.cc | 2 | ||||
-rw-r--r-- | src/main/cpp/util/BUILD | 18 | ||||
-rw-r--r-- | src/main/cpp/util/bazel_log_handler.cc | 95 | ||||
-rw-r--r-- | src/main/cpp/util/bazel_log_handler.h | 51 | ||||
-rw-r--r-- | src/main/cpp/util/errors.h | 2 | ||||
-rw-r--r-- | src/main/cpp/util/logging.cc | 91 | ||||
-rw-r--r-- | src/main/cpp/util/logging.h | 158 | ||||
-rw-r--r-- | src/test/cpp/util/BUILD | 10 | ||||
-rw-r--r-- | src/test/cpp/util/logging_test.cc | 70 |
13 files changed, 509 insertions, 3 deletions
diff --git a/src/main/cpp/BUILD b/src/main/cpp/BUILD index e88611b87a..9a6df3643e 100644 --- a/src/main/cpp/BUILD +++ b/src/main/cpp/BUILD @@ -105,6 +105,7 @@ cc_binary( ":blaze_abrupt_exit", ":blaze_util", "//src/main/cpp/util", + "//src/main/cpp/util:logging", "//src/main/cpp/util:strings", "//src/main/protobuf:command_server_cc_proto", "//third_party/ijar:zip", diff --git a/src/main/cpp/blaze.cc b/src/main/cpp/blaze.cc index abb16ecd64..819d3e0ca5 100644 --- a/src/main/cpp/blaze.cc +++ b/src/main/cpp/blaze.cc @@ -65,6 +65,7 @@ #include "src/main/cpp/util/exit_code.h" #include "src/main/cpp/util/file.h" #include "src/main/cpp/util/file_platform.h" +#include "src/main/cpp/util/logging.h" #include "src/main/cpp/util/numbers.h" #include "src/main/cpp/util/port.h" #include "src/main/cpp/util/strings.h" @@ -1275,6 +1276,7 @@ static void ComputeBaseDirectories(const string &self_path) { ExcludePathFromBackup(output_base); globals->options->output_base = MakeCanonical(output_base); + globals->lockfile = globals->options->output_base + "/lock"; globals->jvm_log_file = globals->options->output_base + "/server/jvm.out"; } @@ -1356,7 +1358,10 @@ static void CheckBinaryPath(const string& argv0) { // code to a file. In case the server becomes unresonsive or terminates // unexpectedly (in a way that isn't already handled), we can observe the file, // if it exists. (If it doesn't, then we know something went horribly wrong.) -int Main(int argc, const char *argv[], OptionProcessor *option_processor) { +int Main(int argc, const char *argv[], OptionProcessor *option_processor, + std::unique_ptr<blaze_util::LogHandler> log_handler) { + // Logging must be set first to assure no log statements are missed. + blaze_util::SetLogHandler(std::move(log_handler)); globals = new GlobalVariables(option_processor); SetupStreams(); diff --git a/src/main/cpp/blaze.h b/src/main/cpp/blaze.h index 25f484e34f..82a63bb38f 100644 --- a/src/main/cpp/blaze.h +++ b/src/main/cpp/blaze.h @@ -15,10 +15,12 @@ #define BAZEL_SRC_MAIN_CPP_BLAZE_H_ #include "src/main/cpp/option_processor.h" +#include "src/main/cpp/util/logging.h" namespace blaze { -int Main(int argc, const char *argv[], OptionProcessor* option_processor); +int Main(int argc, const char* argv[], OptionProcessor* option_processor, + std::unique_ptr<blaze_util::LogHandler> log_handler); } // namespace blaze diff --git a/src/main/cpp/main.cc b/src/main/cpp/main.cc index 02b771465b..2a1928704a 100644 --- a/src/main/cpp/main.cc +++ b/src/main/cpp/main.cc @@ -22,5 +22,6 @@ int main(int argc, const char *argv[]) { std::unique_ptr<blaze::StartupOptions> startup_options( new blaze::StartupOptions()); return blaze::Main(argc, argv, - new blaze::OptionProcessor(std::move(startup_options))); + new blaze::OptionProcessor(std::move(startup_options)), + nullptr /* TODO(b/32939567): Enable Bazel logging. */); } diff --git a/src/main/cpp/option_processor.cc b/src/main/cpp/option_processor.cc index f7340db783..e3224a2ac6 100644 --- a/src/main/cpp/option_processor.cc +++ b/src/main/cpp/option_processor.cc @@ -26,6 +26,7 @@ #include "src/main/cpp/blaze_util_platform.h" #include "src/main/cpp/util/file.h" #include "src/main/cpp/util/file_platform.h" +#include "src/main/cpp/util/logging.h" #include "src/main/cpp/util/strings.h" #include "src/main/cpp/workspace_layout.h" @@ -71,6 +72,7 @@ blaze_exit_code::ExitCode OptionProcessor::RcFile::Parse( list<string>* import_stack, string* error) { string filename(filename_ref); // file + BAZEL_LOG(INFO) << "Parsing the RcFile " << filename; string contents; if (!ReadFile(filename, &contents)) { // We checked for file readability before, so this is unexpected. diff --git a/src/main/cpp/util/BUILD b/src/main/cpp/util/BUILD index ae75b941bc..bf62eff841 100644 --- a/src/main/cpp/util/BUILD +++ b/src/main/cpp/util/BUILD @@ -69,6 +69,24 @@ cc_library( ) cc_library( + name = "logging", + srcs = ["logging.cc"], + hdrs = ["logging.h"], + visibility = ["//visibility:public"], +) + +cc_library( + name = "bazel_log_handler", + srcs = ["bazel_log_handler.cc"], + hdrs = ["bazel_log_handler.h"], + visibility = ["//visibility:public"], + deps = [ + ":file", + ":logging", + ], +) + +cc_library( name = "md5", srcs = ["md5.cc"], hdrs = ["md5.h"], diff --git a/src/main/cpp/util/bazel_log_handler.cc b/src/main/cpp/util/bazel_log_handler.cc new file mode 100644 index 0000000000..458ffc4bdb --- /dev/null +++ b/src/main/cpp/util/bazel_log_handler.cc @@ -0,0 +1,95 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// 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 "src/main/cpp/util/bazel_log_handler.h" + +#include <cstdio> +#include <cstdlib> +#include <fstream> +#include <iostream> +#include <sstream> + +#include "src/main/cpp/util/file.h" +#include "src/main/cpp/util/logging.h" + +namespace blaze_util { + +BazelLogHandler::BazelLogHandler() + : output_dir_set_attempted_(false), + buffer_stream_(new std::stringstream()), + logfile_stream_(nullptr) {} + +BazelLogHandler::~BazelLogHandler() { + // If we never wrote the logs to a file, dump the buffer to stderr, + // otherwise, flush the stream. + if (logfile_stream_ != nullptr) { + logfile_stream_->flush(); + } else if (buffer_stream_ != nullptr) { + std::cerr << buffer_stream_->rdbuf(); + } else { + std::cerr << "Illegal state - neither a logfile nor a logbuffer " + << "existed at program end." << std::endl; + } +} + +void BazelLogHandler::HandleMessage(LogLevel level, const std::string& filename, + int line, const std::string& message) { + // Select the appropriate stream to log to. + std::ostream* log_stream; + if (logfile_stream_ != nullptr) { + log_stream = logfile_stream_.get(); + } else { + log_stream = buffer_stream_.get(); + } + *log_stream << "[bazel " << LogLevelName(level) << " " << filename << ":" + << line << "] " << message << "\n"; + + // If we have a fatal message, we should abort and leave a stack trace - + // normal exit behavior will be lost, so print this log message out to + // stderr and avoid loosing the information. + if (level == LOGLEVEL_FATAL) { + std::cerr << "[bazel " << LogLevelName(level) << " " << filename << ":" + << line << "] " << message << "\n"; + std::abort(); + } +} + +void BazelLogHandler::SetOutputDir(const std::string& new_output_dir) { + // Disallow second calls to this, we only intend this to support setting + // output_base once it is created, not changing the log location. + BAZEL_CHECK(!output_dir_set_attempted_) + << "Tried to SetOutputDir a second time, to " << new_output_dir; + output_dir_set_attempted_ = true; + + // Create a log file in the newly available directory, and flush the + // buffer to it. + const std::string logfile = JoinPath(new_output_dir, "bazel_client.log"); + logfile_stream_ = std::unique_ptr<std::ofstream>( + new std::ofstream(logfile, std::fstream::out)); + if (logfile_stream_->fail()) { + // If opening the stream failed, continue buffering and have the logs + // dump to stderr at shutdown. + logfile_stream_ = std::move(nullptr); + BAZEL_LOG(ERROR) << "Opening the log file failed, in directory " + << new_output_dir; + } else { + // Transfer the contents of the buffer to the logfile's stream before + // replacing it. + *logfile_stream_ << buffer_stream_->rdbuf(); + buffer_stream_ = std::move(nullptr); + logfile_stream_->flush(); + } +} + +} // namespace blaze_util diff --git a/src/main/cpp/util/bazel_log_handler.h b/src/main/cpp/util/bazel_log_handler.h new file mode 100644 index 0000000000..e9a09ae86c --- /dev/null +++ b/src/main/cpp/util/bazel_log_handler.h @@ -0,0 +1,51 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// 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. + +#ifndef BAZEL_SRC_MAIN_CPP_BAZEL_LOG_HANDLER_H_ +#define BAZEL_SRC_MAIN_CPP_BAZEL_LOG_HANDLER_H_ + +#include <iostream> + +#include "src/main/cpp/util/logging.h" + +namespace blaze_util { + +// Handles logging for the Bazel client. +// In order to have the logfile in output_base, which does not exist or is +// unknown at the time of the client's creation, logs are buffered until +// SetOutputDir is called. At that point, all past log statements are dumped +// in the appropriate file, and all following statements are logged directly. +class BazelLogHandler : public blaze_util::LogHandler { + public: + BazelLogHandler(); + ~BazelLogHandler() override; + + void HandleMessage(blaze_util::LogLevel level, const std::string& filename, + int line, const std::string& message) override; + + // Sets the output directory of the logfile. + // Can only be called once - all logs before this call will be buffered and + // dumped to the logfile once this is called. If this is never called, or if + // creating the logfile failed, the buffered logs will be dumped to stderr at + // destruction. + void SetOutputDir(const std::string& new_output_dir) override; + + private: + bool output_dir_set_attempted_; + std::unique_ptr<std::stringstream> buffer_stream_; + std::unique_ptr<std::ofstream> logfile_stream_; +}; +} // namespace blaze_util + +#endif // BAZEL_SRC_MAIN_CPP_BAZEL_LOG_HANDLER_H_ diff --git a/src/main/cpp/util/errors.h b/src/main/cpp/util/errors.h index 1e991d2a52..84de39d715 100644 --- a/src/main/cpp/util/errors.h +++ b/src/main/cpp/util/errors.h @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. // +// TODO(b/32967056) die() and pdie() are really error statements with an exit; +// these can be removed now that logging exists. #ifndef BAZEL_SRC_MAIN_CPP_UTIL_ERRORS_H_ #define BAZEL_SRC_MAIN_CPP_UTIL_ERRORS_H_ diff --git a/src/main/cpp/util/logging.cc b/src/main/cpp/util/logging.cc new file mode 100644 index 0000000000..7d0bd0bfd0 --- /dev/null +++ b/src/main/cpp/util/logging.cc @@ -0,0 +1,91 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// 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. + +// This file is based off the logging work by the protobuf team +#include "src/main/cpp/util/logging.h" + +#include <cstdio> +#include <cstdlib> +#include <iostream> +#include <memory> + +namespace blaze_util { + +const char* LogLevelName(LogLevel level) { + static const char* level_names[] = {"INFO", "WARNING", "ERROR", "FATAL"}; + BAZEL_CHECK(static_cast<int>(level) < 4) + << "LogLevelName: level out of range, there are only 4 levels."; + return level_names[level]; +} + +namespace internal { + +static std::unique_ptr<LogHandler> log_handler_(nullptr); + +LogMessage::LogMessage(LogLevel level, const std::string& filename, int line) + : level_(level), filename_(filename), line_(line) {} + +#undef DECLARE_STREAM_OPERATOR +#define DECLARE_STREAM_OPERATOR(TYPE) \ + LogMessage& LogMessage::operator<<(TYPE value) { \ + message_ << value; \ + return *this; \ + } + +DECLARE_STREAM_OPERATOR(const std::string&) +DECLARE_STREAM_OPERATOR(const char*) +DECLARE_STREAM_OPERATOR(char) +DECLARE_STREAM_OPERATOR(bool) +DECLARE_STREAM_OPERATOR(short) +DECLARE_STREAM_OPERATOR(int) +DECLARE_STREAM_OPERATOR(unsigned int) +DECLARE_STREAM_OPERATOR(long) +DECLARE_STREAM_OPERATOR(unsigned long) +DECLARE_STREAM_OPERATOR(long long) +DECLARE_STREAM_OPERATOR(unsigned long long) +DECLARE_STREAM_OPERATOR(float) +DECLARE_STREAM_OPERATOR(double) +DECLARE_STREAM_OPERATOR(long double) +DECLARE_STREAM_OPERATOR(void*) +#undef DECLARE_STREAM_OPERATOR + +void LogMessage::Finish() { + std::string message(message_.str()); + if (log_handler_ != nullptr) { + log_handler_->HandleMessage(level_, filename_, line_, message); + } else if (level_ == LOGLEVEL_FATAL) { + // Expect the log_handler_ to handle FATAL calls, but we should still fail + // as expected even if no log_handler_ is defined. For ease of debugging, + // we also print out the error statement. + std::cerr << filename_ << ":" << line_ << " FATAL: " << message + << std::endl; + std::abort(); + } +} + +void LogFinisher::operator=(LogMessage& other) { other.Finish(); } + +} // namespace internal + +void SetLogHandler(std::unique_ptr<LogHandler> new_handler) { + internal::log_handler_ = std::move(new_handler); +} + +void SetLogfileDirectory(const std::string& output_dir) { + if (internal::log_handler_ != nullptr) { + internal::log_handler_->SetOutputDir(output_dir); + } +} + +} // namespace blaze_util diff --git a/src/main/cpp/util/logging.h b/src/main/cpp/util/logging.h new file mode 100644 index 0000000000..506ed59eb7 --- /dev/null +++ b/src/main/cpp/util/logging.h @@ -0,0 +1,158 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// 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. +#ifndef BAZEL_SRC_MAIN_CPP_LOGGING_H_ +#define BAZEL_SRC_MAIN_CPP_LOGGING_H_ + +#include <memory> +#include <sstream> +#include <string> + +// This file is based off the logging work by the protobuf team in +// stubs/logging.h, +// +// Users of this logging library should use BAZEL_LOG(level) << ""; format, +// and specify how they wish to handle the output of the log messages by +// creating a LogHandler to pass to SetLogHandler(). +namespace blaze_util { + +enum LogLevel { + LOGLEVEL_INFO, + LOGLEVEL_WARNING, + LOGLEVEL_ERROR, + LOGLEVEL_FATAL, + +#ifdef NDEBUG + LOGLEVEL_DFATAL = LOGLEVEL_ERROR +#else + LOGLEVEL_DFATAL = LOGLEVEL_FATAL +#endif +}; + +// Returns a string representation of the log level. +const char* LogLevelName(LogLevel level); + +namespace internal { + +class LogFinisher; +class LogMessage { + public: + LogMessage(LogLevel level, const std::string& filename, int line); + + LogMessage& operator<<(const std::string& value); + LogMessage& operator<<(const char* value); + LogMessage& operator<<(char value); + LogMessage& operator<<(bool value); + LogMessage& operator<<(short value); + LogMessage& operator<<(int value); + LogMessage& operator<<(unsigned int value); + LogMessage& operator<<(long value); + LogMessage& operator<<(unsigned long value); + LogMessage& operator<<(long long value); + LogMessage& operator<<(unsigned long long value); + LogMessage& operator<<(float value); + LogMessage& operator<<(double value); + LogMessage& operator<<(long double value); + LogMessage& operator<<(void* value); + + private: + friend class LogFinisher; + void Finish(); + + LogLevel level_; + const std::string& filename_; + int line_; + std::stringstream message_; +}; + +// Used to make the entire "LOG(BLAH) << etc." expression have a void return +// type and print a newline after each message. +class LogFinisher { + public: + void operator=(LogMessage& other); +}; + +template <typename T> +bool IsOk(T status) { + return status.ok(); +} +template <> +inline bool IsOk(bool status) { + return status; +} + +} // namespace internal + +#define BAZEL_LOG(LEVEL) \ + ::blaze_util::internal::LogFinisher() = ::blaze_util::internal::LogMessage( \ + ::blaze_util::LOGLEVEL_##LEVEL, __FILE__, __LINE__) +#define BAZEL_LOG_IF(LEVEL, CONDITION) !(CONDITION) ? (void)0 : BAZEL_LOG(LEVEL) + +#define BAZEL_CHECK(EXPRESSION) \ + BAZEL_LOG_IF(FATAL, !(EXPRESSION)) << "CHECK failed: " #EXPRESSION ": " +#define BAZEL_CHECK_OK(A) BAZEL_CHECK(::blaze_util::internal::IsOk(A)) +#define BAZEL_CHECK_EQ(A, B) BAZEL_CHECK((A) == (B)) +#define BAZEL_CHECK_NE(A, B) BAZEL_CHECK((A) != (B)) +#define BAZEL_CHECK_LT(A, B) BAZEL_CHECK((A) < (B)) +#define BAZEL_CHECK_LE(A, B) BAZEL_CHECK((A) <= (B)) +#define BAZEL_CHECK_GT(A, B) BAZEL_CHECK((A) > (B)) +#define BAZEL_CHECK_GE(A, B) BAZEL_CHECK((A) >= (B)) + +#ifdef NDEBUG + +#define BAZEL_DLOG(LEVEL) BAZEL_LOG_IF(LEVEL, false) + +#define BAZEL_DCHECK(EXPRESSION) \ + while (false) BAZEL_CHECK(EXPRESSION) +#define BAZEL_DCHECK_OK(E) BAZEL_DCHECK(::blaze::internal::IsOk(E)) +#define BAZEL_DCHECK_EQ(A, B) BAZEL_DCHECK((A) == (B)) +#define BAZEL_DCHECK_NE(A, B) BAZEL_DCHECK((A) != (B)) +#define BAZEL_DCHECK_LT(A, B) BAZEL_DCHECK((A) < (B)) +#define BAZEL_DCHECK_LE(A, B) BAZEL_DCHECK((A) <= (B)) +#define BAZEL_DCHECK_GT(A, B) BAZEL_DCHECK((A) > (B)) +#define BAZEL_DCHECK_GE(A, B) BAZEL_DCHECK((A) >= (B)) + +#else // NDEBUG + +#define BAZEL_DLOG BAZEL_LOG + +#define BAZEL_DCHECK BAZEL_CHECK +#define BAZEL_DCHECK_OK BAZEL_CHECK_OK +#define BAZEL_DCHECK_EQ BAZEL_CHECK_EQ +#define BAZEL_DCHECK_NE BAZEL_CHECK_NE +#define BAZEL_DCHECK_LT BAZEL_CHECK_LT +#define BAZEL_DCHECK_LE BAZEL_CHECK_LE +#define BAZEL_DCHECK_GT BAZEL_CHECK_GT +#define BAZEL_DCHECK_GE BAZEL_CHECK_GE + +#endif // !NDEBUG + +class LogHandler { + public: + virtual ~LogHandler() {} + virtual void HandleMessage(LogLevel level, const std::string& filename, + int line, const std::string& message) = 0; + virtual void SetOutputDir(const std::string& output_base) = 0; +}; + +// Sets the log handler that routes all log messages. +// SetLogHandler is not thread-safe. You should only call it +// at initialization time, and probably not from library code. +void SetLogHandler(std::unique_ptr<LogHandler> new_handler); + +// Sets the current handler's output directory, given that the Handler cares. +void SetLogfileDirectory(const std::string& output_dir); + +} // namespace blaze_util + +#endif // BAZEL_SRC_MAIN_CPP_LOGGING_H_ diff --git a/src/test/cpp/util/BUILD b/src/test/cpp/util/BUILD index 60c057b3f4..033fca6268 100644 --- a/src/test/cpp/util/BUILD +++ b/src/test/cpp/util/BUILD @@ -32,6 +32,16 @@ cc_test( ) cc_test( + name = "logging_test", + srcs = ["logging_test.cc"], + deps = [ + "//src/main/cpp/util:bazel_log_handler", + "//src/main/cpp/util:logging", + "//third_party:gtest", + ], +) + +cc_test( name = "numbers_test", srcs = ["numbers_test.cc"], deps = [ diff --git a/src/test/cpp/util/logging_test.cc b/src/test/cpp/util/logging_test.cc new file mode 100644 index 0000000000..1f7c6cd585 --- /dev/null +++ b/src/test/cpp/util/logging_test.cc @@ -0,0 +1,70 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// 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 <iostream> +#include <memory> +#include <string> + +#include "src/main/cpp/util/bazel_log_handler.h" +#include "src/main/cpp/util/logging.h" +#include "gtest/gtest.h" + +namespace blaze_util { + +TEST(LoggingTest, BazelLogHandlerDumpsToCerrAtFail) { + // Set up logging and be prepared to capture stderr at destruction. + testing::internal::CaptureStderr(); + std::unique_ptr<blaze_util::BazelLogHandler> handler( + new blaze_util::BazelLogHandler()); + blaze_util::SetLogHandler(std::move(handler)); + + // Log something. + std::string teststring = "test that the log messages get dumped to stderr"; + BAZEL_LOG(INFO) << teststring; + + // Check that stderr isn't getting anything yet. + std::string nothing = testing::internal::GetCapturedStderr(); + ASSERT_TRUE(nothing.find(teststring) == std::string::npos); + testing::internal::CaptureStderr(); + + // Destruct the log handler and get the stderr remains. + blaze_util::SetLogHandler(nullptr); + std::string output = testing::internal::GetCapturedStderr(); + ASSERT_TRUE(output.find(teststring) != std::string::npos); +} + +TEST(LoggingTest, LogLevelNamesMatch) { + EXPECT_STREQ("INFO", LogLevelName(LOGLEVEL_INFO)); + EXPECT_STREQ("WARNING", LogLevelName(LOGLEVEL_WARNING)); + EXPECT_STREQ("ERROR", LogLevelName(LOGLEVEL_ERROR)); + EXPECT_STREQ("FATAL", LogLevelName(LOGLEVEL_FATAL)); +} + +TEST(LoggingTest, ImpossibleFile) { + // Set up to capture logging to stderr. + testing::internal::CaptureStderr(); + std::unique_ptr<blaze_util::BazelLogHandler> handler( + new blaze_util::BazelLogHandler()); + blaze_util::SetLogHandler(std::move(handler)); + + // Deliberately try to log to an impossible location, check that we error out. + blaze_util::SetLogfileDirectory("/this/doesnt/exist"); + + // Cause the logs to be flushed, and capture them. + blaze_util::SetLogHandler(nullptr); + std::string output = testing::internal::GetCapturedStderr(); + ASSERT_TRUE(output.find("ERROR") != std::string::npos); + ASSERT_TRUE(output.find("/this/doesnt/exist") != std::string::npos); +} + +} // namespace blaze_util |