xref: /aosp_15_r20/external/abseil-cpp/absl/log/internal/log_sink_set.cc (revision 9356374a3709195abf420251b3e825997ff56c0f)
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.
ThreadIsLoggingStatus()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 
Send(const absl::LogEntry & entry)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 
Send(const absl::LogEntry & entry)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:
AndroidLogLevel(const absl::LogEntry & entry)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 
Send(const absl::LogEntry & entry)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:
GlobalLogSinkSet()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 
LogToSinks(const absl::LogEntry & entry,absl::Span<absl::LogSink * > extra_sinks,bool extra_sinks_only)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 
AddLogSink(absl::LogSink * sink)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 
RemoveLogSink(absl::LogSink * sink)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 
FlushLogSinks()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:
FlushLogSinksLocked()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.
SendToSinks(const absl::LogEntry & entry,absl::Span<absl::LogSink * > sinks)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.
GlobalSinks()270 GlobalLogSinkSet& GlobalSinks() {
271   static absl::NoDestructor<GlobalLogSinkSet> global_sinks;
272   return *global_sinks;
273 }
274 
275 }  // namespace
276 
ThreadIsLoggingToLogSink()277 bool ThreadIsLoggingToLogSink() { return ThreadIsLoggingStatus(); }
278 
LogToSinks(const absl::LogEntry & entry,absl::Span<absl::LogSink * > extra_sinks,bool extra_sinks_only)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 
AddLogSink(absl::LogSink * sink)284 void AddLogSink(absl::LogSink* sink) {
285   log_internal::GlobalSinks().AddLogSink(sink);
286 }
287 
RemoveLogSink(absl::LogSink * sink)288 void RemoveLogSink(absl::LogSink* sink) {
289   log_internal::GlobalSinks().RemoveLogSink(sink);
290 }
291 
FlushLogSinks()292 void FlushLogSinks() { log_internal::GlobalSinks().FlushLogSinks(); }
293 
294 }  // namespace log_internal
295 ABSL_NAMESPACE_END
296 }  // namespace absl
297