Older/ToolKit/Util/logger.h
amass 9de3af15eb
All checks were successful
Deploy / PullDocker (push) Successful in 12s
Deploy / Build (push) Successful in 1m51s
add ZLMediaKit code for learning.
2024-09-28 23:55:00 +08:00

446 lines
13 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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 <cstdarg>
#include <set>
#include <map>
#include <fstream>
#include <thread>
#include <memory>
#include <mutex>
#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<LogContext>;
typedef enum {
LTrace = 0, LDebug, LInfo, LWarn, LError
} LogLevel;
Logger &getLogger();
void setLogger(Logger *logger);
/**
* 日志类
*/
class Logger : public std::enable_shared_from_this<Logger>, public noncopyable {
public:
friend class AsyncLogWriter;
using Ptr = std::shared_ptr<Logger>;
/**
* 获取日志单例
* @return
*/
static Logger &Instance();
explicit Logger(const std::string &loggerName);
~Logger();
/**
* 添加日志通道,非线程安全的
* @param channel log通道
*/
void add(const std::shared_ptr<LogChannel> &channel);
/**
* 删除日志通道,非线程安全的
* @param name log通道名
*/
void del(const std::string &name);
/**
* 获取日志通道,非线程安全的
* @param name log通道名
* @return 线程通道
*/
std::shared_ptr<LogChannel> get(const std::string &name);
/**
* 设置写log器非线程安全的
* @param writer 写log器
*/
void setWriter(const std::shared_ptr<LogWriter> &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<LogWriter> _writer;
std::shared_ptr<LogChannel> _default_channel;
std::map<std::string, std::shared_ptr<LogChannel> > _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>;
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<typename T>
LogContextCapture &operator<<(T &&data) {
if (!_ctx) {
return *this;
}
(*_ctx) << std::forward<T>(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<std::thread> _thread;
List<std::pair<LogContextPtr, Logger *> > _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<std::string> _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<typename First, typename ...ARGS>
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>(first);
appendLog(log, std::forward<ARGS>(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<typename Log, typename First, typename ...ARGS>
static inline void appendLog(Log &out, First &&first, ARGS &&...args) {
out << std::forward<First>(first);
appendLog(out, std::forward<ARGS>(args)...);
}
template<typename Log>
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_ */