Coverage Report

Created: 2024-09-23 06:29

/src/abseil-cpp/absl/log/internal/log_sink_set.cc
Line
Count
Source (jump to first uncovered line)
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
15.1M
bool& ThreadIsLoggingStatus() {
59
15.1M
#ifdef ABSL_HAVE_THREAD_LOCAL
60
15.1M
  ABSL_CONST_INIT thread_local bool thread_is_logging = false;
61
15.1M
  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
15.1M
}
87
88
class StderrLogSink final : public LogSink {
89
 public:
90
  ~StderrLogSink() override = default;
91
92
3.79M
  void Send(const absl::LogEntry& entry) override {
93
3.79M
    if (entry.log_severity() < absl::StderrThreshold() &&
94
3.79M
        absl::log_internal::IsInitialized()) {
95
0
      return;
96
0
    }
97
98
3.79M
    ABSL_CONST_INIT static absl::once_flag warn_if_not_initialized;
99
3.79M
    absl::call_once(warn_if_not_initialized, []() {
100
1
      if (absl::log_internal::IsInitialized()) return;
101
1
      const char w[] =
102
1
          "WARNING: All log messages before absl::InitializeLog() is called"
103
1
          " are written to STDERR\n";
104
1
      absl::log_internal::WriteToStderr(w, absl::LogSeverity::kWarning);
105
1
    });
106
107
3.79M
    if (!entry.stacktrace().empty()) {
108
0
      absl::log_internal::WriteToStderr(entry.stacktrace(),
109
0
                                        entry.log_severity());
110
3.79M
    } else {
111
      // TODO(b/226937039): do this outside else condition once we avoid
112
      // ReprintFatalMessage
113
3.79M
      absl::log_internal::WriteToStderr(
114
3.79M
          entry.text_message_with_prefix_and_newline(), entry.log_severity());
115
3.79M
    }
116
3.79M
  }
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
1
  GlobalLogSinkSet() {
169
#if defined(__myriad2__) || defined(__Fuchsia__)
170
    // myriad2 and Fuchsia do not log to stderr by default.
171
#else
172
1
    static absl::NoDestructor<StderrLogSink> stderr_log_sink;
173
1
    AddLogSink(stderr_log_sink.get());
174
1
#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
1
  }
184
185
  void LogToSinks(const absl::LogEntry& entry,
186
                  absl::Span<absl::LogSink*> extra_sinks, bool extra_sinks_only)
187
3.79M
      ABSL_LOCKS_EXCLUDED(guard_) {
188
3.79M
    SendToSinks(entry, extra_sinks);
189
190
3.79M
    if (!extra_sinks_only) {
191
3.79M
      if (ThreadIsLoggingToLogSink()) {
192
0
        absl::log_internal::WriteToStderr(
193
0
            entry.text_message_with_prefix_and_newline(), entry.log_severity());
194
3.79M
      } else {
195
3.79M
        absl::ReaderMutexLock global_sinks_lock(&guard_);
196
3.79M
        ThreadIsLoggingStatus() = true;
197
        // Ensure the "thread is logging" status is reverted upon leaving the
198
        // scope even in case of exceptions.
199
3.79M
        auto status_cleanup =
200
3.79M
            absl::MakeCleanup([] { ThreadIsLoggingStatus() = false; });
201
3.79M
        SendToSinks(entry, absl::MakeSpan(sinks_));
202
3.79M
      }
203
3.79M
    }
204
3.79M
  }
205
206
1
  void AddLogSink(absl::LogSink* sink) ABSL_LOCKS_EXCLUDED(guard_) {
207
1
    {
208
1
      absl::WriterMutexLock global_sinks_lock(&guard_);
209
1
      auto pos = std::find(sinks_.begin(), sinks_.end(), sink);
210
1
      if (pos == sinks_.end()) {
211
1
        sinks_.push_back(sink);
212
1
        return;
213
1
      }
214
1
    }
215
0
    ABSL_INTERNAL_LOG(FATAL, "Duplicate log sinks are not supported");
216
0
  }
217
218
0
  void RemoveLogSink(absl::LogSink* sink) ABSL_LOCKS_EXCLUDED(guard_) {
219
0
    {
220
0
      absl::WriterMutexLock global_sinks_lock(&guard_);
221
0
      auto pos = std::find(sinks_.begin(), sinks_.end(), sink);
222
0
      if (pos != sinks_.end()) {
223
0
        sinks_.erase(pos);
224
0
        return;
225
0
      }
226
0
    }
227
0
    ABSL_INTERNAL_LOG(FATAL, "Mismatched log sink being removed");
228
0
  }
229
230
0
  void FlushLogSinks() ABSL_LOCKS_EXCLUDED(guard_) {
231
0
    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
0
      guard_.AssertReaderHeld();
236
0
      FlushLogSinksLocked();
237
0
    } else {
238
0
      absl::ReaderMutexLock global_sinks_lock(&guard_);
239
      // In case if LogSink::Flush overload decides to log
240
0
      ThreadIsLoggingStatus() = true;
241
      // Ensure the "thread is logging" status is reverted upon leaving the
242
      // scope even in case of exceptions.
243
0
      auto status_cleanup =
244
0
          absl::MakeCleanup([] { ThreadIsLoggingStatus() = false; });
245
0
      FlushLogSinksLocked();
246
0
    }
247
0
  }
248
249
 private:
250
0
  void FlushLogSinksLocked() ABSL_SHARED_LOCKS_REQUIRED(guard_) {
251
0
    for (absl::LogSink* sink : sinks_) {
252
0
      sink->Flush();
253
0
    }
254
0
  }
255
256
  // Helper routine for LogToSinks.
257
  static void SendToSinks(const absl::LogEntry& entry,
258
7.58M
                          absl::Span<absl::LogSink*> sinks) {
259
7.58M
    for (absl::LogSink* sink : sinks) {
260
3.79M
      sink->Send(entry);
261
3.79M
    }
262
7.58M
  }
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
3.79M
GlobalLogSinkSet& GlobalSinks() {
271
3.79M
  static absl::NoDestructor<GlobalLogSinkSet> global_sinks;
272
3.79M
  return *global_sinks;
273
3.79M
}
274
275
}  // namespace
276
277
7.58M
bool ThreadIsLoggingToLogSink() { return ThreadIsLoggingStatus(); }
278
279
void LogToSinks(const absl::LogEntry& entry,
280
3.79M
                absl::Span<absl::LogSink*> extra_sinks, bool extra_sinks_only) {
281
3.79M
  log_internal::GlobalSinks().LogToSinks(entry, extra_sinks, extra_sinks_only);
282
3.79M
}
283
284
0
void AddLogSink(absl::LogSink* sink) {
285
0
  log_internal::GlobalSinks().AddLogSink(sink);
286
0
}
287
288
0
void RemoveLogSink(absl::LogSink* sink) {
289
0
  log_internal::GlobalSinks().RemoveLogSink(sink);
290
0
}
291
292
0
void FlushLogSinks() { log_internal::GlobalSinks().FlushLogSinks(); }
293
294
}  // namespace log_internal
295
ABSL_NAMESPACE_END
296
}  // namespace absl