IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 【总结】C++高性能服务器日志系统 -> 正文阅读

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

C++高性能服务器

日志系统

前言

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


1. 运行展示

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

在这里插入图片描述

2. 遇到的困难

在这里插入图片描述

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

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

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

//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)
  1. 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;           //线程id
	uint32_t m_fiberId =0;       	    //携程id
	uint64_t m_time = 0;		    //时间戳
	std::string m_threadName;       //线程名称
    std::stringstream m_ss;         //日志内容流
    std::shared_ptr<Logger> 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<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;		//Appender集合
    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
  1. 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
};

/*-------------ForamtItems-------------*/
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;
};

/*---------------Event----------------*/
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::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);
}

/*-----------------Formatter------------------*/
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;
}
//%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<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);
                    //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("<<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),           //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("<<error_format %" + std::get<0>(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;  
 }

/*----------------Appender的子类-----------------*/
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::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() {
}

}
  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-02-28 16:03:51  更:2022-02-28 16:07:24 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 3:48:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码