栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

【总结】C++高性能服务器日志系统

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【总结】C++高性能服务器日志系统

C++高性能服务器

日志系统

前言

本文内容:日志系统 所属专栏:Linux高性能服务器作者主页:紫荆鱼创作时间:2022-2-26

返回目录(建议收藏):Linux C++高性能服务器Sylar跟写


[文章目录]

日志系统

1. 运行展示2. 遇到的困难3. 代码

1. 运行展示

最下面为输出格式日志信息

2. 遇到的困难


这是困惑我1个月的问题,但是我发现代码的逻辑一点也没有问题,于是我百度“段错误(核心已转储)”是什么原因造成的:

    栈溢出:于是我ulimit改变栈的大小,但是无效!内存泄漏: 我想到的是智能指针shared_ptr可能没有及时释放,导致内存泄漏,于是我排查了很久,发现一切正常阿。这一次我真的疯了,TM难道有野指针?内核段访问:不可能是这个原因,我彻底疯了。

我慢慢按照执行的顺序排查,发现“段错误点”,如下代码:

//m_formatter是LogFormatter的对象
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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#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 ptr;
    LogEvent(std::shared_ptr 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 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;           //线程id
	uint32_t m_fiberId =0;       	    //携程id
	uint64_t m_time = 0;		    //时间戳
	std::string m_threadName;       //线程名称
    std::stringstream m_ss;         //日志内容流
    std::shared_ptr m_logger;   //日志器
    LogLevel::Level m_level;        //日志等级

};

//日志格式器
//默认格式 "%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"
class LogFormatter
{
public:
	typedef std::shared_ptr ptr;

	LogFormatter(const std::string& pattern);
    std::string format(std::shared_ptr logger, LogLevel::Level level, LogEvent::ptr event);
    std::ostream& format(std::ostream& ofs, std::shared_ptr logger,LogLevel::Level level, LogEvent::ptr event);

public:	
	class FormatItem
	{
	public:
		typedef std::shared_ptr ptr;

		virtual ~FormatItem(){}
        //格式化日志流
		virtual void format(std::ostream& os, std::shared_ptr 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 m_items;
    bool m_error = false;
};

//日志输出地类
class LogAppender
{
    friend class Logger;
public:
	typedef std::shared_ptr ptr;
	virtual ~LogAppender(){}
	
    //写入日志
	virtual void log(std::shared_ptr 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
{
    friend class LoggerManager;
public:
	typedef std::shared_ptr 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 m_appenders;		//Appender集合
    LogFormatter::ptr m_formatter;          //日志格式器
    Logger::ptr m_root;         //主日志器
};

//日志输出到控制台
class StdoutLogAppender: public LogAppender
{
public:
	typedef std::shared_ptr 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 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 m_loggers;
    //主日志器
    Logger::ptr m_root;
};

//日志器管理类单例模式
typedef angel::Singleton LoggerMgr;

}

#endif
    log.cpp
#include "log.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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,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, 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, LogLevel::Level level, LogEvent::ptr event)
{
    for(auto& i : m_items)
    {
            i->format(ofs, logger, level, event);
    }
    return ofs;
}
//%xxx %xxx{xxx} %%
//默认格式 "%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"
void LogFormatter::init() {
    //str, format, type
    std::vector > 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);
                    //std::cout << "*" << str << std::endl;
                    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);
                    //std::cout << "#" << fmt << std::endl;
                    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("<>", fmt, 0));
        }
    }

    if(!nstr.empty()) {
        vec.push_back(std::make_tuple(nstr, "", 0));
    }
    static std::map > s_format_items = {
#define XX(str, C) 
        {#str, [](const std::string& fmt) { return FormatItem::ptr(new C(fmt));}}

        XX(m, MessageFormatItem),           //m:消息
        XX(p, LevelFormatItem),             //p:日志级别
        XX(r, ElapseFormatItem),            //r:累计毫秒数
        XX(c, NameFormatItem),              //c:日志名称
        XX(t, ThreadIdFormatItem),          //t:线程id
        XX(n, newlineFormatItem),           //n:换行
        XX(d, DateTimeFormatItem),          //d:时间
        XX(f, FilenameFormatItem),          //f:文件名
        XX(l, LineFormatItem),              //l:行号
        XX(T, TabFormatItem),               //T:Tab
        XX(F, FiberIdFormatItem),           //F:协程id
        XX(N, ThreadNameFormatItem),        //N:线程名称
#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("<(i) + ">>")));
                m_error = true;
            } else {
                m_items.push_back(it->second(std::get<1>(i)));
            }
        }

        //std::cout << "(" << std::get<0>(i) << ") - (" << std::get<1>(i) << ") - (" << std::get<2>(i) << ")" << std::endl;
    }
    //std::cout << m_items.size() << std::endl;  
 }


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, 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, 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; //1 File, 2 Stdout
    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() {
}

}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/743418.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号