/Users/deen/code/yugabyte-db/src/yb/util/logging.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | // |
18 | | // The following only applies to changes made to this file as part of YugaByte development. |
19 | | // |
20 | | // Portions Copyright (c) YugaByte, Inc. |
21 | | // |
22 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
23 | | // in compliance with the License. You may obtain a copy of the License at |
24 | | // |
25 | | // http://www.apache.org/licenses/LICENSE-2.0 |
26 | | // |
27 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
28 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
29 | | // or implied. See the License for the specific language governing permissions and limitations |
30 | | // under the License. |
31 | | // |
32 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
33 | | // you may not use this file except in compliance with the License. |
34 | | // You may obtain a copy of the License at |
35 | | // |
36 | | // http://www.apache.org/licenses/LICENSE-2.0 |
37 | | // |
38 | | // Unless required by applicable law or agreed to in writing, software |
39 | | // distributed under the License is distributed on an "AS IS" BASIS, |
40 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
41 | | // See the License for the specific language governing permissions and |
42 | | // limitations under the License. |
43 | | |
44 | | #include "yb/util/logging.h" |
45 | | |
46 | | #include <signal.h> |
47 | | #include <stdio.h> |
48 | | |
49 | | #include <fstream> |
50 | | #include <iostream> |
51 | | #include <regex> |
52 | | |
53 | | #include <boost/uuid/uuid_generators.hpp> |
54 | | #include <boost/uuid/uuid_io.hpp> |
55 | | #include <glog/logging.h> |
56 | | |
57 | | #include "yb/gutil/callback.h" |
58 | | #include "yb/gutil/ref_counted.h" |
59 | | #include "yb/gutil/spinlock.h" |
60 | | |
61 | | #include "yb/util/debug-util.h" |
62 | | #include "yb/util/flag_tags.h" |
63 | | #include "yb/util/format.h" |
64 | | |
65 | | DEFINE_string(log_filename, "", |
66 | | "Prefix of log filename - " |
67 | | "full path is <log_dir>/<log_filename>.[INFO|WARN|ERROR|FATAL]"); |
68 | | TAG_FLAG(log_filename, stable); |
69 | | |
70 | | DEFINE_string(fatal_details_path_prefix, "", |
71 | | "A prefix to use for the path of a file to save fatal failure stack trace and " |
72 | | "other details to."); |
73 | | DEFINE_string(minicluster_daemon_id, "", |
74 | | "A human-readable 'daemon id', e.g. 'm-1' or 'ts-2', used in tests."); |
75 | | |
76 | | DEFINE_string(ref_counted_debug_type_name_regex, "", |
77 | | "Regex for type names for debugging RefCounted / scoped_refptr based classes. " |
78 | | "An empty string disables RefCounted debug logging."); |
79 | | |
80 | | const char* kProjName = "yb"; |
81 | | |
82 | | bool logging_initialized = false; |
83 | | |
84 | | using namespace std; // NOLINT(*) |
85 | | using namespace boost::uuids; // NOLINT(*) |
86 | | |
87 | | using base::SpinLockHolder; |
88 | | |
89 | | namespace yb { |
90 | | |
91 | | // We cannot initialize this inside a function that could be invoked for the first time in a signal |
92 | | // handler. |
93 | | const std::regex kStackTraceLineFormatRe(R"#(^\s*@\s+(0x[0-9a-f]+)\s+.*\n?$)#"); |
94 | | |
95 | | // Sink which implements special handling for LOG(FATAL) and CHECK failures, such as disabling |
96 | | // core dumps and printing the failure stack trace into a separate file. |
97 | | unique_ptr<LogFatalHandlerSink> log_fatal_handler_sink; |
98 | | |
99 | | namespace { |
100 | | |
101 | | class SimpleSink : public google::LogSink { |
102 | | public: |
103 | 0 | explicit SimpleSink(LoggingCallback cb) : cb_(std::move(cb)) {} |
104 | | |
105 | 0 | ~SimpleSink() override { |
106 | 0 | } |
107 | | |
108 | | virtual void send(google::LogSeverity severity, const char* full_filename, |
109 | | const char* base_filename, int line, |
110 | | const struct ::tm* tm_time, |
111 | 0 | const char* message, size_t message_len) override { |
112 | 0 | LogSeverity yb_severity; |
113 | 0 | switch (severity) { |
114 | 0 | case google::INFO: |
115 | 0 | yb_severity = SEVERITY_INFO; |
116 | 0 | break; |
117 | 0 | case google::WARNING: |
118 | 0 | yb_severity = SEVERITY_WARNING; |
119 | 0 | break; |
120 | 0 | case google::ERROR: |
121 | 0 | yb_severity = SEVERITY_ERROR; |
122 | 0 | break; |
123 | 0 | case google::FATAL: |
124 | 0 | yb_severity = SEVERITY_FATAL; |
125 | 0 | break; |
126 | 0 | default: |
127 | 0 | LOG(FATAL) << "Unknown glog severity: " << severity; |
128 | 0 | } |
129 | 0 | cb_.Run(yb_severity, full_filename, line, tm_time, message, message_len); |
130 | 0 | } |
131 | | |
132 | | private: |
133 | | |
134 | | LoggingCallback cb_; |
135 | | }; |
136 | | |
137 | | base::SpinLock logging_mutex(base::LINKER_INITIALIZED); |
138 | | |
139 | | // There can only be a single instance of a SimpleSink. |
140 | | // |
141 | | // Protected by 'logging_mutex'. |
142 | | SimpleSink* registered_sink = nullptr; |
143 | | |
144 | | // Records the logging severity after the first call to |
145 | | // InitGoogleLoggingSafe{Basic}. Calls to UnregisterLoggingCallback() |
146 | | // will restore stderr logging back to this severity level. |
147 | | // |
148 | | // Protected by 'logging_mutex'. |
149 | | int initial_stderr_severity; |
150 | | |
151 | 0 | void UnregisterLoggingCallbackUnlocked() { |
152 | 0 | CHECK(logging_mutex.IsHeld()); |
153 | 0 | CHECK(registered_sink); |
154 | | |
155 | | // Restore logging to stderr, then remove our sink. This ordering ensures |
156 | | // that no log messages are missed. |
157 | 0 | google::SetStderrLogging(initial_stderr_severity); |
158 | 0 | google::RemoveLogSink(registered_sink); |
159 | 0 | delete registered_sink; |
160 | 0 | registered_sink = nullptr; |
161 | 0 | } |
162 | | |
163 | 590 | void DumpStackTraceAndExit() { |
164 | 590 | const auto stack_trace = GetStackTrace(); |
165 | 590 | if (write(STDERR_FILENO, stack_trace.c_str(), stack_trace.length()) < 0) { |
166 | | // Ignore errors. |
167 | 18 | } |
168 | | |
169 | | // Set the default signal handler for SIGABRT, to avoid invoking our |
170 | | // own signal handler installed by InstallFailureSignalHandler(). |
171 | 590 | struct sigaction sig_action; |
172 | 590 | memset(&sig_action, 0, sizeof(sig_action)); |
173 | 590 | sigemptyset(&sig_action.sa_mask); |
174 | 590 | sig_action.sa_handler = SIG_DFL; |
175 | 590 | sigaction(SIGABRT, &sig_action, NULL); |
176 | | |
177 | 590 | abort(); |
178 | 590 | } |
179 | | |
180 | 137k | void CustomGlogFailureWriter(const char* data, int size) { |
181 | 137k | if (size == 0) { |
182 | 0 | return; |
183 | 0 | } |
184 | | |
185 | 137k | std::smatch match; |
186 | 137k | string line = string(data, size); |
187 | 137k | if (std::regex_match(line, match, kStackTraceLineFormatRe)) { |
188 | 99.5k | size_t pos; |
189 | 99.5k | uintptr_t addr = std::stoul(match[1], &pos, 16); |
190 | 99.5k | string symbolized_line = SymbolizeAddress(reinterpret_cast<void*>(addr)); |
191 | 99.5k | if (symbolized_line.find(':') != string::npos) { |
192 | | // Only replace the output line if we failed to find the line number. |
193 | 53.7k | line = symbolized_line; |
194 | 53.7k | } |
195 | 99.5k | } |
196 | | |
197 | 137k | if (write(STDERR_FILENO, line.data(), line.size()) < 0) { |
198 | | // Ignore errors. |
199 | 60.6k | } |
200 | 137k | } |
201 | | |
202 | | #ifndef NDEBUG |
203 | | void ReportRefCountedDebugEvent( |
204 | | const char* type_name, |
205 | | const void* this_ptr, |
206 | | int64_t current_refcount, |
207 | 0 | int64_t ref_delta) { |
208 | 0 | std::string demangled_type = DemangleName(type_name); |
209 | 0 | LOG(INFO) << demangled_type << "::" << (ref_delta == 1 ? "AddRef" : "Release") |
210 | 0 | << "(this=" << this_ptr << ", ref_count_=" << current_refcount << "):\n" |
211 | 0 | << GetStackTrace(StackTraceLineFormat::DEFAULT, 2); |
212 | 0 | } |
213 | | #endif |
214 | | |
215 | 31.0k | void ApplyFlagsInternal() { |
216 | 31.0k | #ifndef NDEBUG |
217 | 31.0k | subtle::InitRefCountedDebugging( |
218 | 31.0k | FLAGS_ref_counted_debug_type_name_regex, ReportRefCountedDebugEvent); |
219 | 31.0k | #endif |
220 | 31.0k | } |
221 | | |
222 | | } // anonymous namespace |
223 | | |
224 | 31.0k | void InitializeGoogleLogging(const char *arg) { |
225 | | // TODO: re-enable this when we make stack trace symbolization async-safe, which means we have |
226 | | // to get rid of memory allocations there. We also need to make sure that libbacktrace is |
227 | | // async-safe. |
228 | 31.0k | static constexpr bool kUseCustomFailureWriter = false; |
229 | 31.0k | if (kUseCustomFailureWriter) { |
230 | 0 | google::InstallFailureWriter(CustomGlogFailureWriter); |
231 | 0 | } |
232 | 31.0k | google::InitGoogleLogging(arg); |
233 | | |
234 | 31.0k | google::InstallFailureFunction(DumpStackTraceAndExit); |
235 | | |
236 | 31.0k | log_fatal_handler_sink = std::make_unique<LogFatalHandlerSink>(); |
237 | 31.0k | } |
238 | | |
239 | 14.7k | void InitGoogleLoggingSafe(const char* arg) { |
240 | 14.7k | SpinLockHolder l(&logging_mutex); |
241 | 14.7k | if (logging_initialized) return2 ; |
242 | | |
243 | 14.7k | google::InstallFailureWriter(CustomGlogFailureWriter); |
244 | 14.7k | google::InstallFailureSignalHandler(); |
245 | | |
246 | | // Set the logbuflevel to -1 so that all logs are printed out in unbuffered. |
247 | 14.7k | FLAGS_logbuflevel = -1; |
248 | | |
249 | 14.7k | if (!FLAGS_log_filename.empty()) { |
250 | 0 | for (int severity = google::INFO; severity <= google::FATAL; ++severity) { |
251 | 0 | google::SetLogSymlink(severity, FLAGS_log_filename.c_str()); |
252 | 0 | } |
253 | 0 | } |
254 | | |
255 | | // This forces our logging to use /tmp rather than looking for a |
256 | | // temporary directory if none is specified. This is done so that we |
257 | | // can reliably construct the log file name without duplicating the |
258 | | // complex logic that glog uses to guess at a temporary dir. |
259 | 14.7k | if (FLAGS_log_dir.empty()) { |
260 | 5 | FLAGS_log_dir = "/tmp"; |
261 | 5 | } |
262 | | |
263 | 14.7k | if (!FLAGS_logtostderr) { |
264 | | // Verify that a log file can be created in log_dir by creating a tmp file. |
265 | 3 | stringstream ss; |
266 | 3 | random_generator uuid_generator; |
267 | 3 | ss << FLAGS_log_dir << "/" << kProjName << "_test_log." << uuid_generator(); |
268 | 3 | const string file_name = ss.str(); |
269 | 3 | ofstream test_file(file_name.c_str()); |
270 | 3 | if (!test_file.is_open()) { |
271 | 0 | stringstream error_msg; |
272 | 0 | error_msg << "Could not open file in log_dir " << FLAGS_log_dir; |
273 | 0 | perror(error_msg.str().c_str()); |
274 | | // Unlock the mutex before exiting the program to avoid mutex d'tor assert. |
275 | 0 | logging_mutex.Unlock(); |
276 | 0 | exit(1); |
277 | 0 | } |
278 | 3 | remove(file_name.c_str()); |
279 | 3 | } |
280 | | |
281 | 14.7k | InitializeGoogleLogging(arg); |
282 | | |
283 | | // Needs to be done after InitGoogleLogging |
284 | 14.7k | if (FLAGS_log_filename.empty()) { |
285 | 14.7k | CHECK_STRNE(google::ProgramInvocationShortName(), "UNKNOWN") |
286 | 0 | << ": must initialize gflags before glog"; |
287 | 14.7k | FLAGS_log_filename = google::ProgramInvocationShortName(); |
288 | 14.7k | } |
289 | | |
290 | | // File logging: on. |
291 | | // Stderr logging threshold: FLAGS_stderrthreshold. |
292 | | // Sink logging: off. |
293 | 14.7k | initial_stderr_severity = FLAGS_stderrthreshold; |
294 | | |
295 | 14.7k | ApplyFlagsInternal(); |
296 | | |
297 | 14.7k | logging_initialized = true; |
298 | 14.7k | } |
299 | | |
300 | 16.4k | void InitGoogleLoggingSafeBasic(const char* arg) { |
301 | 16.4k | SpinLockHolder l(&logging_mutex); |
302 | 16.4k | if (logging_initialized) return175 ; |
303 | | |
304 | 16.2k | InitializeGoogleLogging(arg); |
305 | | |
306 | | // This also disables file-based logging. |
307 | 16.2k | google::LogToStderr(); |
308 | | |
309 | | // File logging: off. |
310 | | // Stderr logging threshold: INFO. |
311 | | // Sink logging: off. |
312 | 16.2k | initial_stderr_severity = google::INFO; |
313 | | |
314 | 16.2k | ApplyFlagsInternal(); |
315 | | |
316 | 16.2k | logging_initialized = true; |
317 | 16.2k | } |
318 | | |
319 | 0 | void InitGoogleLoggingSafeBasicSuppressNonNativePostgresLogs(const char* arg) { |
320 | 0 | SpinLockHolder l(&logging_mutex); |
321 | 0 | if (logging_initialized) return; |
322 | | |
323 | 0 | InitializeGoogleLogging(arg); |
324 | | |
325 | | // This also disables file-based logging. |
326 | 0 | google::LogToStderr(); |
327 | | |
328 | | // File logging: off. |
329 | | // Stderr logging threshold: NUM_SEVERITIES; |
330 | | // Sink logging: off. |
331 | | // Since Postgres logging collector collects logs from stderr, we never log to stderr. |
332 | 0 | FLAGS_stderrthreshold = google::NUM_SEVERITIES; |
333 | 0 | initial_stderr_severity = google::NUM_SEVERITIES; |
334 | |
|
335 | 0 | ApplyFlagsInternal(); |
336 | |
|
337 | 0 | logging_initialized = true; |
338 | 0 | } |
339 | | |
340 | 1 | bool IsLoggingInitialized() { |
341 | 1 | SpinLockHolder l(&logging_mutex); |
342 | 1 | return logging_initialized; |
343 | 1 | } |
344 | | |
345 | 0 | void RegisterLoggingCallback(const LoggingCallback& cb) { |
346 | 0 | SpinLockHolder l(&logging_mutex); |
347 | 0 | CHECK(logging_initialized); |
348 | |
|
349 | 0 | if (registered_sink) { |
350 | 0 | LOG(WARNING) << "Cannot register logging callback: one already registered"; |
351 | 0 | return; |
352 | 0 | } |
353 | | |
354 | | // AddLogSink() claims to take ownership of the sink, but it doesn't |
355 | | // really; it actually expects it to remain valid until |
356 | | // google::ShutdownGoogleLogging() is called. |
357 | 0 | registered_sink = new SimpleSink(cb); |
358 | 0 | google::AddLogSink(registered_sink); |
359 | | |
360 | | // Even when stderr logging is ostensibly off, it's still emitting |
361 | | // ERROR-level stuff. This is the default. |
362 | 0 | google::SetStderrLogging(google::ERROR); |
363 | | |
364 | | // File logging: yes, if InitGoogleLoggingSafe() was called earlier. |
365 | | // Stderr logging threshold: ERROR. |
366 | | // Sink logging: on. |
367 | 0 | } |
368 | | |
369 | 0 | void UnregisterLoggingCallback() { |
370 | 0 | SpinLockHolder l(&logging_mutex); |
371 | 0 | CHECK(logging_initialized); |
372 | |
|
373 | 0 | if (!registered_sink) { |
374 | 0 | LOG(WARNING) << "Cannot unregister logging callback: none registered"; |
375 | 0 | return; |
376 | 0 | } |
377 | | |
378 | 0 | UnregisterLoggingCallbackUnlocked(); |
379 | | // File logging: yes, if InitGoogleLoggingSafe() was called earlier. |
380 | | // Stderr logging threshold: initial_stderr_severity. |
381 | | // Sink logging: off. |
382 | 0 | } |
383 | | |
384 | 0 | void GetFullLogFilename(google::LogSeverity severity, string* filename) { |
385 | 0 | stringstream ss; |
386 | 0 | ss << FLAGS_log_dir << "/" << FLAGS_log_filename << "." |
387 | 0 | << google::GetLogSeverityName(severity); |
388 | 0 | *filename = ss.str(); |
389 | 0 | } |
390 | | |
391 | 0 | void ShutdownLoggingSafe() { |
392 | 0 | SpinLockHolder l(&logging_mutex); |
393 | 0 | if (!logging_initialized) return; |
394 | | |
395 | 0 | if (registered_sink) { |
396 | 0 | UnregisterLoggingCallbackUnlocked(); |
397 | 0 | } |
398 | |
|
399 | 0 | google::ShutdownGoogleLogging(); |
400 | |
|
401 | 0 | logging_initialized = false; |
402 | 0 | } |
403 | | |
404 | 0 | void LogCommandLineFlags() { |
405 | 0 | LOG(INFO) << "Flags (see also /varz are on debug webserver):" << endl |
406 | 0 | << google::CommandlineFlagsIntoString(); |
407 | 0 | } |
408 | | |
409 | | // Support for the special THROTTLE_MSG token in a log message stream. |
410 | 6.72k | ostream& operator<<(ostream &os, const PRIVATE_ThrottleMsg&) { |
411 | 6.72k | using google::LogMessage; |
412 | | #ifdef DISABLE_RTTI |
413 | | LogMessage::LogStream *log = static_cast<LogMessage::LogStream*>(&os); |
414 | | #else |
415 | 6.72k | LogMessage::LogStream *log = dynamic_cast<LogMessage::LogStream*>(&os); |
416 | 6.72k | #endif |
417 | 6.72k | CHECK(log && log == log->self()) |
418 | 5 | << "You must not use COUNTER with non-glog ostream"; |
419 | 6.72k | int ctr = log->ctr(); |
420 | 6.72k | if (ctr > 0) { |
421 | 2.88k | os << " [suppressed " << ctr << " similar messages]"; |
422 | 2.88k | } |
423 | 6.72k | return os; |
424 | 6.72k | } |
425 | | |
426 | 608 | void DisableCoreDumps() { |
427 | 608 | struct rlimit lim; |
428 | 608 | PCHECK(getrlimit(RLIMIT_CORE, &lim) == 0); |
429 | 608 | lim.rlim_cur = 0; |
430 | 608 | PCHECK(setrlimit(RLIMIT_CORE, &lim) == 0); |
431 | | |
432 | | // Set coredump_filter to not dump any parts of the address space. Although the above disables |
433 | | // core dumps to files, if core_pattern is set to a pipe rather than a file, it's not sufficient. |
434 | | // Setting this pattern results in piping a very minimal dump into the core processor (eg abrtd), |
435 | | // thus speeding up the crash. |
436 | 608 | int f = open("/proc/self/coredump_filter", O_WRONLY); |
437 | 608 | if (f >= 0) { |
438 | 0 | auto ret = write(f, "00000000", 8); |
439 | 0 | if (ret != 8) { |
440 | 0 | LOG(WARNING) << "Error writing to /proc/self/coredump_filter: " << strerror(errno); |
441 | 0 | } |
442 | 0 | close(f); |
443 | 0 | } |
444 | 608 | } |
445 | | |
446 | 4.65k | string GetFatalDetailsPathPrefix() { |
447 | 4.65k | if (!FLAGS_fatal_details_path_prefix.empty()) |
448 | 46 | return FLAGS_fatal_details_path_prefix; |
449 | | |
450 | 4.61k | const char* fatal_details_path_prefix_env_var = getenv("YB_FATAL_DETAILS_PATH_PREFIX"); |
451 | 4.61k | if (fatal_details_path_prefix_env_var) { |
452 | 4.61k | return fatal_details_path_prefix_env_var; |
453 | 4.61k | } |
454 | | |
455 | 0 | string fatal_log_path; |
456 | 0 | GetFullLogFilename(LogSeverity::SEVERITY_FATAL, &fatal_log_path); |
457 | 0 | if (!FLAGS_minicluster_daemon_id.empty()) { |
458 | 0 | fatal_log_path += "." + FLAGS_minicluster_daemon_id; |
459 | 0 | } |
460 | 0 | return fatal_log_path + ".details"; |
461 | 4.61k | } |
462 | | |
463 | | // ------------------------------------------------------------------------------------------------ |
464 | | // LogFatalHandlerSink |
465 | | // ------------------------------------------------------------------------------------------------ |
466 | | |
467 | 31.0k | LogFatalHandlerSink::LogFatalHandlerSink() { |
468 | 31.0k | AddLogSink(this); |
469 | 31.0k | } |
470 | | |
471 | 15.2k | LogFatalHandlerSink::~LogFatalHandlerSink() { |
472 | 15.2k | RemoveLogSink(this); |
473 | 15.2k | } |
474 | | |
475 | | void LogFatalHandlerSink::send( |
476 | | google::LogSeverity severity, const char* full_filename, const char* base_filename, |
477 | 38.5M | int line_number, const struct tm* tm_time, const char* message, size_t message_len) { |
478 | 38.5M | if (severity == LogSeverity::SEVERITY_FATAL) { |
479 | 599 | DisableCoreDumps(); |
480 | 599 | string timestamp_for_filename; |
481 | 599 | StringAppendStrftime(×tamp_for_filename, "%Y-%m-%dT%H_%M_%S", tm_time); |
482 | 599 | const string output_path = Format( |
483 | 599 | "$0.$1.pid$2.txt", GetFatalDetailsPathPrefix(), timestamp_for_filename, getpid()); |
484 | | // Use a line format similar to glog with a couple of slight differences: |
485 | | // - Report full file path. |
486 | | // - Time has no microsecond component. |
487 | 599 | string output_str = "F"; |
488 | 599 | StringAppendStrftime(&output_str, "%Y%m%d %H:%M:%S", tm_time); |
489 | | // TODO: append thread id if we need to. |
490 | 599 | StringAppendF(&output_str, " %s:%d] ", full_filename, line_number); |
491 | 599 | output_str += std::string(message, message_len); |
492 | 599 | output_str += "\n"; |
493 | 599 | output_str += GetStackTrace(); |
494 | | |
495 | 599 | ofstream out_f(output_path); |
496 | 599 | if (out_f) { |
497 | 231 | out_f << output_str << endl; |
498 | 231 | } |
499 | 599 | if (out_f.bad()) { |
500 | 0 | cerr << "Failed to write fatal failure details to " << output_path << endl; |
501 | 599 | } else { |
502 | 599 | cerr << "Fatal failure details written to " << output_path << endl; |
503 | 599 | } |
504 | | // Also output fatal failure details to stderr so make sure we have a properly symbolized stack |
505 | | // trace in the context of a test. |
506 | 599 | cerr << output_str << endl; |
507 | 599 | } |
508 | 38.5M | } |
509 | | |
510 | | namespace logging_internal { |
511 | | |
512 | 58.4k | bool LogRateThrottler::TooMany() { |
513 | 58.4k | const auto now = CoarseMonoClock::Now(); |
514 | 58.4k | const auto drop_limit = now - duration_; |
515 | 58.4k | const auto queue_size = queue_.size(); |
516 | 58.4k | std::lock_guard<std::mutex> lock(mutex_); |
517 | 74.2k | while (count_ > 0 && queue_[head_] < drop_limit74.0k ) { |
518 | 15.7k | ++head_; |
519 | 15.7k | if (head_ >= queue_size) { |
520 | 305 | head_ = 0; |
521 | 305 | } |
522 | 15.7k | --count_; |
523 | 15.7k | } |
524 | 58.4k | if (count_ == queue_size) { |
525 | 42.4k | return true; |
526 | 42.4k | } |
527 | 16.0k | auto tail = head_ + count_; |
528 | 16.0k | if (tail >= queue_size) { |
529 | 7.57k | tail -= queue_size; |
530 | 7.57k | } |
531 | 16.0k | queue_[tail] = now; |
532 | 16.0k | ++count_; |
533 | 16.0k | return false; |
534 | 58.4k | } |
535 | | |
536 | | } // namespace logging_internal |
537 | | |
538 | | } // namespace yb |