代码来自陈硕开源代码库 muduo中
地址是https://github.com/chenshuo/muduo
#pragma once #include <string> #define NAMESPACE(X) namespace X { #define NAMESPACE_END(X) } NAMESPACE(DEF) class noncopyable { protected: noncopyable() {} private: noncopyable(const noncopyable&) = delete; void operator=(const noncopyable&) = delete; }; const int kSmallBuffer = 4000; const int kLargeBuffer = 4000 * 1000; template<int SIZE> class FixedBuffer : DEF::noncopyable { public: FixedBuffer() : cur_(data_) { setCookie(cookieStart); } ~FixedBuffer() { setCookie(cookieEnd); } void append(const char* /*restrict*/ buf, size_t len) { // FIXME: append partially if (static_cast<size_t>(avail()) > len) { memcpy(cur_, buf, len); cur_ += len; } } const char* data() const { return data_; } int length() const { return static_cast<int>(cur_ - data_); } // write to data_ directly char* current() { return cur_; } int avail() const { return static_cast<int>(end() - cur_); } void add(size_t len) { cur_ += len; } void reset() { cur_ = data_; } void bzero() { ::memset(data_,0, sizeof data_); } void setCookie(void(*cookie)()) { cookie_ = cookie; } std::string asString() const { return std::string(data_, length()); } private: const char* end() const { return data_ + sizeof data_; } // Must be outline function for cookies. static void cookieStart() {}; static void cookieEnd() {}; void(*cookie_)(); char data_[SIZE]; char* cur_; }; class LogStream : DEF::noncopyable { typedef LogStream self; public: typedef FixedBuffer<kSmallBuffer> Buffer; self& operator<<(bool v) { buffer_.append(v ? "1" : "0", 1); return *this; } self& operator<<(short); self& operator<<(unsigned short); self& operator<<(int); self& operator<<(unsigned int); self& operator<<(long); self& operator<<(unsigned long); self& operator<<(long long); self& operator<<(unsigned long long); self& operator<<(const void*); self& operator<<(float v) { *this << static_cast<double>(v); return *this; } self& operator<<(double); // self& operator<<(long double); self& operator<<(char v) { buffer_.append(&v, 1); return *this; } // self& operator<<(signed char); // self& operator<<(unsigned char); self& operator<<(const char* str) { if (str) { buffer_.append(str, strlen(str)); } else { buffer_.append("(null)", 6); } return *this; } self& operator<<(const unsigned char* str) { return operator<<(reinterpret_cast<const char*>(str)); } self& operator<<(const std::string& v) { buffer_.append(v.c_str(), v.size()); return *this; } void append(const char* data, int len) { buffer_.append(data, len); } const Buffer& buffer() const { return buffer_; } void resetBuffer() { buffer_.reset(); } private: template<typename T> void formatInteger(T); Buffer buffer_; static const int kMaxNumericSize = 32; }; class Fmt // : boost::noncopyable { public: template<typename T> Fmt(const char* fmt, T val); const char* data() const { return buf_; } int length() const { return length_; } private: char buf_[32]; int length_; }; inline LogStream& operator<<(LogStream& s, const Fmt& fmt) { s.append(fmt.data(), fmt.length()); return s; } NAMESPACE_END(DEF)
#include "LogStream.h" NAMESPACE(DEF) const char digits[] = "9876543210123456789"; const char* zero = digits + 9; const char digitsHex[] = "0123456789ABCDEF"; // Efficient Integer to String Conversions, by Matthew Wilson. template<typename T> size_t convert(char buf[], T value) { T i = value; char* p = buf; do { int lsd = static_cast<int>(i % 10); i /= 10; *p++ = zero[lsd]; } while (i != 0); if (value < 0) { *p++ = '-'; } *p = '\0'; std::reverse(buf, p); return p - buf; } size_t convertHex(char buf[], uintptr_t value) { uintptr_t i = value; char* p = buf; do { int lsd = static_cast<int>(i % 16); i /= 16; *p++ = digitsHex[lsd]; } while (i != 0); *p = '\0'; std::reverse(buf, p); return p - buf; } //template class FixedBuffer<kSmallBuffer>; //template class FixedBuffer<kLargeBuffer>; template<typename T> void LogStream::formatInteger(T v) { if (buffer_.avail() >= kMaxNumericSize) { size_t len = convert(buffer_.current(), v); buffer_.add(len); } } LogStream& LogStream::operator<<(short v) { *this << static_cast<int>(v); return *this; } LogStream& LogStream::operator<<(unsigned short v) { *this << static_cast<unsigned int>(v); return *this; } LogStream& LogStream::operator<<(int v) { formatInteger(v); return *this; } LogStream& LogStream::operator<<(unsigned int v) { formatInteger(v); return *this; } LogStream& LogStream::operator<<(long v) { formatInteger(v); return *this; } LogStream& LogStream::operator<<(unsigned long v) { formatInteger(v); return *this; } LogStream& LogStream::operator<<(long long v) { formatInteger(v); return *this; } LogStream& LogStream::operator<<(unsigned long long v) { formatInteger(v); return *this; } LogStream& LogStream::operator<<(const void* p) { uintptr_t v = reinterpret_cast<uintptr_t>(p); if (buffer_.avail() >= kMaxNumericSize) { char* buf = buffer_.current(); buf[0] = '0'; buf[1] = 'x'; size_t len = convertHex(buf + 2, v); buffer_.add(len + 2); } return *this; } // FIXME: replace this with Grisu3 by Florian Loitsch. LogStream& LogStream::operator<<(double v) { if (buffer_.avail() >= kMaxNumericSize) { int len = snprintf(buffer_.current(), kMaxNumericSize, "%.12g", v); buffer_.add(len); } return *this; } template<typename T> Fmt::Fmt(const char* fmt, T val) { //BOOST_STATIC_ASSERT(boost::is_arithmetic<T>::value == true); length_ = snprintf(buf_, sizeof buf_, fmt, val); assert(static_cast<size_t>(length_) < sizeof buf_); } // Explicit instantiations /* template Fmt::Fmt(const char* fmt, char); template Fmt::Fmt(const char* fmt, short); template Fmt::Fmt(const char* fmt, unsigned short); template Fmt::Fmt(const char* fmt, int); template Fmt::Fmt(const char* fmt, unsigned int); template Fmt::Fmt(const char* fmt, long); template Fmt::Fmt(const char* fmt, unsigned long); template Fmt::Fmt(const char* fmt, long long); template Fmt::Fmt(const char* fmt, unsigned long long); template Fmt::Fmt(const char* fmt, float); template Fmt::Fmt(const char* fmt, double); */ NAMESPACE_END(DEF)
// LogStreamTest.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include "LogStream.h" #include <chrono> #include <sstream> #include <iostream> using namespace DEF; const int N = 1000000; template<typename T> void benchPrintf(const char* fmt) { char buf[32]; std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::steady_clock::now(); for (int i = 0; i < N; ++i) snprintf(buf, sizeof buf, fmt, (T)(i)); std::chrono::steady_clock::time_point t2 = std::chrono::steady_clock::steady_clock::now(); std::chrono::duration<double> time_span = std::chrono::duration_cast<std::chrono::duration<double>> (t2 - t1); std::cout << "benchPrintf took " << time_span.count() << std::endl<< std::endl; } template<typename T> void benchStringStream() { std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::steady_clock::now(); std::ostringstream os; for (int i = 0; i < N; ++i) { os << (T)(i); os.seekp(0, std::ios_base::beg); } std::chrono::steady_clock::time_point t2 = std::chrono::steady_clock::steady_clock::now(); std::chrono::duration<double> time_span = std::chrono::duration_cast<std::chrono::duration<double>> (t2 - t1); std::cout << "benchStringStream took " <<time_span.count() << std::endl << std::endl; } template<typename T> void benchLogStream() { std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::steady_clock::now(); LogStream os; for (int i = 0; i < N; ++i) { os << (T)(i); os.resetBuffer(); } std::chrono::steady_clock::time_point t2 = std::chrono::steady_clock::steady_clock::now(); std::chrono::duration<double> time_span = std::chrono::duration_cast<std::chrono::duration<double>> (t2 - t1); std::cout << "benchLogStream took " << time_span.count() << std::endl << std::endl; } void PrintTest() { LogStream os; int i = 99; os << "this is a test " << 3.14159265 << 's' << " a " << &i << " " << 3499915646225445522; os << "this is a test " << 3.14159265 << 's' << " a " << &i << " " << 3499915646225445522; os << "this is a test " << 3.14159265 << 's' << " a " << &i << " " << 3499915646225445522; os << "this is a test " << 3.14159265 << 's' << " a " << &i << " " << 3499915646225445522; os << "this is a test " << 3.14159265 << 's' << " a " << &i << " " << 3499915646225445522; os << "this is a test " << 3.14159265 << 's' << " a " << &i << " " << 3499915646225445522; os << "this is a test " << 3.14159265 << 's' << " a " << &i << " " << 3499915646225445522; std::cout << os.buffer().data() << std::endl; } void Bench() { benchPrintf<int>("%d"); puts("int"); benchPrintf<int>("%d"); benchStringStream<int>(); benchLogStream<int>(); puts("double"); benchPrintf<double>("%.12g"); benchStringStream<double>(); benchLogStream<double>(); puts("int64_t"); benchPrintf<int64_t>("%ll"); benchStringStream<int64_t>(); benchLogStream<int64_t>(); puts("void*"); benchPrintf<void*>("%p"); benchStringStream<void*>(); benchLogStream<void*>(); } int main() { Bench(); PrintTest(); return 0; }
主要是在内存中自己开辟了一块内存 然后对基本数据的输入 进行了定义
self& operator<<(short);
self& operator<<(unsigned short);
self& operator<<(int);
self& operator<<(unsigned int);
self& operator<<(long);
self& operator<<(unsigned long);
self& operator<<(long long);
self& operator<<(unsigned long long);
self& operator<<(const char* str);
self& operator<<(const unsigned char* str);
self& operator<<(const std::string& v);
相比C++自带的STREAM 没有继承及太多改写
在胜任基本的输入输出任务 自然效率会高一些
然后基本的输出方面 需要记录写入的时间 日志级别和进程ID和名字等
采用生产消费者模式 多个产生日志的生产者产生日志投入队列,然后仅仅一个消费者写入BUFFER
BUFFER采用两个缓存写入日志进行替换,一个接受消费者写入 一个将日志序列化到磁盘中。
如果速度实在太快 可能考虑再创建一个到两个BUFFER 实践中这种情况应该极小概率发生。
然后日志的根据时间和文件的体积的临界值可自动创建新的日志文件