blob: fbb41b02498eed2aa26ec22610cc72ef6e902950 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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 "kudu/util/logging.h"
#include <unistd.h>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <fstream>
#include <functional>
#include <initializer_list>
#include <mutex>
#include <utility>
#include <boost/uuid/random_generator.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <gflags/gflags.h>
#include <glog/logging.h>
#include "kudu/gutil/basictypes.h"
#include "kudu/gutil/port.h"
#include "kudu/gutil/spinlock.h"
#include "kudu/gutil/stringprintf.h"
#include "kudu/gutil/strings/substitute.h"
#include "kudu/util/async_logger.h"
#include "kudu/util/debug-util.h"
#include "kudu/util/debug/leakcheck_disabler.h"
#include "kudu/util/env_util.h"
#include "kudu/util/flag_tags.h"
#include "kudu/util/logging_callback.h"
#include "kudu/util/minidump.h"
#include "kudu/util/signal.h"
#include "kudu/util/status.h"
DEFINE_string(log_filename, "",
"Prefix of log filename - "
"full path is <log_dir>/<log_filename>.[INFO|WARN|ERROR|FATAL]");
TAG_FLAG(log_filename, stable);
DEFINE_bool(log_async, true,
"Enable asynchronous writing to log files. This improves "
"latency and stability.");
TAG_FLAG(log_async, hidden);
DEFINE_int32(log_async_buffer_bytes_per_level, 2 * 1024 * 1024,
"The number of bytes of buffer space used by each log "
"level. Only relevant when --log_async is enabled.");
TAG_FLAG(log_async_buffer_bytes_per_level, hidden);
DEFINE_int32(max_log_files, 10,
"Maximum number of log files to retain per severity level. The most recent "
"log files are retained. If set to 0, all log files are retained.");
TAG_FLAG(max_log_files, runtime);
TAG_FLAG(max_log_files, stable);
#define PROJ_NAME "kudu"
bool logging_initialized = false;
using base::SpinLock;
using base::SpinLockHolder;
using boost::uuids::random_generator;
using std::string;
using std::ofstream;
using std::ostream;
using std::ostringstream;
namespace kudu {
__thread bool tls_redact_user_data = true;
kudu::RedactContext g_should_redact;
const char* const kRedactionMessage = "<redacted>";
namespace {
class SimpleSink : public google::LogSink {
public:
explicit SimpleSink(LoggingCallback cb) : cb_(std::move(cb)) {}
virtual ~SimpleSink() OVERRIDE {
}
virtual void send(google::LogSeverity severity, const char* full_filename,
const char* base_filename, int line,
const struct ::tm* tm_time,
const char* message, size_t message_len) OVERRIDE {
LogSeverity kudu_severity;
switch (severity) {
case google::INFO:
kudu_severity = SEVERITY_INFO;
break;
case google::WARNING:
kudu_severity = SEVERITY_WARNING;
break;
case google::ERROR:
kudu_severity = SEVERITY_ERROR;
break;
case google::FATAL:
kudu_severity = SEVERITY_FATAL;
break;
default:
LOG(FATAL) << "Unknown glog severity: " << severity;
}
cb_(kudu_severity, full_filename, line, tm_time, message, message_len);
}
private:
LoggingCallback cb_;
};
SpinLock logging_mutex(base::LINKER_INITIALIZED);
// There can only be a single instance of a SimpleSink.
//
// Protected by 'logging_mutex'.
SimpleSink* registered_sink = nullptr;
// Records the logging severity after the first call to
// InitGoogleLoggingSafe{Basic}. Calls to UnregisterLoggingCallback()
// will restore stderr logging back to this severity level.
//
// Protected by 'logging_mutex'.
int initial_stderr_severity;
void EnableAsyncLogging() {
debug::ScopedLeakCheckDisabler leaky;
// Enable Async for every level except for FATAL. Fatal should be synchronous
// to ensure that we get the fatal log message written before exiting.
for (auto level : { google::INFO, google::WARNING, google::ERROR }) {
auto* orig = google::base::GetLogger(level);
auto* async = new AsyncLogger(orig, FLAGS_log_async_buffer_bytes_per_level);
async->Start();
google::base::SetLogger(level, async);
}
}
void UnregisterLoggingCallbackUnlocked() {
CHECK(logging_mutex.IsHeld());
CHECK(registered_sink);
// Restore logging to stderr, then remove our sink. This ordering ensures
// that no log messages are missed.
google::SetStderrLogging(initial_stderr_severity);
google::RemoveLogSink(registered_sink);
delete registered_sink;
registered_sink = nullptr;
}
void FlushCoverageOnExit() {
// Coverage flushing is not re-entrant, but this might be called from a
// crash signal context, so avoid re-entrancy.
static __thread bool in_call = false;
if (in_call) return;
in_call = true;
// The failure writer will be called multiple times per exit.
// We only need to flush coverage once. We use a 'once' here so that,
// if another thread is already flushing, we'll block and wait for them
// to finish before allowing this thread to call abort().
static std::once_flag once;
std::call_once(once, [] {
static const char msg[] = "Flushing coverage data before crash...\n";
ignore_result(write(STDERR_FILENO, msg, arraysize(msg)));
TryFlushCoverage();
});
in_call = false;
}
// On SEGVs, etc, glog will call this function to write the error to stderr. This
// implementation is copied from glog with the exception that we also flush coverage
// the first time it's called.
//
// NOTE: this is only used in coverage builds!
void FailureWriterWithCoverage(const char* data, size_t size) {
FlushCoverageOnExit();
// Original implementation from glog:
if (write(STDERR_FILENO, data, size) < 0) {
// Ignore errors.
}
}
// GLog "failure function". This is called in the case of LOG(FATAL) to
// ensure that we flush coverage even on crashes.
//
// NOTE: this is only used in coverage builds!
void FlushCoverageAndAbort() {
FlushCoverageOnExit();
exit(1);
}
} // anonymous namespace
void InitGoogleLoggingSafe(const char* arg) {
SpinLockHolder l(&logging_mutex);
if (logging_initialized) return;
google::InstallFailureSignalHandler();
if (!FLAGS_log_filename.empty()) {
for (int severity = google::INFO; severity <= google::FATAL; ++severity) {
google::SetLogSymlink(severity, FLAGS_log_filename.c_str());
}
}
// This forces our logging to default to /tmp rather than looking for a
// temporary directory if none is specified. This is done so that we
// can reliably construct the log file name without duplicating the
// complex logic that glog uses to guess at a temporary dir.
CHECK_NE("", google::SetCommandLineOptionWithMode("log_dir",
"/tmp", google::FlagSettingMode::SET_FLAGS_DEFAULT));
if (!FLAGS_logtostderr) {
// Verify that a log file can be created in log_dir by creating a tmp file.
ostringstream ss;
random_generator uuid_generator;
ss << FLAGS_log_dir << "/" << PROJ_NAME "_test_log." << uuid_generator();
const string file_name = ss.str();
ofstream test_file(file_name.c_str());
if (!test_file.is_open()) {
ostringstream error_msg;
error_msg << "Could not open file in log_dir " << FLAGS_log_dir;
perror(error_msg.str().c_str());
// Unlock the mutex before exiting the program to avoid mutex d'tor assert.
logging_mutex.Unlock();
exit(1);
}
remove(file_name.c_str());
}
google::InitGoogleLogging(arg);
// In coverage builds, we should flush coverage before exiting on crash.
// This way, fault injection tests still capture coverage of the daemon
// that "crashed".
if (IsCoverageBuild()) {
// We have to use both the "failure writer" and the "FailureFunction".
// This allows us to handle both LOG(FATAL) and unintended crashes like
// SEGVs.
google::InstallFailureWriter(FailureWriterWithCoverage);
google::InstallFailureFunction(
reinterpret_cast<google::logging_fail_func_t>(FlushCoverageAndAbort));
}
// Needs to be done after InitGoogleLogging
if (FLAGS_log_filename.empty()) {
CHECK_STRNE(google::ProgramInvocationShortName(), "UNKNOWN")
<< ": must initialize gflags before glog";
FLAGS_log_filename = google::ProgramInvocationShortName();
}
// File logging: on.
// Stderr logging threshold: FLAGS_stderrthreshold.
// Sink logging: off.
initial_stderr_severity = FLAGS_stderrthreshold;
// Ignore SIGPIPE early in the startup process so that threads writing to TLS
// sockets do not crash when writing to a closed socket. See KUDU-1910.
IgnoreSigPipe();
// For minidump support. Must be called before logging threads started.
CHECK_OK(BlockSigUSR1());
if (FLAGS_log_async) {
EnableAsyncLogging();
}
logging_initialized = true;
}
void InitGoogleLoggingSafeBasic(const char* arg) {
SpinLockHolder l(&logging_mutex);
if (logging_initialized) return;
google::InitGoogleLogging(arg);
// This also disables file-based logging.
google::LogToStderr();
// File logging: off.
// Stderr logging threshold: INFO.
// Sink logging: off.
initial_stderr_severity = google::INFO;
logging_initialized = true;
}
void RegisterLoggingCallback(const LoggingCallback& cb) {
SpinLockHolder l(&logging_mutex);
CHECK(logging_initialized);
if (registered_sink) {
LOG(WARNING) << "Cannot register logging callback: one already registered";
return;
}
// AddLogSink() claims to take ownership of the sink, but it doesn't
// really; it actually expects it to remain valid until
// google::ShutdownGoogleLogging() is called.
registered_sink = new SimpleSink(cb);
google::AddLogSink(registered_sink);
// Even when stderr logging is ostensibly off, it's still emitting
// ERROR-level stuff. This is the default.
google::SetStderrLogging(google::ERROR);
// File logging: yes, if InitGoogleLoggingSafe() was called earlier.
// Stderr logging threshold: ERROR.
// Sink logging: on.
}
void UnregisterLoggingCallback() {
SpinLockHolder l(&logging_mutex);
CHECK(logging_initialized);
if (!registered_sink) {
LOG(WARNING) << "Cannot unregister logging callback: none registered";
return;
}
UnregisterLoggingCallbackUnlocked();
// File logging: yes, if InitGoogleLoggingSafe() was called earlier.
// Stderr logging threshold: initial_stderr_severity.
// Sink logging: off.
}
void GetFullLogFilename(google::LogSeverity severity, string* filename) {
ostringstream ss;
ss << FLAGS_log_dir << "/" << FLAGS_log_filename << "."
<< google::GetLogSeverityName(severity);
*filename = ss.str();
}
std::string FormatTimestampForLog(MicrosecondsInt64 micros_since_epoch) {
time_t secs_since_epoch = micros_since_epoch / 1000000;
size_t usecs = micros_since_epoch % 1000000;
struct tm tm_time;
localtime_r(&secs_since_epoch, &tm_time);
return StringPrintf("%02d%02d %02d:%02d:%02d.%06ld",
1 + tm_time.tm_mon,
tm_time.tm_mday,
tm_time.tm_hour,
tm_time.tm_min,
tm_time.tm_sec,
usecs);
}
void ShutdownLoggingSafe() {
SpinLockHolder l(&logging_mutex);
if (!logging_initialized) return;
if (registered_sink) {
UnregisterLoggingCallbackUnlocked();
}
google::ShutdownGoogleLogging();
logging_initialized = false;
}
Status DeleteExcessLogFiles(Env* env) {
int32_t max_log_files = FLAGS_max_log_files;
// Ignore bad input or disable log rotation.
if (max_log_files <= 0) return Status::OK();
for (int severity = 0; severity < google::NUM_SEVERITIES; ++severity) {
// Build glob pattern for input
// e.g. /var/log/kudu/kudu-master.*.INFO.*
string pattern = strings::Substitute("$0/$1.*.$2.*", FLAGS_log_dir, FLAGS_log_filename,
google::GetLogSeverityName(severity));
// Keep the 'max_log_files' most recent log files, as compared by
// modification time. Glog files contain a second-granularity timestamp in
// the name, so this could potentially use the filename sort order as
// guaranteed by glob, however this code has been adapted from Impala which
// uses mtime to determine which files to delete, and there haven't been any
// issues in production settings.
RETURN_NOT_OK(env_util::DeleteExcessFilesByPattern(env, pattern, max_log_files));
}
return Status::OK();
}
// Support for the special THROTTLE_MSG token in a log message stream.
ostream& operator<<(ostream &os, const PRIVATE_ThrottleMsg& /*unused*/) {
using google::LogMessage;
#ifdef DISABLE_RTTI
LogMessage::LogStream *log = static_cast<LogMessage::LogStream*>(&os);
#else
LogMessage::LogStream *log = dynamic_cast<LogMessage::LogStream*>(&os);
#endif
CHECK(log && log == log->self())
<< "You must not use COUNTER with non-glog ostream";
size_t ctr = log->ctr();
if (ctr > 0) {
os << " [suppressed " << ctr << " similar messages]";
}
return os;
}
} // namespace kudu