C++高性能服务器
日志系统
前言
🔙返回目录(建议收藏):Linux C++高性能服务器Sylar跟写
1. 运行展示
最下面为输出格式日志信息
2. 遇到的困难
这是困惑我1个月的问题,但是我发现代码的逻辑一点也没有问题,于是我百度“段错误(核心已转储)”是什么原因造成的:
栈溢出 :于是我ulimit改变栈的大小,但是无效!内存泄漏 : 我想到的是智能指针shared_ptr可能没有及时释放,导致内存泄漏,于是我排查了很久,发现一切正常阿。这一次我真的疯了,TM难道有野指针?内核段访问 :不可能是这个原因,我彻底疯了。
我慢慢按照执行的顺序排查,发现“段错误点”,如下代码:
m_formatter->format(std::cout, logger, level, event);
我发现对象 调用public function居然无法访问数据成员 ,天哪!难道代码也六亲不认? 这个问题困惑我3天时间,于是我怀疑:
shared_from_this()
返回的是另一个对象 ,但是要访问的是另一个对象的数据,造成越界访问 ,于是我很坚信就是这个错误,就这样有花掉我很多时间。最后依然无效。
最后方案:太搞笑了,我把
m_formatter.reset(new LogFormatter("%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"));
在构造函数中初始化了另一个类的m_formatter。这下我终于明白了!
3. 代码
1.CMake
CMAKE_MINIMUM_REQUIRED(VERSION 3.16.3)
PROJECT(angel)
SET(CMAKE_VERBOSE_MAKEFILE ON)
SET(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} -rdynamic -O0 -ggdb -std=c++11 -Wall -Wno-deprecated -Werror -Wno-unused-function")
SET(LIB_SRC
angel/log.cpp
)
ADD_LIBRARY(angel SHARED ${LIB_SRC})
#ADD_LIBRARY(angel_static STATIC ${LIB_SRC})
#SET_TARGET_PROPERTIES(angel_static PROPERTIES OUTPUT_NAME "angel")
ADD_EXECUTABLE(test tests/log_test.cpp)
ADD_DEPENDENCIES(test angel)
TARGET_LINK_LIBRARIES(test angel)
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
- log. h
#ifndef __ANGEL_LOG_H__
#define __ANGEL_LOG_H__
#include <string>
#include <stdint.h>
#include <memory>
#include <list>
#include <sstream>
#include <fstream>
#include <vector>
#include <stdarg.h>
#include <map>
#include "singleton.h"
#define ANGEL_LOG_ROOT() angel::LoggerMgr::GetInstance()->getRoot()
namespace angel
{
class Logger;
class LogLevel
{
public:
enum Level
{
UNKNOW = 0,
DEBUG = 1,
INFO =2,
WARN =3,
ERROR = 4,
FATAL = 5
};
static const char* ToString(LogLevel::Level level);
static LogLevel::Level FromString(const std::string& str);
};
class LogEvent
{
public:
typedef std::shared_ptr<LogEvent> ptr;
LogEvent(std::shared_ptr<Logger> logger,LogLevel::Level level
,const char* file, int32_t line, uint32_t elapse
,uint32_t thread_id, uint32_t fiber_id, uint64_t time
,const std::string& thread_name);
const char* getFile() const {return m_file;}
int32_t getLine() const {return m_line;}
uint32_t getElapse() const {return m_elapse;}
uint32_t getThreadId() const {return m_threadId;}
uint32_t getFiberId() const {return m_fiberId;}
uint64_t getTime() const {return m_time;}
const std::string& getThreadName() {return m_threadName;}
std::stringstream& getSS() {return m_ss;}
std::string getContent() const {return m_ss.str();}
std::shared_ptr<Logger> getLogger() const {return m_logger;}
LogLevel::Level getLevel() const {return m_level;}
void format(const char* fmt, ...);
void format(const char* fmt, va_list al);
private:
const char* m_file = nullptr;
int32_t m_line = 0;
uint32_t m_elapse = 0;
uint32_t m_threadId = 0;
uint32_t m_fiberId =0;
uint64_t m_time = 0;
std::string m_threadName;
std::stringstream m_ss;
std::shared_ptr<Logger> m_logger;
LogLevel::Level m_level;
};
class LogFormatter
{
public:
typedef std::shared_ptr<LogFormatter> ptr;
LogFormatter(const std::string& pattern);
std::string format(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event);
std::ostream& format(std::ostream& ofs, std::shared_ptr<Logger> logger,LogLevel::Level level, LogEvent::ptr event);
public:
class FormatItem
{
public:
typedef std::shared_ptr<FormatItem> ptr;
virtual ~FormatItem(){}
virtual void format(std::ostream& os, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) = 0;
};
void init();
bool isError() const {return m_error;}
const std::string getPattern() const {return m_pattern;}
private:
std::string m_pattern;
std::vector<FormatItem::ptr> m_items;
bool m_error = false;
};
class LogAppender
{
friend class Logger;
public:
typedef std::shared_ptr<LogAppender> ptr;
virtual ~LogAppender(){}
virtual void log(std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event,LogFormatter::ptr m_temp) = 0;
void setFormatter(LogFormatter::ptr val);
LogFormatter::ptr getFormatter();
LogLevel::Level getLevel() const {return m_level;}
void setLevel(LogLevel::Level val) {m_level = val;}
protected:
LogLevel::Level m_level = LogLevel::DEBUG;
bool m_hasFormatter = false;
LogFormatter::ptr m_formatter;
};
class Logger : public std::enable_shared_from_this<Logger>
{
friend class LoggerManager;
public:
typedef std::shared_ptr<Logger> ptr;
Logger(const std::string& name = "root");
void log(LogLevel::Level level, LogEvent::ptr event);
void debug(LogEvent::ptr event);
void info(LogEvent::ptr event);
void warn(LogEvent::ptr event);
void error(LogEvent::ptr event);
void fatal(LogEvent::ptr event);
void addAppender(LogAppender::ptr appender);
void delAppender(LogAppender::ptr appender);
void clearAppender();
LogLevel::Level getLevel() const {return m_level;}
void setLevel(LogLevel::Level val){m_level = val;}
const std::string& getName() const {return m_name;}
void setFormatter(LogFormatter::ptr val);
void setFormatter(const std::string& val);
LogFormatter::ptr getFormatter();
private:
std::string m_name;
LogLevel::Level m_level;
std::list<LogAppender::ptr> m_appenders;
LogFormatter::ptr m_formatter;
Logger::ptr m_root;
};
class StdoutLogAppender: public LogAppender
{
public:
typedef std::shared_ptr<StdoutLogAppender> ptr;
void log(Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event,LogFormatter::ptr m_temp);
private:
};
class FileLogAppender: public LogAppender
{
public:
typedef std::shared_ptr<FileLogAppender> ptr;
FileLogAppender(const std::string& filename);
void log(Logger::ptr logger, LogLevel::Level level,LogEvent::ptr event,LogFormatter::ptr m_temp);
bool reopen();
private:
std::string m_filename;
std::ofstream m_filestream;
uint64_t m_lastTime = 0;
};
class LoggerManager
{
public:
LoggerManager();
Logger::ptr getLogger(const std::string& name);
void init();
Logger::ptr getRoot() const {return m_root;}
private:
std::map<std::string, Logger::ptr> m_loggers;
Logger::ptr m_root;
};
typedef angel::Singleton<LoggerManager> LoggerMgr;
}
#endif
- log.cpp
#include "log.h"
#include <map>
#include <iostream>
#include <functional>
#include <time.h>
#include <string.h>
#include <vector>
#include <set>
namespace angel
{
const char* LogLevel::ToString(LogLevel::Level level)
{
switch(level)
{
#define XX(name) \
case LogLevel::name: \
return #name; \
break;
XX(DEBUG);
XX(INFO);
XX(WARN);
XX(ERROR);
XX(FATAL);
#undef XX
default:
return "UNKNOW";
}
return "UNKNOW";
}
LogLevel::Level LogLevel::FromString(const std::string& str)
{
#define XX(level, v) \
if(str == #v) { \
return LogLevel::level; \
}
XX(DEBUG, debug);
XX(INFO, info);
XX(WARN, warn);
XX(ERROR, error);
XX(FATAL, fatal);
XX(DEBUG, DEBUG);
XX(INFO, INFO);
XX(WARN, WARN);
XX(ERROR, ERROR);
XX(FATAL, FATAL);
return LogLevel::UNKNOW;
#undef XX
};
class MessageFormatItem : public LogFormatter::FormatItem
{
public:
MessageFormatItem(const std::string& str = ""){}
void format(std::ostream& os,Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override
{
os << event->getContent();
}
};
class LevelFormatItem : public LogFormatter::FormatItem
{
public:
LevelFormatItem(const std::string& str = ""){}
void format(std::ostream& os,Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override
{
os << LogLevel::ToString(level);
}
};
class ElapseFormatItem : public LogFormatter::FormatItem
{
public:
ElapseFormatItem(const std::string& str = ""){}
void format(std::ostream& os,Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override
{
os << event->getElapse();
}
};
class NameFormatItem : public LogFormatter::FormatItem
{
public:
NameFormatItem(const std::string& str = ""){}
void format(std::ostream& os,Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override
{
os << event->getLogger()->getName();
}
};
class ThreadIdFormatItem : public LogFormatter::FormatItem
{
public:
ThreadIdFormatItem(const std::string& str = ""){}
void format(std::ostream& os,Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override
{
os << event->getThreadId();
}
};
class ThreadNameFormatItem : public LogFormatter::FormatItem {
public:
ThreadNameFormatItem(const std::string& str = "") {}
void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
os << event->getThreadName();
}
};
class FiberIdFormatItem : public LogFormatter::FormatItem
{
public:
FiberIdFormatItem(const std::string& str = ""){}
void format(std::ostream& os,Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override
{
os << event->getFiberId();
}
};
class DateTimeFormatItem : public LogFormatter::FormatItem
{
public:
DateTimeFormatItem(const std::string& format = "%Y:%m:%d %H:%M:%S"):m_format(format)
{
if(m_format.empty()){
m_format = "%Y-%m-%d %H-%M-%S";
}
}
void format(std::ostream& os,Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override
{
struct tm tm;
time_t time = event->getTime();
localtime_r(&time, &tm);
char buf[64];
strftime(buf, sizeof(buf), m_format.c_str(), &tm);
os << buf;
}
private:
std::string m_format;
};
class FilenameFormatItem : public LogFormatter::FormatItem
{
public:
FilenameFormatItem(const std::string& str = ""){}
void format(std::ostream& os,Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override
{
os << event->getFile();
}
};
class LineFormatItem : public LogFormatter::FormatItem
{
public:
LineFormatItem(const std::string& str = ""){}
void format(std::ostream& os,Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override
{
os << event->getLine();
}
};
class NewLineFormatItem : public LogFormatter::FormatItem
{
public:
NewLineFormatItem(const std::string& str = ""){}
void format(std::ostream& os,Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override
{
os << std::endl;
}
};
class StringFormatItem : public LogFormatter::FormatItem
{
public:
StringFormatItem(const std::string& str) : m_string(str){}
void format(std::ostream& os,Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override
{
os << m_string;
}
private:
std::string m_string;
};
class TabFormatItem : public LogFormatter::FormatItem
{
public:
TabFormatItem(const std::string& str = ""){}
void format(std::ostream& os,Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override
{
os << "\t";
}
private:
std::string m_string;
};
LogEvent::LogEvent(std::shared_ptr<Logger> logger,LogLevel::Level level
,const char* file, int32_t line, uint32_t elapse
,uint32_t thread_id, uint32_t fiber_id, uint64_t time
,const std::string& thread_name)
:m_file(file)
,m_line(line)
,m_elapse(elapse)
,m_threadId(thread_id)
,m_fiberId(fiber_id)
,m_time(time)
,m_threadName(thread_name)
,m_logger(logger)
,m_level(level)
{}
void LogEvent::format(const char* fmt, ...)
{
va_list al;
va_start(al, fmt);
format(fmt, al);
va_end(al);
}
void LogEvent::format(const char* fmt, va_list al)
{
char* buf = nullptr;
int len = vasprintf(&buf, fmt, al);
if(len != -1) {
m_ss << std::string(buf, len);
free(buf);
}
}
Logger::Logger(const std::string& name)
:m_name(name)
,m_level(LogLevel::DEBUG)
{
m_formatter.reset(new LogFormatter("%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"));
}
void Logger::addAppender(LogAppender::ptr appender)
{
m_appenders.push_back(appender);
}
void Logger::delAppender(LogAppender::ptr appender)
{
for(auto it = m_appenders.begin(); it != m_appenders.end(); ++it)
{
if(*it ==appender)
{
m_appenders.erase(it);
break;
}
}
}
void Logger::log(LogLevel::Level level,LogEvent::ptr event)
{
if(level >= m_level)
{
auto self = shared_from_this();
if(!m_appenders.empty())
{
for(auto& i : m_appenders)
{
i->log(self, level, event,m_formatter);
}
} else if(m_root)
{
m_root->log(level, event);
}
}
}
void Logger::setFormatter(LogFormatter::ptr val)
{
}
void Logger::setFormatter(const std::string& val)
{
}
void Logger::debug(LogEvent::ptr event)
{
log(LogLevel::DEBUG,event);
}
void Logger::info(LogEvent::ptr event)
{
log(LogLevel::INFO,event);
}
void Logger::warn(LogEvent::ptr event)
{
log(LogLevel::WARN,event);
}
void Logger::error(LogEvent::ptr event)
{
log(LogLevel::ERROR,event);
}
void Logger::fatal(LogEvent::ptr event)
{
log(LogLevel::FATAL,event);
}
LogFormatter::LogFormatter(const std::string& pattern):m_pattern(pattern)
{
init();
}
std::string LogFormatter::format(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event)
{
std::stringstream ss;
for(auto& i : m_items)
{
i->format(ss, logger, level, event);
}
return ss.str();
}
std::ostream& LogFormatter::format(std::ostream& ofs,std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event)
{
for(auto& i : m_items)
{
i->format(ofs, logger, level, event);
}
return ofs;
}
void LogFormatter::init() {
std::vector<std::tuple<std::string, std::string, int> > vec;
std::string nstr;
for(size_t i = 0; i < m_pattern.size(); ++i) {
if(m_pattern[i] != '%') {
nstr.append(1, m_pattern[i]);
continue;
}
if((i + 1) < m_pattern.size()) {
if(m_pattern[i + 1] == '%') {
nstr.append(1, '%');
continue;
}
}
size_t n = i + 1;
int fmt_status = 0;
size_t fmt_begin = 0;
std::string str;
std::string fmt;
while(n < m_pattern.size()) {
if(!fmt_status && (!isalpha(m_pattern[n]) && m_pattern[n] != '{'
&& m_pattern[n] != '}'))
{
str = m_pattern.substr(i + 1, n - i - 1);
break;
}
if(fmt_status == 0) {
if(m_pattern[n] == '{') {
str = m_pattern.substr(i + 1, n - i - 1);
fmt_status = 1;
fmt_begin = n;
++n;
continue;
}
} else if(fmt_status == 1) {
if(m_pattern[n] == '}') {
fmt = m_pattern.substr(fmt_begin + 1, n - fmt_begin - 1);
fmt_status = 0;
++n;
break;
}
}
++n;
if(n == m_pattern.size()) {
if(str.empty()) {
str = m_pattern.substr(i + 1);
}
}
}
if(fmt_status == 0) {
if(!nstr.empty()) {
vec.push_back(std::make_tuple(nstr, std::string(), 0));
nstr.clear();
}
vec.push_back(std::make_tuple(str, fmt, 1));
i = n - 1;
} else if(fmt_status == 1) {
std::cout << "pattern parse error: " << m_pattern << " - " << m_pattern.substr(i) << std::endl;
m_error = true;
vec.push_back(std::make_tuple("<<pattern_error>>", fmt, 0));
}
}
if(!nstr.empty()) {
vec.push_back(std::make_tuple(nstr, "", 0));
}
static std::map<std::string, std::function<FormatItem::ptr(const std::string& str)> > s_format_items = {
#define XX(str, C) \
{#str, [](const std::string& fmt) { return FormatItem::ptr(new C(fmt));}}
XX(m, MessageFormatItem),
XX(p, LevelFormatItem),
XX(r, ElapseFormatItem),
XX(c, NameFormatItem),
XX(t, ThreadIdFormatItem),
XX(n, NewLineFormatItem),
XX(d, DateTimeFormatItem),
XX(f, FilenameFormatItem),
XX(l, LineFormatItem),
XX(T, TabFormatItem),
XX(F, FiberIdFormatItem),
XX(N, ThreadNameFormatItem),
#undef XX
};
for(auto& i : vec) {
if(std::get<2>(i) == 0) {
m_items.push_back(FormatItem::ptr(new StringFormatItem(std::get<0>(i))));
} else {
auto it = s_format_items.find(std::get<0>(i));
if(it == s_format_items.end()) {
m_items.push_back(FormatItem::ptr(new StringFormatItem("<<error_format %" + std::get<0>(i) + ">>")));
m_error = true;
} else {
m_items.push_back(it->second(std::get<1>(i)));
}
}
}
}
FileLogAppender::FileLogAppender(const std::string& filename) :m_filename(filename)
{reopen();}
bool FileLogAppender::reopen()
{
if(m_filestream)
{
m_filestream.close();
}
m_filestream.open(m_filename);
return !!m_filestream;
}
void FileLogAppender::log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event,LogFormatter::ptr m_temp)
{
if(level >= m_level) {
uint64_t now = event->getTime();
if(now >= (m_lastTime + 3)) {
reopen();
m_lastTime = now;
}
if(!m_formatter->format(m_filestream, logger, level, event))
{
std::cout << "error" << std::endl;
}
}
}
void StdoutLogAppender::log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event,LogFormatter::ptr m_temp)
{
if(level >= m_level)
{
m_formatter = m_temp;
m_formatter->format(std::cout, logger, level, event);
}
}
LoggerManager::LoggerManager() {
m_root.reset(new Logger);
m_root->addAppender(LogAppender::ptr(new StdoutLogAppender));
m_loggers[m_root->m_name] = m_root;
init();
}
Logger::ptr LoggerManager::getLogger(const std::string& name) {
auto it = m_loggers.find(name);
if(it != m_loggers.end()) {
return it->second;
}
Logger::ptr logger(new Logger(name));
logger->m_root = m_root;
m_loggers[name] = logger;
return logger;
}
struct LogAppenderDefine {
int type = 0;
LogLevel::Level level = LogLevel::UNKNOW;
std::string formatter;
std::string file;
bool operator==(const LogAppenderDefine& oth) const {
return type == oth.type
&& level == oth.level
&& formatter == oth.formatter
&& file == oth.file;
}
};
void LoggerManager::init() {
}
}
|