Abseil Common Libraries (C++) (grcp 依赖)
https://abseil.io/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
296 lines
9.1 KiB
296 lines
9.1 KiB
2 years ago
|
//
|
||
|
// Copyright 2022 The Abseil Authors.
|
||
|
//
|
||
|
// 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
|
||
|
//
|
||
|
// https://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 "absl/log/internal/log_sink_set.h"
|
||
|
|
||
|
#ifndef ABSL_HAVE_THREAD_LOCAL
|
||
|
#include <pthread.h>
|
||
|
#endif
|
||
|
|
||
|
#ifdef __ANDROID__
|
||
|
#include <android/log.h>
|
||
|
#endif
|
||
|
|
||
|
#ifdef _WIN32
|
||
|
#include <windows.h>
|
||
|
#endif
|
||
|
|
||
|
#include <algorithm>
|
||
|
#include <vector>
|
||
|
|
||
|
#include "absl/base/attributes.h"
|
||
|
#include "absl/base/call_once.h"
|
||
|
#include "absl/base/config.h"
|
||
|
#include "absl/base/internal/raw_logging.h"
|
||
|
#include "absl/base/log_severity.h"
|
||
|
#include "absl/base/thread_annotations.h"
|
||
|
#include "absl/cleanup/cleanup.h"
|
||
|
#include "absl/log/globals.h"
|
||
|
#include "absl/log/internal/config.h"
|
||
|
#include "absl/log/internal/globals.h"
|
||
|
#include "absl/log/log_entry.h"
|
||
|
#include "absl/log/log_sink.h"
|
||
|
#include "absl/strings/string_view.h"
|
||
|
#include "absl/synchronization/mutex.h"
|
||
|
#include "absl/types/span.h"
|
||
|
|
||
|
namespace absl {
|
||
|
ABSL_NAMESPACE_BEGIN
|
||
|
namespace log_internal {
|
||
|
namespace {
|
||
|
|
||
|
// Returns a mutable reference to a thread-local variable that should be true if
|
||
|
// a globally-registered `LogSink`'s `Send()` is currently being invoked on this
|
||
|
// thread.
|
||
|
bool& ThreadIsLoggingStatus() {
|
||
|
#ifdef ABSL_HAVE_THREAD_LOCAL
|
||
|
ABSL_CONST_INIT thread_local bool thread_is_logging = false;
|
||
|
return thread_is_logging;
|
||
|
#else
|
||
|
ABSL_CONST_INIT static pthread_key_t thread_is_logging_key;
|
||
|
static const bool unused = [] {
|
||
|
if (pthread_key_create(&thread_is_logging_key, [](void* data) {
|
||
|
delete reinterpret_cast<bool*>(data);
|
||
|
})) {
|
||
|
perror("pthread_key_create failed!");
|
||
|
abort();
|
||
|
}
|
||
|
return true;
|
||
|
}();
|
||
|
bool* thread_is_logging_ptr =
|
||
|
reinterpret_cast<bool*>(pthread_getspecific(thread_is_logging_key));
|
||
|
|
||
|
if (ABSL_PREDICT_FALSE(!thread_is_logging_ptr)) {
|
||
|
thread_is_logging_ptr = new bool{false};
|
||
|
if (pthread_setspecific(thread_is_logging_key, thread_is_logging_ptr)) {
|
||
|
perror("pthread_setspecific failed");
|
||
|
abort();
|
||
|
}
|
||
|
}
|
||
|
return *thread_is_logging_ptr;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
class StderrLogSink final : public LogSink {
|
||
|
public:
|
||
|
~StderrLogSink() override = default;
|
||
|
|
||
|
void Send(const absl::LogEntry& entry) override {
|
||
|
if (entry.log_severity() < absl::StderrThreshold() &&
|
||
|
absl::log_internal::IsInitialized()) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ABSL_CONST_INIT static absl::once_flag warn_if_not_initialized;
|
||
|
absl::call_once(warn_if_not_initialized, []() {
|
||
|
if (absl::log_internal::IsInitialized()) return;
|
||
|
const char w[] =
|
||
|
"WARNING: All log messages before absl::InitializeLog() is called"
|
||
|
" are written to STDERR\n";
|
||
|
absl::log_internal::WriteToStderr(w, absl::LogSeverity::kWarning);
|
||
|
});
|
||
|
|
||
|
if (!entry.stacktrace().empty()) {
|
||
|
absl::log_internal::WriteToStderr(entry.stacktrace(),
|
||
|
entry.log_severity());
|
||
|
} else {
|
||
|
// TODO(b/226937039): do this outside else condition once we avoid
|
||
|
// ReprintFatalMessage
|
||
|
absl::log_internal::WriteToStderr(
|
||
|
entry.text_message_with_prefix_and_newline(), entry.log_severity());
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
#if defined(__ANDROID__)
|
||
|
class AndroidLogSink final : public LogSink {
|
||
|
public:
|
||
|
~AndroidLogSink() override = default;
|
||
|
|
||
|
void Send(const absl::LogEntry& entry) override {
|
||
|
const int level = AndroidLogLevel(entry);
|
||
|
// TODO(b/37587197): make the tag ("native") configurable.
|
||
|
__android_log_write(level, "native",
|
||
|
entry.text_message_with_prefix_and_newline_c_str());
|
||
|
if (entry.log_severity() == absl::LogSeverity::kFatal)
|
||
|
__android_log_write(ANDROID_LOG_FATAL, "native", "terminating.\n");
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
static int AndroidLogLevel(const absl::LogEntry& entry) {
|
||
|
switch (entry.log_severity()) {
|
||
|
case absl::LogSeverity::kFatal:
|
||
|
return ANDROID_LOG_FATAL;
|
||
|
case absl::LogSeverity::kError:
|
||
|
return ANDROID_LOG_ERROR;
|
||
|
case absl::LogSeverity::kWarning:
|
||
|
return ANDROID_LOG_WARN;
|
||
|
default:
|
||
|
if (entry.verbosity() >= 2) return ANDROID_LOG_VERBOSE;
|
||
|
if (entry.verbosity() == 1) return ANDROID_LOG_DEBUG;
|
||
|
return ANDROID_LOG_INFO;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
#endif // !defined(__ANDROID__)
|
||
|
|
||
|
#if defined(_WIN32)
|
||
|
class WindowsDebuggerLogSink final : public LogSink {
|
||
|
public:
|
||
|
~WindowsDebuggerLogSink() override = default;
|
||
|
|
||
|
void Send(const absl::LogEntry& entry) override {
|
||
|
if (entry.log_severity() < absl::StderrThreshold() &&
|
||
|
absl::log_internal::IsInitialized()) {
|
||
|
return;
|
||
|
}
|
||
|
::OutputDebugStringA(entry.text_message_with_prefix_and_newline_c_str());
|
||
|
}
|
||
|
};
|
||
|
#endif // !defined(_WIN32)
|
||
|
|
||
|
class GlobalLogSinkSet final {
|
||
|
public:
|
||
|
GlobalLogSinkSet() {
|
||
|
#if defined(__myriad2__) || defined(__Fuchsia__)
|
||
|
// myriad2 and Fuchsia do not log to stderr by default.
|
||
|
#else
|
||
|
static StderrLogSink* stderr_log_sink = new StderrLogSink;
|
||
|
AddLogSink(stderr_log_sink);
|
||
|
#endif
|
||
|
#ifdef __ANDROID__
|
||
|
static AndroidLogSink* android_log_sink = new AndroidLogSink;
|
||
|
AddLogSink(android_log_sink);
|
||
|
#endif
|
||
|
#if defined(_WIN32)
|
||
|
static WindowsDebuggerLogSink* debugger_log_sink =
|
||
|
new WindowsDebuggerLogSink;
|
||
|
AddLogSink(debugger_log_sink);
|
||
|
#endif // !defined(_WIN32)
|
||
|
}
|
||
|
|
||
|
void LogToSinks(const absl::LogEntry& entry,
|
||
|
absl::Span<absl::LogSink*> extra_sinks, bool extra_sinks_only)
|
||
|
ABSL_LOCKS_EXCLUDED(guard_) {
|
||
|
SendToSinks(entry, extra_sinks);
|
||
|
|
||
|
if (!extra_sinks_only) {
|
||
|
if (ThreadIsLoggingToLogSink()) {
|
||
|
absl::log_internal::WriteToStderr(
|
||
|
entry.text_message_with_prefix_and_newline(), entry.log_severity());
|
||
|
} else {
|
||
|
absl::ReaderMutexLock global_sinks_lock(&guard_);
|
||
|
ThreadIsLoggingStatus() = true;
|
||
|
// Ensure the "thread is logging" status is reverted upon leaving the
|
||
|
// scope even in case of exceptions.
|
||
|
auto status_cleanup =
|
||
|
absl::MakeCleanup([] { ThreadIsLoggingStatus() = false; });
|
||
|
SendToSinks(entry, absl::MakeSpan(sinks_));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void AddLogSink(absl::LogSink* sink) ABSL_LOCKS_EXCLUDED(guard_) {
|
||
|
{
|
||
|
absl::WriterMutexLock global_sinks_lock(&guard_);
|
||
|
auto pos = std::find(sinks_.begin(), sinks_.end(), sink);
|
||
|
if (pos == sinks_.end()) {
|
||
|
sinks_.push_back(sink);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
ABSL_INTERNAL_LOG(FATAL, "Duplicate log sinks are not supported");
|
||
|
}
|
||
|
|
||
|
void RemoveLogSink(absl::LogSink* sink) ABSL_LOCKS_EXCLUDED(guard_) {
|
||
|
{
|
||
|
absl::WriterMutexLock global_sinks_lock(&guard_);
|
||
|
auto pos = std::find(sinks_.begin(), sinks_.end(), sink);
|
||
|
if (pos != sinks_.end()) {
|
||
|
sinks_.erase(pos);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
ABSL_INTERNAL_LOG(FATAL, "Mismatched log sink being removed");
|
||
|
}
|
||
|
|
||
|
void FlushLogSinks() ABSL_LOCKS_EXCLUDED(guard_) {
|
||
|
if (ThreadIsLoggingToLogSink()) {
|
||
|
// The thread_local condition demonstrates that we're already holding the
|
||
|
// lock in order to iterate over `sinks_` for dispatch. The thread-safety
|
||
|
// annotations don't know this, so we use `ABSL_NO_THREAD_SAFETY_ANALYSIS`
|
||
|
guard_.AssertReaderHeld();
|
||
|
FlushLogSinksLocked();
|
||
|
} else {
|
||
|
absl::ReaderMutexLock global_sinks_lock(&guard_);
|
||
|
// In case if LogSink::Flush overload decides to log
|
||
|
ThreadIsLoggingStatus() = true;
|
||
|
// Ensure the "thread is logging" status is reverted upon leaving the
|
||
|
// scope even in case of exceptions.
|
||
|
auto status_cleanup =
|
||
|
absl::MakeCleanup([] { ThreadIsLoggingStatus() = false; });
|
||
|
FlushLogSinksLocked();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
void FlushLogSinksLocked() ABSL_SHARED_LOCKS_REQUIRED(guard_) {
|
||
|
for (absl::LogSink* sink : sinks_) {
|
||
|
sink->Flush();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Helper routine for LogToSinks.
|
||
|
static void SendToSinks(const absl::LogEntry& entry,
|
||
|
absl::Span<absl::LogSink*> sinks) {
|
||
|
for (absl::LogSink* sink : sinks) {
|
||
|
sink->Send(entry);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
using LogSinksSet = std::vector<absl::LogSink*>;
|
||
|
absl::Mutex guard_;
|
||
|
LogSinksSet sinks_ ABSL_GUARDED_BY(guard_);
|
||
|
};
|
||
|
|
||
|
// Returns reference to the global LogSinks set.
|
||
|
GlobalLogSinkSet& GlobalSinks() {
|
||
|
static GlobalLogSinkSet* global_sinks = new GlobalLogSinkSet;
|
||
|
return *global_sinks;
|
||
|
}
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
bool ThreadIsLoggingToLogSink() { return ThreadIsLoggingStatus(); }
|
||
|
|
||
|
void LogToSinks(const absl::LogEntry& entry,
|
||
|
absl::Span<absl::LogSink*> extra_sinks, bool extra_sinks_only) {
|
||
|
log_internal::GlobalSinks().LogToSinks(entry, extra_sinks, extra_sinks_only);
|
||
|
}
|
||
|
|
||
|
void AddLogSink(absl::LogSink* sink) {
|
||
|
log_internal::GlobalSinks().AddLogSink(sink);
|
||
|
}
|
||
|
|
||
|
void RemoveLogSink(absl::LogSink* sink) {
|
||
|
log_internal::GlobalSinks().RemoveLogSink(sink);
|
||
|
}
|
||
|
|
||
|
void FlushLogSinks() { log_internal::GlobalSinks().FlushLogSinks(); }
|
||
|
|
||
|
} // namespace log_internal
|
||
|
ABSL_NAMESPACE_END
|
||
|
} // namespace absl
|