/**
打印日志的示例。
使用:
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