/* * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. * * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). * * Use of this source code is governed by MIT license that can be found in the * LICENSE file in the root of the source tree. All contributing project authors * may be found in the AUTHORS file in the root of the source tree. */ #ifndef UTIL_LOGGER_H_ #define UTIL_LOGGER_H_ #include #include #include #include #include #include #include #include "util.h" #include "List.h" #include "Thread/semaphore.h" namespace toolkit { class LogContext; class LogChannel; class LogWriter; class Logger; using LogContextPtr = std::shared_ptr; typedef enum { LTrace = 0, LDebug, LInfo, LWarn, LError } LogLevel; Logger &getLogger(); void setLogger(Logger *logger); /** * 日志类 */ class Logger : public std::enable_shared_from_this, public noncopyable { public: friend class AsyncLogWriter; using Ptr = std::shared_ptr; /** * 获取日志单例 * @return */ static Logger &Instance(); explicit Logger(const std::string &loggerName); ~Logger(); /** * 添加日志通道,非线程安全的 * @param channel log通道 */ void add(const std::shared_ptr &channel); /** * 删除日志通道,非线程安全的 * @param name log通道名 */ void del(const std::string &name); /** * 获取日志通道,非线程安全的 * @param name log通道名 * @return 线程通道 */ std::shared_ptr get(const std::string &name); /** * 设置写log器,非线程安全的 * @param writer 写log器 */ void setWriter(const std::shared_ptr &writer); /** * 设置所有日志通道的log等级 * @param level log等级 */ void setLevel(LogLevel level); /** * 获取logger名 * @return logger名 */ const std::string &getName() const; /** * 写日志 * @param ctx 日志信息 */ void write(const LogContextPtr &ctx); private: /** * 写日志到各channel,仅供AsyncLogWriter调用 * @param ctx 日志信息 */ void writeChannels(const LogContextPtr &ctx); void writeChannels_l(const LogContextPtr &ctx); private: LogContextPtr _last_log; std::string _logger_name; std::shared_ptr _writer; std::shared_ptr _default_channel; std::map > _channels; }; ///////////////////LogContext/////////////////// /** * 日志上下文 */ class LogContext : public std::ostringstream { public: //_file,_function改成string保存,目的是有些情况下,指针可能会失效 //比如说动态库中打印了一条日志,然后动态库卸载了,那么指向静态数据区的指针就会失效 LogContext() = default; LogContext(LogLevel level, const char *file, const char *function, int line, const char *module_name, const char *flag); ~LogContext() = default; LogLevel _level; int _line; int _repeat = 0; std::string _file; std::string _function; std::string _thread_name; std::string _module_name; std::string _flag; struct timeval _tv; const std::string &str(); private: bool _got_content = false; std::string _content; }; /** * 日志上下文捕获器 */ class LogContextCapture { public: using Ptr = std::shared_ptr; LogContextCapture(Logger &logger, LogLevel level, const char *file, const char *function, int line, const char *flag = ""); LogContextCapture(const LogContextCapture &that); ~LogContextCapture(); /** * 输入std::endl(回车符)立即输出日志 * @param f std::endl(回车符) * @return 自身引用 */ LogContextCapture &operator<<(std::ostream &(*f)(std::ostream &)); template LogContextCapture &operator<<(T &&data) { if (!_ctx) { return *this; } (*_ctx) << std::forward(data); return *this; } void clear(); private: LogContextPtr _ctx; Logger &_logger; }; ///////////////////LogWriter/////////////////// /** * 写日志器 */ class LogWriter : public noncopyable { public: LogWriter() = default; virtual ~LogWriter() = default; virtual void write(const LogContextPtr &ctx, Logger &logger) = 0; }; class AsyncLogWriter : public LogWriter { public: AsyncLogWriter(); ~AsyncLogWriter(); private: void run(); void flushAll(); void write(const LogContextPtr &ctx, Logger &logger) override; private: bool _exit_flag; semaphore _sem; std::mutex _mutex; std::shared_ptr _thread; List > _pending; }; ///////////////////LogChannel/////////////////// /** * 日志通道 */ class LogChannel : public noncopyable { public: LogChannel(const std::string &name, LogLevel level = LTrace); virtual ~LogChannel(); virtual void write(const Logger &logger, const LogContextPtr &ctx) = 0; const std::string &name() const; void setLevel(LogLevel level); static std::string printTime(const timeval &tv); protected: /** * 打印日志至输出流 * @param ost 输出流 * @param enable_color 是否启用颜色 * @param enable_detail 是否打印细节(函数名、源码文件名、源码行) */ virtual void format(const Logger &logger, std::ostream &ost, const LogContextPtr &ctx, bool enable_color = true, bool enable_detail = true); protected: std::string _name; LogLevel _level; }; /** * 输出日至到广播 */ class EventChannel : public LogChannel { public: //输出日志时的广播名 static const std::string kBroadcastLogEvent; //toolkit目前仅只有一处全局变量被外部引用,减少导出相关定义,导出以下函数避免导出kBroadcastLogEvent static const std::string& getBroadcastLogEventName(); //日志广播参数类型和列表 #define BroadcastLogEventArgs const Logger &logger, const LogContextPtr &ctx EventChannel(const std::string &name = "EventChannel", LogLevel level = LTrace); ~EventChannel() override = default; void write(const Logger &logger, const LogContextPtr &ctx) override; }; /** * 输出日志至终端,支持输出日志至android logcat */ class ConsoleChannel : public LogChannel { public: ConsoleChannel(const std::string &name = "ConsoleChannel", LogLevel level = LTrace); ~ConsoleChannel() override = default; void write(const Logger &logger, const LogContextPtr &logContext) override; }; /** * 输出日志至文件 */ class FileChannelBase : public LogChannel { public: FileChannelBase(const std::string &name = "FileChannelBase", const std::string &path = exePath() + ".log", LogLevel level = LTrace); ~FileChannelBase() override; void write(const Logger &logger, const LogContextPtr &ctx) override; bool setPath(const std::string &path); const std::string &path() const; protected: virtual bool open(); virtual void close(); virtual size_t size(); protected: std::string _path; std::ofstream _fstream; }; class Ticker; /** * 自动清理的日志文件通道 * 默认最多保存30天的日志 */ class FileChannel : public FileChannelBase { public: FileChannel(const std::string &name = "FileChannel", const std::string &dir = exeDir() + "log/", LogLevel level = LTrace); ~FileChannel() override = default; /** * 写日志时才会触发新建日志文件或者删除老的日志文件 * @param logger * @param stream */ void write(const Logger &logger, const LogContextPtr &ctx) override; /** * 设置日志最大保存天数 * @param max_day 天数 */ void setMaxDay(size_t max_day); /** * 设置日志切片文件最大大小 * @param max_size 单位MB */ void setFileMaxSize(size_t max_size); /** * 设置日志切片文件最大个数 * @param max_count 个数 */ void setFileMaxCount(size_t max_count); private: /** * 删除日志切片文件,条件为超过最大保存天数与最大切片个数 */ void clean(); /** * 检查当前日志切片文件大小,如果超过限制,则创建新的日志切片文件 */ void checkSize(time_t second); /** * 创建并切换到下一个日志切片文件 */ void changeFile(time_t second); private: bool _can_write = false; //默认最多保存30天的日志文件 size_t _log_max_day = 30; //每个日志切片文件最大默认128MB size_t _log_max_size = 128; //最多默认保持30个日志切片文件 size_t _log_max_count = 30; //当前日志切片文件索引 size_t _index = 0; int64_t _last_day = -1; time_t _last_check_time = 0; std::string _dir; std::set _log_file_map; }; #if defined(__MACH__) || ((defined(__linux) || defined(__linux__)) && !defined(ANDROID)) class SysLogChannel : public LogChannel { public: SysLogChannel(const std::string &name = "SysLogChannel", LogLevel level = LTrace); ~SysLogChannel() override = default; void write(const Logger &logger, const LogContextPtr &logContext) override; }; #endif//#if defined(__MACH__) || ((defined(__linux) || defined(__linux__)) && !defined(ANDROID)) class BaseLogFlagInterface { protected: virtual ~BaseLogFlagInterface() {} // 获得日志标记Flag const char* getLogFlag(){ return _log_flag; } void setLogFlag(const char *flag) { _log_flag = flag; } private: const char *_log_flag = ""; }; class LoggerWrapper { public: template static inline void printLogArray(Logger &logger, LogLevel level, const char *file, const char *function, int line, First &&first, ARGS &&...args) { LogContextCapture log(logger, level, file, function, line); log << std::forward(first); appendLog(log, std::forward(args)...); } static inline void printLogArray(Logger &logger, LogLevel level, const char *file, const char *function, int line) { LogContextCapture log(logger, level, file, function, line); } template static inline void appendLog(Log &out, First &&first, ARGS &&...args) { out << std::forward(first); appendLog(out, std::forward(args)...); } template static inline void appendLog(Log &out) {} //printf样式的日志打印 static void printLog(Logger &logger, int level, const char *file, const char *function, int line, const char *fmt, ...); static void printLogV(Logger &logger, int level, const char *file, const char *function, int line, const char *fmt, va_list ap); }; //可重置默认值 extern Logger *g_defaultLogger; //用法: DebugL << 1 << "+" << 2 << '=' << 3; #define WriteL(level) ::toolkit::LogContextCapture(::toolkit::getLogger(), level, __FILE__, __FUNCTION__, __LINE__) #define TraceL WriteL(::toolkit::LTrace) #define DebugL WriteL(::toolkit::LDebug) #define InfoL WriteL(::toolkit::LInfo) #define WarnL WriteL(::toolkit::LWarn) #define ErrorL WriteL(::toolkit::LError) //只能在虚继承BaseLogFlagInterface的类中使用 #define WriteF(level) ::toolkit::LogContextCapture(::toolkit::getLogger(), level, __FILE__, __FUNCTION__, __LINE__, getLogFlag()) #define TraceF WriteF(::toolkit::LTrace) #define DebugF WriteF(::toolkit::LDebug) #define InfoF WriteF(::toolkit::LInfo) #define WarnF WriteF(::toolkit::LWarn) #define ErrorF WriteF(::toolkit::LError) //用法: PrintD("%d + %s = %c", 1 "2", 'c'); #define PrintLog(level, ...) ::toolkit::LoggerWrapper::printLog(::toolkit::getLogger(), level, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) #define PrintT(...) PrintLog(::toolkit::LTrace, ##__VA_ARGS__) #define PrintD(...) PrintLog(::toolkit::LDebug, ##__VA_ARGS__) #define PrintI(...) PrintLog(::toolkit::LInfo, ##__VA_ARGS__) #define PrintW(...) PrintLog(::toolkit::LWarn, ##__VA_ARGS__) #define PrintE(...) PrintLog(::toolkit::LError, ##__VA_ARGS__) //用法: LogD(1, "+", "2", '=', 3); //用于模板实例化的原因,如果每次打印参数个数和类型不一致,可能会导致二进制代码膨胀 #define LogL(level, ...) ::toolkit::LoggerWrapper::printLogArray(::toolkit::getLogger(), (LogLevel)level, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) #define LogT(...) LogL(::toolkit::LTrace, ##__VA_ARGS__) #define LogD(...) LogL(::toolkit::LDebug, ##__VA_ARGS__) #define LogI(...) LogL(::toolkit::LInfo, ##__VA_ARGS__) #define LogW(...) LogL(::toolkit::LWarn, ##__VA_ARGS__) #define LogE(...) LogL(::toolkit::LError, ##__VA_ARGS__) } /* namespace toolkit */ #endif /* UTIL_LOGGER_H_ */