DPDK的日志log系统代码介绍

DPDK 专栏收录该内容
38 篇文章 2 订阅

全局日志结构变量rte_logs,可见默认的日志级别为调试级别RTE_LOG_DEBUG。

/* global log structure */
struct rte_logs rte_logs = {
    .type = ~0,
    .level = RTE_LOG_DEBUG,
    .file = NULL,
};

日志系统的初始化函数rte_log_init,首先是再次设置日志级别为调试RTE_LOG_DEBUG;之后分配32个日志类型空间(RTE_LOGTYPE_FIRST_EXT_ID=32),这32个类型存放预定义的日志类型,即logtype_strings结构体定义的从RTE_LOGTYPE_EAL到RTE_LOGTYPE_USER8的类型。在分配好空间之后,调用注册函数__rte_log_register注册logtype_strings预定义的日志类型。

RTE_INIT_PRIO(rte_log_init, LOG)
{
    rte_log_set_global_level(RTE_LOG_DEBUG);

    rte_logs.dynamic_types = calloc(RTE_LOGTYPE_FIRST_EXT_ID, sizeof(struct rte_log_dynamic_type));
    if (rte_logs.dynamic_types == NULL)
        return;

    /* register legacy log types */
    for (i = 0; i < RTE_DIM(logtype_strings); i++)
        __rte_log_register(logtype_strings[i].logtype, logtype_strings[i].log_id);

    rte_logs.dynamic_types_len = RTE_LOGTYPE_FIRST_EXT_ID;
}

目前logtype_stings预定义日志类型还没有达到32个。在类型RTE_LOGTYPE_GSO(20)和类型RTE_LOGTYPE_USER1(24)之间还留有3个未定义的类型。

struct logtype {
    uint32_t log_id;
    const char *logtype;
};
static const struct logtype logtype_strings[] = {
    {RTE_LOGTYPE_EAL,        "lib.eal"},
    {RTE_LOGTYPE_MALLOC,     "lib.malloc"},

    {RTE_LOGTYPE_EVENTDEV,   "lib.eventdev"},
    {RTE_LOGTYPE_GSO,        "lib.gso"},
    {RTE_LOGTYPE_USER1,      "user1"},

    {RTE_LOGTYPE_USER8,      "user8"}
};

由以下的日志注册函数__rte_log_register可知,所有预定义的日志类型其日志级别全部设置为了信息级别RTE_LOG_INFO。

/* register an extended log type, assuming table is large enough, and id is not yet registered. */
static int __rte_log_register(const char *name, int id)
{
    char *dup_name = strdup(name);

    if (dup_name == NULL)
        return -ENOMEM;

    rte_logs.dynamic_types[id].name = dup_name;
    rte_logs.dynamic_types[id].loglevel = RTE_LOG_INFO;

    return id;
}

除去DPDK预定义的日志类型,用户可自定义日志类型,如下函数rte_log_register,其实质上为以上介绍的日志内部注册函数__rte_log_register的一个封装,在调用内部注册函数之前,首先检查用户要注册的日志名称是否已经存在,之后扩大日志类型空间以容纳一个新的日志类型结构。最后是调用内部注册函数,注册新的日志类型。

int rte_log_register(const char *name)
{
    struct rte_log_dynamic_type *new_dynamic_types;

    id = rte_log_lookup(name);
    if (id >= 0)
        return id;

    new_dynamic_types = realloc(rte_logs.dynamic_types, sizeof(struct rte_log_dynamic_type) * (rte_logs.dynamic_types_len + 1));
    if (new_dynamic_types == NULL)
        return -ENOMEM;
    rte_logs.dynamic_types = new_dynamic_types;

    ret = __rte_log_register(name, rte_logs.dynamic_types_len);
    if (ret < 0)
        return ret;
		
    rte_logs.dynamic_types_len++;
    return ret;
}

DPDK的其它模块或者示例程序等,使用如下rte_log函数输出日志信息。其封装了函数rte_vlog。

/*
 * Generates a log message The message will be sent in the stream defined by the previous call to rte_openlog_stream().
 * No need to check level here, done by rte_vlog().
 */
int rte_log(uint32_t level, uint32_t logtype, const char *format, ...)
{
    va_list ap;
    int ret;

    va_start(ap, format);
    ret = rte_vlog(level, logtype, format, ap);
    va_end(ap);
    return ret;
}

