Add Header to logging to capture application level information
Summary: This change adds LogHeader provision to the logger. For the rolling logger implementation, the headers are copied over to the new log file every time there is a log roll over. Test Plan: Added a unit test to test the rolling log case. Reviewers: sdong Subscribers: dhruba Differential Revision: https://reviews.facebook.net/D32817
This commit is contained in:
parent
7de4e99a8e
commit
da9cbce731
@ -617,6 +617,15 @@ class Logger {
|
|||||||
: log_level_(log_level) {}
|
: log_level_(log_level) {}
|
||||||
virtual ~Logger();
|
virtual ~Logger();
|
||||||
|
|
||||||
|
// Write a header to the log file with the specified format
|
||||||
|
// It is recommended that you log all header information at the start of the
|
||||||
|
// application. But it is not enforced.
|
||||||
|
virtual void LogHeader(const char* format, va_list ap) {
|
||||||
|
// Default implementation does a simple INFO level log write.
|
||||||
|
// Please override as per the logger class requirement.
|
||||||
|
Logv(format, ap);
|
||||||
|
}
|
||||||
|
|
||||||
// Write an entry to the log file with the specified format.
|
// Write an entry to the log file with the specified format.
|
||||||
virtual void Logv(const char* format, va_list ap) = 0;
|
virtual void Logv(const char* format, va_list ap) = 0;
|
||||||
|
|
||||||
@ -678,6 +687,7 @@ extern void Log(const InfoLogLevel log_level,
|
|||||||
const shared_ptr<Logger>& info_log, const char* format, ...);
|
const shared_ptr<Logger>& info_log, const char* format, ...);
|
||||||
|
|
||||||
// a set of log functions with different log levels.
|
// a set of log functions with different log levels.
|
||||||
|
extern void Header(const shared_ptr<Logger>& info_log, const char* format, ...);
|
||||||
extern void Debug(const shared_ptr<Logger>& info_log, const char* format, ...);
|
extern void Debug(const shared_ptr<Logger>& info_log, const char* format, ...);
|
||||||
extern void Info(const shared_ptr<Logger>& info_log, const char* format, ...);
|
extern void Info(const shared_ptr<Logger>& info_log, const char* format, ...);
|
||||||
extern void Warn(const shared_ptr<Logger>& info_log, const char* format, ...);
|
extern void Warn(const shared_ptr<Logger>& info_log, const char* format, ...);
|
||||||
@ -705,6 +715,7 @@ extern void Log(Logger* info_log, const char* format, ...)
|
|||||||
;
|
;
|
||||||
|
|
||||||
// a set of log functions with different log levels.
|
// a set of log functions with different log levels.
|
||||||
|
extern void Header(Logger* info_log, const char* format, ...);
|
||||||
extern void Debug(Logger* info_log, const char* format, ...);
|
extern void Debug(Logger* info_log, const char* format, ...);
|
||||||
extern void Info(Logger* info_log, const char* format, ...);
|
extern void Info(Logger* info_log, const char* format, ...);
|
||||||
extern void Warn(Logger* info_log, const char* format, ...);
|
extern void Warn(Logger* info_log, const char* format, ...);
|
||||||
|
@ -37,6 +37,27 @@ void AutoRollLogger::RollLogFile() {
|
|||||||
env_->RenameFile(log_fname_, old_fname);
|
env_->RenameFile(log_fname_, old_fname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string AutoRollLogger::ValistToString(const char* format, va_list args) const {
|
||||||
|
// Any log messages longer than 1024 will get truncated.
|
||||||
|
// The user is responsible for chopping longer messages into multi line log
|
||||||
|
static const int MAXBUFFERSIZE = 1024;
|
||||||
|
char buffer[MAXBUFFERSIZE];
|
||||||
|
|
||||||
|
int count = vsnprintf(buffer, MAXBUFFERSIZE, format, args);
|
||||||
|
(void) count;
|
||||||
|
assert(count >= 0);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoRollLogger::LogInternal(const char* format, ...) {
|
||||||
|
mutex_.AssertHeld();
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
logger_->Logv(format, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
void AutoRollLogger::Logv(const char* format, va_list ap) {
|
void AutoRollLogger::Logv(const char* format, va_list ap) {
|
||||||
assert(GetStatus().ok());
|
assert(GetStatus().ok());
|
||||||
|
|
||||||
@ -51,6 +72,8 @@ void AutoRollLogger::Logv(const char* format, va_list ap) {
|
|||||||
// can't really log the error if creating a new LOG file failed
|
// can't really log the error if creating a new LOG file failed
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WriteHeaderInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
// pin down the current logger_ instance before releasing the mutex.
|
// pin down the current logger_ instance before releasing the mutex.
|
||||||
@ -66,6 +89,29 @@ void AutoRollLogger::Logv(const char* format, va_list ap) {
|
|||||||
logger->Logv(format, ap);
|
logger->Logv(format, ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AutoRollLogger::WriteHeaderInfo() {
|
||||||
|
mutex_.AssertHeld();
|
||||||
|
for (auto header : headers_) {
|
||||||
|
LogInternal("%s", header.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoRollLogger::LogHeader(const char* format, va_list args) {
|
||||||
|
// header message are to be retained in memory. Since we cannot make any
|
||||||
|
// assumptions about the data contained in va_list, we will retain them as
|
||||||
|
// strings
|
||||||
|
va_list tmp;
|
||||||
|
va_copy(tmp, args);
|
||||||
|
string data = ValistToString(format, tmp);
|
||||||
|
va_end(tmp);
|
||||||
|
|
||||||
|
MutexLock l(&mutex_);
|
||||||
|
headers_.push_back(data);
|
||||||
|
|
||||||
|
// Log the original message to the current log
|
||||||
|
logger_->Logv(format, args);
|
||||||
|
}
|
||||||
|
|
||||||
bool AutoRollLogger::LogExpired() {
|
bool AutoRollLogger::LogExpired() {
|
||||||
if (cached_now_access_count >= call_NowMicros_every_N_records_) {
|
if (cached_now_access_count >= call_NowMicros_every_N_records_) {
|
||||||
cached_now = static_cast<uint64_t>(env_->NowMicros() * 1e-6);
|
cached_now = static_cast<uint64_t>(env_->NowMicros() * 1e-6);
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
// where enough posix functionality is available.
|
// where enough posix functionality is available.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <list>
|
||||||
|
|
||||||
#include "db/filename.h"
|
#include "db/filename.h"
|
||||||
#include "port/port.h"
|
#include "port/port.h"
|
||||||
#include "util/posix_logger.h"
|
#include "util/posix_logger.h"
|
||||||
@ -40,6 +42,10 @@ class AutoRollLogger : public Logger {
|
|||||||
|
|
||||||
void Logv(const char* format, va_list ap);
|
void Logv(const char* format, va_list ap);
|
||||||
|
|
||||||
|
// Write a header entry to the log. All header information will be written
|
||||||
|
// again every time the log rolls over.
|
||||||
|
virtual void LogHeader(const char* format, va_list ap) override;
|
||||||
|
|
||||||
// check if the logger has encountered any problem.
|
// check if the logger has encountered any problem.
|
||||||
Status GetStatus() {
|
Status GetStatus() {
|
||||||
return status_;
|
return status_;
|
||||||
@ -57,10 +63,15 @@ class AutoRollLogger : public Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
bool LogExpired();
|
bool LogExpired();
|
||||||
Status ResetLogger();
|
Status ResetLogger();
|
||||||
void RollLogFile();
|
void RollLogFile();
|
||||||
|
// Log message to logger without rolling
|
||||||
|
void LogInternal(const char* format, ...);
|
||||||
|
// Serialize the va_list to a string
|
||||||
|
std::string ValistToString(const char* format, va_list args) const;
|
||||||
|
// Write the logs marked as headers to the new log file
|
||||||
|
void WriteHeaderInfo();
|
||||||
|
|
||||||
std::string log_fname_; // Current active info log's file name.
|
std::string log_fname_; // Current active info log's file name.
|
||||||
std::string dbname_;
|
std::string dbname_;
|
||||||
@ -72,6 +83,8 @@ class AutoRollLogger : public Logger {
|
|||||||
Status status_;
|
Status status_;
|
||||||
const size_t kMaxLogFileSize;
|
const size_t kMaxLogFileSize;
|
||||||
const size_t kLogFileTimeToRoll;
|
const size_t kLogFileTimeToRoll;
|
||||||
|
// header information
|
||||||
|
std::list<std::string> headers_;
|
||||||
// to avoid frequent env->NowMicros() calls, we cached the current time
|
// to avoid frequent env->NowMicros() calls, we cached the current time
|
||||||
uint64_t cached_now;
|
uint64_t cached_now;
|
||||||
uint64_t ctime_;
|
uint64_t ctime_;
|
||||||
|
@ -285,6 +285,47 @@ TEST(AutoRollLoggerTest, InfoLogLevel) {
|
|||||||
inFile.close();
|
inFile.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test the logger Header function for roll over logs
|
||||||
|
// We expect the new logs creates as roll over to carry the headers specified
|
||||||
|
TEST(AutoRollLoggerTest, LogHeaderTest) {
|
||||||
|
static const size_t MAX_HEADERS = 10;
|
||||||
|
static const size_t LOG_MAX_SIZE = 1024 * 5;
|
||||||
|
static const std::string HEADER_STR = "Log header line";
|
||||||
|
|
||||||
|
InitTestDb();
|
||||||
|
|
||||||
|
AutoRollLogger logger(Env::Default(), kTestDir, /*db_log_dir=*/ "",
|
||||||
|
LOG_MAX_SIZE, /*log_file_time_to_roll=*/ 0);
|
||||||
|
|
||||||
|
// log some headers
|
||||||
|
for (size_t i = 0; i < MAX_HEADERS; i++) {
|
||||||
|
Header(&logger, "%s %d", HEADER_STR.c_str(), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// log enough data to cause a roll over
|
||||||
|
size_t i = 0;
|
||||||
|
while (logger.GetLogFileSize() < LOG_MAX_SIZE) {
|
||||||
|
Info(&logger, (kSampleMessage + ":LogHeaderTest line %d").c_str(), i);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify that the new log contains all the header logs
|
||||||
|
std::stringstream ssbuf;
|
||||||
|
std::string line;
|
||||||
|
size_t count = 0;
|
||||||
|
|
||||||
|
std::ifstream inFile(AutoRollLoggerTest::kLogFile.c_str());
|
||||||
|
ssbuf << inFile.rdbuf();
|
||||||
|
|
||||||
|
while (getline(ssbuf, line)) {
|
||||||
|
if (line.find(HEADER_STR) != std::string::npos) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(count, MAX_HEADERS);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(AutoRollLoggerTest, LogFileExistence) {
|
TEST(AutoRollLoggerTest, LogFileExistence) {
|
||||||
rocksdb::DB* db;
|
rocksdb::DB* db;
|
||||||
rocksdb::Options options;
|
rocksdb::Options options;
|
||||||
|
18
util/env.cc
18
util/env.cc
@ -59,6 +59,15 @@ void Log(const InfoLogLevel log_level, Logger* info_log, const char* format,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Header(Logger* info_log, const char* format, ...) {
|
||||||
|
if (info_log) {
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
info_log->LogHeader(format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Debug(Logger* info_log, const char* format, ...) {
|
void Debug(Logger* info_log, const char* format, ...) {
|
||||||
if (info_log && info_log->GetInfoLogLevel() <= InfoLogLevel::DEBUG_LEVEL) {
|
if (info_log && info_log->GetInfoLogLevel() <= InfoLogLevel::DEBUG_LEVEL) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
@ -118,6 +127,15 @@ void Log(const InfoLogLevel log_level, const shared_ptr<Logger>& info_log,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Header(const shared_ptr<Logger>& info_log, const char* format, ...) {
|
||||||
|
if (info_log) {
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
info_log->LogHeader(format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Debug(const shared_ptr<Logger>& info_log, const char* format, ...) {
|
void Debug(const shared_ptr<Logger>& info_log, const char* format, ...) {
|
||||||
if (info_log) {
|
if (info_log) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
Loading…
Reference in New Issue
Block a user