转载c++11 linux简易日志库

转载c++11 简易日志库

原作者链接: ELog简单日志库

  • 在原作者基础上修改,屏蔽掉ini配置文件,只需要在项目中包含elog.h、elog.cpp,在文件中引用elog.h就可以使用。

elog.h

#pragma once
#include <map>
#include <list>
#include <mutex>
#include <memory>
#include <string>
#include <atomic>
#include <sstream>
#include <fstream>
#include <thread>
#include <condition_variable>
#include <string.h>
#include <sys/stat.h>

/**
* @brief 使用流式方式将日志级别level的日志写入到logger  c++风格
*/
#define ELOG_LEVEL(level)                                   \
    if(level != ELOG::LogLevel::UNKNOW)                             \
       ELOG::LogEventWrap(                                          \
       ELOG::LogEvent::ptr(                                         \
       new ELOG::LogEvent(level, __FILE__, __LINE__, ELOG::Utils::GetTimeStamp(), "")   \
       )).GetSS()

#define log_debug ELOG_LEVEL(ELOG::LogLevel::DEBUG)
#define log_info ELOG_LEVEL(ELOG::LogLevel::INFO)
#define log_warn ELOG_LEVEL(ELOG::LogLevel::WARN)
#define log_error ELOG_LEVEL(ELOG::LogLevel::ERROR)
#define log_fatal ELOG_LEVEL(ELOG::LogLevel::FATAL)

/**
* @brief 使用格式化方式将日志级别level的日志写入到logger  c风格
*/
#define ELOG_FMT_LEVEL(level, fmt, ...) \
    if(level != ELOG::LogLevel::UNKNOW) \
        ELOG::LogEventWrap( \
        ELOG::LogEvent::ptr(new ELOG::LogEvent(level,__FILE__, __LINE__, ELOG::Utils::GetTimeStamp(), "") \
        )).GetEvent()->Format(fmt, __VA_ARGS__)

#define ELOG_FMT_DEBUG(fmt, ...) ELOG_FMT_LEVEL(ELOG::LogLevel::DEBUG, fmt, __VA_ARGS__)
#define ELOG_FMT_INFO(fmt, ...)  ELOG_FMT_LEVEL(ELOG::LogLevel::INFO, fmt, __VA_ARGS__)
#define ELOG_FMT_WARN(fmt, ...)  ELOG_FMT_LEVEL(ELOG::LogLevel::WARN, fmt, __VA_ARGS__)
#define ELOG_FMT_ERROR(fmt, ...) ELOG_FMT_LEVEL(ELOG::LogLevel::ERROR, fmt, __VA_ARGS__)
#define ELOG_FMT_FATAL(fmt, ...) ELOG_FMT_LEVEL(ELOG::LogLevel::FATAL, fmt, __VA_ARGS__)

namespace ELOG 
{
	/// 输出目标枚举
	enum class OUT_MODEL : uint8_t
	{
		OUT_CONSOLE = 0,
		OUT_FILE,
		OUT_SOCK  ///< 暂不支持
	};

	class Utils
	{
	public:
		/**
		* @brief 根据时间戳获取本地时间  Format: 类似"%Y-%m-%d %H:%M:%S"  %Y表示year %m表示月 %d表示日 %H表示时 %M表示分 %S表示秒,其他符号视需求而定
		* @return  返回格式化的字符串
		*/
		static std::string GetTime(time_t p_timestamp, const char *p_szFormat);

		/**
		* @brief 获取时间戳
		* @return 返回时间戳
		*/
		static time_t GetTimeStamp();

		/**
		* @brief 获取文件大小,文件已打开则无法获取
		* @param[in] strPath 文件名称
		* @return 返回文件大小
		*/
		static long GetFileLength(const std::string &p_strPath);

		/**
		* @brief 获取线程ID
		* @return 返回线程ID字符串
		*/
		static std::string GetThreadId(const std::thread::id &p_ThreadId);
	};

	/**
	* @brief 日志级别
	*/
	class LogLevel
	{
	public:
		/// 日志级别
		enum Level 
		{
			/// 未知级别
			UNKNOW = 0,
			/// DEBUG 级别
			DEBUG = 1,
			/// INFO 级别
			INFO = 2,
			/// WARN 级别
			WARN = 3,
			/// ERROR 级别
			ERROR = 4,
			/// FATAL 级别
			FATAL = 5
		};
		/**
		* @brief 将日志级别转成文本输出
		* @param[in] level 日志级别
		*/
		static const char* ToString(LogLevel::Level p_LevelId);

