env_logger.h 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. //
  6. // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
  7. // Use of this source code is governed by a BSD-style license that can be
  8. // found in the LICENSE file. See the AUTHORS file for names of contributors.
  9. //
  10. // Logger implementation that uses custom Env object for logging.
  11. #pragma once
  12. #include <time.h>
  13. #include <atomic>
  14. #include <memory>
  15. #include "file/writable_file_writer.h"
  16. #include "monitoring/iostats_context_imp.h"
  17. #include "port/sys_time.h"
  18. #include "rocksdb/env.h"
  19. #include "rocksdb/file_system.h"
  20. #include "rocksdb/perf_level.h"
  21. #include "rocksdb/slice.h"
  22. #include "test_util/sync_point.h"
  23. #include "util/mutexlock.h"
  24. namespace ROCKSDB_NAMESPACE {
  25. class EnvLogger : public Logger {
  26. public:
  27. EnvLogger(std::unique_ptr<FSWritableFile>&& writable_file,
  28. const std::string& fname, const EnvOptions& options, Env* env,
  29. InfoLogLevel log_level = InfoLogLevel::ERROR_LEVEL)
  30. : Logger(log_level),
  31. env_(env),
  32. clock_(env_->GetSystemClock().get()),
  33. file_(std::move(writable_file), fname, options, clock_),
  34. last_flush_micros_(0),
  35. flush_pending_(false) {}
  36. ~EnvLogger() {
  37. if (!closed_) {
  38. closed_ = true;
  39. CloseHelper().PermitUncheckedError();
  40. }
  41. }
  42. private:
  43. // A guard to prepare file operations, such as mutex and skip
  44. // I/O context.
  45. class FileOpGuard {
  46. public:
  47. explicit FileOpGuard(EnvLogger& logger)
  48. : logger_(logger), prev_perf_level_(GetPerfLevel()) {
  49. // Preserve iostats not to pollute writes from user writes. We might
  50. // need a better solution than this.
  51. SetPerfLevel(PerfLevel::kDisable);
  52. IOSTATS_SET_DISABLE(true);
  53. logger.mutex_.Lock();
  54. }
  55. ~FileOpGuard() {
  56. logger_.mutex_.Unlock();
  57. IOSTATS_SET_DISABLE(false);
  58. SetPerfLevel(prev_perf_level_);
  59. }
  60. private:
  61. EnvLogger& logger_;
  62. PerfLevel prev_perf_level_;
  63. };
  64. void FlushLocked() {
  65. mutex_.AssertHeld();
  66. if (flush_pending_) {
  67. flush_pending_ = false;
  68. file_.Flush(IOOptions()).PermitUncheckedError();
  69. file_.reset_seen_error();
  70. }
  71. last_flush_micros_ = clock_->NowMicros();
  72. }
  73. void Flush() override {
  74. TEST_SYNC_POINT("EnvLogger::Flush:Begin1");
  75. TEST_SYNC_POINT("EnvLogger::Flush:Begin2");
  76. FileOpGuard guard(*this);
  77. FlushLocked();
  78. }
  79. Status CloseImpl() override { return CloseHelper(); }
  80. Status CloseHelper() {
  81. FileOpGuard guard(*this);
  82. const auto close_status = file_.Close(IOOptions());
  83. if (close_status.ok()) {
  84. return close_status;
  85. }
  86. return Status::IOError("Close of log file failed with error:" +
  87. (close_status.getState()
  88. ? std::string(close_status.getState())
  89. : std::string()));
  90. }
  91. using Logger::Logv;
  92. void Logv(const char* format, va_list ap) override {
  93. IOSTATS_TIMER_GUARD(logger_nanos);
  94. const uint64_t thread_id = env_->GetThreadID();
  95. // We try twice: the first time with a fixed-size stack allocated buffer,
  96. // and the second time with a much larger dynamically allocated buffer.
  97. char buffer[500];
  98. for (int iter = 0; iter < 2; iter++) {
  99. char* base;
  100. int bufsize;
  101. if (iter == 0) {
  102. bufsize = sizeof(buffer);
  103. base = buffer;
  104. } else {
  105. bufsize = 65536;
  106. base = new char[bufsize];
  107. }
  108. char* p = base;
  109. char* limit = base + bufsize;
  110. port::TimeVal now_tv;
  111. port::GetTimeOfDay(&now_tv, nullptr);
  112. const time_t seconds = now_tv.tv_sec;
  113. struct tm t;
  114. port::LocalTimeR(&seconds, &t);
  115. p += snprintf(p, limit - p, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llu ",
  116. t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour,
  117. t.tm_min, t.tm_sec, static_cast<int>(now_tv.tv_usec),
  118. static_cast<long long unsigned int>(thread_id));
  119. // Print the message
  120. if (p < limit) {
  121. va_list backup_ap;
  122. va_copy(backup_ap, ap);
  123. p += vsnprintf(p, limit - p, format, backup_ap);
  124. va_end(backup_ap);
  125. }
  126. // Truncate to available space if necessary
  127. if (p >= limit) {
  128. if (iter == 0) {
  129. continue; // Try again with larger buffer
  130. } else {
  131. p = limit - 1;
  132. }
  133. }
  134. // Add newline if necessary
  135. if (p == base || p[-1] != '\n') {
  136. *p++ = '\n';
  137. }
  138. assert(p <= limit);
  139. {
  140. FileOpGuard guard(*this);
  141. // We will ignore any error returned by Append().
  142. file_.Append(IOOptions(), Slice(base, p - base)).PermitUncheckedError();
  143. file_.reset_seen_error();
  144. flush_pending_ = true;
  145. const uint64_t now_micros = clock_->NowMicros();
  146. if (now_micros - last_flush_micros_ >= flush_every_seconds_ * 1000000) {
  147. FlushLocked();
  148. }
  149. }
  150. if (base != buffer) {
  151. delete[] base;
  152. }
  153. break;
  154. }
  155. }
  156. size_t GetLogFileSize() const override {
  157. MutexLock l(&mutex_);
  158. return file_.GetFileSize();
  159. }
  160. private:
  161. Env* env_;
  162. SystemClock* clock_;
  163. WritableFileWriter file_;
  164. mutable port::Mutex mutex_; // Mutex to protect the shared variables below.
  165. const static uint64_t flush_every_seconds_ = 5;
  166. std::atomic_uint_fast64_t last_flush_micros_;
  167. std::atomic<bool> flush_pending_;
  168. };
  169. } // namespace ROCKSDB_NAMESPACE