日志作用
(1)开发过程中:调试错误,更好的理解程序。
(2)运行过程中:诊断系统故障并处理,记录系统运行状态。
日志级别
(1)TRACE
指出比DEBUG粒度更细的一些信息事件。(开发过程中使用)
(2)DEBUG
指出细粒度信息事件对调试应用程序是非常有帮助的。(开发过程中使用)
(3)INFO
表明消息在粗粒度级别上突出强调应用程序的运行过程。
(4)WARN
系统能正常运行,但可能会出现潜在错误的情形。
(5)ERROR
指出虽然发生错误事件,但仍然不影响系统的继续运行。
(6)FATAL
指出每个严重的错误事件将会导致应用程序的终止。
Logger使用时序图
LogStream类图
调用
自已重新整理了调用的逻辑图:
调用时,直接使用宏来调用,而具体调用Logger类的哪个构造函数则通过全局变量g_logLevel来确定,由于枚举类型的声明在Logger类中,所以g_logLevel的类型为enum Logger::LogLevel
,enum可以省略:
muduo库的日志默认是INFO级别(从全局函数initLogLevel()中可以看出),即全局变量g_logLevel的默认值为Logger::INFO。又因为INFO、TRACE、DEBUG只能三选一,所以如果要修改g_logLevel,可以通过initLogLevel(),比如:
Logger::LogLevel initLogLevel()
{
return Logger::TRACE;
/*
if (::getenv("MUDUO_LOG_TRACE"))
return Logger::TRACE;
else if (::getenv("MUDUO_LOG_DEBUG"))
return Logger::DEBUG;
else
return Logger::INFO;
*/
}
当然,修改环境变量也可以。
还有一种方式就是通过Logger类的静态成员函数static void setLogLevel(LogLevel level);
写入日志的字符串的拼接过程
还有一个地方值得分析一下,就是写入日志的字符串的拼接过程。
1.第一次发生在Impl类的构造函数的函数体中:
2.第二次还是发生在Impl类的构造函数的函数体中:
3.第三次发生在调用时,比如:
LOG_INFO<<"info ...";
4.第四次发生在Logger类的析构函数中:
这样就完成了字符串的拼接,最后再调用g_output函数将其从buffer_中输出到实际设备。
源码
Logging.h
#ifndef MUDUO_BASE_LOGGING_H
#define MUDUO_BASE_LOGGING_H
#include <muduo/base/LogStream.h>
#include <muduo/base/Timestamp.h>
namespace muduo
{
class Logger
{
public:
//枚举类型LogLevel(enum可省略),类外可直接通过Logger::
enum LogLevel
{
TRACE,
DEBUG,
INFO,
WARN,
ERROR,
FATAL,
NUM_LOG_LEVELS,
};
// compile time calculation of basename of source file
class SourceFile//Logger类中的嵌套类
{
public:
template<int N>
inline 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, '/');// char *strrchr(const char *str, int c) 在参数 str 所指向的字符串中搜索最后一次出现字符 c(一个无符号字符)的位置
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_; }//返回LogStream类的对象
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//Logger类中的嵌套类
{
public:
typedef Logger::LogLevel LogLevel;
Impl(LogLevel level, int old_errno, const SourceFile& file, int line);
void formatTime();
void finish();
Timestamp time_;
LogStream stream_;//Impl类中包含一个LogStream类的对象
LogLevel level_;
int line_;
SourceFile basename_;
};
Impl impl_;//Logger类中包含一个Impl类的对象
};//Logger类
extern Logger::LogLevel g_logLevel;//定义在Logging.cc中
inline Logger::LogLevel Logger::logLevel()
{
return g_logLevel;
}
//__FILE__表示当前行代码所在文件名,__LINE__表示当前行代码所在的行号
#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;
}
}
#endif // MUDUO_BASE_LOGGING_H
Logging.cc
#include <muduo/base/Logging.h>
#include <muduo/base/CurrentThread.h>
#include <muduo/base/StringPiece.h>
#include <muduo/base/Timestamp.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sstream>
namespace muduo
{
__thread char t_errnobuf[512];
__thread char t_time[32];
__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();//定义全局变量g_logLevel并初始化,类型为Logger::LogLevel(这是自定义的枚举类型)
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;//创建函数指针g_output(全局变量)
Logger::FlushFunc g_flush = defaultFlush;//创建函数指针g_flush(全局变量)
}//namespace muduo
using namespace muduo;
Logger::Impl::Impl(LogLevel level, int savedErrno, const SourceFile& file, int line)
: time_(Timestamp::now()),
stream_(),
level_(level),
line_(line),
basename_(file)
{
formatTime();
CurrentThread::tid();
stream_ << T(CurrentThread::tidString(), 6);
stream_ << T(LogLevelName[level], 6);
if (savedErrno != 0)
{
stream_ << strerror_tl(savedErrno) << " (errno=" << savedErrno << ") ";
}
}
void Logger::Impl::formatTime()
{
int64_t microSecondsSinceEpoch = time_.microSecondsSinceEpoch();
time_t seconds = static_cast<time_t>(microSecondsSinceEpoch / 1000000);
int microseconds = static_cast<int>(microSecondsSinceEpoch % 1000000);
if (seconds != t_lastSecond)
{
t_lastSecond = seconds;
struct tm tm_time;
::gmtime_r(&seconds, &tm_time); // FIXME TimeZone::fromUtcTime
int len = 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);
assert(len == 17); (void)len;
}
Fmt us(".%06dZ ", microseconds);
assert(us.length() == 9);
stream_ << T(t_time, 17) << T(us.data(), 9);
}
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
#ifndef MUDUO_BASE_LOGSTREAM_H
#define MUDUO_BASE_LOGSTREAM_H
#include <muduo/base/StringPiece.h>
#include <muduo/base/Types.h>
#include <assert.h>
#include <string.h> // memcpy
#ifndef MUDUO_STD_STRING
#include <string>
#endif
#include <boost/noncopyable.hpp>
namespace muduo
{
namespace detail
{
const int kSmallBuffer = 4000;
const int kLargeBuffer = 4000*1000;
//SIZE为非类型参数
template<int SIZE>
class FixedBuffer : boost::noncopyable
{
public:
FixedBuffer()
: cur_(data_)
{
setCookie(cookieStart);
}
~FixedBuffer()
{
setCookie(cookieEnd);
}
void append(const char* /*restrict*/ buf, size_t len)//把buf中的数据拷贝到_data数组
{
// 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_); }//data_数组当前已用空间
// write to data_ directly
char* current() { return cur_; }
int avail() const { return static_cast<int>(end() - cur_); }//data_数组当前可用空间
void add(size_t len) { cur_ += len; }//移动cur_指针
void reset() { cur_ = data_; }
void bzero() { ::bzero(data_, sizeof data_); }//同memset(data_,0,sizeof(data_));
// for used by GDB
const char* debugString();//加'\0'
void setCookie(void (*cookie)()) { cookie_ = cookie; }
// for used by unit test
string asString() const { return string(data_, length()); }
private:
const char* end() const { return data_ + sizeof data_; }//返回data_数组尾地址+1
// Must be outline function for cookies.
static void cookieStart();
static void cookieEnd();
void (*cookie_)();//没有用到
char data_[SIZE];//缓冲区
char* cur_;//data_数组空闲区首地址
};
}
class LogStream : boost::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* v)
{
buffer_.append(v, strlen(v));
return *this;
}
self& operator<<(const string& v)
{
buffer_.append(v.c_str(), v.size());
return *this;
}
#ifndef MUDUO_STD_STRING
self& operator<<(const std::string& v)
{
buffer_.append(v.c_str(), v.size());
return *this;
}
#endif
self& operator<<(const StringPiece& v)
{
buffer_.append(v.data(), 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_;//typedef detail::FixedBuffer<detail::kSmallBuffer> Buffer;
static const int kMaxNumericSize = 32;
};
class Fmt // : boost::noncopyable
{
public:
template<typename T>
Fmt(const char* fmt, T val);//把val按照fmt的格式进行格式化,存到buf_中
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;
}
}
#endif // MUDUO_BASE_LOGSTREAM_H
LogStream.cc
#include <muduo/base/LogStream.h>
#include <algorithm>
#include <limits>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_arithmetic.hpp>
#include <assert.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
using namespace muduo;
using namespace muduo::detail;
#pragma GCC diagnostic ignored "-Wtype-limits"
//#pragma GCC diagnostic error "-Wtype-limits"
namespace muduo
{
namespace detail
{
const char digits[] = "9876543210123456789";
const char* zero = digits + 9;
BOOST_STATIC_ASSERT(sizeof(digits) == 20);
const char digitsHex[] = "0123456789ABCDEF";
BOOST_STATIC_ASSERT(sizeof digitsHex == 17);
// 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 = i % 16;
i /= 16;
*p++ = digitsHex[lsd];
} while (i != 0);
*p = '\0';
std::reverse(buf, p);
return p - buf;
}
}
}
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()
{
}
template class FixedBuffer<kSmallBuffer>;
template class FixedBuffer<kLargeBuffer>;
void LogStream::staticCheck()
{
BOOST_STATIC_ASSERT(kMaxNumericSize - 10 > std::numeric_limits<double>::digits10);
BOOST_STATIC_ASSERT(kMaxNumericSize - 10 > std::numeric_limits<long double>::digits10);
BOOST_STATIC_ASSERT(kMaxNumericSize - 10 > std::numeric_limits<long>::digits10);
BOOST_STATIC_ASSERT(kMaxNumericSize - 10 > std::numeric_limits<long long>::digits10);
}
template<typename T>
void LogStream::formatInteger(T v)//把v转换成字符串,然后存到buffer_中
{
if (buffer_.avail() >= kMaxNumericSize)
{
size_t len = convert(buffer_.current(), v);
buffer_.add(len);//移动buffer_中的cur_指针
}
}
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对于32位平台就是unsigned int,对于64位平台就是unsigned long int
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);//snprintf格式化
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);
测试
自己写了个测试代码:
Log_test1.cc
#include <muduo/base/Logging.h>
#include <errno.h>
using namespace muduo;
int main()
{
//INFO、TRACE、DEBUG只能三选一,所以,级别切换时需要修改全局变量g_logLevel
LOG_INFO<<"info ...";
Logger::setLogLevel(Logger::TRACE);
LOG_TRACE<<"trace ...";
Logger::setLogLevel(Logger::DEBUG);
LOG_DEBUG<<"debug ...";
LOG_WARN<<"warn ...";
LOG_ERROR<<"error ...";
//LOG_FATAL<<"fatal ...";
errno = 13;
LOG_SYSERR<<"syserr ...";
LOG_SYSFATAL<<"sysfatal ...";
return 0;
}
再写一个例子,将日志不输出到stdout而是输出到文件中,其实本质上只需要给全局函数指针g_output和g_flush重新赋值即可,赋值操作通过Logger类的以下两个静态成员函数完成:
typedef void (*OutputFunc)(const char* msg, int len);
typedef void (*FlushFunc)();
static void setOutput(OutputFunc);
static void setFlush(FlushFunc);
Log_test2.cc
#include <muduo/base/Logging.h>
#include <errno.h>
#include <stdio.h>
using namespace muduo;
FILE* g_file;
void myOutput(const char* msg, int len)
{
if (g_file)
{
fwrite(msg, 1, len, g_file);
}
}
void myFlush()
{
fflush(g_file);
}
int main()
{
g_file = ::fopen("/tmp/muduo_log", "ae");
Logger::setOutput(myOutput);//修改全局函数指针g_output
Logger::setFlush(myFlush);//修改全局函数指针g_flush
LOG_INFO<<"info ...";
Logger::setLogLevel(Logger::TRACE);
LOG_TRACE<<"trace ...";
Logger::setLogLevel(Logger::DEBUG);
LOG_DEBUG<<"debug ...";
LOG_WARN<<"warn ...";
LOG_ERROR<<"error ...";
//LOG_FATAL<<"fatal ...";
errno = 13;
LOG_SYSERR<<"syserr ...";
LOG_SYSFATAL<<"sysfatal ...";
::fclose(g_file);
return 0;
}
rm /tmp/muduo_log