		/**
		* @brief 将文本转换成日志级别
		* @param[in] str 日志级别文本
		*/
		static LogLevel::Level FromString(const std::string& p_strFrom);
		 
	};

	/// 每一行日志需要输出的项
	/// 时间 级别 线程id 线程名 用户日志  文件名:行号
	class LogItem
	{
	public:
		uint32_t           m_nLineNum;         /// 行号
		uint64_t           m_llTime;         /// 时间戳
		std::string        msg;          /// 用户日志
		std::string        m_strThreadName;   /// 线程名称
		std::string        m_strFileName;         /// 文件名
		std::thread::id    m_threadId;     /// 线程id
		LogLevel::Level    m_LevelId;        /// 日志级别
	};

	class LogEvent /// 日志事件
	{
	public:
		// 日志文件名
		class SourceFile
		{
		public:
			template<int N>
			inline SourceFile(const char(&arr)[N])
				:m_data(arr),
				m_size(N - 1) 
			{
				const char* slash = strrchr(m_data, '/'); // builtin function
				if (slash) 
				{
					m_data = slash + 1;
					m_size -= static_cast<int>(m_data - arr);
				}
			}

			explicit SourceFile(const char* filename)
				: m_data(filename) 
			{
				const char* slash = strrchr(filename, '/');
				if (slash) 
				{
					m_data = slash + 1;
				}
				m_size = static_cast<int>(strlen(m_data));
			}

			const char* m_data;
			int m_size;
		};

		using ptr = std::shared_ptr<LogEvent>;

		/**
		* @brief 构造函数
		* @param[in] level 日志级别
		* @param[in] file 文件名
		* @param[in] line 文件行号
		* @param[in] time 日志时间(秒)
		* @param[in] thread_name 线程名称
		*/
		LogEvent(LogLevel::Level p_LevelId, SourceFile p_fileName, uint32_t p_nLine, uint64_t p_time, 
			const std::string &p_strThread_name = "");

		/**
		*@brief 返回日志包含项,时间,行号等信息
		*/
		inline const LogItem &GetLogItem() const { return m_logItem; }

		inline void SetContent(const std::string &LogMsg) { m_logItem.msg = LogMsg; }

		/**
		* @brief 返回日志内容字符串流
		*/
		inline std::stringstream &GetSS() { return m_ss; }

		/**
		* @brief 格式化写入日志内容
		*/
		void Format(const char* p_szfmt, ...);
		void Format(const char* p_szfmt, va_list al);

	private:
		LogItem m_logItem;               /// 日志包含项
		std::stringstream m_ss;          /// 日志内容流
	};

	/**
	* @brief 日志输出目标
	*/
	class LogAppender
	{
	public:
		using ptr = std::shared_ptr<LogAppender>;

		/**
		* @brief 析构函数
		*/
		virtual ~LogAppender() {}

		/**
		* @brief 开一个线程输出
		*/
		virtual void Start() final;

		/**
		* @brief 结束线程
		*/
		virtual void Stop() final; 

		/**
		* @brief 写入日志
		*/
		virtual void log(LogEvent::ptr event) = 0;

		/**
		* @brief 添加新事件,这个函数是需要被多线程调用的
		*/
		inline void AddEvent(const LogEvent::ptr &event);

	protected:
		void Run();

	protected: 
		std::mutex m_mutex;
		std::atomic_bool m_isRun{ false };
		std::condition_variable   m_cv;
		std::list<LogEvent::ptr> m_listEvent;

	};

	/**
	* @brief 输出管理器
	*/
	class LogAppenderManager
	{
	public:
		inline void AddAppender(OUT_MODEL out, std::shared_ptr<LogAppender> &logAppender) 
		{ 
			std::lock_guard<std::mutex> lock(m_mutex);
			m_mapLogAppender.insert(std::pair<OUT_MODEL, std::shared_ptr<LogAppender>>(out, logAppender));
		}

		inline void RemoveAppender(OUT_MODEL out) 
		{
			std::lock_guard<std::mutex> lock(m_mutex);
			m_mapLogAppender.erase(out);
		}

		void Start();
		void Stop();

		void AddEvent(const LogEvent::ptr &event);

	private:
		std::mutex m_mutex;
		std::map<OUT_MODEL, std::shared_ptr<LogAppender> > m_mapLogAppender;
		std::atomic_bool m_isRun{ false };
	};

