• log4c面向对象设计 (转)


    转自 http://blog.csdn.net/xkarl/article/details/6340180

    Log4C,Log4CPlus/Log4cpp,Log4j,Log4Net,Log4Perl,Log4PHP,Log4PLSQL,Log4LS,Log4py,Log4r,qmmslog,JDK1.4's util.logging framework是常用的LOG调试库,log4**的架构都类似

           虽然C不是面向对象语言,但是log4c的实现完全是面向对象思想,和log4j的实现方式几乎雷同

           Log4c需要设置的,也就是面向用户的,接口有

    category:log的日志模块

    Appender:log输出方式stream,mem,syslog等

    Layout:log打印方式,基本的,详细的(比如详细时间等)


    1、  Factory Method工厂方法的管理

           这里的SD(Service Design)就相当于一个N个工厂集合(log4c_category_factory和log4c_appender_factory和log4c_layout_factory是单态的工厂,通过sd_factory_xx来做管理)

           不要理解为polymorphism,只不过提供一个Service Design的统一API而已,根据实际需要是可以分离开的,

    factory.h :

    struct __sd_factory { 

       char* fac_name; 

       const sd_factory_ops_t* fac_ops; 

       sd_hash_t* fac_hash; 

    };

    typedef struct __sd_factory sd_factory_t;

    extern sd_factory_t* sd_factory_new(const char* a_name, const sd_factory_ops_t* a_ops);

    extern voidsd_factory_delete(sd_factory_t* a_this);
    extern void*sd_factory_get(sd_factory_t* a_this, const char* a_name);
    extern voidsd_factory_destroy(sd_factory_t* a_this, void* a_pr);
    extern voidsd_factory_print(const sd_factory_t* a_this, FILE* a_stream);
    extern intsd_factory_list(const sd_factory_t* a_this, void** a_items,
    int a_nitems);

    2、  Factory Method工厂方法

           因为面向用户配置选项,appender和layout在用户看来接口都一样,创建和删除以及打印,拥有共同的操作,fac_new/fac_delete/fac_print

    factory.h :

    struct __sd_factory { 

       char* fac_name; 

       const sd_factory_ops_t* fac_ops; 

       sd_hash_t* fac_hash; 

    };

    typedef struct __sd_factory sd_factory_t;

    struct __sd_factory_ops

    {

       void* (*fac_new)     (const char*);

       void  (*fac_delete)         (void*);

       void  (*fac_print)   (void*, FILE*);

    };

    typedef struct __sd_factory_opssd_factory_ops_t;

           fac_name用来区分appender/layout,fac_hash用来储存,方便查 找,如果更多的接口的话都无妨(个人喜好用链表代替hash),当不同模块Appender.c/ Layout.c都预先添加到hash中,如果还有ABC模块/XYZ模块,照样预先添加到HASH,模块非常的独立

    Appender.c :

    static const sd_factory_ops_tlog4c_appender_factory_ops = {

     (void*) log4c_appender_new,

     (void*) log4c_appender_delete,

     (void*) log4c_appender_print,

    };

    Layout.c :

    static const sd_factory_ops_tlog4c_layout_factory_ops = {

             (void*)log4c_layout_new,

             (void*)log4c_layout_delete,

             (void*)log4c_layout_print,

    };

           添加子fac到HASH后,在用户接口层,根据fac_name,通过 sd_hash_lookup查找HASH,找到fac_ops,有了fac_ops,就有了fac_new/fac_delete /fac_print,这个时候的地址是相应的函数指针----或者appender或者layout或者ABC或者XYZ

    3、  Descriptor多态

           在log4c_appender_new具体函数中,又面临一个问题是,appender有不同的输出方式:stream,mem,syslog等,

    appender.h :

    typedef struct log4c_appender_type {

       const char*       name;

       int (*open)          (log4c_appender_t*);

       int (*append) (log4c_appender_t*, const log4c_logging_event_t*);

       int (*close)  (log4c_appender_t*);

    } log4c_appender_type_t;

    Appender.c :

    static const log4c_appender_type_t * constappender_types[] = {

       &log4c_appender_type_stream,

       &log4c_appender_type_stream2,

       &log4c_appender_type_mmap,

       &log4c_appender_type_syslog,   

       &log4c_appender_type_rollingfile

    };

    static size_t nappender_types =sizeof(appender_types) / sizeof(appender_types[0]);

           这里用一个指针数组,毕竟输出类型是有限的

    Appender_type_stream.c :

    const log4c_appender_type_tlog4c_appender_type_stream = {

       "stream",

       stream_open,

       stream_append,

       stream_close,

    };

    Appender_type_mmap.c :

    const log4c_appender_type_tlog4c_appender_type_mmap = {

       "mmap",

       mmap_open,

       mmap_append,

       mmap_close,

    };

    Appender_type_syslog.c :

    const log4c_appender_type_tlog4c_appender_type_syslog = {

       "syslog",

       syslog_open,

        syslog_append,

       syslog_close,

    };

           同样操作,将上面不同类型添加到HASH,上面是指针数组的成员,在 log4c_appender_type_get运行时候,根据appender_name,通过通过sd_hash_lookup查找HASH,找到 open/ append/ close的指针,也就是具体的函数指针,或者stream_open/ mmap_open/ syslog_open等

           在log4c_layout_new具体函数中,有不同的输出方式:data,time等,同样是一个多态的实现

    4、  实际应用例子

    int main()
    {
    ...
    root = log4c_category_get("root");
    sub1 = log4c_category_get("sub1");


    layout1   = log4c_layout_get("layout1");
    log4c_appender_t* appender1 = log4c_appender_get("appender1");


    log4c_layout_set_type(layout1, &log4c_layout_type_basic_r);


    log4c_appender_set_layout(appender1, layout1);
    log4c_appender_set_udata(appender1,  stdout);


    log4c_category_set_appender(sub1, appender1);
    log4c_category_set_priority(sub1, LOG4C_PRIORITY_ERROR);
    ...
    }

           factory打印结果,udata是stderr、stdout等:
    factory[log4c_category_factory]:
    { name:'root' priority:NOTSET additive:1 appender:'(nil)' parent:'(nil)' }
    { name:'sub1' priority:ERROR additive:1 appender:'appender1' parent:'root' }

    factory[log4c_appender_factory]:
    { name:'stderr' type:'stream' layout:'basic' isopen:0 udata:10311428}
    { name:'stdout' type:'stream' layout:'basic' isopen:0 udata:10311408}
    { name:'appender1' type:'stream' layout:'layout1' isopen:0 udata:10311428}

    factory[log4c_layout_factory]:
    { name:'layout1' type:'basic_r' udata:00000000 }
    { name:'basic' type:'basic' udata:00000000 }
    { name:'dated' type:'dated' udata:00000000 }
           (1)可以看到有几个默认设置的,这是因为代码中默认配置了几项:
    /* build default appenders */
    log4c_appender_set_udata(log4c_appender_get("stderr"), stderr);
    log4c_appender_set_udata(log4c_appender_get("stdout"), stdout);

    /* build default layouts */
    log4c_layout_set_type(log4c_layout_get("dated"), &log4c_layout_type_dated);
    log4c_layout_set_type(log4c_layout_get("basic"), &log4c_layout_type_basic); 

           (2)3次fac_ops

    log4c_category_factory = sd_factory_new("log4c_category_factory",&log4c_category_factory_ops);
    log4c_appender_factory = sd_factory_new("log4c_appender_factory",&log4c_appender_factory_ops);
    log4c_layout_factory = sd_factory_new("log4c_layout_factory",&log4c_layout_factory_ops);  
    (后两个new的时候是默认的类型log4c_appender_type_stream log4c_layout_type_basic)
    (log4c_appender_factory->fac_hash,log4c_layout_factory->fac_hash类似LIST_HEAD的概念)
    (a_name对应就是log4c_***_type_t中的name)

           (3)如果配置在log4crc:
    <category name="mycat" priority="debug" appender="stdout" />     
    <appender name="stdout" type="stream" layout="basic"/>    
    <layout name="basic" type="basic"/>
    3次fac_ops、3次sd_factory_get,不同的是,会增加
    log4c_category_set_priority
    log4c_appender_set_type
    log4c_layout_set_type
    来配置类型

    5、  小结

           其实Factory_ops和Discriptor都没有必要HASH或者链表,毕竟这里分类有限,特别是对于log4c的代码而言,Discriptor直接数组中循环判断即可,HASH完全是多余

           反倒是Factory_ops在分类很多的情况下,采用HASH或者链表,因为 Factory_ops面向的层次更高,实际项目中分工是每个人几个子fac,采用数组耦合性太强,采用HASH或者链表,只要新加一个fac_ops, 只需添加到HASH或者链表,到时候查找即可

           在fac_ops具体实现中,name不同,就必须新开一块内存,所以必须采用HASH或者链表,比如log4c_appender_factory->fac_hash,log4c_layout_factory->fac_has就是HASH的HEAD

  • 相关阅读:
    解决网页元素无法定位的几种方法
    转载:pycharm最新版新建工程没导入本地包问题:module 'selenium.webdriver' has no attribute 'Firefox'
    关于list的漏删
    春风十里不如你
    记我兵荒马乱的一周(0808-0812)--用户反馈及修改点验证
    vue定时器
    业务系统多机房多活实现思路
    分布式开发之:id生成器
    关于部署系统的一些思考
    web系统认证与鉴权中的一些问题
  • 原文地址:https://www.cnblogs.com/little-ant/p/3664222.html
Copyright © 2020-2023  润新知