YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/rocksdb/db/auto_roll_logger.cc
Line
Count
Source (jump to first uncovered line)
1
//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2
//  This source code is licensed under the BSD-style license found in the
3
//  LICENSE file in the root directory of this source tree. An additional grant
4
//  of patent rights can be found in the PATENTS file in the same directory.
5
//
6
// The following only applies to changes made to this file as part of YugaByte development.
7
//
8
// Portions Copyright (c) YugaByte, Inc.
9
//
10
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
11
// in compliance with the License.  You may obtain a copy of the License at
12
//
13
// http://www.apache.org/licenses/LICENSE-2.0
14
//
15
// Unless required by applicable law or agreed to in writing, software distributed under the License
16
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
17
// or implied.  See the License for the specific language governing permissions and limitations
18
// under the License.
19
//
20
21
#include "yb/rocksdb/db/auto_roll_logger.h"
22
23
#include "yb/rocksdb/util/mutexlock.h"
24
25
#include "yb/util/path_util.h"
26
#include "yb/util/status_log.h"
27
28
using std::string;
29
30
namespace rocksdb {
31
32
AutoRollLogger::AutoRollLogger(Env* env, const std::string& dbname,
33
               const std::string& db_log_dir, size_t log_max_size,
34
               size_t log_file_time_to_roll,
35
               const InfoLogLevel log_level)
36
    : Logger(log_level),
37
      dbname_(dbname),
38
      db_log_dir_(db_log_dir),
39
      env_(env),
40
      status_(Status::OK()),
41
      kMaxLogFileSize(log_max_size),
42
      kLogFileTimeToRoll(log_file_time_to_roll),
43
      cached_now(static_cast<uint64_t>(env_->NowMicros() * 1e-6)),
44
      ctime_(cached_now),
45
      cached_now_access_count(0),
46
      call_NowMicros_every_N_records_(100),
47
13
      mutex_() {
48
13
  CHECK_OK(env->GetAbsolutePath(dbname, &db_absolute_path_));
49
13
  log_fname_ = InfoLogFileName(dbname_, db_absolute_path_, db_log_dir_);
50
13
  RollLogFile();
51
13
  CHECK_OK(ResetLogger());
52
13
}
53
54
// -- AutoRollLogger
55
26
Status AutoRollLogger::ResetLogger() {
56
26
  TEST_SYNC_POINT("AutoRollLogger::ResetLogger:BeforeNewLogger");
57
26
  status_ = env_->NewLogger(log_fname_, &logger_);
58
26
  TEST_SYNC_POINT("AutoRollLogger::ResetLogger:AfterNewLogger");
59
60
26
  if (!status_.ok()) {
61
0
    return status_;
62
0
  }
63
64
26
  if (logger_->GetLogFileSize() == Logger::kDoNotSupportGetLogFileSize) {
65
0
    status_ = STATUS(NotSupported,
66
0
        "The underlying logger doesn't support GetLogFileSize()");
67
0
  }
68
26
  if (status_.ok()) {
69
26
    cached_now = static_cast<uint64_t>(env_->NowMicros() * 1e-6);
70
26
    ctime_ = cached_now;
71
26
    cached_now_access_count = 0;
72
26
  }
73
74
26
  return status_;
75
26
}
76
77
26
void AutoRollLogger::RollLogFile() {
78
  // This function is called when log is rotating. Two rotations
79
  // can happen quickly (NowMicro returns same value). To not overwrite
80
  // previous log file we increment by one micro second and try again.
81
26
  if (!env_->FileExists(log_fname_).ok()) {
82
12
    return;
83
12
  }
84
14
  uint64_t now = env_->NowMicros();
85
14
  std::string old_fname;
86
14
  do {
87
14
    old_fname = OldInfoLogFileName(
88
14
      dbname_, now, db_absolute_path_, db_log_dir_);
89
14
    now++;
90
14
  } while (env_->FileExists(old_fname).ok());
91
14
  CHECK_OK(env_->RenameFile(log_fname_, old_fname));
92
14
}
93
94
96
string AutoRollLogger::ValistToString(const char* format, va_list args) const {
95
  // Any log messages longer than 1024 will get truncated.
96
  // The user is responsible for chopping longer messages into multi line log
97
96
  static const int MAXBUFFERSIZE = 1024;
98
96
  char buffer[MAXBUFFERSIZE];
99
100
96
  int count = vsnprintf(buffer, MAXBUFFERSIZE, format, args);
101
96
  (void) count;
102
96
  assert(count >= 0);
103
104
96
  return buffer;
105
96
}
106
107
40
void AutoRollLogger::LogInternal(const char* format, ...) {
108
40
  mutex_.AssertHeld();
109
40
  va_list args;
110
40
  va_start(args, format);
111
40
  logger_->Logv(format, args);
112
40
  va_end(args);
113
40
}
114
115
442
void AutoRollLogger::Logv(const char* format, va_list ap) {
116
442
  assert(GetStatus().ok());
117
118
442
  std::shared_ptr<Logger> logger;
119
442
  {
120
442
    MutexLock l(&mutex_);
121
442
    if ((kLogFileTimeToRoll > 0 && LogExpired()) ||
122
438
        (kMaxLogFileSize > 0 && logger_->GetLogFileSize() >= kMaxLogFileSize)) {
123
13
      RollLogFile();
124
13
      Status s = ResetLogger();
125
13
      if (!s.ok()) {
126
        // can't really log the error if creating a new LOG file failed
127
0
        return;
128
0
      }
129
130
13
      WriteHeaderInfo();
131
13
    }
132
133
    // pin down the current logger_ instance before releasing the mutex.
134
442
    logger = logger_;
135
442
  }
136
137
  // Another thread could have put a new Logger instance into logger_ by now.
138
  // However, since logger is still hanging on to the previous instance
139
  // (reference count is not zero), we don't have to worry about it being
140
  // deleted while we are accessing it.
141
  // Note that logv itself is not mutex protected to allow maximum concurrency,
142
  // as thread safety should have been handled by the underlying logger.
143
442
  logger->Logv(format, ap);
144
442
}
145
146
13
void AutoRollLogger::WriteHeaderInfo() {
147
13
  mutex_.AssertHeld();
148
40
  for (auto& header : headers_) {
149
40
    LogInternal("%s", header.c_str());
150
40
  }
151
13
}
152
153
void AutoRollLogger::LogHeaderWithContext(const char* file, const int line,
154
96
    const char *format, va_list args) {
155
  // header message are to be retained in memory. Since we cannot make any
156
  // assumptions about the data contained in va_list, we will retain them as
157
  // strings
158
96
  va_list tmp;
159
96
  va_copy(tmp, args);
160
96
  string data = ValistToString(format, tmp);
161
96
  va_end(tmp);
162
163
96
  MutexLock l(&mutex_);
164
96
  headers_.push_back(data);
165
166
  // Log the original message to the current log
167
96
  logger_->LogvWithContext(file, line, InfoLogLevel::HEADER_LEVEL, format, args);
168
96
}
169
170
131
bool AutoRollLogger::LogExpired() {
171
131
  if (cached_now_access_count >= call_NowMicros_every_N_records_) {
172
44
    cached_now = static_cast<uint64_t>(env_->NowMicros() * 1e-6);
173
44
    cached_now_access_count = 0;
174
44
  }
175
176
131
  ++cached_now_access_count;
177
131
  return cached_now >= ctime_ + kLogFileTimeToRoll;
178
131
}
179
180
28.3k
CHECKED_STATUS CreateDirs(Env* env, const std::string& dir) {
181
28.3k
  if (dir == "/" || env->DirExists(dir)) {
182
21.0k
    return Status::OK();
183
21.0k
  }
184
185
7.34k
  RETURN_NOT_OK(CreateDirs(env, yb::DirName(dir)));
186
7.34k
  return env->CreateDir(dir);
187
7.34k
}
188
189
Status CreateLoggerFromOptions(const std::string& dbname,
190
                               const DBOptions& options,
191
21.0k
                               std::shared_ptr<Logger>* logger) {
192
21.0k
  if (options.info_log) {
193
0
    *logger = options.info_log;
194
0
    return Status::OK();
195
0
  }
196
197
21.0k
  Env* env = options.env;
198
21.0k
  std::string db_absolute_path;
199
21.0k
  RETURN_NOT_OK(env->GetAbsolutePath(dbname, &db_absolute_path));
200
21.0k
  std::string fname =
201
21.0k
      InfoLogFileName(dbname, db_absolute_path, options.db_log_dir);
202
203
21.0k
  RETURN_NOT_OK(CreateDirs(env, yb::DirName(fname)));  // In case it does not exist
204
  // Currently we only support roll by time-to-roll and log size
205
21.0k
  if (options.log_file_time_to_roll > 0 || options.max_log_file_size > 0) {
206
5
    AutoRollLogger* result = new AutoRollLogger(
207
5
        env, dbname, options.db_log_dir, options.max_log_file_size,
208
5
        options.log_file_time_to_roll, options.info_log_level);
209
5
    Status s = result->GetStatus();
210
5
    if (!s.ok()) {
211
0
      delete result;
212
5
    } else {
213
5
      logger->reset(result);
214
5
    }
215
5
    return s;
216
21.0k
  } else {
217
    // Open a log file in the same directory as the db
218
21.0k
    if (env->FileExists(fname).ok()) {
219
12.9k
      RETURN_NOT_OK(env->RenameFile(
220
12.9k
          fname, OldInfoLogFileName(dbname, env->NowMicros(), db_absolute_path,
221
12.9k
                                    options.db_log_dir)));
222
12.9k
    }
223
21.0k
    auto s = env->NewLogger(fname, logger);
224
21.0k
    if (logger->get() != nullptr) {
225
21.0k
      (*logger)->SetInfoLogLevel(options.info_log_level);
226
21.0k
    }
227
21.0k
    return s;
228
21.0k
  }
229
21.0k
}
230
231
}  // namespace rocksdb