同时具备多线程和多进程安全的写日志工具
时间:2022-06-06
本文章向大家介绍同时具备多线程和多进程安全的写日志工具,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
接口请浏览:https://github.com/eyjian/mooon/blob/master/mooon/include/mooon/sys/log.h 实现头文件请浏览:https://github.com/eyjian/mooon/blob/master/mooon/include/mooon/sys/safe_logger.h 测试代码:https://github.com/eyjian/mooon/blob/master/mooon/test/sys/test_safe_logger.cpp 使用示例: MYLOG_DEBUG("[%d]MMMMM", 2015); 支持自动添加换行符,默认会自动添加换行符,但可以禁止自动添加换行符。还支持自动在行尾添加点号等功能。 下面是实现:
#include "mooon/sys/safe_logger.h"
#include "mooon/sys/datetime_utils.h"
#include "mooon/sys/file_locker.h"
#include "mooon/sys/file_utils.h"
#include "mooon/utils/scoped_ptr.h"
#include "mooon/utils/string_utils.h"
#include <libgen.h>
#include <pthread.h>
#include <sstream>
#include <syslog.h>
#include <unistd.h>
#define WRITE_SYSLOG 0 // 出错时是否记录系统日志,1表示记录
SYS_NAMESPACE_BEGIN
// 线程级别的
static __thread int sg_thread_log_fd = -1;
CSafeLogger* create_safe_logger(bool enable_program_path, uint16_t log_line_size) throw (CSyscallException)
{
std::string log_dirpath = get_log_dirpath(enable_program_path);
std::string log_filename = get_log_filename();
CSafeLogger* logger = new CSafeLogger(log_dirpath.c_str(), log_filename.c_str(), log_line_size);
set_log_level_by_env(logger);
enable_screen_log_by_env(logger);
enable_trace_log_by_env(logger);
set_log_filesize_by_env(logger);
set_log_backup_by_env(logger);
return logger;
}
CSafeLogger* create_safe_logger(const std::string& log_dirpath, const std::string& cpp_filename, uint16_t log_line_size) throw (CSyscallException)
{
char* cpp_filepath = strdup(cpp_filename.c_str());
std::string only_filename = basename(cpp_filepath);
free(cpp_filepath);
std::string log_filename = utils::CStringUtils::replace_suffix(only_filename, ".log");
CSafeLogger* logger = new CSafeLogger(log_dirpath.c_str(), log_filename.c_str(), log_line_size);
set_log_level_by_env(logger);
enable_screen_log_by_env(logger);
enable_trace_log_by_env(logger);
set_log_filesize_by_env(logger);
set_log_backup_by_env(logger);
return logger;
}
////////////////////////////////////////////////////////////////////////////////
CSafeLogger::CSafeLogger(const char* log_dir, const char* log_filename, uint16_t log_line_size) throw (CSyscallException)
:_auto_adddot(false)
,_auto_newline(true)
,_bin_log_enabled(false)
,_trace_log_enabled(false)
,_raw_log_enabled(false)
,_screen_enabled(false)
,_log_dir(log_dir)
,_log_filename(log_filename)
,_log_filepath(_log_dir + std::string("/") + _log_filename)
{
atomic_set(&_max_bytes, DEFAULT_LOG_FILE_SIZE);
atomic_set(&_log_level, LOG_LEVEL_INFO);
atomic_set(&_backup_number, DEFAULT_LOG_FILE_BACKUP_NUMBER);
// 保证日志行最大长度不小于指定值
_log_line_size = (log_line_size < LOG_LINE_SIZE_MIN)? LOG_LINE_SIZE_MIN: log_line_size;
if (_log_line_size > LOG_LINE_SIZE_MAX)
_log_line_size = LOG_LINE_SIZE_MAX;
int log_fd = open(_log_filepath.c_str(), O_WRONLY|O_CREAT|O_APPEND, FILE_DEFAULT_PERM);
if (-1 == log_fd)
{
int errcode = errno;
fprintf(stderr, "[%d:%lu] SafeLogger open %s error: %mn", getpid(), pthread_self(), _log_filepath.c_str());
THROW_SYSCALL_EXCEPTION(NULL, errcode, "open");
}
else
{
close(log_fd);
}
}
CSafeLogger::~CSafeLogger()
{
(void)release();
}
int CSafeLogger::release()
{
int ret = 0;
if (sg_thread_log_fd != -1)
{
#if 0 // 由系统决定何时fsync
ret = fsync(sg_thread_log_fd);
if (-1 == ret)
{
fprintf(stderr, "process(%u,%lu) fsync fd(%d) error: %mn", getpid(), pthread_self(), sg_thread_log_fd);
}
else
#endif
{
ret = close(sg_thread_log_fd);
if (0 == ret)
sg_thread_log_fd = -1;
else
fprintf(stderr, "process(%u,%lu) close fd(%d) error: %mn", getpid(), pthread_self(), sg_thread_log_fd);
}
}
return ret;
}
void CSafeLogger::enable_screen(bool enabled)
{
_screen_enabled = enabled;
}
void CSafeLogger::enable_bin_log(bool enabled)
{
_bin_log_enabled = enabled;
}
void CSafeLogger::enable_trace_log(bool enabled)
{
_trace_log_enabled = enabled;
}
void CSafeLogger::enable_raw_log(bool enabled)
{
_raw_log_enabled = enabled;
}
void CSafeLogger::enable_auto_adddot(bool enabled)
{
_auto_adddot = enabled;
}
void CSafeLogger::enable_auto_newline(bool enabled)
{
_auto_newline = enabled;
}
void CSafeLogger::set_log_level(log_level_t log_level)
{
atomic_set(&_log_level, log_level);
}
void CSafeLogger::set_single_filesize(uint32_t filesize)
{
uint32_t max_bytes = (filesize < LOG_LINE_SIZE_MIN*10)? LOG_LINE_SIZE_MIN*10: filesize;
atomic_set(&_max_bytes, max_bytes);
}
void CSafeLogger::set_backup_number(uint16_t backup_number)
{
atomic_set(&_backup_number, backup_number);
}
bool CSafeLogger::enabled_bin()
{
return _bin_log_enabled;
}
bool CSafeLogger::enabled_detail()
{
return atomic_read(&_log_level) <= LOG_LEVEL_DETAIL;
}
bool CSafeLogger::enabled_debug()
{
return atomic_read(&_log_level) <= LOG_LEVEL_DEBUG;
}
bool CSafeLogger::enabled_info()
{
return atomic_read(&_log_level) <= LOG_LEVEL_INFO;
}
bool CSafeLogger::enabled_warn()
{
return atomic_read(&_log_level) <= LOG_LEVEL_WARN;
}
bool CSafeLogger::enabled_error()
{
return atomic_read(&_log_level) <= LOG_LEVEL_ERROR;
}
bool CSafeLogger::enabled_fatal()
{
return atomic_read(&_log_level) <= LOG_LEVEL_FATAL;
}
bool CSafeLogger::enabled_state()
{
return atomic_read(&_log_level) <= LOG_LEVEL_STATE;
}
bool CSafeLogger::enabled_trace()
{
return _trace_log_enabled;
}
bool CSafeLogger::enabled_raw()
{
return _raw_log_enabled;
}
void CSafeLogger::log_detail(const char* filename, int lineno, const char* module_name, const char* format, va_list& args)
{
if (enabled_detail())
do_log(LOG_LEVEL_DETAIL, filename, lineno, module_name, format, args);
}
void CSafeLogger::log_detail(const char* filename, int lineno, const char* module_name, const char* format, ...)
{
if (enabled_detail())
{
va_list args;
va_start(args, format);
utils::VaListHelper vh(args);
do_log(LOG_LEVEL_DETAIL, filename, lineno, module_name, format, args);
}
}
void CSafeLogger::log_debug(const char* filename, int lineno, const char* module_name, const char* format, va_list& args)
{
if (enabled_detail())
do_log(LOG_LEVEL_DEBUG, filename, lineno, module_name, format, args);
}
void CSafeLogger::log_debug(const char* filename, int lineno, const char* module_name, const char* format, ...)
{
if (enabled_debug())
{
va_list args;
va_start(args, format);
utils::VaListHelper vh(args);
do_log(LOG_LEVEL_DEBUG, filename, lineno, module_name, format, args);
}
}
void CSafeLogger::log_info(const char* filename, int lineno, const char* module_name, const char* format, va_list& args)
{
if (enabled_info())
do_log(LOG_LEVEL_INFO, filename, lineno, module_name, format, args);
}
void CSafeLogger::log_info(const char* filename, int lineno, const char* module_name, const char* format, ...)
{
if (enabled_info())
{
va_list args;
va_start(args, format);
utils::VaListHelper vh(args);
do_log(LOG_LEVEL_INFO, filename, lineno, module_name, format, args);
}
}
void CSafeLogger::log_warn(const char* filename, int lineno, const char* module_name, const char* format, va_list& args)
{
if (enabled_warn())
do_log(LOG_LEVEL_WARN, filename, lineno, module_name, format, args);
}
void CSafeLogger::log_warn(const char* filename, int lineno, const char* module_name, const char* format, ...)
{
if (enabled_warn())
{
va_list args;
va_start(args, format);
utils::VaListHelper vh(args);
do_log(LOG_LEVEL_WARN, filename, lineno, module_name, format, args);
}
}
void CSafeLogger::log_error(const char* filename, int lineno, const char* module_name, const char* format, va_list& args)
{
if (enabled_error())
do_log(LOG_LEVEL_ERROR, filename, lineno, module_name, format, args);
}
void CSafeLogger::log_error(const char* filename, int lineno, const char* module_name, const char* format, ...)
{
if (enabled_error())
{
va_list args;
va_start(args, format);
utils::VaListHelper vh(args);
do_log(LOG_LEVEL_ERROR, filename, lineno, module_name, format, args);
}
}
void CSafeLogger::log_fatal(const char* filename, int lineno, const char* module_name, const char* format, va_list& args)
{
if (enabled_fatal())
do_log(LOG_LEVEL_FATAL, filename, lineno, module_name, format, args);
}
void CSafeLogger::log_fatal(const char* filename, int lineno, const char* module_name, const char* format, ...)
{
if (enabled_fatal())
{
va_list args;
va_start(args, format);
utils::VaListHelper vh(args);
do_log(LOG_LEVEL_FATAL, filename, lineno, module_name, format, args);
}
}
void CSafeLogger::log_state(const char* filename, int lineno, const char* module_name, const char* format, va_list& args)
{
if (enabled_state())
do_log(LOG_LEVEL_STATE, filename, lineno, module_name, format, args);
}
void CSafeLogger::log_state(const char* filename, int lineno, const char* module_name, const char* format, ...)
{
if (enabled_state())
{
va_list args;
va_start(args, format);
utils::VaListHelper vh(args);
do_log(LOG_LEVEL_STATE, filename, lineno, module_name, format, args);
}
}
void CSafeLogger::log_trace(const char* filename, int lineno, const char* module_name, const char* format, va_list& args)
{
if (enabled_trace())
do_log(LOG_LEVEL_TRACE, filename, lineno, module_name, format, args);
}
void CSafeLogger::log_trace(const char* filename, int lineno, const char* module_name, const char* format, ...)
{
if (enabled_trace())
{
va_list args;
va_start(args, format);
utils::VaListHelper vh(args);
do_log(LOG_LEVEL_TRACE, filename, lineno, module_name, format, args);
}
}
void CSafeLogger::log_raw(const char* format, va_list& args)
{
if (enabled_raw())
do_log(LOG_LEVEL_RAW, NULL, -1, NULL, format, args);
}
void CSafeLogger::log_raw(const char* format, ...)
{
if (enabled_raw())
{
va_list args;
va_start(args, format);
utils::VaListHelper vh(args);
do_log(LOG_LEVEL_RAW, NULL, -1, NULL, format, args);
}
}
void CSafeLogger::log_bin(const char* filename, int lineno, const char* module_name, const char* log, uint16_t size)
{
if (enabled_bin())
{
std::string str(size*2, ' ');
char* str_p = const_cast<char*>(str.data());
for (uint16_t i=0; i<size; ++i)
{
snprintf(str_p, 3, "%02X", (int)log[i]);
str_p += 2;
}
va_list args;
do_log(LOG_LEVEL_BIN, filename, lineno, module_name, str.c_str(), args);
}
}
int CSafeLogger::get_thread_log_fd() const
{
if (-1 == sg_thread_log_fd)
{
sg_thread_log_fd = open(_log_filepath.c_str(), O_WRONLY|O_CREAT|O_APPEND, FILE_DEFAULT_PERM);
if (-1 == sg_thread_log_fd)
fprintf(stderr, "open %s error: %mn", _log_filepath.c_str());
}
return sg_thread_log_fd;
}
bool CSafeLogger::need_rotate(int fd) const
{
off_t file_size = CFileUtils::get_file_size(fd);
return file_size > static_cast<off_t>(atomic_read(&_max_bytes));
}
void CSafeLogger::do_log(log_level_t log_level, const char* filename, int lineno, const char* module_name, const char* format, va_list& args)
{
int log_real_size;
utils::ScopedArray<char> log_line(new char[_log_line_size]);
if (LOG_LEVEL_RAW == log_level)
{
// fix_vsnprintf()的返回值包含了结尾符在内的长度
log_real_size = utils::CStringUtils::fix_vsnprintf(log_line.get(), _log_line_size, format, args);
--log_real_size; // 结尾符不需要写入日志文件中
}
else
{
std::stringstream log_header; // 每条日志的头
char datetime[sizeof("2012-12-12 12:12:12/0123456789")];
get_formatted_current_datetime(datetime, sizeof(datetime));
// 日志头内容:[日期][线程ID/进程ID][日志级别][模块名][代码文件名][代码行号]
log_header << "[" << datetime << "]"
<< "[" << pthread_self() << "/" << getpid() << "]"
<< "[" << get_log_level_name(log_level) << "]";
if (module_name != NULL)
log_header << "[" << module_name << "]";
if (filename != NULL)
log_header << "[" << utils::CStringUtils::extract_filename(filename) << ":" << lineno << "]";
int m, n;
// 注意fix_snprintf()的返回值大小包含了结尾符
m = utils::CStringUtils::fix_snprintf(log_line.get(), _log_line_size, "%s", log_header.str().c_str());
if (LOG_LEVEL_BIN == log_level)
n = utils::CStringUtils::fix_snprintf(log_line.get()+m-1, _log_line_size-m, "%s", format);
else
n = utils::CStringUtils::fix_vsnprintf(log_line.get()+m-1, _log_line_size-m, format, args);
log_real_size = m + n - 2;
// 是否自动添加结尾用的点号
if (_auto_adddot)
{
// 如果已有结尾的点,则不再添加,以免重复
if (log_line.get()[log_real_size-1] != '.')
{
log_line.get()[log_real_size] = '.';
++log_real_size;
}
}
// 是否自动换行
if (_auto_newline)
{
// 如果已有一个换行符,则不再添加
if (log_line.get()[log_real_size-1] != 'n')
{
log_line.get()[log_real_size] = 'n';
++log_real_size;
}
}
}
// 允许打屏
if (_screen_enabled)
{
(void)write(STDOUT_FILENO, log_line.get(), log_real_size);
}
if (false)
{
// 异步写入日志文件
//log_line.release();
}
else
{
// 同步写入日志文件
int thread_log_fd = get_thread_log_fd();
if (thread_log_fd != -1)
{
write_log(thread_log_fd, log_line.get(), log_real_size);
}
else
{
fprintf(stderr, "process(%u,%lu) without thread logn", getpid(), pthread_self());
#if WRITE_SYSLOG==1
openlog("mooon-safe-logger", LOG_CONS|LOG_PID, 0);
syslog(LOG_ERR, "process(%u,%lu) without thread logn", getpid(), pthread_self());
closelog();
#endif // WRITE_SYSLOG
}
}
}
void CSafeLogger::rotate_log()
{
std::string new_path; // 滚动后的文件路径,包含目录和文件名
std::string old_path; // 滚动前的文件路径,包含目录和文件名
// 历史滚动
int backup_number = atomic_read(&_backup_number);
for (int i=backup_number-1; i>1; --i)
{
new_path = _log_dir + std::string("/") + _log_filename + std::string(".") + utils::CStringUtils::any2string(static_cast<int>(i));
old_path = _log_dir + std::string("/") + _log_filename + std::string(".") + utils::CStringUtils::any2string(static_cast<int>(i-1));
if (0 == access(old_path.c_str(), F_OK))
{
if (-1 == rename(old_path.c_str(), new_path.c_str()))
fprintf(stderr, "[%d:%lu] SafeLogger rename %s to %s error: %m.n", getpid(), pthread_self(), old_path.c_str(), new_path.c_str());
}
else
{
if (errno != ENOENT)
fprintf(stderr, "[%d:%lu] SafeLogger access %s error: %m.n", getpid(), pthread_self(), old_path.c_str());
}
}
if (backup_number > 0)
{
// 当前滚动
new_path = _log_dir + std::string("/") + _log_filename + std::string(".1");
if (0 == access(_log_filepath.c_str(), F_OK))
{
if (-1 == rename(_log_filepath.c_str(), new_path.c_str()))
fprintf(stderr, "[%d:%lu] SafeLogger rename %s to %s error: %mn", getpid(), pthread_self(), _log_filepath.c_str(), new_path.c_str());
}
else
{
if (errno != ENOENT)
fprintf(stderr, "[%d:%lu] SafeLogger access %s error: %mn", getpid(), pthread_self(), _log_filepath.c_str());
}
}
// 重新创建
//fprintf(stdout, "[%d:%lu] SafeLogger create %sn", getpid(), pthread_self(), _log_filepath.c_str());
int new_log_fd = open(_log_filepath.c_str(), O_WRONLY|O_CREAT|O_APPEND, FILE_DEFAULT_PERM); // O_EXCL
if (new_log_fd != -1)
{
sg_thread_log_fd = new_log_fd;
}
else
{
fprintf(stderr, "[%d:%lu] SafeLogger create %s error: %mn", getpid(), pthread_self(), _log_filepath.c_str());
#if WRITE_SYSLOG==1
openlog("mooon-safe-logger", LOG_CONS|LOG_PID, 0);
syslog(LOG_ERR, "[%d:%lu] SafeLogger create %s error: %mn", getpid(), pthread_self(), _log_filepath.c_str());
closelog();
#endif // WRITE_SYSLOG
}
}
void CSafeLogger::write_log(int thread_log_fd, const char* log_line, int log_line_size)
{
int bytes = write(thread_log_fd, log_line, log_line_size);
if (-1 == bytes)
{
fprintf(stderr, "[%d:%lu] SafeLogger[%d] write error: %mn", getpid(), pthread_self(), thread_log_fd);
}
else if (0 == bytes)
{
fprintf(stderr, "[%d:%lu] write nothing: SafeLogger[%d]n", getpid(), pthread_self(), thread_log_fd);
}
else if (bytes > 0)
{
try
{
// 判断是否需要滚动
if (need_rotate(thread_log_fd))
{
std::string lock_path = _log_dir + std::string("/.") + _log_filename + std::string(".lock");
FileLocker file_locker(lock_path.c_str(), true); // 确保这里一定加锁
// _fd可能已被其它进程或线程滚动了,所以这里需要重新open一下
int new_log_fd = open(_log_filepath.c_str(), O_WRONLY|O_CREAT|O_APPEND, FILE_DEFAULT_PERM);
if (-1 == new_log_fd)
{
fprintf(stderr, "[%d:%lu] SafeLogger open %s error: %mn", getpid(), pthread_self(), _log_filepath.c_str());
}
else
{
try
{
release();
if (!need_rotate(new_log_fd))
{
// 其它进程或线程抢先做了滚动
sg_thread_log_fd = new_log_fd;
}
else
{
close(new_log_fd);
rotate_log();
}
}
catch (CSyscallException& syscall_ex)
{
fprintf(stderr, "[%d:%lu] SafeLogger[%d] rotate error: %s.n", getpid(), pthread_self(), new_log_fd, syscall_ex.str().c_str());
}
}
}
}
catch (CSyscallException& syscall_ex)
{
fprintf(stderr, "[%d:%lu] SafeLogger[%d] rotate error: %sn", getpid(), pthread_self(), thread_log_fd, syscall_ex.str().c_str());
}
}
}
SYS_NAMESPACE_END
- 【释疑文】DeveMobile、EaseMobile 及Devework 主题的区别
- DeveMobile/EaseMobile 主题双双更新1.1,增加离线存储,社交媒体关注等功能
- Angularjs基础(六)
- 如果机器人拥有痛觉,这个世界会怎样
- 阻止iOS Web APP中点击链接跳转到Safari 浏览器新标签页
- 专门攻击工资支付系统的网络犯罪
- Angularjs基础(五)
- DW Replace Open Sans:将WordPress 后台中的open-sans字体加载源从Google Fonts换为360 CDN
- Google Fonts 已可正常访问,相关插件可卸
- 微信支付宝完成这几个设置,别人偷不走你一毛钱!
- Angularjs基础(四)
- Google Fonts导致WordPress 速度问题的三个解决方案
- DW Replace Open Sans:将WordPress 后台中的open-sans字体加载源从Google Fonts换为360 CDN
- Angularjs基础(三)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 微信小程序的自定义组件(入门)
- linux 中关于PAM的点滴笔记
- 49. Vue使用axios发送Ajax请求
- UCSC-browser学习:创建自己的track hubs
- 五分钟快速搭建Serverless免费邮件服务
- 基于qiankun落地部署微前端爬”坑“记
- Android推送的群魔乱舞
- 用百度接口实现图片文字识别,并打包成安装包软件
- 视野前端(二)V8引擎是如何工作的
- 【干货】Chrome插件(扩展)开发全攻略
- 超性感的React Hooks(一):为何她独具魅力
- 超性感的React Hooks(二)再谈闭包
- Python全栈(一)基础之11.函数(3)
- Python全栈(二)数据结构和算法之1.算法和数据结构引入
- Android开发(第一行代码 第二版) 常见异常和解决办法(基于Android Studio)(一)