C语言日志类库 zlog 使用指南(第六章 zlog API)

4 篇文章 0 订阅

第六章 zlog API

zlog 是一个用于 C 语言项目的日志库,其 API 设计是线程安全的。以下是 zlog API 的主要功能和使用方法:

6.1 初始化和结束

引入头文件

在你使用 zlog API 之前,需要先包含 zlog 的头文件:

#include "zlog.h"
函数原型
int zlog_init(const char *confpath);
int zlog_reload(const char *confpath);
void zlog_fini(void);
函数说明
zlog_init()

zlog_init() 用于初始化 zlog 库并加载配置文件。

  • 参数confpath 是配置文件的路径。如果 confpathNULL,那么 zlog 会尝试从环境变量 ZLOG_CONF_PATH 获取配置文件路径。如果环境变量 ZLOG_CONF_PATH 也为空,那么日志将输出到标准输出(stdout)并使用内部格式。
  • 返回值:初始化成功返回 0,失败返回 -1,并且详细的错误日志会记录到由 ZLOG_PROFILE_ERROR 指定的日志文件中。
  • 限制:每个进程只允许第一次调用 zlog_init() 有效,后续调用将无效。
zlog_reload()

zlog_reload() 用于在运行时重新加载配置文件。

  • 参数confpath 是新的配置文件路径。如果 confpathNULL,将重新加载最近一次调用 zlog_init()zlog_reload() 使用的配置文件。
  • 功能:重新计算类别规则关系,重建线程缓冲区,重置用户定义的输出函数规则。
  • 返回值:重新加载成功返回 0,失败返回 -1,并且在失败的情况下,当前内存中的配置将保持不变。因此,zlog_reload() 是一个原子操作,可以被多次调用。
zlog_fini()

zlog_fini() 用于释放所有 zlog API 的内存并关闭已经打开的文件。

  • 功能:可以被多次调用,每次调用都会进行清理工作。

示例代码

以下是一个简单的示例,展示了如何使用这些 API:

#include <stdio.h>
#include "zlog.h"

int main() {
    // 初始化 zlog 系统
    if (zlog_init("/path/to/zlog.conf")) {
        printf("zlog 初始化失败\n");
        return -1;
    }

    // 你的应用程序代码...
    
    // 在运行时重新加载日志配置文件
    if (zlog_reload("/path/to/new/zlog.conf")) {
        printf("zlog 重新加载失败\n");
    }

    // 应用程序结束前,释放 zlog 资源
    zlog_fini();
    return 0;
}

总结

  • 线程安全:所有 zlog API 都是线程安全的。
  • 初始化:使用 zlog_init() 初始化日志系统。
  • 重新加载:使用 zlog_reload() 在运行时重新加载日志配置文件。
  • 清理:使用 zlog_fini() 释放资源。

通过这些 API 函数,你可以方便地在 C 语言项目中实现灵活的日志管理。如果有更多细节需要了解,可以参考 zlog 的用户指南或相关文档。

6.2 分类操作(Category Operation)

概述
typedef struct zlog_category_s zlog_category_t;

zlog_category_t *zlog_get_category(const char *cname);
描述

zlog_get_category 是一个用于获取日志分类(category)的方法。这个方法从 zlog 的分类表(category_table)中获取一个分类,以便将来进行日志操作。如果指定的分类 cname 不存在,它将被创建。zlog 随后会根据配置中的所有规则处理这个分类。这个方法返回一个指向匹配规则的指针,该规则与 cname 对应。

规则匹配机制如下:

  1. * 匹配所有的 cname
  2. 以下划线 "_" 结尾的分类字符串会匹配超级分类和子分类。例如,"aa_" 匹配 cname"aa", "aa_", "aa_bb", "aa_bb_cc"
  3. 不以下划线 "_" 结尾的分类字符串严格匹配 cname。例如,"aa_bb" 仅匹配 cname"aa_bb"
  4. ! 匹配没有规则匹配到的 cname

当调用 zlog_reload() 时,每个分类的规则会自动重新计算。无需担心分类的内存释放问题,zlog_fini() 会在结束时进行清理。

返回值
  • 成功时,返回 zlog_category_t 的地址。
  • 失败时,返回 NULL,并且一个详细的错误日志会记录到由 ZLOG_PROFILE_ERROR 指示的日志文件中。

6.3 日志函数和宏

概述
void zlog(zlog_category_t * category, 

          const char *file, size_t filelen,

          const char *func, size_t funclen, 

          long line, int level,

          const char *format, ...); 

