rcpp_framework/modules/drogon/trantor/utils/AsyncFileLogger.cc

254 lines
6.2 KiB
C++

/**
*
* AsyncFileLogger.cc
* An Tao
*
* Public header file in trantor lib.
*
* Copyright 2018, An Tao. All rights reserved.
* Use of this source code is governed by a BSD-style license
* that can be found in the License file.
*
*
*/
#include <trantor/utils/AsyncFileLogger.h>
#ifndef _WIN32
#include <unistd.h>
#ifdef __linux__
#include <sys/prctl.h>
#endif
#endif
#include <string.h>
#include <iostream>
#include <functional>
#include <chrono>
namespace trantor
{
static constexpr std::chrono::seconds kLogFlushTimeout{1};
static constexpr size_t kMemBufferSize{4 * 1024 * 1024};
extern const char *strerror_tl(int savedErrno);
} // namespace trantor
using namespace trantor;
AsyncFileLogger::AsyncFileLogger()
: logBufferPtr_(new std::string), nextBufferPtr_(new std::string)
{
logBufferPtr_->reserve(kMemBufferSize);
nextBufferPtr_->reserve(kMemBufferSize);
}
AsyncFileLogger::~AsyncFileLogger()
{
// std::cout << "~AsyncFileLogger" << std::endl;
stopFlag_ = true;
if (threadPtr_)
{
cond_.notify_all();
threadPtr_->join();
}
// std::cout << "thread exit" << std::endl;
{
std::lock_guard<std::mutex> guard_(mutex_);
if (logBufferPtr_->length() > 0)
{
writeBuffers_.push(logBufferPtr_);
}
while (!writeBuffers_.empty())
{
StringPtr tmpPtr = (StringPtr &&) writeBuffers_.front();
writeBuffers_.pop();
writeLogToFile(tmpPtr);
}
}
}
void AsyncFileLogger::output(const char *msg, const uint64_t len)
{
std::lock_guard<std::mutex> guard_(mutex_);
if (len > kMemBufferSize)
return;
if (!logBufferPtr_)
{
logBufferPtr_ = std::make_shared<std::string>();
logBufferPtr_->reserve(kMemBufferSize);
}
if (logBufferPtr_->capacity() - logBufferPtr_->length() < len)
{
swapBuffer();
cond_.notify_one();
}
if (writeBuffers_.size() > 25) // 100M bytes logs in buffer
{
++lostCounter_;
return;
}
if (lostCounter_ > 0)
{
char logErr[128];
auto strlen =
snprintf(logErr,
sizeof(logErr),
"%llu log information is lost\n",
static_cast<long long unsigned int>(lostCounter_));
lostCounter_ = 0;
logBufferPtr_->append(logErr, strlen);
}
logBufferPtr_->append(msg, len);
}
void AsyncFileLogger::flush()
{
std::lock_guard<std::mutex> guard_(mutex_);
if (logBufferPtr_->length() > 0)
{
// std::cout<<"flush log buffer
// len:"<<logBufferPtr_->length()<<std::endl;
swapBuffer();
cond_.notify_one();
}
}
void AsyncFileLogger::writeLogToFile(const StringPtr buf)
{
if (!loggerFilePtr_)
{
loggerFilePtr_ = std::unique_ptr<LoggerFile>(
new LoggerFile(filePath_, fileBaseName_, fileExtName_));
}
loggerFilePtr_->writeLog(buf);
if (loggerFilePtr_->getLength() > sizeLimit_)
{
loggerFilePtr_.reset();
}
}
void AsyncFileLogger::logThreadFunc()
{
#ifdef __linux__
prctl(PR_SET_NAME, "AsyncFileLogger");
#endif
while (!stopFlag_)
{
{
std::unique_lock<std::mutex> lock(mutex_);
while (writeBuffers_.size() == 0 && !stopFlag_)
{
if (cond_.wait_for(lock, kLogFlushTimeout) ==
std::cv_status::timeout)
{
if (logBufferPtr_->length() > 0)
{
swapBuffer();
}
break;
}
}
tmpBuffers_.swap(writeBuffers_);
}
while (!tmpBuffers_.empty())
{
StringPtr tmpPtr = (StringPtr &&) tmpBuffers_.front();
tmpBuffers_.pop();
writeLogToFile(tmpPtr);
tmpPtr->clear();
{
std::unique_lock<std::mutex> lock(mutex_);
nextBufferPtr_ = tmpPtr;
}
}
if (loggerFilePtr_)
loggerFilePtr_->flush();
}
}
void AsyncFileLogger::startLogging()
{
threadPtr_ = std::unique_ptr<std::thread>(
new std::thread(std::bind(&AsyncFileLogger::logThreadFunc, this)));
}
AsyncFileLogger::LoggerFile::LoggerFile(const std::string &filePath,
const std::string &fileBaseName,
const std::string &fileExtName)
: creationDate_(Date::date()),
filePath_(filePath),
fileBaseName_(fileBaseName),
fileExtName_(fileExtName)
{
fileFullName_ = filePath + fileBaseName + fileExtName;
#ifndef _MSC_VER
fp_ = fopen(fileFullName_.c_str(), "a");
#else
fp_ = _fsopen(fileFullName_.c_str(), "a+", _SH_DENYWR);
#endif
if (fp_ == nullptr)
{
std::cout << strerror_tl(errno) << std::endl;
}
}
uint64_t AsyncFileLogger::LoggerFile::fileSeq_{0};
void AsyncFileLogger::LoggerFile::writeLog(const StringPtr buf)
{
if (fp_)
{
// std::cout<<"write "<<buf->length()<<" bytes to file"<<std::endl;
fwrite(buf->c_str(), 1, buf->length(), fp_);
}
}
void AsyncFileLogger::LoggerFile::flush()
{
if (fp_)
{
fflush(fp_);
}
}
uint64_t AsyncFileLogger::LoggerFile::getLength()
{
if (fp_)
return ftell(fp_);
return 0;
}
AsyncFileLogger::LoggerFile::~LoggerFile()
{
if (fp_)
{
fclose(fp_);
char seq[12];
snprintf(seq,
sizeof(seq),
".%06llu",
static_cast<long long unsigned int>(fileSeq_ % 1000000));
++fileSeq_;
std::string newName =
filePath_ + fileBaseName_ + "." +
creationDate_.toCustomedFormattedString("%y%m%d-%H%M%S") +
std::string(seq) + fileExtName_;
rename(fileFullName_.c_str(), newName.c_str());
}
}
void AsyncFileLogger::swapBuffer()
{
writeBuffers_.push(logBufferPtr_);
if (nextBufferPtr_)
{
logBufferPtr_ = nextBufferPtr_;
nextBufferPtr_.reset();
logBufferPtr_->clear();
}
else
{
logBufferPtr_ = std::make_shared<std::string>();
logBufferPtr_->reserve(kMemBufferSize);
}
}