	/**
	* @brief 输出到控制台的Appender
	*/
	class StdoutLogAppender : public LogAppender 
	{
	public:
		using ptr = std::shared_ptr<StdoutLogAppender>;
		StdoutLogAppender();
		void log(LogEvent::ptr event);  
	};

	/**
	* @brief 输出到文件的Appender
	* 思考:写文件有必要单独开一个线程吗?
	*/
	class FileLogAppender : public LogAppender 
	{
	public:
		using ptr = std::shared_ptr<FileLogAppender>;
	
		FileLogAppender(const std::string& p_filename, size_t p_nmaxSize, bool p_boverwrite = false);
	
		~FileLogAppender() override;
	
		void log(LogEvent::ptr p_event) override;

		/**
		* @brief 重新打开日志文件
		* @return 成功返回true
		*/
		bool ReOpen();

		/**
		* @brief 关闭日志文件
		*/
		void Close();

	private:
		/// 文件路径
		std::string m_filename;
		/// 文件大小 单位MB
		uint32_t m_fileSize{ 0 };
		/// 是否覆盖文件
		bool m_overwrite{ false };
		/// 文件流
		std::ofstream m_filestream;
	};

	class Logger
	{
	public:
		using ptr = std::shared_ptr<Logger>;

		/**
		* @brief 单例的接口
		*/
		static std::shared_ptr<Logger> &Instance();

		/**
		* @brief 日志器析构
		* 不写成保护类型或私有类型,是因为使用的智能指针创建的单例,保护后无法delete
		*/
		~Logger();

		/**
		* @brief 禁用拷贝和赋值
		*/
		Logger(const Logger &other) = delete;
		Logger(const Logger &&other) = delete;
		Logger &operator=(const Logger &other) = delete;
		Logger &operator=(const Logger &&other) = delete;

		/**
		* @brief 日志器初始化
		* @param[in] cfgFile 文件名称
		*/
		void Init(const std::string &p_cfgFile);

		/**
		* @brief 添加新的日志事件
		* @param[in] p 事件指针
		*/
		void AddEvent(const LogEvent::ptr &p);

	private:
		/**
		* @brief 日志器构造
		* 保护起来,不让其他地方进行Logger的实例化操作
		*/
		Logger();

		std::shared_ptr<LogAppenderManager> m_LogAppenderManager; /// 输出管理,对他需不需要进行上锁管理
	};

	/**
	* @brief 日志事件包装器
	*/
	class LogEventWrap
	{
	public:
		/**
		* @brief 构造函数
		* @param[in] e 日志事件
		*/
		LogEventWrap(LogEvent::ptr e);

		/**
		* @brief 析构函数
		*/
		~LogEventWrap();

		/**
		* @brief 获取日志事件
		*/
		inline LogEvent::ptr GetEvent() const { return m_event; }

		/**
		* @brief 获取日志内容流
		*/
		inline std::stringstream &GetSS() const { return m_event->GetSS(); }
	private:

		/**
		* @brief 日志事件
		*/
		LogEvent::ptr m_event;
	};
}  // namespace ELOG 


elog.cpp

#include "ELog.h"

#include <iostream> 
#include <cstdarg>  
#include <algorithm>

/// 线程休眠
//#define mSleep(ms) std::this_thread::sleep_for(std::chrono::milliseconds(ms)
//#define uSleep(us) std::this_thread::sleep_for(std::chrono::microseconds(us)


/// 定义换行符
#undef LINE_END_SYMBOL
#ifndef LINE_END_SYMBOL
#    ifdef _MSC_VER
#        define LINE_END_SYMBOL  "\r\n"
#    else
#        define LINE_END_SYMBOL  "\n"
#    endif
#endif

/// 定义vasprintf
#if !defined(HAVE_VASPRINTF)
#    if defined(_MSC_VER)
int vasprintf(char **ptr, const char *format, va_list ap)
{
	int len;

	len = _vscprintf_p(format, ap) + 1;
	*ptr = (char *)malloc(len * sizeof(char));
	if (!*ptr)
	{
		return -1;
	}

	return _vsprintf_p(*ptr, len, format, ap);
}
#    else
int vasprintf(char **ptr, const char *format, va_list ap)
{
	va_list ap_copy;

	/* Make sure it is determinate, despite manuals indicating otherwise */
	*ptr = nullptr;

	va_copy(ap_copy, ap);
	int count = vsnprintf(nullptr, 0, format, ap);
	if (count >= 0)
	{
		char* buffer = (char*)malloc(count + 1);
		if (buffer != nullptr)
		{
			count = vsnprintf(buffer, count + 1, format, ap_copy);
			if (count < 0)
				free(buffer);
			else
				*ptr = buffer;
		}
	}
	va_end(ap_copy);

	return count;
}
#    endif
#endif /* !HAVE_VASPRINTF */

