SRS源码框架,日志记录SrsFileLog的使用

本章内容解读SRS开源代码框架,无二次开发,以学习交流为目的。

SRS是国人开发的开源流媒体服务器,C++语言开发,本章使用版本:https://github.com/ossrs/srs/tree/5.0release。

目录

    • SRS日志记录
    • 源码
    • 源码测试

SRS日志记录

SRS封装了日志类SrsFileLog(输出到文件或控制台)、SrsConsoleLog(输出到控制台),具备常用的日志功能。

功能包括:日期,时间,日志级别,格式化参数列表,pid,上下文ID(方便定位问题,每个协程有个唯一的上下文ID),可输出到控制台或文件等;提供了使用方便的宏接口。

需要注意的地方:配有线程锁,多线程存在抢锁(SRS几乎是单线程运行,所有还好);不能自动分割日志,不处理的话会一直写入同一个文件(SRS提供了手动分割日志方式,详见SRS官网)。

这部分代码还包含了SrsThreadMutex类,对pthread_mutex_t 进行封装,使用时不需要对pthread_mutex_t 进行初始化和销毁,并加入了错误断言。

源码

源码部分,对源码进行了裁减,可以直接使用。上下文SrsContextId使用std::string代替。

├── chw_adapt.cpp
├── chw_adapt.h
├── srs_app_log.cpp
├── srs_app_log.hpp
├── srs_kernel_log.cpp
└── srs_kernel_log.hpp

srs_kernel_log.hpp

#ifndef SRS_KERNEL_LOG_HPP
#define SRS_KERNEL_LOG_HPP

#include 
#include 
#include 
#include 
#include 
#include "chw_adapt.h"

// Please note that the enum name might not be the string, to keep compatible with previous definition.
//日志级别
enum SrsLogLevel
{
    SrsLogLevelForbidden = 0x00,

    // Only used for very verbose debug, generally,
    // we compile without this level for high performance.
    SrsLogLevelVerbose = 0x01,
    SrsLogLevelInfo = 0x02,
    SrsLogLevelTrace = 0x04,
    SrsLogLevelWarn = 0x08,
    SrsLogLevelError = 0x10,

    SrsLogLevelDisabled = 0x20,
};

// Get the level in string.
extern const char* srs_log_level_strings[];

// The log interface provides method to write log.  日志接口提供了写入日志的方法。
// but we provides some macro, which enable us to disable the log when compile.但是我们提供了一些宏,它使我们能够在编译时禁用日志。
class ISrsLog
{
public:
    ISrsLog();
    virtual ~ISrsLog();
public:
    // Initialize log utilities.初始化日志实用程序。
    virtual /*srs_error_t*/int initialize() = 0;
    // Reopen the log file for log rotate.重新打开日志文件以进行日志轮替。
    virtual void reopen() = 0;
public:
    // Write a application level log. All parameters are required except the tag.写应用程序级别日志
    virtual void log(SrsLogLevel level, const char* tag, std::string context_id, const char* fmt, va_list args) = 0;
};

// The logic context, for example, a RTMP connection, or RTC Session, etc.逻辑上下文,例如,一个RTMP连接,或RTC会话等。
// We can grep the context id to identify the logic unit, for debugging.我们可以通过上下文id来标识逻辑单元,用于调试。
class ISrsContext
{
public:
    ISrsContext();
    virtual ~ISrsContext();
public:
    // Get the context id of current thread.获取当前线程的上下文id。
    virtual std::string get_id()
    {
        return "ID9527";
    }
};

// @global User must implements the LogContext and define a global instance.用户必须实现日志上下文并定义一个全局实例。
extern ISrsContext* _srs_context;

// @global User must provides a log object  用户必须提供一个日志对象
extern ISrsLog* _srs_log;

// Global log function implementation. Please use helper macros, for example, srs_trace or srs_error.
//全局日志函数的实现。请使用助手宏,例如,srs_trace或srs_error。
extern void srs_logger_impl(SrsLogLevel level, const char* tag, std::string context_id, const char* fmt, ...);

