一.简介
Log4c 是纯C写的第三发开源日志软件
二、安装与使用
2.1.Ubuntu 可以用命令
sudo apt-get install liblog4c-dev liblog4c-doc
2.2其他平台用源码安装
2.2.1 下载源码
http://sourceforge.net/projects/log4c/ (log4c官网)
2.2.2 编译安装
tar -zxvf log4c-1.2.4.tar.gz
cd log4c-1.2.4
mkdir build
cd build
../configure --prefix=/usr/local/log4c
make
sudo make install
2.2.3配置log4c的lib所在目录
安装完成之后为了让你的程序库能找到log4c 动态库,执行一下指令 sudo ldconfig
如果还没有解决,可以尝试:
方法一:
1) 将/usr/local/log4c/include的文件拷贝到/usr/include
执行ldconfig
export LD_LIBRARY_PATH=/home/other/test/lib:$LD_LIBRARY_PATH 或者执行动态库
方法二:
编译的时候连接 -I /usr/local/log4c/include 和 -L /usr/local/log4c/lib
三、log4c配置文件解读
3.1 配置文件存放的路径(程序运行时会从以下路径中查找log4crc配置文件)
${ LOG4C_RCPATH }/log4crc //环境变量中设置的配置文件夹下log4crc
${HOME}/.log4crc //HOME文件夹下log4crc
./log4crc //当前可执行文件夹下log4crc
3.2 配置文件各个名称说明
3.2.1 Category
name:日志的名称
priority:日志的优先级,共有fatal/alert/crit/error/warn/notice/info/debug/trace/notest和unknow 11个级别,其级别逐步递增,记录在日志中的数据小于等于指定级别的数据。
appender:输出流类型名称,为appender节点的name属性的值。
3.2.2.Appender
name :输出流名称
type:输出流类型,共有stream/syslog/rollingfile三大类
layout:输出日志的格式的名称,为layout节点的name属性的值。
rollingpolicy:输出日志文件配置策略名称,为rollingpolicy节点的name属性的值。
Rollingpolicy 的timewiin类型的日志输出为自定义类型。
3.2.3 layout
name:输出日志的格式名称
type:输出日志格式的类型,共有base,dated,dated_t等格式类型,对于自定义类型配置在这里,否则不能加载
base:%P %c -%m/n
%P 日志信息的优先级
%c 日志的名称
%m 日志信息内容
dated:%d %P %c -%m/n
%d 日志信息产生的时间,UTC格式为yyymmdd hh:mm:ss:mis
%P 日志信息的优先级
%c 日志的名称
%m 日志信息内容
3.2.4 rollingpolicy
name:日志文件输出的配置策略名称
type: 日志输出文件策略的类型,有sizewin一种类型
maxsize:输出日志文件的最大值。默认值为20kb
maxnum:保存的历史日志文件总数。默认值为5.
<rollingpolicy name="Policy1" type="timewin" timetype="1" maxnum="10" />
<rollingpolicy name="RollingPolicy" type="sizewin" maxsize="102400" maxnum="10" />
type = “sizewin” maxsize 设置一个文件的最大字节(单位bytes) maxnum 日志文件的最大个数
type = “timewin” timetype表示日期格式,0---按小时
1---按日
2---按月
3---年
3.3 log4c配置文件示例
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE log4c SYSTEM "">
<log4c version="1.2.4">
<config>
<bufsize>0</bufsize>
<debug level="2"/>
<nocleanup>0</nocleanup>
<reread>1</reread>
</config>
<category name="root" priority="notice"/>
<category name ="Test" priority="debug" appender="testlog"/>
<rollingpolicy name="policy" type="sizewin" maxsize="1024" maxnum="10"/>
<appender name="testlog" type="rollingfile" logdir="/var/log" prefix="log" layout="dated_local" rollingpolicy="policy"/>
<appender name="stdout" type="stream" layout="basic"/>
<appender name="stderr" type="stream" layout="dated"/>
<appender name="syslog" type="syslog" layout="dated"/>
<layout name="basic" type="basic"/>
<layout name="dated_local" type="dated_local"/>
</log4c>
3.4 log4c 代码部分
log.c
#include <log4c.h>
#include <assert.h>
#include "log.h"
#include "time.h"
static log4c_category_t *log_category = NULL;
extern rollingfile_udata_t *rollingfile_make_udata(void);
extern log4c_rc_t* const log4c_rc;
// === FUNCTION ======================================================================
// Name: log_init
// Description: 从配置文件"log4crc"中读取配置信息到内存,使用分类category初始化logger
// @param category [in]: 分类
// =====================================================================================
int log_init(const char *category)
{
setenv("LOG4C_RCPATH","/root/AasServer/conf",1); //指定log4c的配置文件读取位置
printf("LOG4C_RCPATH = %s\n",getenv("LOG4C_RCPATH"));
if (log4c_init() == 1)
{
return -1;
}
log_category = log4c_category_get(category);
return 0 ;
}
// === FUNCTION ======================================================================
// Name: log_message
// Description: 记录日志信息
// @param priority [in]: 日志类别
// @param file [in]: 文件
// @param line [in]: 行
// @param fun [in]: 函数
// @param fmt [in]: 格式化参数
// =====================================================================================
void log_message(int priority ,
const char *file, int line, const char *fun,
const char *fmt , ...)
{
char new_fmt[1024*500];
const char * head_fmt = "[file:%s, line:%d, function:%s]";
va_list ap;
int n;
assert(log_category != NULL);
if(log4c_category_is_priority_enabled(log_category, priority))
{
n = sprintf(new_fmt, head_fmt , file, line , fun);
strcat(new_fmt + n , fmt);
va_start(ap , fmt);
log4c_category_vlog(log_category , priority, new_fmt , ap);
va_end(ap);
}
return;
}
// === FUNCTION ======================================================================
// Name: log_fini
// Description: 清理所有申请的内存,关闭它们打开的文件
// =====================================================================================
int log_fini()
{
return (log4c_fini());
}
int main(void)
{
int i = 0;
log_init("Test");
for(i=0;;i++)
{
MY_LOG_ERROR("error %d",i);
MY_LOG_WARN("warn%d",i);
MY_LOG_NOTICE("notice%d",i);
MY_LOG_DEBUG("debug%d",i);
MY_LOG_INFO("info%d",i);
MY_LOG_FATAL("fatal%d",i);
MY_LOG_TRACE("trace%d",i);
}
log_fini();
return 0;
}
log.h
#ifndef __LOG_H_
#define __LOG_H_
#include <string.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C"
{
#endif
#include "log4c.h"
#ifdef __cplusplus
}
#endif
#define LOG_PRI_FATAL LOG4C_PRIORITY_FATAL
#define LOG_PRI_ERROR LOG4C_PRIORITY_ERROR
#define LOG_PRI_WARN LOG4C_PRIORITY_WARN
#define LOG_PRI_NOTICE LOG4C_PRIORITY_NOTICE
#define LOG_PRI_INFO LOG4C_PRIORITY_INFO
#define LOG_PRI_DEBUG LOG4C_PRIORITY_DEBUG
#define LOG_PRI_TRACE LOG4C_PRIORITY_TRACE
extern int log_init(const char *category);
extern void log_message(int priority ,
const char *file, int line, const char *fun,
const char *fmt , ...);
extern int log_fini();
#define MY_LOG_FATAL(fmt,args...) \
log_message(LOG_PRI_FATAL,__FILE__ , __LINE__ , __FUNCTION__ , fmt , ##args)
#define MY_LOG_ERROR(fmt , args...) \
log_message(LOG_PRI_ERROR,__FILE__ , __LINE__ , __FUNCTION__ , fmt, ##args)
#define MY_LOG_WARN(fmt, args...) \
log_message(LOG_PRI_WARN,__FILE__ , __LINE__ , __FUNCTION__ , fmt , ##args)
#define MY_LOG_NOTICE(fmt , args...) \
log_message(LOG_PRI_NOTICE,__FILE__ , __LINE__ , __FUNCTION__ , fmt , ##args)
#define MY_LOG_INFO(fmt,args...) \
log_message(LOG_PRI_INFO,__FILE__ , __LINE__ , __FUNCTION__ , fmt , ##args)
#define MY_LOG_DEBUG(fmt , args...) \
log_message(LOG_PRI_DEBUG,__FILE__ , __LINE__ , __FUNCTION__ , fmt , ##args)
#define MY_LOG_TRACE(fmt,args...) \
log_message(LOG_PRI_TRACE, __FILE__ , __LINE__ , __FUNCTION__ , fmt ,##args)
#endif
四、log4c遇到的问题
问题一:如何输出日志到文件
修改log4crc的文件,将添加的log4c.examples.helloworld节点appender指定为日志文件名字,例如我指定了aaa
<category name="log4c.examples.helloworld" priority="debug" appender="aaa"/>
执行hello 在hello所在目录下生成一个文件aaa,打开里面的内容是:
[aaa] ERROR log4c.examples.helloworld - Hello World!
问题二:程序重启后,每次都重新写新的文件,不是接在在原来的文件中写
原文链接:https://blog.csdn.net/smilestone_322/article/details/48826757
开源日志库log4c存在的一个bug,程序重启后,每次都重新写新的文件,不是接在在原来的文件中写;这样在ARM中应用时,当应用重启了,但是log0还没有达到maxsize,就切换了一个新的文件,会造成空间浪费,网上查了好久,log4c没有提供这样的接口,维护别人的代码,用到了log4c这个库,只能修改log4c的源码了。
源码修改的地方如下:
static int get_file_size(const char *filename)
{
FILE*fp;
if((fp=fopen(filename,"rb"))==NULL)
{
return -1;
}
fseek(fp, 0, SEEK_END);
int len = ftell(fp);
fseek(fp, 0, SEEK_SET);
fclose(fp);
return len;
}
/*******************************************************************************/
static int sizewin_rollover(log4c_rollingpolicy_t *this, FILE ** current_fpp ){
int rc = 0;
rollingpolicy_sizewin_udata_t *swup = log4c_rollingpolicy_get_udata(this);
int k = 0;
int i = 0;
sd_debug("sizewin_rollover[");
/* Starting at the last_index work back renaming the files and
leaving space for the .0 file.
If the last index is negative then it means the file doesn't exist
so we create the first file
*/
if ( !swup || !swup->sw_logdir){
sd_error("rollingpolicy '%s' not yet configured (logdir,prefix etc.)",
log4c_rollingpolicy_get_name(this));
} else {
swup->sw_last_index = sizewin_get_last_index(swup);
k = swup->sw_last_index;
if ( k < 0 ) {
sd_debug("creating first file");
if (sizewin_open_zero_file(swup->sw_filenames[0], current_fpp,1)){
swup->sw_flags |= SW_LAST_FOPEN_FAILED;
sd_error("open zero file failed");
} else{
swup->sw_flags &= !SW_LAST_FOPEN_FAILED;
}
swup->sw_last_index = 0;
} else {
sd_debug("rolling up existing files");
if ( k == swup->sw_conf.swc_file_max_num_files-1) {
if(unlink(swup->sw_filenames[k])){
sd_error("unlink failed");
rc = 1;
} else {
k = swup->sw_conf.swc_file_max_num_files-2;
}
} else {
/* not yet reached the max num of files
* so there's still room to rotate the list up */
}
/* If there's a current open fp, close it.*/
if ( !(swup->sw_flags & SW_LAST_FOPEN_FAILED) && *current_fpp) {
if(fclose(*current_fpp)){
sd_error("failed to close current log file");
rc = 1;
}
} else {
if( (swup->sw_flags & SW_LAST_FOPEN_FAILED)){
sd_debug("Not closing log file...last open failed");
} else if (*current_fpp == 0) {
sd_debug("No log file currentlty open...so not closing it");
}else {
sd_debug("Not closing current log file...not sure why");
}
}
/* Now, rotate the list up if all seems ok, otherwise
* don't mess with teh files if something seems to have gone wrong
*/
int new_file_flag=0;
if ( !rc){
sd_debug("rotate up , last index is %d", k);
int len= get_file_size(swup->sw_filenames[0]);
int max_len=swup->sw_conf.swc_file_maxsize/100000*100000;
if(len>max_len ||len==-1)
{
i = k;
while ( i >= 0 ) {
sd_debug("Renaming %s to %s",
swup->sw_filenames[i], swup->sw_filenames[i+1]);
if(rename( swup->sw_filenames[i], swup->sw_filenames[i+1])){
sd_error("rename failed");
rc = 1;
// break;
}
i--;
}
if ( !rc){
swup->sw_last_index = k + 1;
}
new_file_flag=1;
}
} else {
sd_debug("not rotating up--some file access error");
}
/* Now open up the 0'th file for writing */
if (sizewin_open_zero_file(swup->sw_filenames[0], current_fpp,new_file_flag)){
swup->sw_flags |= SW_LAST_FOPEN_FAILED;
sd_error("open zero file failed");
rc = 1;
} else{
swup->sw_flags &= !SW_LAST_FOPEN_FAILED;
rc = 0;
}
}
sd_debug("current file descriptor '%d'", fileno(*current_fpp));
}
sd_debug("]");
return(rc);
}
static int sizewin_open_zero_file(char *filename, FILE **fpp,int flag ){
int rc = 0;
sd_debug("sizewin_open_zero_file['%s'", filename);
if(flag)
{
*fpp = fopen(filename, "w+");
}else
{
*fpp = fopen(filename, "a+");
}
if ( (*fpp) == NULL){
sd_error("failed to open zero file '%s'--defaulting to stderr--error='%s'",
filename, strerror(errno));
*fpp = stderr;
rc = 1;
}
/* unbuffered mode at the filesystem level
xxx make this configurable from the outside ?
*/
setbuf(*fpp, NULL);
sd_debug("]");
return(rc);