/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 |