YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/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
190
void DumpStackTraceAndExit() {
164
190
  const auto stack_trace = GetStackTrace();
165
190
  if (write(STDERR_FILENO, stack_trace.c_str(), stack_trace.length()) < 0) {
166
    // Ignore errors.
167
7
  }
168
169
  // Set the default signal handler for SIGABRT, to avoid invoking our
170
  // own signal handler installed by InstallFailureSignalHandler().
171
190
  struct sigaction sig_action;
172
190
  memset(&sig_action, 0, sizeof(sig_action));
173
190
  sigemptyset(&sig_action.sa_mask);
174
190
  sig_action.sa_handler = SIG_DFL;
175
190
  sigaction(SIGABRT, &sig_action, NULL);
176
177
190
  abort();
178
190
}
179
180
100k
void CustomGlogFailureWriter(const char* data, int size) {
181
100k
  if (size == 0) {
182
0
    return;
183
0
  }
184
185
100k
  std::smatch match;
186
100k
  string line = string(data, size);
187
100k
  if (std::regex_match(line, match, kStackTraceLineFormatRe)) {
188
73.1k
    size_t pos;
189
73.1k
    uintptr_t addr = std::stoul(match[1], &pos, 16);
190
73.1k
    string symbolized_line = SymbolizeAddress(reinterpret_cast<void*>(addr));
191
73.1k
    if (symbolized_line.find(':') != string::npos) {
192
      // Only replace the output line if we failed to find the line number.
193
39.9k
      line = symbolized_line;
194
39.9k
    }
195
73.1k
  }
196
197
100k
  if (write(STDERR_FILENO, line.data(), line.size()) < 0) {
198
    // Ignore errors.
199
40.7k
  }
200
100k
}
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
17.9k
void ApplyFlagsInternal() {
216
17.9k
#ifndef NDEBUG
217
17.9k
  subtle::InitRefCountedDebugging(
218
17.9k
      FLAGS_ref_counted_debug_type_name_regex, ReportRefCountedDebugEvent);
219
17.9k
#endif
220
17.9k
}
221
222
} // anonymous namespace
223
224
17.9k
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
17.9k
  static constexpr bool kUseCustomFailureWriter = false;
229
17.9k
  if (kUseCustomFailureWriter) {
230
0
    google::InstallFailureWriter(CustomGlogFailureWriter);
231
0
  }
232
17.9k
  google::InitGoogleLogging(arg);
233
234
17.9k
  google::InstallFailureFunction(DumpStackTraceAndExit);
235
236
17.9k
  log_fatal_handler_sink = std::make_unique<LogFatalHandlerSink>();