#if !defined(HAVE_ASPRINTF)
int asprintf(char **ptr, const char *format, ...)
{
	va_list ap;
	int ret;

	*ptr = nullptr;

	va_start(ap, format);
	ret = vasprintf(ptr, format, ap);
	va_end(ap);

	return ret;
}
#endif /* !HAVE_ASPRINTF */

namespace ELOG
{ 
	std::string Utils::GetTime(time_t p_timestamp, const char* p_szFormat)
	{
		std::tm tm;
#if defined(_MSC_VER)
		localtime_s(&tm, &p_timestamp);
#elif defined(__GNUC__)
		localtime_r(&p_timestamp, &tm);
#else
		/// do nothing
#endif
		char buf[64];
		strftime(buf, sizeof(buf), p_szFormat, &tm);
		return  std::string(buf, strlen(buf));
	}

	time_t Utils::GetTimeStamp() 
	{
		std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> tp = std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now());
		return std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch()).count();
	}

	long Utils::GetFileLength(const std::string &p_strPath)
	{
		struct stat info;
		::stat(p_strPath.c_str(), &info);
		return  info.st_size;
	}

	std::string Utils::GetThreadId(const std::thread::id & p_ThreadId)
	{
		std::ostringstream oss; 
		oss.setf(std::ios::left); //设置对齐方式为left 
		oss.width(6); //设置宽度为7,不足用空格填充 
		oss << p_ThreadId;
		return oss.str();
	}
 
	const char *LogLevel::ToString(LogLevel::Level p_LevelId)
	{
		switch (p_LevelId)
		{
			/// 使用宏函数来简化代码 
        case LogLevel::Level::DEBUG:
			return "DEBUG";
    	case LogLevel::Level::INFO:
			return "INFO ";
	    case LogLevel::Level::WARN:
			return "WARN ";
	    case LogLevel::Level::ERROR:
			return "ERROR";
    	case LogLevel::Level::FATAL:
			return "FATAL";
		default:
			return "UNKNOW";
		} 
	}

	LogLevel::Level LogLevel::FromString(const std::string & p_strFrom)
	{
		/// 将str字符全部转换成大写,并存储在tStr中
		std::string tStr(p_strFrom);
		std::transform(tStr.begin(), tStr.end(), tStr.begin(), ::toupper);

#define XX(name, v) \
    if(tStr == #v) { \
    return LogLevel::Level::name; \
}
		XX(DEBUG, DEBUG);
		XX(INFO, INFO);
		XX(WARN, WARN);
		XX(ERROR, ERROR);
		XX(FATAL, FATAL);
		return LogLevel::Level::UNKNOW;
#undef XX
	}
 

	LogEvent::LogEvent(LogLevel::Level p_LevelId, SourceFile p_fileName, uint32_t p_nLine, uint64_t p_time,
		const std::string& p_strThread_name)
	{
		m_logItem.m_strFileName = p_fileName.m_data;
		m_logItem.m_nLineNum = p_nLine;
		m_logItem.m_llTime = p_time;
		m_logItem.m_LevelId = p_LevelId;
		m_logItem.m_strThreadName = p_strThread_name;
		m_logItem.m_threadId = std::this_thread::get_id();
	}

	void LogEvent::Format(const char * p_szfmt, ...)
	{
		va_list al;
		va_start(al, p_szfmt);
		Format(p_szfmt, al);
		va_end(al);
	}

	void LogEvent::Format(const char * p_szfmt, va_list al)
	{
		char* buf = nullptr;
		int nLen = vasprintf(&buf, p_szfmt, al);
		if (nLen != -1)
		{
			m_ss << std::string(buf, nLen);
			free(buf);
		}
	}

	StdoutLogAppender::StdoutLogAppender()
	{
#ifdef _MSC_VER
		// 控制台显示乱码纠正
		system("chcp 65001"); //设置字符集 (使用SetConsoleCP(65001)设置无效,原因未知)
#endif // WIN32
	}
	
	
	void StdoutLogAppender::log(LogEvent::ptr event)
	{
		/// 时间 级别 线程id 线程名 用户日志  文件名:行号
		std::cout << "[" << Utils::GetTime(event->GetLogItem().m_llTime, "%Y-%m-%d %H:%M:%S") << "] ["
			<< LogLevel::ToString(event->GetLogItem().m_LevelId)  << "] ["
			<< Utils::GetThreadId(event->GetLogItem().m_threadId) << "] ["
			// << event->GetLogItem().threadName << ":"
			<< event->GetLogItem().m_strFileName << ":"
			<< event->GetLogItem().m_nLineNum << "] "
			<< event->GetLogItem().msg << std::endl;
		// std::cout.flush();
	}

	FileLogAppender::FileLogAppender(const std::string& p_filename, size_t p_nmaxSize, bool p_boverwrite)
		: m_filename(p_filename),
		m_fileSize(p_nmaxSize),
		m_overwrite(p_boverwrite)
	{
		ReOpen();
	}

	FileLogAppender::~FileLogAppender()
	{
		Close();
	}

	void FileLogAppender::log(LogEvent::ptr p_event)
	{
		/// 判断文件大小,若超过10M,则将现有文件另存为,下一条日志写新文件里面
		// m_filestream.seekp(0, SEEK_END); /// 定位到文件尾部,其实可以不用加这行代码
		if (m_filestream.tellp() >= (m_fileSize* 1024 * 1024))
		{
			/// 关闭文件流
			m_filestream.flush(); 
			m_filestream.close(); 

			if (m_overwrite) 
			{
				// 覆盖旧文件
				remove(m_filename.c_str());
			}
			else
			{
				size_t nPos = m_filename.find_last_of('.');
				time_t nCurTime = Utils::GetTimeStamp();
				std::string strTime = Utils::GetTime(nCurTime, "%Y%m%d%H%M%S");
				std::string strFilenew = m_filename;
				std::string strFileold = m_filename;
				strFilenew = strFilenew.insert(nPos, strTime);
				/// 文件改名 
				rename(strFileold.c_str(), strFilenew.c_str()); /// 文件名中不能包含特殊字符,否则会调用失败,返回-1
			} 

		}
		if (!m_filestream.is_open())
		{
			ReOpen();
		} 

		/// 写文件 
		m_filestream << "[" << Utils::GetTime(p_event->GetLogItem().m_llTime, "%Y-%m-%d %H:%M:%S") << "] ["
			<< LogLevel::ToString(p_event->GetLogItem().m_LevelId) << "] ["
			<< Utils::GetThreadId(p_event->GetLogItem().m_threadId) << "] ["
			<< p_event->GetLogItem().m_strFileName << ":"
			<< p_event->GetLogItem().m_nLineNum << "] "
			<< p_event->GetLogItem().msg << std::endl;
		//m_filestream.flush();
	} 

	bool FileLogAppender::ReOpen()
	{ 
		if (m_filestream.is_open())
		{
			m_filestream.close();
		}
		m_filestream.open(m_filename.c_str(), std::ios::out | std::ios::app | std::ios::binary);
		return m_filestream.is_open();
	}

	void FileLogAppender::Close()
	{ 
		if (m_filestream.is_open())
		{
			m_filestream.flush();
			m_filestream.close();
		}
	}

	void LogAppenderManager::Start()
	{
		std::lock_guard<std::mutex> lock(m_mutex);
		auto it = m_mapLogAppender.begin();
		for (; it != m_mapLogAppender.end(); ++it)
		{
			it->second->Start();
		}
	}

	void LogAppenderManager::Stop()
	{
		std::lock_guard<std::mutex> lock(m_mutex);
		auto it = m_mapLogAppender.begin();
		for (; it != m_mapLogAppender.end(); ++it)
		{
			it->second->Stop();
		}
	}

	void LogAppenderManager::AddEvent(const LogEvent::ptr &event)
	{
		std::lock_guard<std::mutex> lock(m_mutex);
		auto it = m_mapLogAppender.begin();
		for (; it != m_mapLogAppender.end(); ++it)
		{
			it->second->AddEvent(event);
		}
	}

	void LogAppender::Start()
	{
		m_isRun = true;
		std::thread([this]() 
			{
			Run();
		}).detach();
	}

	void LogAppender::Stop()
	{
		m_isRun = false;
		m_cv.notify_all();

		std::unique_lock<std::mutex> lock(m_mutex);
		m_cv.wait(lock);
	}

	inline void LogAppender::AddEvent(const LogEvent::ptr &event)
	{
		std::lock_guard<std::mutex> lock(m_mutex);
		m_listEvent.push_back(event);
		m_cv.notify_one();
	}

 	void LogAppender::Run()
	{
		while (m_isRun)
		{ 
			std::unique_lock<std::mutex> lock(m_mutex);
			m_cv.wait_for(lock, std::chrono::milliseconds(1000));
			while (!m_listEvent.empty())
			{ 
				/// 取当前第一个元素
				LogEvent::ptr event = m_listEvent.front();
				m_listEvent.pop_front();
				log(event);
			}
		}
		m_cv.notify_all();
	}

	Logger::Logger()
	{
		/// 创建输出管理
		m_LogAppenderManager = std::make_shared<LogAppenderManager>();

		/// 创建控制台输出器 //默认就使用控制台输出
		LogAppender::ptr p(new StdoutLogAppender());
		m_LogAppenderManager->AddAppender(OUT_MODEL::OUT_CONSOLE, p);

		/// 启动输出
		m_LogAppenderManager->Start();

		Init("");
	}

	std::shared_ptr<Logger> & Logger::Instance()
	{
		static std::shared_ptr<Logger> instance; // 使用shared_ptr还是unique_ptr,哪个好一点
		static std::once_flag s_flag;
		std::call_once(s_flag, [&]()
		{
			instance.reset(new Logger); /// 默认构建的是类T的无参构造函数
		});

		return instance;
	}

	Logger::~Logger()
	{
		m_LogAppenderManager->Stop();
	}

	void Logger::Init(const std::string & p_cfgFile)
	{
		// 停止输出
		m_LogAppenderManager->Stop();

		/// 配置属性
		std::string strFileName = "license.log";
		std::string strFilePath = "./log";
		unsigned int nFileSize = 10; /// 单位 MB
		bool bFileOverwrite = false;   /// true表示超过设置大小后对日志文件进行覆盖写,丢弃旧的日志
		bool bOutFile = true;
		bool bOutSocket = false;
		bool bOutConsole = true;

		struct stat buffer;
		if (stat("./log", &buffer) != 0)
		{
			size_t nPre = 0, nPos;
			std::string strDir;
			int nMdret;

			if (strFilePath[strFilePath.size() - 1] != '/')
			{
				// force trailing / so we can handle everything in loop
				strFilePath += '/';
			}

			while ((nPos = strFilePath.find_first_of('/', nPre)) != std::string::npos)
			{
				strDir = strFilePath.substr(0, nPos++);
				nPre = nPos;
				if (strDir.size() == 0)
					continue; // if leading / first time is 0 length
				if ((nMdret = ::mkdir(strDir.c_str(), 0755)) && errno != EEXIST)
				{
					;
				}
			}
		}

		 输出配置
		if (!bOutConsole) // 用户配置文件不需要输出到控制台
		{
			// 因为配置文件中去掉了控制台的输出,所以这个地方要把构造函数中添加的Appender去掉
			m_LogAppenderManager->RemoveAppender(OUT_MODEL::OUT_CONSOLE);
		}

		if (bOutFile)
		{ 
			std::string strFile = strFilePath + '/' + strFileName;
			std::replace(strFile.begin(), strFile.end(), '\\', '/');

			/// 去除多余的'/'
			int nPos1 = 0;
			while (true)
			{
				nPos1 = strFile.find('/', nPos1);
				if (nPos1 < 0)
				{
					break;
				}
				int nPos2 = strFile.find('/', nPos1 + 1);
				if (nPos1 == nPos2 - 1) /// 相邻
				{
					strFile.erase(nPos1, 1);
				}
				++nPos1;
			}

			/// 创建控制台输出器
			LogAppender::ptr p(new FileLogAppender(strFile, nFileSize, bFileOverwrite));
			m_LogAppenderManager->AddAppender(OUT_MODEL::OUT_FILE, p);
		}

		// 启动输出
		m_LogAppenderManager->Start();
	} 

	void Logger::AddEvent(const LogEvent::ptr &p) 
	{
		m_LogAppenderManager->AddEvent(p);
	}

	LogEventWrap::LogEventWrap(LogEvent::ptr e)
		:m_event(e)
	{
	}

	LogEventWrap::~LogEventWrap()
	{
		m_event->SetContent(m_event->GetSS().str());
		Logger::Instance()->AddEvent(m_event);
	}
}


main.cpp

#include "ELog.h"

int main()
{
	for (int i = 0; i < 10; i++)
	{
		log_info << "AsyncLogging  info." << 100;
		log_debug << "AsyncLogging  info." << 159;
		log_error << "AsyncLogging  info." << 258;
		log_warn << "AsyncLogging  info." << 369;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值