log_sink_set.cc (9363B)
1 // 2 // Copyright 2022 The Abseil Authors. 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // https://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 #include "absl/log/internal/log_sink_set.h" 17 18 #ifndef ABSL_HAVE_THREAD_LOCAL 19 #include <pthread.h> 20 #endif 21 22 #ifdef __ANDROID__ 23 #include <android/log.h> 24 #endif 25 26 #ifdef _WIN32 27 #include <windows.h> 28 #endif 29 30 #include <algorithm> 31 #include <vector> 32 33 #include "absl/base/attributes.h" 34 #include "absl/base/call_once.h" 35 #include "absl/base/config.h" 36 #include "absl/base/internal/raw_logging.h" 37 #include "absl/base/log_severity.h" 38 #include "absl/base/no_destructor.h" 39 #include "absl/base/thread_annotations.h" 40 #include "absl/cleanup/cleanup.h" 41 #include "absl/log/globals.h" 42 #include "absl/log/internal/config.h" 43 #include "absl/log/internal/globals.h" 44 #include "absl/log/log_entry.h" 45 #include "absl/log/log_sink.h" 46 #include "absl/strings/string_view.h" 47 #include "absl/synchronization/mutex.h" 48 #include "absl/types/span.h" 49 50 namespace absl { 51 ABSL_NAMESPACE_BEGIN 52 namespace log_internal { 53 namespace { 54 55 // Returns a mutable reference to a thread-local variable that should be true if 56 // a globally-registered `LogSink`'s `Send()` is currently being invoked on this 57 // thread. 58 bool& ThreadIsLoggingStatus() { 59 #ifdef ABSL_HAVE_THREAD_LOCAL 60 ABSL_CONST_INIT thread_local bool thread_is_logging = false; 61 return thread_is_logging; 62 #else 63 ABSL_CONST_INIT static pthread_key_t thread_is_logging_key; 64 static const bool unused = [] { 65 if (pthread_key_create(&thread_is_logging_key, [](void* data) { 66 delete reinterpret_cast<bool*>(data); 67 })) { 68 perror("pthread_key_create failed!"); 69 abort(); 70 } 71 return true; 72 }(); 73 (void)unused; // Fixes -wunused-variable warning 74 bool* thread_is_logging_ptr = 75 reinterpret_cast<bool*>(pthread_getspecific(thread_is_logging_key)); 76 77 if (ABSL_PREDICT_FALSE(!thread_is_logging_ptr)) { 78 thread_is_logging_ptr = new bool{false}; 79 if (pthread_setspecific(thread_is_logging_key, thread_is_logging_ptr)) { 80 perror("pthread_setspecific failed"); 81 abort(); 82 } 83 } 84 return *thread_is_logging_ptr; 85 #endif 86 } 87 88 class StderrLogSink final : public LogSink { 89 public: 90 ~StderrLogSink() override = default; 91 92 void Send(const absl::LogEntry& entry) override { 93 if (entry.log_severity() < absl::StderrThreshold() && 94 absl::log_internal::IsInitialized()) { 95 return; 96 } 97 98 ABSL_CONST_INIT static absl::once_flag warn_if_not_initialized; 99 absl::call_once(warn_if_not_initialized, []() { 100 if (absl::log_internal::IsInitialized()) return; 101 const char w[] = 102 "WARNING: All log messages before absl::InitializeLog() is called" 103 " are written to STDERR\n"; 104 absl::log_internal::WriteToStderr(w, absl::LogSeverity::kWarning); 105 }); 106 107 if (!entry.stacktrace().empty()) { 108 absl::log_internal::WriteToStderr(entry.stacktrace(), 109 entry.log_severity()); 110 } else { 111 // TODO(b/226937039): do this outside else condition once we avoid 112 // ReprintFatalMessage 113 absl::log_internal::WriteToStderr( 114 entry.text_message_with_prefix_and_newline(), entry.log_severity()); 115 } 116 } 117 }; 118 119 #if defined(__ANDROID__) 120 class AndroidLogSink final : public LogSink { 121 public: 122 ~AndroidLogSink() override = default; 123 124 void Send(const absl::LogEntry& entry) override { 125 const int level = AndroidLogLevel(entry); 126 const char* const tag = GetAndroidNativeTag(); 127 __android_log_write(level, tag, 128 entry.text_message_with_prefix_and_newline_c_str()); 129 if (entry.log_severity() == absl::LogSeverity::kFatal) 130 __android_log_write(ANDROID_LOG_FATAL, tag, "terminating.\n"); 131 } 132 133 private: 134 static int AndroidLogLevel(const absl::LogEntry& entry) { 135 switch (entry.log_severity()) { 136 case absl::LogSeverity::kFatal: 137 return ANDROID_LOG_FATAL; 138 case absl::LogSeverity::kError: 139 return ANDROID_LOG_ERROR; 140 case absl::LogSeverity::kWarning: 141 return ANDROID_LOG_WARN; 142 default: 143 if (entry.verbosity() >= 2) return ANDROID_LOG_VERBOSE; 144 if (entry.verbosity() == 1) return ANDROID_LOG_DEBUG; 145 return ANDROID_LOG_INFO; 146 } 147 } 148 }; 149 #endif // !defined(__ANDROID__) 150 151 #if defined(_WIN32) 152 class WindowsDebuggerLogSink final : public LogSink { 153 public: 154 ~WindowsDebuggerLogSink() override = default; 155 156 void Send(const absl::LogEntry& entry) override { 157 if (entry.log_severity() < absl::StderrThreshold() && 158 absl::log_internal::IsInitialized()) { 159 return; 160 } 161 ::OutputDebugStringA(entry.text_message_with_prefix_and_newline_c_str()); 162 } 163 }; 164 #endif // !defined(_WIN32) 165 166 class GlobalLogSinkSet final { 167 public: 168 GlobalLogSinkSet() { 169 #if defined(__myriad2__) || defined(__Fuchsia__) 170 // myriad2 and Fuchsia do not log to stderr by default. 171 #else 172 static absl::NoDestructor<StderrLogSink> stderr_log_sink; 173 AddLogSink(stderr_log_sink.get()); 174 #endif 175 #ifdef __ANDROID__ 176 static absl::NoDestructor<AndroidLogSink> android_log_sink; 177 AddLogSink(android_log_sink.get()); 178 #endif 179 #if defined(_WIN32) 180 static absl::NoDestructor<WindowsDebuggerLogSink> debugger_log_sink; 181 AddLogSink(debugger_log_sink.get()); 182 #endif // !defined(_WIN32) 183 } 184 185 void LogToSinks(const absl::LogEntry& entry, 186 absl::Span<absl::LogSink*> extra_sinks, bool extra_sinks_only) 187 ABSL_LOCKS_EXCLUDED(guard_) { 188 SendToSinks(entry, extra_sinks); 189 190 if (!extra_sinks_only) { 191 if (ThreadIsLoggingToLogSink()) { 192 absl::log_internal::WriteToStderr( 193 entry.text_message_with_prefix_and_newline(), entry.log_severity()); 194 } else { 195 absl::ReaderMutexLock global_sinks_lock(&guard_); 196 ThreadIsLoggingStatus() = true; 197 // Ensure the "thread is logging" status is reverted upon leaving the 198 // scope even in case of exceptions. 199 auto status_cleanup = 200 absl::MakeCleanup([] { ThreadIsLoggingStatus() = false; }); 201 SendToSinks(entry, absl::MakeSpan(sinks_)); 202 } 203 } 204 } 205 206 void AddLogSink(absl::LogSink* sink) ABSL_LOCKS_EXCLUDED(guard_) { 207 { 208 absl::WriterMutexLock global_sinks_lock(&guard_); 209 auto pos = std::find(sinks_.begin(), sinks_.end(), sink); 210 if (pos == sinks_.end()) { 211 sinks_.push_back(sink); 212 return; 213 } 214 } 215 ABSL_INTERNAL_LOG(FATAL, "Duplicate log sinks are not supported"); 216 } 217 218 void RemoveLogSink(absl::LogSink* sink) ABSL_LOCKS_EXCLUDED(guard_) { 219 { 220 absl::WriterMutexLock global_sinks_lock(&guard_); 221 auto pos = std::find(sinks_.begin(), sinks_.end(), sink); 222 if (pos != sinks_.end()) { 223 sinks_.erase(pos); 224 return; 225 } 226 } 227 ABSL_INTERNAL_LOG(FATAL, "Mismatched log sink being removed"); 228 } 229 230 void FlushLogSinks() ABSL_LOCKS_EXCLUDED(guard_) { 231 if (ThreadIsLoggingToLogSink()) { 232 // The thread_local condition demonstrates that we're already holding the 233 // lock in order to iterate over `sinks_` for dispatch. The thread-safety 234 // annotations don't know this, so we use `ABSL_NO_THREAD_SAFETY_ANALYSIS` 235 guard_.AssertReaderHeld(); 236 FlushLogSinksLocked(); 237 } else { 238 absl::ReaderMutexLock global_sinks_lock(&guard_); 239 // In case if LogSink::Flush overload decides to log 240 ThreadIsLoggingStatus() = true; 241 // Ensure the "thread is logging" status is reverted upon leaving the 242 // scope even in case of exceptions. 243 auto status_cleanup = 244 absl::MakeCleanup([] { ThreadIsLoggingStatus() = false; }); 245 FlushLogSinksLocked(); 246 } 247 } 248 249 private: 250 void FlushLogSinksLocked() ABSL_SHARED_LOCKS_REQUIRED(guard_) { 251 for (absl::LogSink* sink : sinks_) { 252 sink->Flush(); 253 } 254 } 255 256 // Helper routine for LogToSinks. 257 static void SendToSinks(const absl::LogEntry& entry, 258 absl::Span<absl::LogSink*> sinks) { 259 for (absl::LogSink* sink : sinks) { 260 sink->Send(entry); 261 } 262 } 263 264 using LogSinksSet = std::vector<absl::LogSink*>; 265 absl::Mutex guard_; 266 LogSinksSet sinks_ ABSL_GUARDED_BY(guard_); 267 }; 268 269 // Returns reference to the global LogSinks set. 270 GlobalLogSinkSet& GlobalSinks() { 271 static absl::NoDestructor<GlobalLogSinkSet> global_sinks; 272 return *global_sinks; 273 } 274 275 } // namespace 276 277 bool ThreadIsLoggingToLogSink() { return ThreadIsLoggingStatus(); } 278 279 void LogToSinks(const absl::LogEntry& entry, 280 absl::Span<absl::LogSink*> extra_sinks, bool extra_sinks_only) { 281 log_internal::GlobalSinks().LogToSinks(entry, extra_sinks, extra_sinks_only); 282 } 283 284 void AddLogSink(absl::LogSink* sink) { 285 log_internal::GlobalSinks().AddLogSink(sink); 286 } 287 288 void RemoveLogSink(absl::LogSink* sink) { 289 log_internal::GlobalSinks().RemoveLogSink(sink); 290 } 291 292 void FlushLogSinks() { log_internal::GlobalSinks().FlushLogSinks(); } 293 294 } // namespace log_internal 295 ABSL_NAMESPACE_END 296 } // namespace absl