void vzlog(zlog_category_t * category,

          const char *file, size_t filelen,

          const char *func, size_t funclen, 

          long line, int level,

          const char *format, va_list args);

void hzlog(zlog_category_t * category,

          const char *file, size_t filelen,

          const char *func, size_t funclen, 

          long line, int level,

          const void *buf, size_t buflen);
描述

这三个函数是产生用户日志信息的实际日志函数,它们对应于配置文件中的 %m 条目。category 由前面描述的 zlog_get_category() 获得。

zlog()vzlog() 根据类似 printf(3)vprintf(3) 的格式产生输出。

vzlog() 相当于 zlog(),只是它用 va_list 而不是可变数量的参数调用。vzlog() 调用了 va_copy 宏,调用后 args 的值不变。参见 stdarg(3)

hzlog() 略有不同,它的输出如下所示,是 buf 的十六进制表示形式,输出的长度是 buf_len

hex_buf_len=[5365]  

             0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F      0123456789ABCDEF

0000000001   23 21 20 2f 62 69 6e 2f 62 61 73 68 0a 0a 23 20   #! /bin/bash..#

0000000002   74 65 73 74 5f 68 65 78 20 2d 20 74 65 6d 70 6f   test_hex - tempo

0000000003   72 61 72 79 20 77 72 61 70 70 65 72 20 73 63 72   rary wrapper scr

参数 fileline 通常由 __FILE____LINE__ 宏填充,它们指示日志事件发生的位置。参数 func__func____FUNCTION__ 填充,如果编译器支持它,否则将被填充为 "<unknown>"

level 是一个当前级别列表中的整数,默认如下:

typedef enum {                 
    ZLOG_LEVEL_DEBUG = 20,
    ZLOG_LEVEL_INFO = 40,
    ZLOG_LEVEL_NOTICE = 60,
    ZLOG_LEVEL_WARN = 80,
    ZLOG_LEVEL_ERROR = 100,
    ZLOG_LEVEL_FATAL = 120
} zlog_level;

每个函数都有方便使用的宏。例如:

