天天看點

同時具備多線程和多程序安全的寫日志工具

使用示例:

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::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\n", 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\n", 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\n", 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, ...)

        va_list args;

        va_start(args, format);

        utils::VaListHelper vh(args);

void CSafeLogger::log_debug(const char* filename, int lineno, const char* module_name, const char* format, va_list& args)

        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())

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, ...)

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, ...)

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, ...)

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, ...)

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, ...)

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, ...)

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, ...)

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;

        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\n", _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; // 結尾符不需要寫入日志檔案中

        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);

            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';

    // 允許打屏

    if (_screen_enabled)

        (void)write(STDOUT_FILENO, log_line.get(), log_real_size);

    if (false)

        // 異步寫入日志檔案

        //log_line.release();

        // 同步寫入日志檔案

        int thread_log_fd = get_thread_log_fd();

        if (thread_log_fd != -1)

            write_log(thread_log_fd, log_line.get(), log_real_size);

            fprintf(stderr, "process(%u,%lu) without thread log\n", 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\n", 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.\n", getpid(), pthread_self(), old_path.c_str(), new_path.c_str());

            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: %m\n", getpid(), pthread_self(), _log_filepath.c_str(), new_path.c_str());

                fprintf(stderr, "[%d:%lu] SafeLogger access %s error: %m\n", getpid(), pthread_self(), _log_filepath.c_str());

    // 重新建立

    //fprintf(stdout, "[%d:%lu] SafeLogger create %s\n", 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;

        fprintf(stderr, "[%d:%lu] SafeLogger create %s error: %m\n", getpid(), pthread_self(), _log_filepath.c_str());

        openlog("mooon-safe-logger", LOG_CONS|LOG_PID, 0);

        syslog(LOG_ERR, "[%d:%lu] SafeLogger create %s error: %m\n", getpid(), pthread_self(), _log_filepath.c_str());

        closelog();

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\n", 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: %m\n", 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: %s\n", getpid(), pthread_self(), thread_log_fd, syscall_ex.str().c_str());

SYS_NAMESPACE_END

繼續閱讀