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

219 lines
5.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 <chrono>
#include <functional>
#include <iostream>
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);
}
}