muduo库的AsyncLogging异步日志记录使用双缓冲+任务队列实现,同时,在日志内容过多时,主动丢弃部分日志,确保系统高效的运行,防止被日志阻塞。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| BufferPtr currentBuffer_ GUARDED_BY(mutex_); BufferPtr nextBuffer_ GUARDED_BY(mutex_); BufferVector buffers_ GUARDED_BY(mutex_);
void AsyncLogging::append(const char* logline, int len) { muduo::MutexLockGuard lock(mutex_); if (currentBuffer_->avail() > len) { currentBuffer_->append(logline, len); } else { buffers_.push_back(std::move(currentBuffer_)); if (nextBuffer_) { currentBuffer_ = std::move(nextBuffer_); } else { currentBuffer_.reset(new Buffer); } currentBuffer_->append(logline, len); cond_.notify(); } }
|
后端一直循环写入日志,看起来比较绕,currentBuffer,nextBuffer,buffers,newBuffer1,newBuffer2,buffersToWrite,一共六个buffer
- currentBuffer:前端写入日志的buffer
- nextBuffer:前端备用buffer
- buffers:前端待写入日志队列
- newBuffer1:后端备用buffer
- newBuffer2:后端备用buffer
- bufferToWrite:真正要写入日志的buffer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
| void AsyncLogging::threadFunc() { assert(running_ == true); latch_.countDown(); LogFile output(basename_, rollSize_, false);
BufferPtr newBuffer1(new Buffer); BufferPtr newBuffer2(new Buffer); newBuffer1->bzero(); newBuffer2->bzero();
BufferVector buffersToWrite; buffersToWrite.reserve(16);
while (running_) { assert(newBuffer1 && newBuffer1->length() == 0); assert(newBuffer2 && newBuffer2->length() == 0); assert(buffersToWrite.empty());
{ muduo::MutexLockGuard lock(mutex_);
if (buffers_.empty()) { cond_.waitForSeconds(flushInterval_); }
buffers_.push_back(std::move(currentBuffer_)); currentBuffer_ = std::move(newBuffer1);
buffersToWrite.swap(buffers_);
if (!nextBuffer_) { nextBuffer_ = std::move(newBuffer2); } }
assert(!buffersToWrite.empty());
if (buffersToWrite.size() > 25) { char buf[256]; snprintf(buf, sizeof buf, "Dropped log messages at %s, %zd larger buffers\n", Timestamp::now().toFormattedString().c_str(), buffersToWrite.size()-2); fputs(buf, stderr); output.append(buf, static_cast<int>(strlen(buf))); buffersToWrite.erase(buffersToWrite.begin()+2, buffersToWrite.end()); }
for (const auto& buffer : buffersToWrite) { output.append(buffer->data(), buffer->length()); } if (buffersToWrite.size() > 2) { buffersToWrite.resize(2); }
if (!newBuffer1) { assert(!buffersToWrite.empty()); newBuffer1 = std::move(buffersToWrite.back()); buffersToWrite.pop_back(); newBuffer1->reset(); } if (!newBuffer2) { assert(!buffersToWrite.empty()); newBuffer2 = std::move(buffersToWrite.back()); buffersToWrite.pop_back(); newBuffer2->reset(); }
buffersToWrite.clear(); output.flush(); } output.flush(); }
|
如何使用:
这个AsyncLogging需要配合Logger类使用
LOG_INFO 宏创建 Logger 对象
1 2 3 4
| #define LOG_INFO if (muduo::Logger::logLevel() <= muduo::Logger::INFO) \ muduo::Logger(__FILE__, __LINE__).stream()
LOG_INFO << "hello world";
|
日志会创建一个临时对象,然后这个对象在结束的时候通过g_output将内容添加到currentBuffer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| Logger::~Logger() { impl_.finish(); const LogStream::Buffer& buf(stream().buffer()); g_output(buf.data(), buf.length()); if (impl_.level_ == FATAL) { g_flush(); abort(); } } Logger::OutputFunc g_output = defaultOutput;
void defaultOutput(const char* msg, int len) { size_t n = fwrite(msg, 1, len, stdout); (void)n; }
|
从这里可以看出默认的输出是输出到终端,所以我们在使用的时候需要自定义输出器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include <muduo/base/CurrentThread.h> #include <muduo/base/AsyncLogging.h> #include <muduo/base/Logging.h> #include <muduo/base/Thread.h>
muduo::AsyncLogging* g_asyncLogging = nullptr;
void asyncOutput(const char* msg, int len) { g_asyncLogging->append(msg, len); }
int main() { muduo::AsyncLogging log("log.txt", 0); g_asyncLogging = &log;
muduo::Logger::setOutput(asyncOutput); log.start(); LOG_INFO << "hello world"; muduo::CurrentThread::sleepUsec(1000 * 1000); return 0; }
|