本文参考自陈硕老师的muduo网络库,删减了繁多的依赖文件,精简如此,供更多的人参考学习(感谢陈硕老师)。
copyable.h
#ifndef MUDUO_BASE_COPYABLE_H
#define MUDUO_BASE_COPYABLE_H
namespace muduo
{
/// A tag class emphasises the objects are copyable.
/// The empty base class optimization applies.
/// Any derived class of copyable should be a value type.
class copyable
{
protected:
copyable() = default;
~copyable() = default;
};
} // namespace muduo
#endif // MUDUO_BASE_COPYABLE_H
noncopyable.h
#ifndef MUDUO_BASE_NONCOPYABLE_H
#define MUDUO_BASE_NONCOPYABLE_H
namespace muduo
{
class noncopyable
{
public:
noncopyable(const noncopyable&) = delete;
void operator=(const noncopyable&) = delete;
protected:
noncopyable() = default;
~noncopyable() = default;
};
} // namespace muduo
#endif // MUDUO_BASE_NONCOPYABLE_H
Logging.h
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
//
// Author: Shuo Chen (chenshuo at chenshuo dot com)
#ifndef MUDUO_BASE_LOGGING_H
#define MUDUO_BASE_LOGGING_H
#include "LogStream.h"
namespace muduo
{
class TimeZone;
class Logger
{
public:
enum LogLevel
{
TRACE,
DEBUG,
INFO,
WARN,
ERROR,
FATAL,
NUM_LOG_LEVELS,
};
// compile time calculation of basename of source file
class SourceFile
{
public:
template<int N>
SourceFile(const char (&arr)[N])
: data_(arr),
size_(N-1)
{
const char* slash = strrchr(data_, '/'); // builtin function
if (slash)
{
data_ = slash + 1;
size_ -= static_cast<int>(data_ - arr);
}
}
explicit SourceFile(const char* filename)
: data_(filename)
{
const char* slash = strrchr(filename, '/');
if (slash)
{
data_ = slash + 1;
}
size_ = static_cast<int>(strlen(data_));
}
const char* data_;
int size_;
};
Logger(SourceFile file, int line);
Logger(SourceFile file, int line, LogLevel level);
Logger(SourceFile file, int line, LogLevel level, const char* func);
Logger(SourceFile file, int line, bool toAbort);
~Logger();
LogStream& stream() { return impl_.stream_; }
static LogLevel logLevel();
static void setLogLevel(LogLevel level);
typedef void (*OutputFunc)(const char* msg, int len);
typedef void (*FlushFunc)();
static void setOutput(OutputFunc);
static void setFlush(FlushFunc);
private:
class Impl
{
public:
typedef Logger::LogLevel LogLevel;
Impl(LogLevel level, int old_errno, const SourceFile& file, int line);
void formatTime();
void finish();
time_t time_;
LogStream stream_;
LogLevel level_;
int line_;
SourceFile basename_;
};
Impl impl_;
};
extern Logger::LogLevel g_logLevel;
inline Logger::LogLevel Logger::logLevel()
{
return g_logLevel;
}
//
// CAUTION: do not write:
//
// if (good)
// LOG_INFO << "Good news";
// else
// LOG_WARN << "Bad news";
//
// this expends to
//
// if (good)
// if (logging_INFO)
// logInfoStream << "Good news";
// else
// logWarnStream << "Bad news";
//
#define LOG_TRACE if (muduo::Logger::logLevel() <= muduo::Logger::TRACE) \
muduo::Logger(__FILE__, __LINE__, muduo::Logger::TRACE, __func__).stream()
#define LOG_DEBUG if (muduo::Logger::logLevel() <= muduo::Logger::DEBUG) \
muduo::Logger(__FILE__, __LINE__, muduo::Logger::DEBUG, __func__).stream()
#define LOG_INFO if (muduo::Logger::logLevel() <= muduo::Logger::INFO) \
muduo::Logger(__FILE__, __LINE__).stream()
#define LOG_WARN muduo::Logger(__FILE__, __LINE__, muduo::Logger::WARN).stream()
#define LOG_ERROR muduo::Logger(__FILE__, __LINE__, muduo::Logger::ERROR).stream()
#define LOG_FATAL muduo::Logger(__FILE__, __LINE__, muduo::Logger::FATAL).stream()
#define LOG_SYSERR muduo::Logger(__FILE__, __LINE__, false).stream()
#define LOG_SYSFATAL muduo::Logger(__FILE__, __LINE__, true).stream()
const char* strerror_tl(int savedErrno);
// Taken from glog/logging.h
//
// Check that the input is non NULL. This very useful in constructor
// initializer lists.
#define CHECK_NOTNULL(val) \
::muduo::CheckNotNull(__FILE__, __LINE__, "'" #val "' Must be non NULL", (val))
// A small helper for CHECK_NOTNULL().
template <typename T>
T* CheckNotNull(Logger::SourceFile file, int line, const char *names, T* ptr)
{
if (ptr == NULL)
{
Logger(file, line, Logger::FATAL).stream() << names;
}
return ptr;
}
} // namespace muduo
#endif // MUDUO_BASE_LOGGING_H
Logging.cc
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
//
// Author: Shuo Chen (chenshuo at chenshuo dot com)
#include "Logging.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <thread>
#include <sstream>
namespace muduo
{
/*
class LoggerImpl
{
public:
typedef Logger::LogLevel LogLevel;
LoggerImpl(LogLevel level, int old_errno, const char* file, int line);
void finish();
Timestamp time_;
LogStream stream_;
LogLevel level_;
int line_;
const char* fullname_;
const char* basename_;
};
*/
__thread char t_errnobuf[512];
__thread char t_time[64];
__thread time_t t_lastSecond;
const char* strerror_tl(int savedErrno)
{
return strerror_r(savedErrno, t_errnobuf, sizeof t_errnobuf);
}
Logger::LogLevel initLogLevel()
{
if (::getenv("MUDUO_LOG_TRACE"))
return Logger::TRACE;
else if (::getenv("MUDUO_LOG_DEBUG"))
return Logger::DEBUG;
else
return Logger::INFO;
}
Logger::LogLevel g_logLevel = initLogLevel();
const char* LogLevelName[Logger::NUM_LOG_LEVELS] =
{
"TRACE ",
"DEBUG ",
"INFO ",
"WARN ",
"ERROR ",
"FATAL ",
};
// helper class for known string length at compile time
class T
{
public:
T(const char* str, unsigned len)
:str_(str),
len_(len)
{
assert(strlen(str) == len_);
}
const char* str_;
const unsigned len_;
};
inline LogStream& operator<<(LogStream& s, T v)
{
s.append(v.str_, v.len_);
return s;
}
inline LogStream& operator<<(LogStream& s, const Logger::SourceFile& v)
{
s.append(v.data_, v.size_);
return s;
}
void defaultOutput(const char* msg, int len)
{
size_t n = fwrite(msg, 1, len, stdout);
//FIXME check n
(void)n;
}
void defaultFlush()
{
fflush(stdout);
}
Logger::OutputFunc g_output = defaultOutput;
Logger::FlushFunc g_flush = defaultFlush;
} // namespace muduo
using namespace muduo;
Logger::Impl::Impl(LogLevel level, int savedErrno, const SourceFile& file, int line)
: time_(time(nullptr)),
stream_(),
level_(level),
line_(line),
basename_(file)
{
formatTime();
stringstream ss;
ss << this_thread::get_id();
stream_ << "T(" << ss.str() << ") ";
stream_ << T(LogLevelName[level], 6);
stream_ << basename_ << ':' << line_ << " ";
if (savedErrno != 0)
{
stream_ << strerror_tl(savedErrno) << " (errno=" << savedErrno << ") ";
}
}
void Logger::Impl::formatTime()
{
char tmp[64] = {0};
strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S ",localtime(&time_));
stream_ << tmp;
}
void Logger::Impl::finish()
{
stream_ /*<< " - " << basename_ << ':' << line_*/ << '\n';
}
Logger::Logger(SourceFile file, int line)
: impl_(INFO, 0, file, line)
{
}
Logger::Logger(SourceFile file, int line, LogLevel level, const char* func)
: impl_(level, 0, file, line)
{
// impl_.stream_ << func << ' ';
}
Logger::Logger(SourceFile file, int line, LogLevel level)
: impl_(level, 0, file, line)
{
}
Logger::Logger(SourceFile file, int line, bool toAbort)
: impl_(toAbort?FATAL:ERROR, errno, file, line)
{
}
Logger::~Logger()
{
impl_.finish();
const LogStream::Buffer& buf(stream().buffer());
g_output(buf.data(), buf.length());
if (impl_.level_ == FATAL)
{
g_flush();
abort();
}
}
void Logger::setLogLevel(Logger::LogLevel level)
{
g_logLevel = level;
}
void Logger::setOutput(OutputFunc out)
{
g_output = out;
}
void Logger::setFlush(FlushFunc flush)
{
g_flush = flush;
}
LogStream.h
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
//
// Author: Shuo Chen (chenshuo at chenshuo dot com)
#ifndef MUDUO_BASE_LOGSTREAM_H
#define MUDUO_BASE_LOGSTREAM_H
#include "noncopyable.h"
#include <assert.h>
#include <string.h> // memcpy
#include <iostream>
using namespace std;
template<typename To, typename From>
inline To implicit_cast(From const &f)
{
return f;
}
namespace muduo
{
namespace detail
{
const int kSmallBuffer = 4000;
const int kLargeBuffer = 4000*1000;
template<int SIZE>
class FixedBuffer : noncopyable
{
public:
FixedBuffer()
: cur_(data_)
{
setCookie(cookieStart);
}
~FixedBuffer()
{
setCookie(cookieEnd);
}
void append(const char* /*restrict*/ buf, size_t len)
{
// FIXME: append partially
if (implicit_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_); }
// for used by GDB
const char* debugString();
void setCookie(void (*cookie)()) { cookie_ = cookie; }
// for used by unit test
string toString() const { return string(data_, length()); }
// StringPiece toStringPiece() const { return StringPiece(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_;
};
} // namespace detail
class LogStream : noncopyable
{
typedef LogStream self;
public:
typedef detail::FixedBuffer<detail::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 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:
void staticCheck();
template<typename T>
void formatInteger(T);
Buffer buffer_;
static const int kMaxNumericSize = 32;
};
class Fmt // : 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;
}
// Format quantity n in SI units (k, M, G, T, P, E).
// The returned string is atmost 5 characters long.
// Requires n >= 0
string formatSI(int64_t n);
// Format quantity n in IEC (binary) units (Ki, Mi, Gi, Ti, Pi, Ei).
// The returned string is atmost 6 characters long.
// Requires n >= 0
string formatIEC(int64_t n);
} // namespace muduo
#endif // MUDUO_BASE_LOGSTREAM_H
LogStream.cc
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
//
// Author: Shuo Chen (chenshuo at chenshuo dot com)
#include "LogStream.h"
#include <algorithm>
#include <limits>
#include <type_traits>
#include <assert.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#include <inttypes.h>
using namespace muduo;
using namespace muduo::detail;
// TODO: better itoa.
#if defined(__clang__)
#pragma clang diagnostic ignored "-Wtautological-compare"
#else
#pragma GCC diagnostic ignored "-Wtype-limits"
#endif
namespace muduo
{
namespace detail
{
const char digits[] = "9876543210123456789";
const char* zero = digits + 9;
static_assert(sizeof(digits) == 20, "wrong number of digits");
const char digitsHex[] = "0123456789ABCDEF";
static_assert(sizeof digitsHex == 17, "wrong number of digitsHex");
// 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>;
} // namespace detail
/*
Format a number with 5 characters, including SI units.
[0, 999]
[1.00k, 999k]
[1.00M, 999M]
[1.00G, 999G]
[1.00T, 999T]
[1.00P, 999P]
[1.00E, inf)
*/
std::string formatSI(int64_t s)
{
double n = static_cast<double>(s);
char buf[64];
if (s < 1000)
snprintf(buf, sizeof(buf), "%" PRId64, s);
else if (s < 9995)
snprintf(buf, sizeof(buf), "%.2fk", n/1e3);
else if (s < 99950)
snprintf(buf, sizeof(buf), "%.1fk", n/1e3);
else if (s < 999500)
snprintf(buf, sizeof(buf), "%.0fk", n/1e3);
else if (s < 9995000)
snprintf(buf, sizeof(buf), "%.2fM", n/1e6);
else if (s < 99950000)
snprintf(buf, sizeof(buf), "%.1fM", n/1e6);
else if (s < 999500000)
snprintf(buf, sizeof(buf), "%.0fM", n/1e6);
else if (s < 9995000000)
snprintf(buf, sizeof(buf), "%.2fG", n/1e9);
else if (s < 99950000000)
snprintf(buf, sizeof(buf), "%.1fG", n/1e9);
else if (s < 999500000000)
snprintf(buf, sizeof(buf), "%.0fG", n/1e9);
else if (s < 9995000000000)
snprintf(buf, sizeof(buf), "%.2fT", n/1e12);
else if (s < 99950000000000)
snprintf(buf, sizeof(buf), "%.1fT", n/1e12);
else if (s < 999500000000000)
snprintf(buf, sizeof(buf), "%.0fT", n/1e12);
else if (s < 9995000000000000)
snprintf(buf, sizeof(buf), "%.2fP", n/1e15);
else if (s < 99950000000000000)
snprintf(buf, sizeof(buf), "%.1fP", n/1e15);
else if (s < 999500000000000000)
snprintf(buf, sizeof(buf), "%.0fP", n/1e15);
else
snprintf(buf, sizeof(buf), "%.2fE", n/1e18);
return buf;
}
/*
[0, 1023]
[1.00Ki, 9.99Ki]
[10.0Ki, 99.9Ki]
[ 100Ki, 1023Ki]
[1.00Mi, 9.99Mi]
*/
std::string formatIEC(int64_t s)
{
double n = static_cast<double>(s);
char buf[64];
const double Ki = 1024.0;
const double Mi = Ki * 1024.0;
const double Gi = Mi * 1024.0;
const double Ti = Gi * 1024.0;
const double Pi = Ti * 1024.0;
const double Ei = Pi * 1024.0;
if (n < Ki)
snprintf(buf, sizeof buf, "%" PRId64, s);
else if (n < Ki*9.995)
snprintf(buf, sizeof buf, "%.2fKi", n / Ki);
else if (n < Ki*99.95)
snprintf(buf, sizeof buf, "%.1fKi", n / Ki);
else if (n < Ki*1023.5)
snprintf(buf, sizeof buf, "%.0fKi", n / Ki);
else if (n < Mi*9.995)
snprintf(buf, sizeof buf, "%.2fMi", n / Mi);
else if (n < Mi*99.95)
snprintf(buf, sizeof buf, "%.1fMi", n / Mi);
else if (n < Mi*1023.5)
snprintf(buf, sizeof buf, "%.0fMi", n / Mi);
else if (n < Gi*9.995)
snprintf(buf, sizeof buf, "%.2fGi", n / Gi);
else if (n < Gi*99.95)
snprintf(buf, sizeof buf, "%.1fGi", n / Gi);
else if (n < Gi*1023.5)
snprintf(buf, sizeof buf, "%.0fGi", n / Gi);
else if (n < Ti*9.995)
snprintf(buf, sizeof buf, "%.2fTi", n / Ti);
else if (n < Ti*99.95)
snprintf(buf, sizeof buf, "%.1fTi", n / Ti);
else if (n < Ti*1023.5)
snprintf(buf, sizeof buf, "%.0fTi", n / Ti);
else if (n < Pi*9.995)
snprintf(buf, sizeof buf, "%.2fPi", n / Pi);
else if (n < Pi*99.95)
snprintf(buf, sizeof buf, "%.1fPi", n / Pi);
else if (n < Pi*1023.5)
snprintf(buf, sizeof buf, "%.0fPi", n / Pi);
else if (n < Ei*9.995)
snprintf(buf, sizeof buf, "%.2fEi", n / Ei );
else
snprintf(buf, sizeof buf, "%.1fEi", n / Ei );
return buf;
}
} // namespace muduo
template<int SIZE>
const char* FixedBuffer<SIZE>::debugString()
{
*cur_ = '\0';
return data_;
}
template<int SIZE>
void FixedBuffer<SIZE>::cookieStart()
{
}
template<int SIZE>
void FixedBuffer<SIZE>::cookieEnd()
{
}
void LogStream::staticCheck()
{
static_assert(kMaxNumericSize - 10 > std::numeric_limits<double>::digits10,
"kMaxNumericSize is large enough");
static_assert(kMaxNumericSize - 10 > std::numeric_limits<long double>::digits10,
"kMaxNumericSize is large enough");
static_assert(kMaxNumericSize - 10 > std::numeric_limits<long>::digits10,
"kMaxNumericSize is large enough");
static_assert(kMaxNumericSize - 10 > std::numeric_limits<long long>::digits10,
"kMaxNumericSize is large enough");
}
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)
{
static_assert(std::is_arithmetic<T>::value == true, "Must be arithmetic type");
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);
测试代码如下:
#include "Logging.h"
FILE* g_logFile = nullptr;
void output(const char* msg, int len)
{
fwrite(msg, 1, len, g_logFile);
fflush(g_logFile);
}
void initialize()
{
g_logFile = fopen("debuglog.txt", "w");
if(!g_logFile)
g_logFile = stdout;
muduo::Logger::setOutput(output);
muduo::Logger::setLogLevel(muduo::Logger::DEBUG);
}
void fun()
{
int num1 = 1993;
long long num2 = 69542354126;
unsigned num3 = 15;
string str1 = "Hello world!";
char* str2 = "How are you!";
char c = 'a';
LOG_INFO << str1 << " " << num1;
LOG_DEBUG << str2 << " " << num2;
LOG_DEBUG << c << " " << num3;
}
int main()
{
initialize();
fun();
return 0;
}