237
17.9k
}
238
239
10.3k
void InitGoogleLoggingSafe(const char* arg) {
240
10.3k
  SpinLockHolder l(&logging_mutex);
241
10.3k
  if (logging_initialized) return;
242
243
10.3k
  google::InstallFailureWriter(CustomGlogFailureWriter);
244
10.3k
  google::InstallFailureSignalHandler();
245
246
  // Set the logbuflevel to -1 so that all logs are printed out in unbuffered.
247
10.3k
  FLAGS_logbuflevel = -1;
248
249
10.3k
  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
10.3k
  if (FLAGS_log_dir.empty()) {
260
2
    FLAGS_log_dir = "/tmp";
261
2
  }
262
263
10.3k
  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
10.3k
  InitializeGoogleLogging(arg);
282
283
  // Needs to be done after InitGoogleLogging
284
10.3k
  if (FLAGS_log_filename.empty()) {
285
0
    CHECK_STRNE(google::ProgramInvocationShortName(), "UNKNOWN")
286
0
        << ": must initialize gflags before glog";
287
10.3k
    FLAGS_log_filename = google::ProgramInvocationShortName();
288
10.3k
  }
289
290
  // File logging: on.
291
  // Stderr logging threshold: FLAGS_stderrthreshold.
292
  // Sink logging: off.
293
10.3k
  initial_stderr_severity = FLAGS_stderrthreshold;
294
295
10.3k
  ApplyFlagsInternal();
296
297
10.3k
  logging_initialized = true;
298
10.3k
}
299
300
7.63k
void InitGoogleLoggingSafeBasic(const char* arg) {
301
7.63k
  SpinLockHolder l(&logging_mutex);
302
7.63k
  if (logging_initialized) return;
303
304
7.63k
  InitializeGoogleLogging(arg);
305
306
  // This also disables file-based logging.
307
7.63k
  google::LogToStderr();
308
309
  // File logging: off.
310
  // Stderr logging threshold: INFO.
311
  // Sink logging: off.
312
7.63k
  initial_stderr_severity = google::INFO;
313
314
7.63k
  ApplyFlagsInternal();
315
316
7.63k
  logging_initialized = true;
317
7.63k
}
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
0
bool IsLoggingInitialized() {
341
0
  SpinLockHolder l(&logging_mutex);
342
0
  return logging_initialized;
343
0
}
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
3.81k
ostream& operator<<(ostream &os, const PRIVATE_ThrottleMsg&) {
411
3.81k
  using google::LogMessage;
412
#ifdef DISABLE_RTTI
413
  LogMessage::LogStream *log = static_cast<LogMessage::LogStream*>(&os);
414
#else
415
3.81k
  LogMessage::LogStream *log = dynamic_cast<LogMessage::LogStream*>(&os);
416
3.81k
#endif
417
0
  CHECK(log && log == log->self())
418
0
      << "You must not use COUNTER with non-glog ostream";
419
3.81k
  int ctr = log->ctr();
420
3.81k
  if (ctr > 0) {
421
1.18k
    os << " [suppressed " << ctr << " similar messages]";
422
1.18k
  }
423
3.81k
  return os;
424
3.81k
}
425
426
207
void DisableCoreDumps() {
427
207
  struct rlimit lim;
428
207
  PCHECK(getrlimit(RLIMIT_CORE, &lim) == 0);
429
207
  lim.rlim_cur = 0;
430
207
  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
207
  int f = open("/proc/self/coredump_filter", O_WRONLY);
437
207
  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
207
}
445
446
2.34k
string GetFatalDetailsPathPrefix() {
447
2.34k
  if (!FLAGS_fatal_details_path_prefix.empty())
448
30
    return FLAGS_fatal_details_path_prefix;
449
450
2.31k
  const char* fatal_details_path_prefix_env_var = getenv("YB_FATAL_DETAILS_PATH_PREFIX");
451
2.31k
  if (fatal_details_path_prefix_env_var) {
452
2.31k
    return fatal_details_path_prefix_env_var;
453
2.31k
  }
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
0
}
462
463
// ------------------------------------------------------------------------------------------------
464
// LogFatalHandlerSink
465
// ------------------------------------------------------------------------------------------------
466
467
17.9k
LogFatalHandlerSink::LogFatalHandlerSink() {
468
17.9k
  AddLogSink(this);
469
17.9k
}
470
471
7.17k
LogFatalHandlerSink::~LogFatalHandlerSink() {
472
7.17k
  RemoveLogSink(this);
473
7.17k
}
474
475
void LogFatalHandlerSink::send(
476
    google::LogSeverity severity, const char* full_filename, const char* base_filename,
477
18.6M
    int line_number, const struct tm* tm_time, const char* message, size_t message_len) {
478
18.6M
  if (severity == LogSeverity::SEVERITY_FATAL) {
479
196
    DisableCoreDumps();
480
196
    string timestamp_for_filename;
481
196
    StringAppendStrftime(&timestamp_for_filename, "%Y-%m-%dT%H_%M_%S", tm_time);
482
196
    const string output_path = Format(
483
196
        "$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
196
    string output_str = "F";
488
196
    StringAppendStrftime(&output_str, "%Y%m%d %H:%M:%S", tm_time);
489
    // TODO: append thread id if we need to.
490
196
    StringAppendF(&output_str, " %s:%d] ", full_filename, line_number);
491
196
    output_str += std::string(message, message_len);
492
196
    output_str += "\n";
493
196
    output_str += GetStackTrace();
494
495
196
    ofstream out_f(output_path);
496
196
    if (out_f) {
497
121
      out_f << output_str << endl;
498
121
    }
499
196
    if (out_f.bad()) {
500
0
      cerr << "Failed to write fatal failure details to " << output_path << endl;
501
196
    } else {
502
196
      cerr << "Fatal failure details written to " << output_path << endl;
503
196
    }
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
196
    cerr << output_str << endl;
507
196
  }
508
18.6M
}
509
510
namespace logging_internal {
511
512
49.6k
bool LogRateThrottler::TooMany() {
513
49.6k
  const auto now = CoarseMonoClock::Now();
514
49.6k
  const auto drop_limit = now - duration_;
515
49.6k
  const auto queue_size = queue_.size();
516
49.6k
  std::lock_guard<std::mutex> lock(mutex_);
517
59.8k
  while (count_ > 0 && queue_[head_] < drop_limit) {
518
10.1k
    ++head_;
519
10.1k
    if (head_ >= queue_size) {
520
200
      head_ = 0;
521
200
    }
522
10.1k
    --count_;
523
10.1k
  }
524
49.6k
  if (count_ == queue_size) {
525
39.4k
    return true;
526
39.4k
  }
527
10.2k
  auto tail = head_ + count_;
528
10.2k
  if (tail >= queue_size) {
529
6.36k
    tail -= queue_size;
530
6.36k
  }
531
10.2k
  queue_[tail] = now;
532
10.2k
  ++count_;
533
10.2k
  return false;
534
10.2k
}
535
536
} // namespace logging_internal
537
538
} // namespace yb