核心的日志输出函数rte_vlog如下,由以上对全局变量rte_logs的介绍可知,其file指针为空,如果默认的日志流default_log_stream为空,日记信息将输出到标志错误stderr上,但是实际上载EAL初始化中,DPDK给default_log_stream赋予了值,接下来会看到。日志输出的前提是其基本要低于全局设定的基本,并且还要低于为其类型设置的特定级别。

/*
 * Generates a log message The message will be sent in the stream defined by the previous call to rte_openlog_stream().
 */
int rte_vlog(uint32_t level, uint32_t logtype, const char *format, va_list ap)
{
    FILE *f = rte_logs.file;
    if (f == NULL) {
        f = default_log_stream;
        if (f == NULL) {
            /* Grab the current value of stderr here, rather than just initializing default_log_stream to stderr. This
             * ensures that we will always use the current value of stderr, even if the application closes and reopens it.
             */
            f = stderr;
        }
    }

    if (level > rte_logs.level)
        return 0;
    if (logtype >= rte_logs.dynamic_types_len)
        return -1;
    if (level > rte_logs.dynamic_types[logtype].loglevel)
        return 0;

    /* save loglevel and logtype in a global per-lcore variable */
    RTE_PER_LCORE(log_cur_msg).loglevel = level;
    RTE_PER_LCORE(log_cur_msg).logtype = logtype;

    ret = vfprintf(f, format, ap);
    fflush(f);
    return ret;
}

EAL在初始化中调用rte_eal_log_init函数,根据命令行的配置初始化日志系统。

int rte_eal_init(int argc, char **argv)
{
    if (rte_eal_log_init(logid, internal_config.syslog_facility) < 0) {
        rte_eal_init_alert("Cannot init logging.");
        rte_errno = ENOMEM;
        rte_atomic32_clear(&run_once);
        return -1;
    }
}

如下的命令行参数,设置日志类型RTE_LOGTYPE_EAL的级别为调试级别RTE_LOG_DEBUG(8)。

--log-level eal:8


日志初始化函数rte_eal_log_init,将默认的日志输出流default_log_stream配置为log_stream,而log_stream的操作函数集为console_log_func。

int rte_eal_log_init(const char *id, int facility)
{
    FILE *log_stream;

    log_stream = fopencookie(NULL, "w+", console_log_func);
    if (log_stream == NULL)
        return -1;

    openlog(id, LOG_NDELAY | LOG_PID, facility);

    eal_log_set_default(log_stream);
    return 0;
}
void eal_log_set_default(FILE *default_log)
{
    default_log_stream = default_log;

#if RTE_LOG_DP_LEVEL >= RTE_LOG_DEBUG
    RTE_LOG(NOTICE, EAL, "Debug dataplane logs available - lower performance\n");
#endif
}

流操作函数集console_log_func只有一个写函数指针,赋予了函数console_log_write的值,即将日志信息输出到标准输出上stdout。

static ssize_t console_log_write(__attribute__((unused)) void *c, const char *buf, size_t size)
{      
    ret = fwrite(buf, 1, size, stdout);
    fflush(stdout);

    syslog(rte_log_cur_msg_loglevel() - 1, "%.*s", (int)size, buf);
}
static cookie_io_functions_t console_log_func = {
    .write = console_log_write,
};

以上为日志输出的默认行为。由函数rte_vlog可知,如要改变默认的stdout输出,可设置rte_logs的成员file,即函数rte_openlog_stream的行为。此file输出流指针优先于DPDK中默认的default_log_stream流。

int rte_openlog_stream(FILE *f)
{
    rte_logs.file = f;
    return 0;
}

如下将DPDK的log信息定向到/var/log/dpdk.log文件中。只需调用rte_openlog_stream函数,参数传入打开的日志文件的FILE指针。

    FILE *fp = fopen("/var/log/rte.log", "w+");
    if (fp) {
        rte_openlog_stream(fp);
    }

此时可通过/var/log/rte.log文件查看DPDK的日志输出:

/ # cat /var/log/rte.log 
EAL: Detected 4 lcore(s)
EAL: Detected 1 NUMA nodes
...

 

DPDK-19.02

 

  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值