// Log style.
// Use __FUNCTION__ to print c method
// Use __PRETTY_FUNCTION__ to print c++ class:method
#define srs_verbose(msg, ...) srs_logger_impl(SrsLogLevelVerbose, NULL, _srs_context->get_id(), msg, ##__VA_ARGS__)
#define srs_info(msg, ...) srs_logger_impl(SrsLogLevelInfo, NULL, _srs_context->get_id(), msg, ##__VA_ARGS__)
#define srs_trace(msg, ...) srs_logger_impl(SrsLogLevelTrace, NULL, _srs_context->get_id(), msg, ##__VA_ARGS__)
#define srs_warn(msg, ...) srs_logger_impl(SrsLogLevelWarn, NULL, _srs_context->get_id(), msg, ##__VA_ARGS__)
#define srs_error(msg, ...) srs_logger_impl(SrsLogLevelError, NULL, _srs_context->get_id(), msg, ##__VA_ARGS__)

#endif

srs_kernel_log.cpp

#include 
#include 

const char* srs_log_level_strings[] = {
#ifdef SRS_LOG_LEVEL_V2
        // The v2 log level specs by log4j.
        "FORB",     "TRACE",     "DEBUG",    NULL,   "INFO",    NULL, NULL, NULL,
        "WARN",     NULL,       NULL,       NULL,   NULL,       NULL, NULL, NULL,
        "ERROR",    NULL,       NULL,       NULL,   NULL,       NULL, NULL, NULL,
        NULL,       NULL,       NULL,       NULL,   NULL,       NULL, NULL, NULL,
        "OFF",
#else
        // SRS 4.0 level definition, to keep compatible.
        "Forb",     "Verb",     "Debug",    NULL,   "Trace",    NULL, NULL, NULL,
        "Warn",     NULL,       NULL,       NULL,   NULL,       NULL, NULL, NULL,
        "Error",    NULL,       NULL,       NULL,   NULL,       NULL, NULL, NULL,
        NULL,       NULL,       NULL,       NULL,   NULL,       NULL, NULL, NULL,
        "Off",
#endif
};

ISrsLog::ISrsLog()
{
}

ISrsLog::~ISrsLog()
{
}

ISrsContext::ISrsContext()
{
}

ISrsContext::~ISrsContext()
{
}

void srs_logger_impl(SrsLogLevel level, const char* tag, std::string context_id, const char* fmt, ...)
{
    if (!_srs_log) return;

    va_list args;
    va_start(args, fmt);
    _srs_log->log(level, tag, context_id, fmt, args);
    va_end(args);
}

srs_app_log.hpp

#ifndef SRS_APP_LOG_HPP
#define SRS_APP_LOG_HPP

#include 
#include 
#include "chw_adapt.h"
#include "srs_kernel_log.hpp"

class SrsFileLog : public ISrsLog/*, public ISrsReloadHandler*/
{
private:
    // Defined in SrsLogLevel.
    SrsLogLevel level_;
private:
    char* log_data;
    // Log to file if specified srs_log_file
    int fd;
    // Whether log to file tank
    bool log_to_file_tank;//false输出到控制台,true输出到文件
    // Whether use utc time.
    bool utc;
    // TODO: FIXME: use macro define like SRS_MULTI_THREAD_LOG to switch enable log mutex or not.
    // Mutex for multithread log.多线程日志的互斥锁。
    SrsThreadMutex* mutex_;

    std::string filename_;
public:
    SrsFileLog();
    virtual ~SrsFileLog();
// Interface ISrsLog
public:
    virtual /*srs_error_t*/int initialize();
    virtual void reopen();
    //chw,日志记录包含上下文ID,更容易定位问题,这里SrsContextId使用std::string代替,减少依赖
    virtual void log(SrsLogLevel level, const char* tag, std::string context_id, const char* fmt, va_list args);
private:
    virtual void write_log(int& fd, char* str_log, int size, int level);
    virtual void open_log_file();
};

#endif

srs_app_log.cpp

#include 

#include 
#include 

#include 
#include 
#include 
#include 

// the max size of a line of log.
#define LOG_MAX_SIZE 8192

// the tail append to each log.
#define LOG_TAIL '\n'
// reserved for the end of log data, it must be strlen(LOG_TAIL)
#define LOG_TAIL_SIZE 1

SrsFileLog::SrsFileLog()
{
    level_ = SrsLogLevelTrace;
    log_data = new char[LOG_MAX_SIZE];
    
    fd = -1;
    log_to_file_tank = true;
    utc = false;

    mutex_ = new SrsThreadMutex();
    filename_ = "./srs.log";
}

SrsFileLog::~SrsFileLog()
{
    srs_freepa(log_data);
    
    if (fd > 0) {
        ::close(fd);
        fd = -1;
    }
    srs_freep(mutex_);
}

