嵌入式linux下的c语言日志log模块,功能增强,linux 日志库

嵌入式linux下的c语言日志log模块,功能增强/**
日志打印示例。
使用:
mylog(DEBUG, \”This is debug info\\n\”);
结果:
[2018-07-22 23:37:27:17

/**

打印日志的示例。

使用:

mylog(DEBUG, \’这是调试信息\\n\’);

结果:

[2018-07-22 23:37:27:172] [调试] [main.cpp:5] 这是调试信息

默认情况下,打印当前时间(精确到毫秒)、文件名和行号。

*/

#include stdarg.h

#include stdio.h

#include 字符串.h

#包括时间.h

#include unistd.h

#include sys/time.h

#include sys/stat.h

#include pthread.h

#include sys/msg.h

#include sys/ipc.h

#include errno.h

#include dirent.h

#包括stdlib.h

#include \’log.h\’

//#ifndef 日志级别

//#定义日志级别调试

//万一

//使用GNU C 扩展语法。这仅对gcc(C 语言)有效。

//C++ 版本的g++ 无法编译

静态const char* s_loginfo[]={

[错误]=\’错误\’,

[警告]=\’警告\’,

[信息]=\’信息\’,

[调试]=\’调试\’,

};

静态字符文件名[LOGFILE_MAXCOUNT][LOGFILE_NAMELENTH];

//记录文件名前缀(最好从终端号获取)

static char file_prifix[LOGFILE_NAMELENTH];

//Linux消息队列

静态int s_msg_id;

静态int r_msg_id;

#定义MSG_TYPE 1001

#defineMAX_TEXT 1024

结构体msg_st{

长整型msg_type;

字符文本[MAX_TEXT];

};

静态pthread_t tid;

//================================================

静态无效get_timestamp(char *buffer)

{

时间_t t;

结构tm*p;

结构时间电视;

int 长度;

int 毫秒;

t=时间(NULL);

p=当地时间(t);

gettimeofday(电视, NULL);

毫秒=(int)(tv.tv_usec/1000);

/* 时间格式: [2011-11-15 12:47:34:888] */

len=snprintf(缓冲区, 32, \'[%04d-%02d-%02d %02d:%02d:%02d:%03d] \’,

p-tm_year+1900, p-tm_mon+1,

p-tm_mday、p-tm_hour、p-tm_min、p-tm_sec、milsec);

缓冲区[长度]=\’\\0\’;

}

//获取当前时间

静态int get_curtime(char* outTimeStr)

{

int ret=0;

time_t t 时间;

结构tm *tmTime;

结构timeval mTime;

时间(tTime);

tmTime=本地时间(tTime);

gettimeofday(mTime, NULL);

sprintf( outTimeStr, \’%04d%02d%02d%02d%02d%02d\’,

tmTime-tm_year + 1900,tmTime-tm_mon + 1,

tmTime ~ tm_mday, tmTime ~ tm_hour,

tmTime-tm_min、tmTime-tm_sec );

返回ret。

}

//创建文件夹

static int create_dir(const char *sPathName)

{

字符目录名[256];

strcpy(dirName, sPathName);

int i,len=strlen(dirName);

for(i=1; ilen; i++)

{

if(dirName[i]==\’/\’)

{

目录名[i]=0;

if(access(目录名, 0)!=0)

{

if(mkdir(目录名, 0755)==-1)

{

fprintf(stderr,\’mkdir 错误\\n\’);

返回-1。

}

}

目录名[i]=\’/\’;

}

}

返回0。

}

//获取文件大小

静态无符号长get_size(const char *path)

{

无符号长文件大小=-1;

struct stat statbuff;

if(stat(路径, statbuff) 0){

返回文件大小。

}除此之外{

文件大小=statbuff.st_size;

}

返回文件大小。

}

//文件是否存在,如果文件存在则返回1,如果文件不存在则返回0

static int file_exists(char *文件名)

{

返回(访问(文件名,0)==0);

}

静态int read_filelist(char *basePath)

