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