#define zlog_fatal(cat, format, args...) \         
    zlog(cat, __FILE__, sizeof(__FILE__)-1, \
    __func__, sizeof(__func__)-1, __LINE__, \
    ZLOG_LEVEL_FATAL, format, ##args)

完整的宏列表如下:

/* zlog 宏 */
#define zlog_fatal(cat, format, ...)   zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, ZLOG_LEVEL_FATAL, format, ##__VA_ARGS__)
#define zlog_error(cat, format, ...)   zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, ZLOG_LEVEL_ERROR, format, ##__VA_ARGS__)
#define zlog_warn(cat, format, ...)    zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, ZLOG_LEVEL_WARN, format, ##__VA_ARGS__)
#define zlog_notice(cat, format, ...)  zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, ZLOG_LEVEL_NOTICE, format, ##__VA_ARGS__)
#define zlog_info(cat, format, ...)    zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, ZLOG_LEVEL_INFO, format, ##__VA_ARGS__)
#define zlog_debug(cat, format, ...)   zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, ZLOG_LEVEL_DEBUG, format, ##__VA_ARGS__)

/* vzlog 宏 */
#define vzlog_fatal(cat, format, args)  vzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, ZLOG_LEVEL_FATAL, format, args)
#define vzlog_error(cat, format, args)  vzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, ZLOG_LEVEL_ERROR, format, args)
#define vzlog_warn(cat, format, args)   vzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, ZLOG_LEVEL_WARN, format, args)
#define vzlog_notice(cat, format, args) vzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, ZLOG_LEVEL_NOTICE, format, args)
#define vzlog_info(cat, format, args)   vzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, ZLOG_LEVEL_INFO, format, args)
#define vzlog_debug(cat, format, args)  vzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, ZLOG_LEVEL_DEBUG, format, args)

/* hzlog 宏 */
#define hzlog_fatal(cat, buf, buf_len)  hzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, ZLOG_LEVEL_FATAL, buf, buf_len)
#define hzlog_error(cat, buf, buf_len)  hzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, ZLOG_LEVEL_ERROR, buf, buf_len)
#define hzlog_warn(cat, buf, buf_len)   hzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, ZLOG_LEVEL_WARN, buf, buf_len)
#define hzlog_notice(cat, buf, buf_len) hzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, ZLOG_LEVEL_NOTICE, buf, buf_len)
#define hzlog_info(cat, buf, buf_len)   hzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, ZLOG_LEVEL_INFO, buf, buf_len)
#define hzlog_debug(cat, buf, buf_len)  hzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, ZLOG_LEVEL_DEBUG, buf, buf_len)
返回值

这些函数不返回任何值。但是如果发生错误,则将详细的错误日志记录到由 ZLOG_PROFILE_ERROR 指示的日志文件中。

总结

这些日志函数和宏为你提供了灵活且详细的日志记录功能,通过结合使用不同的日志级别和格式,可以满足不同场景下的日志需求。在开发和调试时,可以使用更高的详细级别(如 DEBUG),而在生产环境中,可以使用更高层次的日志级别(如 ERRORFATAL)。

6.4 MDC 操作 (MDC Operation)

概述 (SYNOPSIS)
int zlog_put_mdc(const char *key, const char *value);

char *zlog_get_mdc(const char *key);

void zlog_remove_mdc(const char *key);

void zlog_clean_mdc(void);
描述 (DESCRIPTION)

MDC(映射诊断上下文,Mapped Diagnostic Context)是一个线程的键值对映射,与分类(category)无关。

keyvalue都是字符串,每个字符串的长度都不应超过 MAXLEN_PATH(1024)。如果输入的字符串长度超过了 MAXLEN_PATH,输入会被截断。

需要注意的是,这个映射是绑定到线程的。因此,如果你在一个线程中设置了一个键值对,它不会影响到其他线程。

返回值 (RETURN VALUE)
  • zlog_put_mdc() 返回 0 表示成功,返回 -1 表示失败。
  • zlog_get_mdc() 成功时返回指向value的指针,失败时或键不存在时返回 NULL。
  • 如果发生任何错误,详细的错误日志会被记录到 ZLOG_PROFILE_ERROR 指定的日志文件中。
详细解释

在VS Code使用WSL进行C语言开发时,你可以将MDC理解为一组在特定线程中可以使用的日志上下文信息。具体的API介绍如下:

  1. int zlog_put_mdc(const char *key, const char *value);

    • 功能:将键值对(key-value pair)放入MDC中。
    • 参数:
      • key:键,字符串类型。
      • value:值,字符串类型。
    • 返回值:成功返回0,失败返回-1。
    // 示例
    if (zlog_put_mdc("request_id", "12345") == 0) {
        // 成功放入
    } else {
        // 处理失败情况
    }
    
  2. char *zlog_get_mdc(const char *key);

    • 功能:从MDC中获取键对应的值。
    • 参数:
      • key:键,字符串类型。
    • 返回值:成功返回值的指针,键不存在或失败时返回NULL。
    // 示例
    char *value = zlog_get_mdc("request_id");
    if (value != NULL) {
        printf("Request ID: %s\n", value);
    } else {
        // 处理键不存在或错误情况
    }
    
  3. void zlog_remove_mdc(const char *key);

    • 功能:从MDC中移除指定的键值对。
    • 参数:
      • key:键,字符串类型。
    // 示例
    zlog_remove_mdc("request_id");
    
  4. void zlog_clean_mdc(void);

    • 功能:清空当前线程的所有MDC键值对。
    // 示例
    zlog_clean_mdc();
    
重要注意事项
  • keyvalue都是字符串,长度不能超过1024字符,超出部分会被截断。
  • MDC是线程绑定的,即一个线程中的修改不会影响其他线程。

这样,你在编写多线程程序时可以利用MDC记录每个线程的上下文信息,从而可以在日志中区分不同线程的操作。不过要记得处理超长的字符串以及在多线程环境中正确管理这些上下文信息。

补充问题:上文中提到的MDC 是什么东西?

好的,让我们详细讲解一下MDC。

什么是MDC?

MDC(Mapped Diagnostic Context,映射诊断上下文)是一个用于在日志记录过程中关联特定上下文信息的机制。它的主要作用是为每个线程维护一个独立的键值对映射,从而方便在日志中记录和区分不同线程的上下文信息。

工作原理

MDC为每个线程维护一份独立的键值对映射,允许你在一个线程中存储、获取和删除特定的上下文信息,这些信息在记录日志时会被附加到日志条目中。例如,你可以在日志中记录请求ID、用户ID等信息,以便在日志分析时能够轻松区分来自不同请求或用户的日志条目。

主要功能
  • 存储上下文信息:你可以将一些关键信息(如请求ID、用户ID等)存储在MDC中。
  • 获取上下文信息:当需要记录日志时,可以从MDC中获取存储的上下文信息并添加到日志条目中。
  • 删除上下文信息:执行完一项任务后,可以删除对应的上下文信息使其不影响后续任务。
  • 清空上下文信息:可以一键清空当前线程的所有MDC信息。
使用举例

假设你有一个多线程的Web服务器,为了在日志中区分不同的请求,你可以在每个线程中设置请求ID。这时,MDC就很有用了。

  • 设置上下文信息

    if (zlog_put_mdc("request_id", "12345") == 0) {
        // 成功设置请求ID
    } else {
        // 处理失败情况
    }
    
  • 记录日志时获取上下文信息

    char *request_id = zlog_get_mdc("request_id");
    if (request_id != NULL) {
        printf("Logging request ID: %s\n", request_id);
    } else {
        // 没有请求ID的信息
    }
    
  • 任务结束后删除上下文信息

    zlog_remove_mdc("request_id");
    
线程隔离

MDC 的关键特性之一是它绑定到具体的线程。这意味着在一个线程中设置的键值对对其他线程是不可见的。例如:

void thread_function() {
    zlog_put_mdc("thread_id", "thread_1");
    // 进行一些操作
    zlog_clean_mdc();
}

int main() {
    std::thread t1(thread_function);
    std::thread t2(thread_function);
    t1.join();
    t2.join();
    return 0;
}

在上面的代码中,t1t2 线程各自维护自己的MDC信息,互不干扰。

优势
  • 日志上下文丰富:通过MDC,可以在日志中嵌入更多的上下文信息,便于故障排查和系统监控。
  • 线程隔离:每个线程独立维护自己的MDC信息,避免了线程间数据共享的问题。
总结

MDC 是一个非常有用的工具,特别是在处理复杂的多线程应用程序时。它为每个线程提供了独立的上下文环境,可以帮助开发者在日志中准确、细致地记录和区分不同上下文中的操作,从而提高系统的可维护性和可调试性。

希望这些详细的解释能帮助你更好地理解什么是MDC,以及如何在实际项目中使用它。

6.5 zlog API

说明 (DESCRIPTION)

dzlog 是一组简化的日志记录函数,这些函数省略了 zlog_category_t 类型。在内部,它使用一个默认的分类,并将该分类置于锁保护之下,因此 dzlog 也是线程安全的。省略分类意味着用户不需要创建、保存或传递 zlog_category_t 变量。然而,为了灵活性,你仍然可以通过正常的 API 获取和使用其他分类值。

我们来看一下各函数的具体作用:

dzlog_init
int dzlog_init(const char *confpath, const char *cname);

这个函数类似于 zlog_init,但它需要一个额外的参数 cname,用于内部默认分类的名称。dzlog_init 会初始化日志系统,并设置默认的日志分类。这个默认分类将被内部使用。

成功时返回零(0),出错时返回负一(-1),出错时详细的错误日志会记录到由 ZLOG_PROFILE_ERROR 指定的日志文件中。

dzlog_set_category
int dzlog_set_category(const char *cname);

这个函数用于更改默认分类。新的分类将替换上一个默认分类。无需担心内存释放问题,因为所有的分类分配内存会在调用 zlog_fini 时被清理干净。

成功时返回零(0),出错时返回负一(-1)。

dzlog
void dzlog(const char *file, size_t filelen,
           const char *func, size_t funclen,
           long line, int level,
           const char *format, ...);

这个函数用于记录日志信息,采用可变参数列表的形式。它记录日志时,会包含源文件名、函数名、行号和日志级别等信息。

vdzlog
void vdzlog(const char *file, size_t filelen,
            const char *func, size_t funclen,
            long line, int level,
            const char *format, va_list args);

dzlog 类似,但使用 va_list 类型的参数列表。这对需要传递可变参数但希望以 C 标准库的方式处理这些参数的场景非常有用。

hdzlog
void hdzlog(const char *file, size_t filelen,
            const char *func, size_t funclen,
            long line, int level,
            const void *buf, size_t buflen);

这个函数用于记录二进制数据日志。它接受一个缓冲区及其长度,并记录这些原始的二进制数据。

宏定义 (Macros)

zlog.h 中定义了一些宏用于简化日志记录。这些宏将日志记录功能封装起来,使得调用更加方便。

dzlog_* 系列:
dzlog_fatal(format, ...)
dzlog_error(format, ...)
dzlog_warn(format, ...)
dzlog_notice(format, ...)
dzlog_info(format, ...)
dzlog_debug(format, ...)

它们分别对应致命错误、错误、警告、提示、信息和调试等不同级别的日志记录。

vdzlog_* 系列:
vdzlog_fatal(format, args)
vdzlog_error(format, args)
vdzlog_warn(format, args)
vdzlog_notice(format, args)
vdzlog_info(format, args)
vdzlog_debug(format, args)

dzlog_* 系列类似,但接受 va_list 类型的参数。

hdzlog_* 系列:
hdzlog_fatal(buf, buf_len)
hdzlog_error(buf, buf_len)
hdzlog_warn(buf, buf_len)
hdzlog_notice(buf, buf_len)
hdzlog_info(buf, buf_len)
hdzlog_debug(buf, buf_len)

用于记录二进制数据的不同级别日志。

返回值 (RETURN VALUE)

dzlog_initdzlog_set_category 两个函数,成功时返回 0,出错时返回 -1。详细的错误日志将记录在由 ZLOG_PROFILE_ERROR 指定的日志文件中。

总结

通过 dzlog 系列 API,你可以方便地初始化日志系统,设定默认日志分类,并记录日志信息。这些函数设计简单,线程安全,适合需要高效、可靠日志记录的项目。

希望这个解释对你有所帮助。如果你有更多问题,欢迎继续讨论。

6.6 用户定义输出

原型
typedef struct zlog_msg_s {
    char *buf;
    size_t len;
    char *path; 
} zlog_msg_t; 

typedef int (*zlog_record_fn)(zlog_msg_t *msg); 

int zlog_set_record(const char *rname, zlog_record_fn record); 
描述

zlog 允许用户定义输出函数,该输出函数与配置文件中的一种特殊规则相关联。一个典型的规则示例如下:

*.*     $name, "record path %c %d"; simple

zlog_set_record() 函数用于完成这种绑定操作。带有 $rname 的规则将通过用户定义的函数 record 进行输出。回调函数的类型为 zlog_record_fn

struct zlog_msg_t 的成员描述如下:

  • path: 该成员来源于带有 $name 的规则的第二个参数,类似于文件路径,它是动态生成的。
  • buflen: 这两个成员分别表示 zlog 格式化后的日志消息及其长度。

所有 zlog_set_record() 的设置在调用 zlog_reload() 后仍然有效。

返回值

成功时,zlog_set_record() 返回 0。出错时,返回 -1,并且会记录详细的错误日志到由 ZLOG_PROFILE_ERROR 指定的日志文件中。

6.7 调试和分析

原型
void zlog_profile(void);
描述

环境变量 ZLOG_PROFILE_ERROR 指定了 zlog 的错误日志路径。

环境变量 ZLOG_PROFILE_DEBUG 指定了 zlog 的调试日志路径。

zlog_profile() 函数在运行时会将内存中的所有信息打印到 zlog 的错误日志文件中。你可以将这些信息与配置文件进行对比以找到可能的错误。

数据治理是确保数据准确性、可靠性、安全性、可用性和完整性的体系和框架。它定义了组织内部如何使用、存储、保护和共享数据的规则和流程。数据治理的重要性随着数字化转型的加速而日益凸显,它能够提高决策效率、增强业务竞争力、降低风险,并促进业务创新。有效的数据治理体系可以确保数据在采集、存储、处理、共享和保护等环节的合规性和有效性。 数据质量管理是数据治理中的关键环节,它涉及数据质量评估、数据清洗、标准化和监控。高质量的数据能够提升业务决策的准确性,优化业务流程,并挖掘潜在的商业价值。随着大数据和人工智能技术的发展,数据质量管理在确保数据准确性和可靠性方面的作用愈发重要。企业需要建立完善的数据质量管理和校验机制,并通过数据清洗和标准化提高数据质量。 数据安全与隐私保护是数据治理中的另一个重要领域。随着数据量的快速增长和互联网技术的迅速发展,数据安全与隐私保护面临前所未有的挑战。企业需要加强数据安全与隐私保护的法律法规和技术手段,采用数据加密、脱敏和备份恢复等技术手段,以及加强培训和教育,提高安全意识和技能水平。 数据流程管理与监控是确保数据质量、提高数据利用率、保护数据安全的重要环节。有效的数据流程管理可以确保数据流程的合规性和高效性,而实时监控则有助于及时发现并解决潜在问题。企业需要设计合理的数据流程架构,制定详细的数据管理流程规范,并运用数据审计和可视化技术手段进行监控。 数据资产管理是将数据视为组织的重要资产,通过有效的管理和利用,为组织带来经济价值。数据资产管理涵盖数据的整个生命周期,包括数据的创建、存储、处理、共享、使用和保护。它面临的挑战包括数据量的快速增长、数据类型的多样化和数据更新的迅速性。组织需要建立完善的数据管理体系,提高数据处理和分析能力,以应对这些挑战。同时,数据资产的分类与评估、共享与使用规范也是数据资产管理的重要组成部分,需要制定合理的标准和规范,确保数据共享的安全性和隐私保护,以及建立合理的利益分配和权益保障机制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值