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