/*srs_error_t*/int SrsFileLog::initialize()
{
    level_ = SrsLogLevelTrace;
    
    return /*srs_success*/0;
}

void SrsFileLog::reopen()
{
    if (fd > 0) {
        ::close(fd);
    }
    
    if (!log_to_file_tank) {
        return;
    }
    
    open_log_file();
}

void SrsFileLog::log(SrsLogLevel level, const char* tag, std::string context_id, const char* fmt, va_list args)
{
    if (level < level_ || level >= SrsLogLevelDisabled) {
        return;
    }

    SrsThreadLocker(mutex_);

    int size = 0;
    bool header_ok = srs_log_header(
        log_data, LOG_MAX_SIZE, utc, level >= SrsLogLevelWarn, tag, context_id, srs_log_level_strings[level], &size
    );
    if (!header_ok) {
        return;
    }

    // Something not expected, drop the log.
    int r0 = vsnprintf(log_data + size, LOG_MAX_SIZE - size, fmt, args);
    if (r0 <= 0 || r0 >= LOG_MAX_SIZE - size) {
        return;
    }
    size += r0;

    // Add errno and strerror() if error. Check size to avoid security issue https://github.com/ossrs/srs/issues/1229
    if (level == SrsLogLevelError && errno != 0 && size < LOG_MAX_SIZE) {
        r0 = snprintf(log_data + size, LOG_MAX_SIZE - size, "(%s)", strerror(errno));

        // Something not expected, drop the log.
        if (r0 <= 0 || r0 >= LOG_MAX_SIZE - size) {
            return;
        }
        size += r0;
    }

    write_log(fd, log_data, size, level);
}

void SrsFileLog::write_log(int& fd, char *str_log, int size, int level)
{
    // ensure the tail and EOF of string
    //      LOG_TAIL_SIZE for the TAIL char.
    //      1 for the last char(0).
    size = srs_min(LOG_MAX_SIZE - 1 - LOG_TAIL_SIZE, size);
    
    // add some to the end of char.
    str_log[size++] = LOG_TAIL;
    
    // if not to file, to console and return.
    if (!log_to_file_tank) {
        // if is error msg, then print color msg.
        // \033[31m : red text code in shell
        // \033[32m : green text code in shell
        // \033[33m : yellow text code in shell
        // \033[0m : normal text code
        if (level <= SrsLogLevelTrace) {
            printf("%.*s", size, str_log);
        } else if (level == SrsLogLevelWarn) {
            printf("\033[33m%.*s\033[0m", size, str_log);
        } else{
            printf("\033[31m%.*s\033[0m", size, str_log);
        }
        fflush(stdout);
        
        return;
    }
    
    // open log file. if specified
    if (fd < 0) {
        open_log_file();
    }
    
    // write log to file.
    if (fd > 0) {
        ::write(fd, str_log, size);
    }
}

void SrsFileLog::open_log_file()
{
    std::string filename = filename_;
    
    if (filename.empty()) {
        return;
    }

    fd = ::open(filename.c_str(),
        O_RDWR | O_CREAT | O_APPEND,
        S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH
    );
}

chw_adapt.h

#ifndef CHW_ADAPT_H
#define CHW_ADAPT_H

#include 

#define srs_assert(expression) SrsCplxError::srs_assert(expression)
#define srs_min(a, b) (((a) < (b))? (a) : (b))
#define srs_max(a, b) (((a) < (b))? (b) : (a))
#define srs_freep(p) \
    delete p; \
    p = NULL; \
    (void)0
// Please use the freepa(T[]) to free an array, otherwise the behavior is undefined.
#define srs_freepa(pa) \
    delete[] pa; \
    pa = NULL; \
    (void)0

// The thread mutex wrapper, without error.
class SrsThreadMutex
{
private:
    pthread_mutex_t lock_;
    pthread_mutexattr_t attr_;
public:
    SrsThreadMutex();
    virtual ~SrsThreadMutex();
public:
    void lock();
    void unlock();
};

#define SrsThreadLocker(instance) \
    impl__SrsThreadLocker _SRS_free_##instance(instance)

class impl__SrsThreadLocker
{
private:
    SrsThreadMutex* lock;
public:
    impl__SrsThreadLocker(SrsThreadMutex* l) {
        lock = l;
        lock->lock();
    }
    virtual ~impl__SrsThreadLocker() {
        lock->unlock();
    }
};
bool srs_log_header(char* buffer, int size, bool utc, bool dangerous, const char* tag, std::string cid, const char* level, int* psize);
#endif // CHW_ADAPT_H

