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.
295 lines
9.1 KiB
295 lines
9.1 KiB
// |
|
// 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
|
|
|