{

DIR *目录;

结构体dirent *ptr;

字符库[1000];

整数计数=0;

if ((dir=opendir(basePath))==NULL)

{

fprintf(stderr,\’打开目录时出错.\’);

返回-1。

}

while ((ptr=readdir(dir)) !=NULL)

{

//printf(\’count=%d\\n\’,i++);

if(strcmp(ptr-d_name,\’.\’)==0 || strcmp(ptr-d_name,\’.\’)==0) ///当前目录OR父目录

继续;

else if(ptr-d_type==8) ///文件

{

printf(\’f_name:%s/%s\\n\’,basePath,ptr-d_name);

sprintf(文件名[计数],\’%s\’,ptr-d_name);

计数++;

如果(计数LOGFILE_MAXCOUNT -1){

fprintf(stderr,\’错误!只允许文件数=%d\\n\’,LOGFILE_MAXCOUNT);

返回-2。

}

}

else if(ptr-d_type==10) ///链接文件

printf(\’l_name:%s/%s\\n\’,basePath,ptr-d_name);

else if(ptr-d_type==4) ///目录

{

printf(\’d_name:%s/%s\\n\’,basePath,ptr-d_name);

printf(\’这是一个目录\\n\’);

继续;

//这是一个目录。在目录下继续递归阅读

//memset(base,\’\\0\’,sizeof(base));

//strcpy(base,basePath);

//strcat(base,\’/\’);

//strcat(base,ptr-d_name);

//读取文件列表(基);

}

}

关闭目录(目录);

返回0。

}

//处理是否保存日志文件

//原理算法:根据配置的允许保留天数将日期转换为时间戳和时间范围。

//扫描日志目录下的所有文件名,提取日期,保持在这个时间范围内,否则删除。

//关键是计算允许保留文件的时间范围。作为一般规则,将日期转换为时间戳,然后将时间戳转换为日期。

静态int file_alives_proc()

{

int ret=0;

char curtime[20]; //当前日期和时间

char Deadtime[20] //过去的截止日期

printf(\’file_alives_proc:\\n\’);

get_curtime(curtime);

//只获取日期,节省时间

memset(curtime[8],0x30,6);

printf(\’aive maxdays:%d\\n\’,LOGFILE_ALIVEDAYS);

printf(\’curtime:%s\\n\’,curtime);

struct tm* tmp_time=(struct tm*)malloc(sizeof(struct tm));

//将字符串转换为时间

strptime(curtime,\’%Y%m%d%H%M%S\’,tmp_time);

time_t t=mktime(tmp_time);

printf(\’t 现在=%ld\\n\’,t);

空闲(tmp_时间);

time_t t1=t – LOGFILE_ALIVEDAYS*24*60*60;

//将t1转换为时间。即将时间戳转换为时间。

结构tm*p;

p=gmtime(t1);

//将日期和时间转换为字符串。由于仅比较日期,因此时间被忽略。

strftime(死区时间, sizeof(死区时间), \’%Y%m%d000000\’, p);

printf(\’死区时间:%s\\n\’, 死区时间);

//上面已经获得了curtime和Deadtime。然后搜索该范围内的日志。

//保持日志文件日期在此范围内,否则删除

for(int i=0; i LOGFILE_MAXCOUNT; i++ )

{

if(strlen(文件名[i]) 0)

{

printf(\’文件名=%s\\n\’,文件名[i]);

字符ftime[20];

memset(ftime,0,20);

memset(ftime,0x30,8);

//重要的是这个拦截不能错

memcpy(ftime,file_names[i][strlen(LOGFILE_PREFIX)+1+strlen(file_prifix)],8);

printf(\’file_time=%s\\n\’,ftime);

//开始比较是否在日期范围内

if(memcmp(ftime, 死区时间, 8) 0)

{

//保留文件超过截止日期

printf(\’%s——保持活力\\n\’,file_names[i]);

}除此之外{

printf(\’%s—————-死了,必须删除!\\n\’,file_names[i]);

//删除文件

字符dfname[50];

sprintf(dfname,\’%s/%s\’,LOGFILE_PATH,file_names[i]);

删除(dfname);

}

//

}除此之外{

//printf(\’fname=NULL\\n\’);

}

}

返回ret。

}

int open_msg(void) {

key_t 消息键;

if ((msgkey=ftok(\’/tmp\’, \’a\’)) 0) {

printf(\’发送ftok 失败!\\n\’);

返回-1。

}

printf(\’—-msgkey 是%d\\n\’,msgkey);

if ((s_msg_id=msgget(msgkey, IPC_CREAT | 0666))==-1) {

printf(\’消息获取失败!\\n\’);

返回-1。

}

printf(\’—-s_msg_id 为%d\\n\’,s_msg_id);

msgctl(s_msg_id,IPC_RMID,0);//先删除。否则它可能会满,因为它的生命周期与内核的生命周期相同。

if ((s_msg_id=msgget(msgkey, IPC_CREAT | 0666))==-1) {

printf(\’消息获取失败!\\n\’);

返回-1。

}

r_msg_id=s_msg_id;

返回0。

}

