文章目录
-
- 全流程实现博客链接
- 前引
- (二十)---- C++ High-Performance WebServer源码实现(Logging核心代码部分)
-
- 1、asynclogging.h
- 1、asynclogging.cc
- 2、logfile.h
- 2、logfile.cc
- 3、logging.h
- 3、logging.cc
- 4、logstream.h
- 4、logstream.cc
全流程实现博客链接
从零开始自制实现C++ High-Performance WebServer 全流程记录(基于muduo网络库)
前引
这部分写完了 待会把总结博客写了
就回寝室好好休息 吃顿饭 看部电影休息去啦~
(二十)---- C++ High-Performance WebServer源码实现(Logging核心代码部分)
1、asynclogging.h
#ifndef TINY_MUDUO_ASYNCLOGGING_H_
#define TINY_MUDUO_ASYNCLOGGING_H_#include <vector>
#include <memory>#include "mutex.h"
#include "condition.h"
#include "latch.h"
#include "thread.h"
#include "logging.h"
#include "logstream.h"
#include "noncopyable.h"
#include "logfile.h"namespace tiny_muduo {
static const double kBufferWriteTimeout = 3.0;
static const int64_t kSingleFileMaximumSize = 1024 * 1024 * 1024;class AsyncLogging : public NonCopyAble {
public:typedef FixedBuffer<kLargeSize> Buffer;typedef std::unique_ptr<Buffer> BufferPtr;typedef std::vector<BufferPtr> BufferVector;typedef std::unique_ptr<LogFile> LogFilePtr;AsyncLogging(const char* filepath = nullptr) : running_(false),filepath_(filepath),mutex_(),cond_(mutex_),latch_(1),thread_(std::bind(&AsyncLogging::ThreadFunc, this), "AsyncLogThread"),current_(new Buffer()),next_(new Buffer()) {
}~AsyncLogging() {
if (running_) {
Stop();} }void Stop() {
running_ = false;cond_.Notify();thread_.Join();}void StartAsyncLogging() {
running_ = true;thread_.StartThread(); latch_.Wait();}void Append(const char* data, int len);void Flush();void ThreadFunc();private:bool running_;const char* filepath_;MutexLock mutex_;Condition cond_;Latch latch_;Thread thread_;BufferPtr current_;BufferPtr next_; BufferVector buffers_to_write_;
};} // namespace tiny_muduo#endif
1、asynclogging.cc
#include "asynclogging.h"#include <functional>
#include <utility>
#include <memory>using namespace tiny_muduo;void AsyncLogging::Append(const char* data, int len) {
MutexLockGuard guard(mutex_);if (current_->writablebytes() >= len) {
current_->Append(data, len);} else {
buffers_to_write_.emplace_back(std::move(current_)); if (next_) {
current_ = std::move(next_);} else {
current_.reset(new Buffer());}cond_.Notify();}
}void AsyncLogging::Flush() {
fflush(stdout);
}void AsyncLogging::ThreadFunc() {
latch_.CountDown();BufferPtr newbuffer_current(new Buffer());BufferPtr newbuffer_next(new Buffer());LogFilePtr log(new LogFile(filepath_));newbuffer_current->SetBufferZero();newbuffer_next->SetBufferZero();BufferVector buffers;while (running_) {
{
MutexLockGuard guard(mutex_);if (buffers_to_write_.empty()) {
cond_.WaitForFewSeconds(kBufferWriteTimeout);}buffers_to_write_.emplace_back(std::move(current_));buffers.swap(buffers_to_write_);current_ = std::move(newbuffer_current);if (!next_) {
next_ = std::move(newbuffer_next);}}for (const auto& buffer : buffers) {
log->Write(buffer->data(), buffer->len());}if (log->writtenbytes() >= kSingleFileMaximumSize) {
log.reset(new LogFile(filepath_));} if (buffers.size() > 2) {
buffers.resize(2);}if (!newbuffer_current) {
newbuffer_current = std::move(buffers.back());buffers.pop_back();newbuffer_current->SetBufferZero();}if (!newbuffer_next) {
newbuffer_current = std::move(buffers.back());buffers.pop_back();newbuffer_current->SetBufferZero();}buffers.clear();}
}
2、logfile.h
#ifndef TINY_MUDUO_LOGFILE_H_
#define TINY_MUDUO_LOGFILE_H_#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>#include <string>#include "timestamp.h"namespace tiny_muduo {
static const time_t kFlushInterval = 3;class LogFile {
public:LogFile(const char* filepath);~LogFile();void Write(const char* data, int len);void Flush() {
fflush(fp_); }int64_t writtenbytes() const {
return written_bytes_; }private:FILE* fp_; int64_t written_bytes_;time_t lastwrite_;time_t lastflush_;
};}#endif
2、logfile.cc
#include "logfile.h"using namespace tiny_muduo;LogFile::LogFile(const char* filepath = nullptr): fp_(::fopen(filepath, "ae")),written_bytes_(0),lastwrite_(0),lastflush_(0) {
if (!fp_) {
std::string DefaultPath = std::move("./LogFiles/LogFile_" +Timestamp::Now().Timestamp::ToFormattedDefaultLogString() +".log");fp_ = ::fopen(DefaultPath.data(), "ae");}
}LogFile::~LogFile() {
Flush();fclose(fp_);
}void LogFile::Write(const char* data, int len) {
int pos = 0;while (pos != len) {
pos += static_cast<int>(fwrite_unlocked(data + pos, sizeof(char), len - pos, fp_));}time_t now = ::time(nullptr);if (len != 0) {
lastwrite_ = now;written_bytes_ += len;}if (lastwrite_ - lastflush_ > kFlushInterval) {
Flush();lastflush_ = now;}
}
3、logging.h
#ifndef TINY_MUDUO_LOGGING_H_
#define TINY_MUDUO_LOGGING_H_#include <stdio.h>
#include <sys/time.h>
#include <errno.h>
#include <string.h>#include <cstring>#include "timestamp.h"
#include "logstream.h"
#include "currentthread.h"
#include "noncopyable.h"namespace tiny_muduo {
class SourceClass {
public:SourceClass(const char* data) : data_(data), len_(static_cast<int>(strlen(data_))) {
const char* forward_slash = strrchr(data, '/');if (forward_slash) {
data_ = forward_slash + 1;len_ -= static_cast<int>((data_ - data)); } } const char* data_;int len_;
};class Logger : public NonCopyAble {
public:enum Level {
DEBUG,INFO,WARN,ERROR,FATAL};Logger(const char* file_, int line, Level level) : implement_(file_, line, level) {
}LogStream& stream() {
return implement_.stream(); }typedef void (*OutputFunc)(const char* data, int len);typedef void (*FlushFunc)();private:class Implement : public NonCopyAble {
public:typedef Logger::Level Level;Implement(SourceClass&& source, int line, Level level); ~Implement();void FormattedTime();const char* GetLogLevel() const;void Finish() {
stream_ << " - "<< GeneralTemplate(fileinfo_.data_, fileinfo_.len_) << ':' << line_ << '\n'; }LogStream& stream() {
return stream_; }private:SourceClass fileinfo_;int line_; Level level_;LogStream stream_;};Implement implement_;
};} //namespace tiny_muduotiny_muduo::Logger::Level LogLevel();
void SetLogLevel(tiny_muduo::Logger::Level nowlevel);
const char* ErrorToString(int err);#define LOG_DEBUG if (LogLevel() <= tiny_muduo::Logger::DEBUG) \tiny_muduo::Logger(__FILE__, __LINE__, tiny_muduo::Logger::DEBUG).stream()
#define LOG_INFO if (LogLevel() <= tiny_muduo::Logger::INFO) \tiny_muduo::Logger(__FILE__, __LINE__, tiny_muduo::Logger::INFO).stream()
#define LOG_WARN tiny_muduo::Logger(__FILE__, __LINE__, tiny_muduo::Logger::WARN).stream()
#define LOG_ERROR tiny_muduo::Logger(__FILE__, __LINE__, tiny_muduo::Logger::ERROR).stream()
#define LOG_FATAL tiny_muduo::Logger(__FILE__, __LINE__, tiny_muduo::Logger::FATAL).stream()#endif
3、logging.cc
#include "logging.h"#include <utility>using namespace tiny_muduo;
using namespace CurrentThread;namespace CurrentThread {
__thread time_t t_lastsecond;
__thread char t_time[64];
__thread const int t_formattedtimeLength = 18;
__thread char t_errorbuf[512];} // namespace CurrentThreadconst char* ErrorToString(int err) {
return strerror_r(err, t_errorbuf, sizeof(t_errorbuf));
}static const int kLogLevelStringLength = 6;static void DefaultOutput(const char* data, int len) {
fwrite(data, len, sizeof(char), stdout);
}static void DefaultFlush() {
fflush(stdout);
}Logger::OutputFunc g_Output = DefaultOutput;
Logger::FlushFunc g_Flush = DefaultFlush;
Logger::Level g_level = Logger::Level::INFO;void SetOutputFunc(Logger::OutputFunc func) {
g_Output = func;
}void SetFlushFunc(Logger::FlushFunc func) {
g_Flush = func;
}Logger::Level LogLevel() {
return g_level; }
void SetLogLevel(tiny_muduo::Logger::Level nowlevel) {
g_level = nowlevel; }Logger::Implement::Implement(SourceClass&& source, int line, Level level) : fileinfo_(std::move(source)),line_(line),level_(level) {
FormattedTime();CurrentThread::tid();
} Logger::Implement::~Implement() {
Finish();const LogStream::Buffer& buffer = stream_.buffer();g_Output(buffer.data(), buffer.len());
}const char* Logger::Implement::GetLogLevel() const {
switch(level_) {
case DEBUG:return "DEBUG ";case INFO:return "INFO ";case WARN:return "WARN ";case ERROR:return "ERROR ";case FATAL:return "FATAL ";} return nullptr;
} void Logger::Implement::FormattedTime() {
Timestamp now = Timestamp::Now();time_t seconds = static_cast<time_t>(now.microseconds() / kMicrosecond2Second);int microseconds = static_cast<int>(now.microseconds() % kMicrosecond2Second);if (t_lastsecond != seconds) {
struct tm tm_time;localtime_r(&seconds, &tm_time);snprintf(t_time, sizeof(t_time), "%4d%02d%02d %02d:%02d:%02d.",tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec);t_lastsecond = seconds;}char buf[32] = {
0};int microlen = snprintf(buf, sizeof(buf), "%06d ", microseconds);stream_ << GeneralTemplate(t_time, t_formattedtimeLength) << GeneralTemplate(buf, microlen);stream_ << GeneralTemplate(CurrentThread::t_formattedTid, CurrentThread::t_formattedTidLength);stream_ << GeneralTemplate(GetLogLevel(), kLogLevelStringLength);
}
4、logstream.h
#ifndef TINY_MUDUO_LOGSTREAM_H_
#define TINY_MUDUO_LOGSTREAM_H_#include <stdio.h>
#include <string.h>#include <string>
#include <algorithm>#include "noncopyable.h"namespace tiny_muduo {
const int kSmallSize = 4096;
const int kLargeSize = 4096 * 1000;
static const int kNum2MaxStringLength = 48;
static const char digits[] = {
'9', '8', '7', '6', '5', '4', '3', '2', '1', '0','1', '2', '3', '4', '5', '6', '7', '8', '9'}; class GeneralTemplate : public NonCopyAble {
public:GeneralTemplate() : data_(nullptr), len_(0) {
}explicit GeneralTemplate(const char* data, int len) : data_(data), len_(len) {
} const char* data_;int len_;
};template <int SIZE>
class FixedBuffer : public NonCopyAble {
public:FixedBuffer();~FixedBuffer();static void CookieStart();static void CookieEnd();void Append(const char* input_data, int length) {
if (writablebytes() < length) {
length = writablebytes();}memcpy(cur_, input_data, length);cur_ += length;}void SetBufferZero() {
memset(buf_, '\0', sizeof(buf_));cur_ = buf_;}void SetCookie(void (*cookie)()) {
cookie_ = cookie; } void Add(int length) {
cur_ += length; }const char* data() const {
return buf_; }int len() const {
return static_cast<int>(cur_ - buf_); }int writablebytes() const {
return static_cast<int>(end() - cur_); }char* current() const {
return cur_; }const char* end() const {
return buf_ + sizeof(buf_); } private:void (*cookie_)();char buf_[SIZE]; char* cur_;
};class LogStream {
public:typedef FixedBuffer<kSmallSize> Buffer;typedef LogStream Self;LogStream() {
}~LogStream() {
}Buffer& buffer() {
return buffer_; }template <typename T>void FormatInteger(T num) {
if (buffer_.writablebytes() >= kNum2MaxStringLength) {
char* buf = buffer_.current();char* now = buf;const char* zero = digits + 9;bool negative = num < 0;do {
int remainder = static_cast<int>(num % 10); *(now++) = zero[remainder];num /= 10; } while (num != 0); if (negative) *(now++) = '-';*now = '\0';std::reverse(buf, now); buffer_.Add(static_cast<int>(now - buf));} }Self& operator<<(short num) {
return (*this) << static_cast<int>(num); }Self& operator<<(unsigned short num) {
return (*this) << static_cast<unsigned int>(num); } Self& operator<<(int num) {
FormatInteger(num); return *this;} Self& operator<<(unsigned int num) {
FormatInteger(num); return *this;}Self& operator<<(long num) {
FormatInteger(num);return *this;} Self& operator<<(unsigned long num) {
FormatInteger(num);return *this;}Self& operator<<(long long num) {
FormatInteger(num);return *this;}Self& operator<<(unsigned long long num) {
FormatInteger(num);return *this;}Self& operator<<(const float& num) {
return (*this) << static_cast<double>(num);}Self& operator<<(const double& num) {
char buf[32];int len = snprintf(buf, sizeof(buf), "%g", num); buffer_.Append(buf, len);return *this;}Self& operator<<(bool boolean) {
return (*this) << (boolean ? 1 : 0);}Self& operator<<(char chr) {
buffer_.Append(&chr, 1);return *this;}Self& operator<<(const void* data) {
return (*this) << static_cast<const char*>(data); }Self& operator<<(const char* data) {
buffer_.Append(data, static_cast<int>(strlen(data)));return *this;}Self& operator<<(const GeneralTemplate& source) {
buffer_.Append(source.data_, source.len_);return *this;}Self& operator<<(const std::string& str) {
buffer_.Append(str.data(), static_cast<int>(str.size()));return *this;}private:Buffer buffer_;};}#endif
4、logstream.cc
#include "logstream.h"using namespace tiny_muduo;template <int SIZE>
void FixedBuffer<SIZE>::CookieStart() {
}template <int SIZE>
void FixedBuffer<SIZE>::CookieEnd() {
}template <int SIZE>
FixedBuffer<SIZE>::FixedBuffer() : cur_(buf_) {
SetCookie(CookieStart);
}template <int SIZE>
FixedBuffer<SIZE>::~FixedBuffer() {
SetCookie(CookieEnd);
}template class FixedBuffer<kLargeSize>;
template class FixedBuffer<kSmallSize>;