• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/thread_annotations.h"
39 #include "absl/cleanup/cleanup.h"
40 #include "absl/log/globals.h"
41 #include "absl/log/internal/config.h"
42 #include "absl/log/internal/globals.h"
43 #include "absl/log/log_entry.h"
44 #include "absl/log/log_sink.h"
45 #include "absl/strings/string_view.h"
46 #include "absl/synchronization/mutex.h"
47 #include "absl/types/span.h"
48 
49 namespace absl {
50 ABSL_NAMESPACE_BEGIN
51 namespace log_internal {
52 namespace {
53 
54 // Returns a mutable reference to a thread-local variable that should be true if
55 // a globally-registered `LogSink`'s `Send()` is currently being invoked on this
56 // thread.
ThreadIsLoggingStatus()57 bool& ThreadIsLoggingStatus() {
58 #ifdef ABSL_HAVE_THREAD_LOCAL
59   ABSL_CONST_INIT thread_local bool thread_is_logging = false;
60   return thread_is_logging;
61 #else
62   ABSL_CONST_INIT static pthread_key_t thread_is_logging_key;
63   static const bool unused = [] {
64     if (pthread_key_create(&thread_is_logging_key, [](void* data) {
65           delete reinterpret_cast<bool*>(data);
66         })) {
67       perror("pthread_key_create failed!");
68       abort();
69     }
70     return true;
71   }();
72   (void)unused;  // Fixes -wunused-variable warning
73   bool* thread_is_logging_ptr =
74       reinterpret_cast<bool*>(pthread_getspecific(thread_is_logging_key));
75 
76   if (ABSL_PREDICT_FALSE(!thread_is_logging_ptr)) {
77     thread_is_logging_ptr = new bool{false};
78     if (pthread_setspecific(thread_is_logging_key, thread_is_logging_ptr)) {
79       perror("pthread_setspecific failed");
80       abort();
81     }
82   }
83   return *thread_is_logging_ptr;
84 #endif
85 }
86 
87 class StderrLogSink final : public LogSink {
88  public:
89   ~StderrLogSink() override = default;
90 
Send(const absl::LogEntry & entry)91   void Send(const absl::LogEntry& entry) override {
92     if (entry.log_severity() < absl::StderrThreshold() &&
93         absl::log_internal::IsInitialized()) {
94       return;
95     }
96 
97     ABSL_CONST_INIT static absl::once_flag warn_if_not_initialized;
98     absl::call_once(warn_if_not_initialized, []() {
99       if (absl::log_internal::IsInitialized()) return;
100       const char w[] =
101           "WARNING: All log messages before absl::InitializeLog() is called"
102           " are written to STDERR\n";
103       absl::log_internal::WriteToStderr(w, absl::LogSeverity::kWarning);
104     });
105 
106     if (!entry.stacktrace().empty()) {
107       absl::log_internal::WriteToStderr(entry.stacktrace(),
108                                         entry.log_severity());
109     } else {
110       // TODO(b/226937039): do this outside else condition once we avoid
111       // ReprintFatalMessage
112       absl::log_internal::WriteToStderr(
113           entry.text_message_with_prefix_and_newline(), entry.log_severity());
114     }
115   }
116 };
117 
118 #if defined(__ANDROID__)
119 class AndroidLogSink final : public LogSink {
120  public:
121   ~AndroidLogSink() override = default;
122 
Send(const absl::LogEntry & entry)123   void Send(const absl::LogEntry& entry) override {
124     const int level = AndroidLogLevel(entry);
125     // TODO(b/37587197): make the tag ("native") configurable.
126     __android_log_write(level, "native",
127                         entry.text_message_with_prefix_and_newline_c_str());
128     if (entry.log_severity() == absl::LogSeverity::kFatal)
129       __android_log_write(ANDROID_LOG_FATAL, "native", "terminating.\n");
130   }
131 
132  private:
AndroidLogLevel(const absl::LogEntry & entry)133   static int AndroidLogLevel(const absl::LogEntry& entry) {
134     switch (entry.log_severity()) {
135       case absl::LogSeverity::kFatal:
136         return ANDROID_LOG_FATAL;
137       case absl::LogSeverity::kError:
138         return ANDROID_LOG_ERROR;
139       case absl::LogSeverity::kWarning:
140         return ANDROID_LOG_WARN;
141       default:
142         if (entry.verbosity() >= 2) return ANDROID_LOG_VERBOSE;
143         if (entry.verbosity() == 1) return ANDROID_LOG_DEBUG;
144         return ANDROID_LOG_INFO;
145     }
146   }
147 };
148 #endif  // !defined(__ANDROID__)
149 
150 #if defined(_WIN32)
151 class WindowsDebuggerLogSink final : public LogSink {
152  public:
153   ~WindowsDebuggerLogSink() override = default;
154 
Send(const absl::LogEntry & entry)155   void Send(const absl::LogEntry& entry) override {
156     if (entry.log_severity() < absl::StderrThreshold() &&
157         absl::log_internal::IsInitialized()) {
158       return;
159     }
160     ::OutputDebugStringA(entry.text_message_with_prefix_and_newline_c_str());
161   }
162 };
163 #endif  // !defined(_WIN32)
164 
165 class GlobalLogSinkSet final {
166  public:
GlobalLogSinkSet()167   GlobalLogSinkSet() {
168 #if defined(__myriad2__) || defined(__Fuchsia__)
169     // myriad2 and Fuchsia do not log to stderr by default.
170 #else
171     static StderrLogSink* stderr_log_sink = new StderrLogSink;
172     AddLogSink(stderr_log_sink);
173 #endif
174 #ifdef __ANDROID__
175     static AndroidLogSink* android_log_sink = new AndroidLogSink;
176     AddLogSink(android_log_sink);
177 #endif
178 #if defined(_WIN32)
179     static WindowsDebuggerLogSink* debugger_log_sink =
180         new WindowsDebuggerLogSink;
181     AddLogSink(debugger_log_sink);
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 GlobalLogSinkSet* global_sinks = new GlobalLogSinkSet;
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