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).
9 #include "logging/auto_roll_logger.h"
22 #include "db/db_test_util.h"
23 #include "env/emulated_clock.h"
24 #include "logging/env_logger.h"
25 #include "logging/logging.h"
26 #include "port/port.h"
27 #include "rocksdb/db.h"
28 #include "rocksdb/file_system.h"
29 #include "rocksdb/system_clock.h"
30 #include "test_util/sync_point.h"
31 #include "test_util/testharness.h"
32 #include "test_util/testutil.h"
34 namespace ROCKSDB_NAMESPACE
{
36 // In this test we only want to Log some simple log message with
37 // no format. LogMessage() provides such a simple interface and
38 // avoids the [format-security] warning which occurs when you
39 // call ROCKS_LOG_INFO(logger, log_message) directly.
41 void LogMessage(Logger
* logger
, const char* message
) {
42 ROCKS_LOG_INFO(logger
, "%s", message
);
45 void LogMessage(const InfoLogLevel log_level
, Logger
* logger
,
46 const char* message
) {
47 Log(log_level
, logger
, "%s", message
);
51 class AutoRollLoggerTest
: public testing::Test
{
53 static void InitTestDb() {
54 // TODO replace the `system` calls with Env/FileSystem APIs.
56 // Replace all slashes in the path so windows CompSpec does not
58 std::string
testDbDir(kTestDbDir
);
60 testDbDir
.begin(), testDbDir
.end(), [](char ch
) { return ch
== '/'; },
62 std::string deleteDbDirCmd
=
63 "if exist " + testDbDir
+ " rd /s /q " + testDbDir
;
64 ASSERT_TRUE(system(deleteDbDirCmd
.c_str()) == 0);
66 std::string
testDir(kTestDir
);
68 testDir
.begin(), testDir
.end(), [](char ch
) { return ch
== '/'; },
70 std::string deleteCmd
= "if exist " + testDir
+ " rd /s /q " + testDir
;
72 std::string deleteCmd
= "rm -rf " + kTestDir
+ " " + kTestDbDir
;
74 ASSERT_TRUE(system(deleteCmd
.c_str()) == 0);
75 ASSERT_OK(Env::Default()->CreateDir(kTestDir
));
76 ASSERT_OK(Env::Default()->CreateDir(kTestDbDir
));
79 void RollLogFileBySizeTest(AutoRollLogger
* logger
, size_t log_max_size
,
80 const std::string
& log_message
);
81 void RollLogFileByTimeTest(const std::shared_ptr
<FileSystem
>& fs
,
82 const std::shared_ptr
<SystemClock
>& sc
,
83 AutoRollLogger
* logger
, size_t time
,
84 const std::string
& log_message
);
85 // return list of files under kTestDir that contains "LOG"
86 std::vector
<std::string
> GetLogFiles() {
87 std::vector
<std::string
> ret
;
88 std::vector
<std::string
> files
;
89 Status s
= default_env
->GetChildren(kTestDir
, &files
);
90 // Should call ASSERT_OK() here but it doesn't compile. It's not
91 // worth the time figuring out why.
93 for (const auto& f
: files
) {
94 if (f
.find("LOG") != std::string::npos
) {
101 // Delete all log files under kTestDir
102 void CleanupLogFiles() {
103 for (const std::string
& f
: GetLogFiles()) {
104 ASSERT_OK(default_env
->DeleteFile(kTestDir
+ "/" + f
));
108 void RollNTimesBySize(Logger
* auto_roll_logger
, size_t file_num
,
109 size_t max_log_file_size
) {
110 // Roll the log 4 times, and it will trim to 3 files.
111 std::string dummy_large_string
;
112 dummy_large_string
.assign(max_log_file_size
, '=');
113 auto_roll_logger
->SetInfoLogLevel(InfoLogLevel::INFO_LEVEL
);
114 for (size_t i
= 0; i
< file_num
+ 1; i
++) {
115 // Log enough bytes to trigger at least one roll.
116 LogMessage(auto_roll_logger
, dummy_large_string
.c_str());
117 LogMessage(auto_roll_logger
, "");
121 static const std::string kSampleMessage
;
122 static const std::string kTestDir
;
123 static const std::string kTestDbDir
;
124 static const std::string kLogFile
;
125 static Env
* default_env
;
128 const std::string
AutoRollLoggerTest::kSampleMessage(
129 "this is the message to be written to the log file!!");
130 const std::string
AutoRollLoggerTest::kTestDir(
131 test::PerThreadDBPath("db_log_test"));
132 const std::string
AutoRollLoggerTest::kTestDbDir(
133 test::PerThreadDBPath("db_log_test_db"));
134 const std::string
AutoRollLoggerTest::kLogFile(
135 test::PerThreadDBPath("db_log_test") + "/LOG");
136 Env
* AutoRollLoggerTest::default_env
= Env::Default();
138 void AutoRollLoggerTest::RollLogFileBySizeTest(AutoRollLogger
* logger
,
140 const std::string
& log_message
) {
141 logger
->SetInfoLogLevel(InfoLogLevel::INFO_LEVEL
);
142 ASSERT_EQ(InfoLogLevel::INFO_LEVEL
, logger
->GetInfoLogLevel());
143 ASSERT_EQ(InfoLogLevel::INFO_LEVEL
,
144 logger
->TEST_inner_logger()->GetInfoLogLevel());
145 // measure the size of each message, which is supposed
146 // to be equal or greater than log_message.size()
147 LogMessage(logger
, log_message
.c_str());
148 size_t message_size
= logger
->GetLogFileSize();
149 size_t current_log_size
= message_size
;
151 // Test the cases when the log file will not be rolled.
152 while (current_log_size
+ message_size
< log_max_size
) {
153 LogMessage(logger
, log_message
.c_str());
154 current_log_size
+= message_size
;
155 ASSERT_EQ(current_log_size
, logger
->GetLogFileSize());
158 // Now the log file will be rolled
159 LogMessage(logger
, log_message
.c_str());
160 // Since rotation is checked before actual logging, we need to
161 // trigger the rotation by logging another message.
162 LogMessage(logger
, log_message
.c_str());
164 ASSERT_TRUE(message_size
== logger
->GetLogFileSize());
167 void AutoRollLoggerTest::RollLogFileByTimeTest(
168 const std::shared_ptr
<FileSystem
>& fs
,
169 const std::shared_ptr
<SystemClock
>& sc
, AutoRollLogger
* logger
, size_t time
,
170 const std::string
& log_message
) {
171 uint64_t expected_ctime
;
172 uint64_t actual_ctime
;
174 uint64_t total_log_size
;
175 EXPECT_OK(fs
->GetFileSize(kLogFile
, IOOptions(), &total_log_size
, nullptr));
176 expected_ctime
= logger
->TEST_ctime();
177 logger
->SetCallNowMicrosEveryNRecords(0);
179 // -- Write to the log for several times, which is supposed
180 // to be finished before time.
181 for (int i
= 0; i
< 10; ++i
) {
182 sc
->SleepForMicroseconds(50000);
183 LogMessage(logger
, log_message
.c_str());
184 EXPECT_OK(logger
->GetStatus());
185 // Make sure we always write to the same log file (by
186 // checking the create time);
188 actual_ctime
= logger
->TEST_ctime();
190 // Also make sure the log size is increasing.
191 EXPECT_EQ(expected_ctime
, actual_ctime
);
192 EXPECT_GT(logger
->GetLogFileSize(), total_log_size
);
193 total_log_size
= logger
->GetLogFileSize();
196 // -- Make the log file expire
197 sc
->SleepForMicroseconds(static_cast<int>(time
* 1000000));
198 LogMessage(logger
, log_message
.c_str());
200 // At this time, the new log file should be created.
201 actual_ctime
= logger
->TEST_ctime();
202 EXPECT_LT(expected_ctime
, actual_ctime
);
203 EXPECT_LT(logger
->GetLogFileSize(), total_log_size
);
206 TEST_F(AutoRollLoggerTest
, RollLogFileBySize
) {
208 size_t log_max_size
= 1024 * 5;
209 size_t keep_log_file_num
= 10;
211 AutoRollLogger
logger(FileSystem::Default(), SystemClock::Default(), kTestDir
,
212 "", log_max_size
, 0, keep_log_file_num
);
214 RollLogFileBySizeTest(&logger
, log_max_size
,
215 kSampleMessage
+ ":RollLogFileBySize");
218 TEST_F(AutoRollLoggerTest
, RollLogFileByTime
) {
220 std::make_shared
<EmulatedSystemClock
>(SystemClock::Default(), true);
223 size_t log_size
= 1024 * 5;
224 size_t keep_log_file_num
= 10;
227 // -- Test the existence of file during the server restart.
228 ASSERT_EQ(Status::NotFound(), default_env
->FileExists(kLogFile
));
229 AutoRollLogger
logger(default_env
->GetFileSystem(), nsc
, kTestDir
, "",
230 log_size
, time
, keep_log_file_num
);
231 ASSERT_OK(default_env
->FileExists(kLogFile
));
233 RollLogFileByTimeTest(default_env
->GetFileSystem(), nsc
, &logger
, time
,
234 kSampleMessage
+ ":RollLogFileByTime");
237 TEST_F(AutoRollLoggerTest
, SetInfoLogLevel
) {
240 options
.info_log_level
= InfoLogLevel::FATAL_LEVEL
;
241 options
.max_log_file_size
= 1024;
242 std::shared_ptr
<Logger
> logger
;
243 ASSERT_OK(CreateLoggerFromOptions(kTestDir
, options
, &logger
));
244 auto* auto_roll_logger
= dynamic_cast<AutoRollLogger
*>(logger
.get());
245 ASSERT_NE(nullptr, auto_roll_logger
);
246 ASSERT_EQ(InfoLogLevel::FATAL_LEVEL
, auto_roll_logger
->GetInfoLogLevel());
247 ASSERT_EQ(InfoLogLevel::FATAL_LEVEL
,
248 auto_roll_logger
->TEST_inner_logger()->GetInfoLogLevel());
249 auto_roll_logger
->SetInfoLogLevel(InfoLogLevel::DEBUG_LEVEL
);
250 ASSERT_EQ(InfoLogLevel::DEBUG_LEVEL
, auto_roll_logger
->GetInfoLogLevel());
251 ASSERT_EQ(InfoLogLevel::DEBUG_LEVEL
, logger
->GetInfoLogLevel());
252 ASSERT_EQ(InfoLogLevel::DEBUG_LEVEL
,
253 auto_roll_logger
->TEST_inner_logger()->GetInfoLogLevel());
256 TEST_F(AutoRollLoggerTest
, OpenLogFilesMultipleTimesWithOptionLog_max_size
) {
257 // If only 'log_max_size' options is specified, then every time
258 // when rocksdb is restarted, a new empty log file will be created.
261 // avoid complier's complaint of "comparison between signed
262 // and unsigned integer expressions" because literal 0 is
263 // treated as "singed".
265 size_t log_size
= 1024;
266 size_t keep_log_file_num
= 10;
268 AutoRollLogger
* logger
=
269 new AutoRollLogger(FileSystem::Default(), SystemClock::Default(),
270 kTestDir
, "", log_size
, 0, keep_log_file_num
);
272 LogMessage(logger
, kSampleMessage
.c_str());
273 ASSERT_GT(logger
->GetLogFileSize(), kZero
);
276 // reopens the log file and an empty log file will be created.
277 logger
= new AutoRollLogger(FileSystem::Default(), SystemClock::Default(),
278 kTestDir
, "", log_size
, 0, 10);
279 ASSERT_EQ(logger
->GetLogFileSize(), kZero
);
283 TEST_F(AutoRollLoggerTest
, CompositeRollByTimeAndSizeLogger
) {
284 size_t time
= 2, log_max_size
= 1024 * 5;
285 size_t keep_log_file_num
= 10;
290 std::make_shared
<EmulatedSystemClock
>(SystemClock::Default(), true);
291 AutoRollLogger
logger(FileSystem::Default(), nsc
, kTestDir
, "", log_max_size
,
292 time
, keep_log_file_num
);
294 // Test the ability to roll by size
295 RollLogFileBySizeTest(&logger
, log_max_size
,
296 kSampleMessage
+ ":CompositeRollByTimeAndSizeLogger");
298 // Test the ability to roll by Time
299 RollLogFileByTimeTest(FileSystem::Default(), nsc
, &logger
, time
,
300 kSampleMessage
+ ":CompositeRollByTimeAndSizeLogger");
304 // TODO: does not build for Windows because of EnvLogger use below. Need to
306 TEST_F(AutoRollLoggerTest
, CreateLoggerFromOptions
) {
309 std::make_shared
<EmulatedSystemClock
>(SystemClock::Default(), true);
310 std::unique_ptr
<Env
> nse(new CompositeEnvWrapper(Env::Default(), nsc
));
312 std::shared_ptr
<Logger
> logger
;
315 ASSERT_OK(CreateLoggerFromOptions(kTestDir
, options
, &logger
));
316 ASSERT_TRUE(dynamic_cast<EnvLogger
*>(logger
.get()));
320 options
.max_log_file_size
= 1024;
321 ASSERT_OK(CreateLoggerFromOptions(kTestDir
, options
, &logger
));
322 AutoRollLogger
* auto_roll_logger
=
323 dynamic_cast<AutoRollLogger
*>(logger
.get());
324 ASSERT_TRUE(auto_roll_logger
);
325 RollLogFileBySizeTest(auto_roll_logger
, options
.max_log_file_size
,
326 kSampleMessage
+ ":CreateLoggerFromOptions - size");
329 options
.env
= nse
.get();
331 options
.max_log_file_size
= 0;
332 options
.log_file_time_to_roll
= 2;
333 ASSERT_OK(CreateLoggerFromOptions(kTestDir
, options
, &logger
));
334 auto_roll_logger
= dynamic_cast<AutoRollLogger
*>(logger
.get());
335 RollLogFileByTimeTest(options
.env
->GetFileSystem(), nsc
, auto_roll_logger
,
336 options
.log_file_time_to_roll
,
337 kSampleMessage
+ ":CreateLoggerFromOptions - time");
339 // roll by both Time and size
341 options
.max_log_file_size
= 1024 * 5;
342 options
.log_file_time_to_roll
= 2;
343 ASSERT_OK(CreateLoggerFromOptions(kTestDir
, options
, &logger
));
344 auto_roll_logger
= dynamic_cast<AutoRollLogger
*>(logger
.get());
345 RollLogFileBySizeTest(auto_roll_logger
, options
.max_log_file_size
,
346 kSampleMessage
+ ":CreateLoggerFromOptions - both");
347 RollLogFileByTimeTest(options
.env
->GetFileSystem(), nsc
, auto_roll_logger
,
348 options
.log_file_time_to_roll
,
349 kSampleMessage
+ ":CreateLoggerFromOptions - both");
351 // Set keep_log_file_num
353 const size_t kFileNum
= 3;
355 options
.max_log_file_size
= 512;
356 options
.log_file_time_to_roll
= 2;
357 options
.keep_log_file_num
= kFileNum
;
358 ASSERT_OK(CreateLoggerFromOptions(kTestDir
, options
, &logger
));
359 auto_roll_logger
= dynamic_cast<AutoRollLogger
*>(logger
.get());
361 // Roll the log 4 times, and it will trim to 3 files.
362 std::string dummy_large_string
;
363 dummy_large_string
.assign(options
.max_log_file_size
, '=');
364 auto_roll_logger
->SetInfoLogLevel(InfoLogLevel::INFO_LEVEL
);
365 for (size_t i
= 0; i
< kFileNum
+ 1; i
++) {
366 // Log enough bytes to trigger at least one roll.
367 LogMessage(auto_roll_logger
, dummy_large_string
.c_str());
368 LogMessage(auto_roll_logger
, "");
371 std::vector
<std::string
> files
= GetLogFiles();
372 ASSERT_EQ(kFileNum
, files
.size());
377 // Set keep_log_file_num and dbname is different from
380 const size_t kFileNum
= 3;
382 options
.max_log_file_size
= 512;
383 options
.log_file_time_to_roll
= 2;
384 options
.keep_log_file_num
= kFileNum
;
385 options
.db_log_dir
= kTestDir
;
386 ASSERT_OK(CreateLoggerFromOptions(kTestDbDir
, options
, &logger
));
387 auto_roll_logger
= dynamic_cast<AutoRollLogger
*>(logger
.get());
389 // Roll the log 4 times, and it will trim to 3 files.
390 std::string dummy_large_string
;
391 dummy_large_string
.assign(options
.max_log_file_size
, '=');
392 auto_roll_logger
->SetInfoLogLevel(InfoLogLevel::INFO_LEVEL
);
393 for (size_t i
= 0; i
< kFileNum
+ 1; i
++) {
394 // Log enough bytes to trigger at least one roll.
395 LogMessage(auto_roll_logger
, dummy_large_string
.c_str());
396 LogMessage(auto_roll_logger
, "");
399 std::vector
<std::string
> files
= GetLogFiles();
400 ASSERT_EQ(kFileNum
, files
.size());
401 for (const auto& f
: files
) {
402 ASSERT_TRUE(f
.find("db_log_test_db") != std::string::npos
);
405 // Cleaning up those files.
410 TEST_F(AutoRollLoggerTest
, AutoDeleting
) {
411 for (int attempt
= 0; attempt
< 2; attempt
++) {
412 // In the first attemp, db_log_dir is not set, while in the
414 std::string dbname
= (attempt
== 0) ? kTestDir
: "/test/dummy/dir";
415 std::string db_log_dir
= (attempt
== 0) ? "" : kTestDir
;
418 const size_t kMaxFileSize
= 512;
421 AutoRollLogger
logger(FileSystem::Default(), SystemClock::Default(),
422 dbname
, db_log_dir
, kMaxFileSize
, 0, log_num
);
423 RollNTimesBySize(&logger
, log_num
, kMaxFileSize
);
425 ASSERT_EQ(log_num
, GetLogFiles().size());
427 // Shrink number of files
430 AutoRollLogger
logger(FileSystem::Default(), SystemClock::Default(),
431 dbname
, db_log_dir
, kMaxFileSize
, 0, log_num
);
432 ASSERT_EQ(log_num
, GetLogFiles().size());
434 RollNTimesBySize(&logger
, 3, kMaxFileSize
);
435 ASSERT_EQ(log_num
, GetLogFiles().size());
438 // Increase number of files again.
441 AutoRollLogger
logger(FileSystem::Default(), SystemClock::Default(),
442 dbname
, db_log_dir
, kMaxFileSize
, 0, log_num
);
443 ASSERT_EQ(6, GetLogFiles().size());
445 RollNTimesBySize(&logger
, 3, kMaxFileSize
);
446 ASSERT_EQ(log_num
, GetLogFiles().size());
453 TEST_F(AutoRollLoggerTest
, LogFlushWhileRolling
) {
455 std::shared_ptr
<Logger
> logger
;
458 options
.max_log_file_size
= 1024 * 5;
459 ASSERT_OK(CreateLoggerFromOptions(kTestDir
, options
, &logger
));
460 AutoRollLogger
* auto_roll_logger
=
461 dynamic_cast<AutoRollLogger
*>(logger
.get());
462 ASSERT_TRUE(auto_roll_logger
);
463 ROCKSDB_NAMESPACE::port::Thread flush_thread
;
466 // (1) Need to pin the old logger before beginning the roll, as rolling grabs
467 // the mutex, which would prevent us from accessing the old logger. This
468 // also marks flush_thread with AutoRollLogger::Flush:PinnedLogger.
469 // (2) New logger will be cut in AutoRollLogger::RollLogFile only when flush
470 // is completed and reference to pinned logger is released.
471 // (3) EnvLogger::Flush() happens in both threads but its SyncPoints only
472 // are enabled in flush_thread (the one pinning the old logger).
473 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependencyAndMarkers(
474 {{"AutoRollLogger::Flush:PinnedLogger",
475 "AutoRollLoggerTest::LogFlushWhileRolling:PreRollAndPostThreadInit"}},
476 {{"AutoRollLogger::Flush:PinnedLogger", "EnvLogger::Flush:Begin1"},
477 {"AutoRollLogger::Flush:PinnedLogger", "EnvLogger::Flush:Begin2"}});
478 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
480 flush_thread
= port::Thread([&]() { auto_roll_logger
->Flush(); });
482 "AutoRollLoggerTest::LogFlushWhileRolling:PreRollAndPostThreadInit");
483 RollLogFileBySizeTest(auto_roll_logger
, options
.max_log_file_size
,
484 kSampleMessage
+ ":LogFlushWhileRolling");
486 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
491 TEST_F(AutoRollLoggerTest
, InfoLogLevel
) {
494 size_t log_size
= 8192;
495 size_t log_lines
= 0;
496 // an extra-scope to force the AutoRollLogger to flush the log file when it
497 // becomes out of scope.
499 AutoRollLogger
logger(FileSystem::Default(), SystemClock::Default(),
500 kTestDir
, "", log_size
, 0, 10);
501 for (int log_level
= InfoLogLevel::HEADER_LEVEL
;
502 log_level
>= InfoLogLevel::DEBUG_LEVEL
; log_level
--) {
503 logger
.SetInfoLogLevel((InfoLogLevel
)log_level
);
504 for (int log_type
= InfoLogLevel::DEBUG_LEVEL
;
505 log_type
<= InfoLogLevel::HEADER_LEVEL
; log_type
++) {
506 // log messages with log level smaller than log_level will not be
508 LogMessage((InfoLogLevel
)log_type
, &logger
, kSampleMessage
.c_str());
510 log_lines
+= InfoLogLevel::HEADER_LEVEL
- log_level
+ 1;
512 for (int log_level
= InfoLogLevel::HEADER_LEVEL
;
513 log_level
>= InfoLogLevel::DEBUG_LEVEL
; log_level
--) {
514 logger
.SetInfoLogLevel((InfoLogLevel
)log_level
);
516 // again, messages with level smaller than log_level will not be logged.
517 ROCKS_LOG_HEADER(&logger
, "%s", kSampleMessage
.c_str());
518 ROCKS_LOG_DEBUG(&logger
, "%s", kSampleMessage
.c_str());
519 ROCKS_LOG_INFO(&logger
, "%s", kSampleMessage
.c_str());
520 ROCKS_LOG_WARN(&logger
, "%s", kSampleMessage
.c_str());
521 ROCKS_LOG_ERROR(&logger
, "%s", kSampleMessage
.c_str());
522 ROCKS_LOG_FATAL(&logger
, "%s", kSampleMessage
.c_str());
523 log_lines
+= InfoLogLevel::HEADER_LEVEL
- log_level
+ 1;
526 std::ifstream
inFile(AutoRollLoggerTest::kLogFile
.c_str());
527 size_t lines
= std::count(std::istreambuf_iterator
<char>(inFile
),
528 std::istreambuf_iterator
<char>(), '\n');
529 ASSERT_EQ(log_lines
, lines
);
533 TEST_F(AutoRollLoggerTest
, Close
) {
536 size_t log_size
= 8192;
537 size_t log_lines
= 0;
538 AutoRollLogger
logger(FileSystem::Default(), SystemClock::Default(), kTestDir
,
539 "", log_size
, 0, 10);
540 for (int log_level
= InfoLogLevel::HEADER_LEVEL
;
541 log_level
>= InfoLogLevel::DEBUG_LEVEL
; log_level
--) {
542 logger
.SetInfoLogLevel((InfoLogLevel
)log_level
);
543 for (int log_type
= InfoLogLevel::DEBUG_LEVEL
;
544 log_type
<= InfoLogLevel::HEADER_LEVEL
; log_type
++) {
545 // log messages with log level smaller than log_level will not be
547 LogMessage((InfoLogLevel
)log_type
, &logger
, kSampleMessage
.c_str());
549 log_lines
+= InfoLogLevel::HEADER_LEVEL
- log_level
+ 1;
551 for (int log_level
= InfoLogLevel::HEADER_LEVEL
;
552 log_level
>= InfoLogLevel::DEBUG_LEVEL
; log_level
--) {
553 logger
.SetInfoLogLevel((InfoLogLevel
)log_level
);
555 // again, messages with level smaller than log_level will not be logged.
556 ROCKS_LOG_HEADER(&logger
, "%s", kSampleMessage
.c_str());
557 ROCKS_LOG_DEBUG(&logger
, "%s", kSampleMessage
.c_str());
558 ROCKS_LOG_INFO(&logger
, "%s", kSampleMessage
.c_str());
559 ROCKS_LOG_WARN(&logger
, "%s", kSampleMessage
.c_str());
560 ROCKS_LOG_ERROR(&logger
, "%s", kSampleMessage
.c_str());
561 ROCKS_LOG_FATAL(&logger
, "%s", kSampleMessage
.c_str());
562 log_lines
+= InfoLogLevel::HEADER_LEVEL
- log_level
+ 1;
564 ASSERT_EQ(logger
.Close(), Status::OK());
566 std::ifstream
inFile(AutoRollLoggerTest::kLogFile
.c_str());
567 size_t lines
= std::count(std::istreambuf_iterator
<char>(inFile
),
568 std::istreambuf_iterator
<char>(), '\n');
569 ASSERT_EQ(log_lines
, lines
);
573 // Test the logger Header function for roll over logs
574 // We expect the new logs creates as roll over to carry the headers specified
575 static std::vector
<std::string
> GetOldFileNames(const std::string
& path
) {
576 std::vector
<std::string
> ret
;
578 const std::string dirname
= path
.substr(/*start=*/0, path
.find_last_of("/"));
579 const std::string fname
= path
.substr(path
.find_last_of("/") + 1);
581 std::vector
<std::string
> children
;
582 EXPECT_OK(Env::Default()->GetChildren(dirname
, &children
));
584 // We know that the old log files are named [path]<something>
585 // Return all entities that match the pattern
586 for (auto& child
: children
) {
587 if (fname
!= child
&& child
.find(fname
) == 0) {
588 ret
.push_back(dirname
+ "/" + child
);
595 TEST_F(AutoRollLoggerTest
, LogHeaderTest
) {
596 static const size_t MAX_HEADERS
= 10;
597 static const size_t LOG_MAX_SIZE
= 1024 * 5;
598 static const std::string HEADER_STR
= "Log header line";
600 // test_num == 0 -> standard call to Header()
601 // test_num == 1 -> call to Log() with InfoLogLevel::HEADER_LEVEL
602 for (int test_num
= 0; test_num
< 2; test_num
++) {
605 AutoRollLogger
logger(FileSystem::Default(), SystemClock::Default(),
606 kTestDir
, /*db_log_dir=*/"", LOG_MAX_SIZE
,
607 /*log_file_time_to_roll=*/0,
608 /*keep_log_file_num=*/10);
611 // Log some headers explicitly using Header()
612 for (size_t i
= 0; i
< MAX_HEADERS
; i
++) {
613 Header(&logger
, "%s %" ROCKSDB_PRIszt
, HEADER_STR
.c_str(), i
);
615 } else if (test_num
== 1) {
616 // HEADER_LEVEL should make this behave like calling Header()
617 for (size_t i
= 0; i
< MAX_HEADERS
; i
++) {
618 ROCKS_LOG_HEADER(&logger
, "%s %" ROCKSDB_PRIszt
, HEADER_STR
.c_str(), i
);
622 const std::string newfname
= logger
.TEST_log_fname();
624 // Log enough data to cause a roll over
626 for (size_t iter
= 0; iter
< 2; iter
++) {
627 while (logger
.GetLogFileSize() < LOG_MAX_SIZE
) {
628 Info(&logger
, (kSampleMessage
+ ":LogHeaderTest line %d").c_str(), i
);
632 Info(&logger
, "Rollover");
635 // Flush the log for the latest file
638 const auto oldfiles
= GetOldFileNames(newfname
);
640 ASSERT_EQ(oldfiles
.size(), (size_t)2);
642 for (auto& oldfname
: oldfiles
) {
643 // verify that the files rolled over
644 ASSERT_NE(oldfname
, newfname
);
645 // verify that the old log contains all the header logs
646 ASSERT_EQ(test::GetLinesCount(oldfname
, HEADER_STR
), MAX_HEADERS
);
651 TEST_F(AutoRollLoggerTest
, LogFileExistence
) {
652 ROCKSDB_NAMESPACE::DB
* db
;
653 ROCKSDB_NAMESPACE::Options options
;
655 // Replace all slashes in the path so windows CompSpec does not
657 std::string
testDir(kTestDir
);
659 testDir
.begin(), testDir
.end(), [](char ch
) { return ch
== '/'; }, '\\');
660 std::string deleteCmd
= "if exist " + testDir
+ " rd /s /q " + testDir
;
662 std::string deleteCmd
= "rm -rf " + kTestDir
;
664 ASSERT_EQ(system(deleteCmd
.c_str()), 0);
665 options
.max_log_file_size
= 100 * 1024 * 1024;
666 options
.create_if_missing
= true;
667 ASSERT_OK(ROCKSDB_NAMESPACE::DB::Open(options
, kTestDir
, &db
));
668 ASSERT_OK(default_env
->FileExists(kLogFile
));
672 TEST_F(AutoRollLoggerTest
, FileCreateFailure
) {
674 options
.max_log_file_size
= 100 * 1024 * 1024;
675 options
.db_log_dir
= "/a/dir/does/not/exist/at/all";
677 std::shared_ptr
<Logger
> logger
;
678 ASSERT_NOK(CreateLoggerFromOptions("", options
, &logger
));
679 ASSERT_TRUE(!logger
);
682 TEST_F(AutoRollLoggerTest
, RenameOnlyWhenExists
) {
684 SpecialEnv
env(Env::Default());
688 // Originally no LOG exists. Should not see a rename.
690 std::shared_ptr
<Logger
> logger
;
691 ASSERT_OK(CreateLoggerFromOptions(kTestDir
, options
, &logger
));
692 ASSERT_EQ(0, env
.rename_count_
);
695 // Now a LOG exists. Create a new one should see a rename.
697 std::shared_ptr
<Logger
> logger
;
698 ASSERT_OK(CreateLoggerFromOptions(kTestDir
, options
, &logger
));
699 ASSERT_EQ(1, env
.rename_count_
);
703 TEST_F(AutoRollLoggerTest
, RenameError
) {
705 SpecialEnv
env(Env::Default());
706 env
.rename_error_
= true;
710 // Originally no LOG exists. Should not be impacted by rename error.
712 std::shared_ptr
<Logger
> logger
;
713 ASSERT_OK(CreateLoggerFromOptions(kTestDir
, options
, &logger
));
714 ASSERT_TRUE(logger
!= nullptr);
717 // Now a LOG exists. Rename error should cause failure.
719 std::shared_ptr
<Logger
> logger
;
720 ASSERT_NOK(CreateLoggerFromOptions(kTestDir
, options
, &logger
));
721 ASSERT_TRUE(logger
== nullptr);
725 } // namespace ROCKSDB_NAMESPACE
727 int main(int argc
, char** argv
) {
728 ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
729 ::testing::InitGoogleTest(&argc
, argv
);
730 return RUN_ALL_TESTS();
736 int main(int /*argc*/, char** /*argv*/) {
738 "SKIPPED as AutoRollLogger is not supported in ROCKSDB_LITE\n");
742 #endif // !ROCKSDB_LITE