chw_adapt.cpp

#include "chw_adapt.h"

#include 
#include 
#include 
#include 

SrsThreadMutex::SrsThreadMutex()
{
    // https://man7.org/linux/man-pages/man3/pthread_mutexattr_init.3.html
    int r0 = pthread_mutexattr_init(&attr_);
    assert(!r0);

    // https://man7.org/linux/man-pages/man3/pthread_mutexattr_gettype.3p.html
    r0 = pthread_mutexattr_settype(&attr_, PTHREAD_MUTEX_ERRORCHECK);
    assert(!r0);

    // https://michaelkerrisk.com/linux/man-pages/man3/pthread_mutex_init.3p.html
    r0 = pthread_mutex_init(&lock_, &attr_);
    assert(!r0);
}

SrsThreadMutex::~SrsThreadMutex()
{
    int r0 = pthread_mutex_destroy(&lock_);
    assert(!r0);

    r0 = pthread_mutexattr_destroy(&attr_);
    assert(!r0);
}

void SrsThreadMutex::lock()
{
    // https://man7.org/linux/man-pages/man3/pthread_mutex_lock.3p.html
    //        EDEADLK
    //                 The mutex type is PTHREAD_MUTEX_ERRORCHECK and the current
    //                 thread already owns the mutex.
    int r0 = pthread_mutex_lock(&lock_);
    assert(!r0);
}

void SrsThreadMutex::unlock()
{
    int r0 = pthread_mutex_unlock(&lock_);
    assert(!r0);
}

bool srs_log_header(char* buffer, int size, bool utc, bool dangerous, const char* tag, std::string cid, const char* level, int* psize)
{
    // clock time
    timeval tv;
    if (gettimeofday(&tv, NULL) == -1) {
        return false;
    }

    // to calendar time
    struct tm now;
    // Each of these functions returns NULL in case an error was detected. @see https://linux.die.net/man/3/localtime_r
    if (utc) {
        if (gmtime_r(&tv.tv_sec, &now) == NULL) {
            return false;
        }
    } else {
        if (localtime_r(&tv.tv_sec, &now) == NULL) {
            return false;
        }
    }

    int written = -1;
    if (dangerous) {
        if (tag) {
            written = snprintf(buffer, size,
                "[%d-%02d-%02d %02d:%02d:%02d.%03d][%s][%d][%s][%d][%s] ",
                1900 + now.tm_year, 1 + now.tm_mon, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, (int)(tv.tv_usec / 1000),
                level, getpid(), cid.c_str(), errno, tag);
        } else {
            written = snprintf(buffer, size,
                "[%d-%02d-%02d %02d:%02d:%02d.%03d][%s][%d][%s][%d] ",
                1900 + now.tm_year, 1 + now.tm_mon, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, (int)(tv.tv_usec / 1000),
                level, getpid(), cid.c_str(), errno);
        }
    } else {
        if (tag) {
            written = snprintf(buffer, size,
                "[%d-%02d-%02d %02d:%02d:%02d.%03d][%s][%d][%s][%s] ",
                1900 + now.tm_year, 1 + now.tm_mon, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, (int)(tv.tv_usec / 1000),
                level, getpid(), cid.c_str(), tag);
        } else {
            written = snprintf(buffer, size,
                "[%d-%02d-%02d %02d:%02d:%02d.%03d][%s][%d][%s] ",
                1900 + now.tm_year, 1 + now.tm_mon, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, (int)(tv.tv_usec / 1000),
                level, getpid(), cid.c_str());
        }
    }

    // Exceed the size, ignore this log.
    // Check size to avoid security issue https://github.com/ossrs/srs/issues/1229
    if (written <= 0 || written >= size) {
        return false;
    }

    // write the header size.
    *psize = written;

    return true;
}

源码测试

#include "srs_kernel_log.hpp"
#include "srs_app_log.hpp"
ISrsLog* _srs_log = NULL;
ISrsContext* _srs_context = NULL;

_srs_context = new ISrsContext();  
_srs_log = new SrsFileLog();       
_srs_log->initialize();            
                                   
srs_trace("hello");                

可执行文件同级目录生成日志文件srs.log

[2023-07-26 15:06:51.529][Trace][10876][ID9527] hello

你可能感兴趣的:(C/C++,linux,服务器,网络,c++,c语言)