静态int send_msg(char *buf,int len){

结构msg_st数据;

数据.msg_type=MSG_TYPE;

strcpy(data.text,buf);

//s_msg_id=0;

if(msgsnd(s_msg_id,数据,len,IPC_NOWAIT)==-1){

printf(\’msgsnd 失败。\\n\’);

错误(\’msgsnd\’);

返回-1。

}

返回0。

}

//如果大量连续消息发送太快而接收太慢,则会导致发送失败。

静态int recv_msg(void)

{

整数大小;

结构msg_st数据;

int msgtype=MSG_TYPE;

字符tmpfname[ 50 ];

字符tmpTime[ 14 + 1 ];

文件*fp;

无符号长文件大小。

memset(data.text,0,sizeof(data.text));

/*接收消息队列*/

//阻塞接收

rsize=msgrcv(r_msg_id,data,sizeof(data.text),msgtype,MSG_NOERROR);

if(rsize==-1)

{

if (errno!=ENOMSG) {

错误(\’msgrcv\’);

}

printf(\’msgrcv() 没有可用消息\\n\’);

返回-1。

}

除此之外

{

//printf(\’收到消息: %s\\n\’, data.text);

get_curtime(tmpTime);

sprintf( tmpfname, \’%s/%s%s_%8.8s.log\’, LOGFILE_PATH, LOGFILE_PREFIX,file_prifix, tmpTime );

fp=fopen( tmpfname, \’a\’ );

如果(NULL==fp)

{

fprintf(stderr,\’无法打开文件,文件名=%s\\n\’,tmpfname);

返回-2。

}

除此之外

{

文件大小=get_size(tmpfname);

//printf(\’文件大小=%u\\n\’,文件大小);

if((文件大小/1024) LOGFILE_MAXSIZE){

fprintf(stderr,\’写入日志失败,只允许maxsize=%ukb,当前大小=%ukb\\n\’,LOGFILE_MAXSIZE,filesize/1024);

fclose(fp);

返回-3。

}

//printf(\’%s\\n\’,tmpfname);

fprintf( fp, \’%s\’, data.text );

fclose(fp);

}

}

返回0。

}

void mylog1(const char* 文件名, int 行, 枚举LogLevel 级别, const char* fmt,)

