同時具備多執行緒和多程式安全的寫日誌工具
介面請瀏覽: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: %m
“, 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: %m
“, 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: %m
“, 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, `0`);
-
char* str_p = const_castchar*>(str.data());
-
for (uint16_t i=0; isize; ++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: %m
“, _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_castoff_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::ScopedArraychar> 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 log
“, getpid(), pthread_self()); - #if WRITE_SYSLOG==1
- openlog(“mooon-safe-logger”, LOG_CONS|LOG_PID, 0);
-
syslog(LOG_ERR, “process(%u,%lu) without thread log
“, 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_castint>(i));
-
old_path = _log_dir + std::string(“/”) + _log_filename + std::string(“.”) + utils::CStringUtils::any2string(static_castint>(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.
“, getpid(), pthread_self(), old_path.c_str(), new_path.c_str()); - }
- else
- {
- if (errno != ENOENT)
-
fprintf(stderr, “[%d:%lu] SafeLogger access %s error: %m.
“, 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: %m
“, getpid(), pthread_self(), _log_filepath.c_str(), new_path.c_str()); - }
- else
- {
- if (errno != ENOENT)
-
fprintf(stderr, “[%d:%lu] SafeLogger access %s error: %m
“, getpid(), pthread_self(), _log_filepath.c_str()); - }
- }
- // 重新建立
-
//fprintf(stdout, “[%d:%lu] SafeLogger create %s
“, 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: %m
“, 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: %m
“, 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: %m
“, getpid(), pthread_self(), thread_log_fd); - }
- else if (0 == bytes)
- {
-
fprintf(stderr, “[%d:%lu] write nothing: SafeLogger[%d]
“, 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: %m
“, 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.
“, getpid(), pthread_self(), new_log_fd, syscall_ex.str().c_str()); - }
- }
- }
- }
- catch (CSyscallException& syscall_ex)
- {
-
fprintf(stderr, “[%d:%lu] SafeLogger[%d] rotate error: %s
“, getpid(), pthread_self(), thread_log_fd, syscall_ex.str().c_str()); - }
- }
- }
- SYS_NAMESPACE_END
相關文章
- 多執行緒 日誌 和截圖的問題執行緒
- GCD 多執行緒安全 單寫多讀GC執行緒
- 多執行緒-程式和執行緒的概述執行緒
- Python的多程式和多執行緒Python執行緒
- 多執行緒和多執行緒同步執行緒
- .NET多執行緒程式設計(1):多工和多執行緒 (轉)執行緒程式設計
- 多執行緒,多程式執行緒
- 多執行緒和多程式模型的選用執行緒模型
- 執行緒以及多執行緒,多程式的選擇執行緒
- 【Java多執行緒】執行緒安全的集合Java執行緒
- 多執行緒具體實現執行緒
- 搞定python多執行緒和多程式Python執行緒
- 多執行緒系列之 執行緒安全執行緒
- iOS 多執行緒之執行緒安全iOS執行緒
- iOS多執行緒之執行緒安全iOS執行緒
- 多執行緒安全(一)執行緒
- 多執行緒和多程式的區別(小結)執行緒
- 避免DbContext同時在多個執行緒呼叫Context執行緒
- python 多程式和多執行緒學習Python執行緒
- [短文速讀 -5] 多執行緒程式設計引子:程式、執行緒、執行緒安全執行緒程式設計
- Python 多執行緒多程式Python執行緒
- 多執行緒-以前的執行緒安全的類回顧執行緒
- Android中的多程式、多執行緒Android執行緒
- 多執行緒-定時器的概述和使用執行緒定時器
- Python——程式、執行緒、協程、多程式、多執行緒(個人向)Python執行緒
- Java多執行緒——獲取多個執行緒任務執行完的時間Java執行緒
- Java 多執行緒基礎(四)執行緒安全Java執行緒
- iOS多執行緒安全-13種執行緒鎖?iOS執行緒
- mysql多執行緒備份與還原工具mydumperMySql執行緒
- 多執行緒-執行緒組的概述和使用執行緒
- 多執行緒-執行緒池的概述和使用執行緒
- VC編寫多執行緒sql盲注工具.doc執行緒SQL
- 多執行緒安全性和Java中的鎖執行緒Java
- 【多執行緒總結(二)-執行緒安全與執行緒同步】執行緒
- 【Python】 多程式與多執行緒Python執行緒
- 造成類在多執行緒時不安全的原因執行緒
- 執行緒和程式基礎以及多執行緒的基本使用(iOS)執行緒iOS
- 多執行緒-匿名內部類的方式實現多執行緒程式執行緒