{

if(级别LOGLEVEL)

返回;

va_list 参数列表;

字符缓冲区[1024];

字符sbuf[MAX_TEXT];

memset(buf, 0, 1024);

memset(sbuf,0,MAX_TEXT);

va_start(arg_list, fmt);

vsnprintf(buf, 1024, fmt, arg_list);

字符时间[32]={0};

//删除可能存在的任何目录路径,仅保留文件名

const char* tmp=strrchr(文件名, \’/\’);

if (!tmp) tmp=文件名;

否则tmp++;

获取时间戳(时间);

开关(级别){

案例DEBUG:

//绿色的

sprintf(sbuf,\’\\033[1;32m%s[%s] [%s:%d] %s\\n\\033[0m\’, 时间, s_loginfo[级别], tmp, 行, buf);

休息;

案例信息:

//蓝色的

sprintf(sbuf,\’\\033[1;34m%s[%s] [%s:%d] %s\\n\\033[0m\’, 时间, s_loginfo[级别], tmp, 行, buf);

休息;

大小写错误:

//红色的

sprintf(sbuf,\’\\033[1;31m%s[%s] [%s:%d] %s\\n\\033[0m\’, 时间, s_loginfo[级别], tmp, 行, buf);

休息;

案例WARN:

//黄色的

sprintf(sbuf,\’\\033[1;33m%s[%s] [%s:%d] %s\\n\\033[0m\’, 时间, s_loginfo[级别], tmp, 行, buf);

休息;

}

printf(\’%s\’,sbuf);

#if(LOGFILE_ENABLE)

//记录日志到文件

send_msg(sbuf,strlen(sbuf));

万一

va_end(arg_list);

}

//如果考虑效率的话,IO操作需要时间,所以我们异步写吧

//想要使用单独的线程将日志写入文件并实现自己的消息队列?

//不用多做,直接使用Linux消息队列。

静态void* thread_writelog(void* args)

{

mylog(INFO,\’thread_writelog 开始.\\n\’);

同时(1)

{

recv_msg();

我们睡觉(20*1000);

}

}

//参数前缀、日志文件名前缀、终端号

int init_log(char *prifix)

{

int ret=0;

printf(\’初始化log:\\n\’);

#if(LOGFILE_ENABLE)

//检查目录是否存在

ret=create_dir(LOGFILE_PATH);

如果(0!=ret){

返回ret。

}

printf(\’创建目录%s\\n 成功!\\n\’,LOGFILE_PATH);

ret=read_filelist(LOGFILE_PATH);

如果(0!=ret){

printf(\’read_filelist 错误!ret=%d\\n\’,ret);

//返回ret.

}

//文件名前缀

if(strlen(前缀) 0){

strcpy(file_prifix,prifix);

}

//指定是否保存历史文件

file_alives_proc();

//for(int i=0; i30;i++){

//printf(\’%s\\n\’,file_names[i]);

//}

//创建消息队列

ret=open_msg();

如果(0!=ret){

返回ret。

}

printf(\’消息队列创建成功!\\n\’);

//创建日志文件写入线程

ret=pthread_create(tid,

无效的,

线程写日志,

无效的);

if(0!=ret)

{

fprintf(stderr, \’无法创建thread_writelog,错误号%d\\n\’, ret);

}

除此之外

{

printf(\’thread_writelog创建成功!\\n\’);

}

printf(\’日志初始化成功!日志文件现已启用.\\n\’);

除此之外

printf(\’日志初始化成功!日志文件将不会被启用.\\n\’);

万一

返回ret。

}

//

//

/*

//使用demo:

//mylog(调试,\’h

ello world!\\n\”);
//输出:[2019-07-26 14:31:51:882] [DEBUG] comLib.c:1257] hello world!
//
//目前只为个人使用,暂无考虑线程安全,高效率和高并发
//考虑了一点儿效率,写文件操作IO比较耗时,因此日志使用了异步写入,linux消息队列。
//因linux的消息队列,容量和长度有限制,因此若单个消息超1024byte或并发发送几千个消息
//且发送速度很快,大于了队列的接收速度,那么肯定,会发送失败
*/
#ifndef LOG_H_
#define LOG_H_
#ifdef __cplusplus
extern \”C\” {
#endif
enum LogLevel
{
ERROR = 1,
WARN = 2,
INFO = 3,
DEBUG = 4,
};
void mylog1(const char* filename, int line, enum LogLevel level, const char* fmt, …) __attribute__((format(printf,4,5)));
#define mylog(level, format, …) mylog1(__FILE__, __LINE__, level, format, ## __VA_ARGS__)
//================================================================
//============日志模块操作接口,配置和使用都在下面================
//日志级别定义,小于该级别的日志方能输出
#ifndef LOGLEVEL
#define LOGLEVEL DEBUG
#endif
//目前暂只支持管理目录下的30个日志文件名,文件名长度50以内。大了不处理
#define LOGFILE_MAXCOUNT 30
#define LOGFILE_NAMELENTH 50
//===========日志文件的配置=======================================
//是否启用记录日志到文件功能
#define LOGFILE_ENABLE 1
//日志文件的路径,后面不能带\”/\”
#define LOGFILE_PATH \”/log\”
//日志文件名称的前缀
#define LOGFILE_PREFIX \”log_b503_\”
//日志文件存在的时间 单位(天),会自动删除当前日期-ALIVEDAYS 之前的文件
//限制日志最长保留时间不能超 LOGFILE_MAXCOUNT 天
#define LOGFILE_ALIVEDAYS 7
//单个日志文件的限制大小 单位kb
#define LOGFILE_MAXSIZE 1*1024
//================================================================
//日志模块初始化,若要记录日志到文件中,必须init_log且开启LOGFILE_ENABLE
//若不需要记录日志到文件功能,则不要调用init_log();
//调用了也没事,只是别开启LOGFILE_ENABLE
//参数prifix,日志文件名前缀,最好传终端唯一编号
extern int init_log(char *prifix);
#ifdef __cplusplus
};
#endif
#

附:

go实现的简易FTP功能,先对日志文件进行zip压缩,然后ftp至后台服务器。

package main
import (
\”archive/zip\”
\”flag\”
\”fmt\”
\”io\”
\”log\”
\”os\”
\”strings\”
\”github.com/dutchcoders/goftp\”
\”github.com/larspensjo/config\”
)
var (
ftp *goftp.FTP
conFile = flag.String(\”ftpcfg\”, \”/ftpcfg.ini\”, \”config file\”)
Server string = \”127.0.0.1:21\”
User string = \”\”
Pwd string = \”\”
)
func checkErr(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, \”Fatal error: %s\”, err.Error())
}
}
/**
@files:需要压缩的文件
@compreFile:压缩之后的文件
*/
func CompressZip(files []*os.File, zipfileName string) (err error) {
zipfile, err := os.Create(zipfileName)
if err != nil {
return err
}
defer zipfile.Close()
zw := zip.NewWriter(zipfile)
defer zw.Close()
for _, file := range files {
err := compressZip(file, zw)
if err != nil {
return err
}
file.Close()
}
return nil
}
/**
功能:压缩文件
@file:压缩文件
@prefix:压缩文件内部的路径
为了做好运维面试路上的助攻手,特整理了上百道 **【运维技术栈面试题集锦】** ,让你面试不慌心不跳,高薪offer怀里抱!
这次整理的面试题,**小到shell、MySQL,大到K8s等云原生技术栈,不仅适合运维新人入行面试需要,还适用于想提升进阶跳槽加薪的运维朋友。**
![](https://img-blog.csdnimg.cn/img_convert/7287bb49ab7f789c40e4bd0005cc3600.png)
本份面试集锦涵盖了
* **174 道运维工程师面试题**
* **128道k8s面试题**
* **108道shell脚本面试题**
* **200道Linux面试题**
* **51道docker面试题**
* **35道Jenkis面试题**
* **78道MongoDB面试题**
* **17道ansible面试题**
* **60道dubbo面试题**
* **53道kafka面试**
* **18道mysql面试题**
* **40道nginx面试题**
* **77道redis面试题**
* **28道zookeeper**
**总计 1000+ 道面试题, 内容 又全含金量又高**
* **174道运维工程师面试题**
> 1、什么是运维?
> 2、在工作中,运维人员经常需要跟运营人员打交道,请问运营人员是做什么工作的?
> 3、现在给你三百台服务器,你怎么对他们进行管理?
> 4、简述raid0 raid1raid5二种工作模式的工作原理及特点
> 5、LVS、Nginx、HAproxy有什么区别?工作中你怎么选择?
> 6、Squid、Varinsh和Nginx有什么区别,工作中你怎么选择?
> 7、Tomcat和Resin有什么区别,工作中你怎么选择?
> 8、什么是中间件?什么是jdk?
> 9、讲述一下Tomcat8005、8009、8080三个端口的含义?
> 10、什么叫CDN?
> 11、什么叫网站灰度发布?
> 12、简述DNS进行域名解析的过程?
> 13、RabbitMQ是什么东西?
> 14、讲一下Keepalived的工作原理?
> 15、讲述一下LVS三种模式的工作过程?
> 16、mysql的innodb如何定位锁问题,mysql如何减少主从复制延迟?
oDB面试题**
* **17道ansible面试题**
* **60道dubbo面试题**
* **53道kafka面试**
* **18道mysql面试题**
* **40道nginx面试题**
* **77道redis面试题**
* **28道zookeeper**
**总计 1000+ 道面试题, 内容 又全含金量又高**
* **174道运维工程师面试题**
> 1、什么是运维?
> 2、在工作中,运维人员经常需要跟运营人员打交道,请问运营人员是做什么工作的?
> 3、现在给你三百台服务器,你怎么对他们进行管理?
> 4、简述raid0 raid1raid5二种工作模式的工作原理及特点
> 5、LVS、Nginx、HAproxy有什么区别?工作中你怎么选择?
> 6、Squid、Varinsh和Nginx有什么区别,工作中你怎么选择?
> 7、Tomcat和Resin有什么区别,工作中你怎么选择?
> 8、什么是中间件?什么是jdk?
> 9、讲述一下Tomcat8005、8009、8080三个端口的含义?
> 10、什么叫CDN?
> 11、什么叫网站灰度发布?
> 12、简述DNS进行域名解析的过程?
> 13、RabbitMQ是什么东西?
> 14、讲一下Keepalived的工作原理?
> 15、讲述一下LVS三种模式的工作过程?
> 16、mysql的innodb如何定位锁问题,mysql如何减少主从复制延迟?
> 17、如何重置mysql root密码?

#以上关于嵌入式linux下的c语言日志log模块,功能增强的相关内容来源网络仅供参考,相关信息请以官方公告为准!

原创文章,作者:CSDN,如若转载,请注明出处:https://www.sudun.com/ask/91411.html

Like (0)
CSDN的头像CSDN
Previous 2024年6月22日
Next 2024年6月22日

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注