(自用)共享单车服务器(二) 项目日志

stdin、stdout、stderr

 注意:stderr是不缓存的,stdout则进行行间缓存。接下来我们看下行间缓存的效果,请参考以下代码:

#include "stdio.h"
#include <unistd.h>int main(int argc, char** argv)
{for (int i = 0; i < 5; i++){fprintf(stdout, "This is stdout[%d]", i);sleep(1);}sleep(1);fprintf(stdout,"\n");for (int i = 0; i < 5; i++){fprintf(stderr, "This is stderr[%d]", i);sleep(1);}return 0;}

运行程序会发现,stdout在输出\n时才瞬间输出

而stderr是一个个输出的

概念解释(可以略过)

在通常情况下,Linux/UNIX每个程序在开始运行的时刻,都会打开3个已经打开的stream. 分别用来输入,输出,打印诊断和错误信息。通常他们会被连接到用户终端。这3个句柄的类型为指向FILE的指针。可以被fprintf、fread等函数使用,他们在程序开始启动后,stdin, stdout, and stderr 的文件描述符是 0, 1和2,其它的文件描述符则排在其后。   

Linux的本质就是一切皆文件,输入输出设备也是以文件形式存在和管理的。

重定向

#include <stdio.h>int main(int argc, char** argv)
{printf("welcome to qiniu!\n");fprintf(stdout, "I am martin!\n");perror("are you all ready?\n");fprintf(stderr, "Martin always stay with you!\n");return 0;
}

覆盖式重定向>

默认情况下会使得标准输出重定向到文件:

./test > test.txt

  1. 标准输出重定向到文件:  ./test 1 > testout.txt
  2. 标准错误重定向到文件:  ./test 2 > testerr.txt
  3. 标准输出和标准出错重定向到文件: ./test > test.txt 2>&1
追加式重定向>>

标准输出重定向到文件:  ./test 1 >> testout.txt

输出内容在testout,txt后面追加

程序代码中利用文件流进行重定向

 利用freopen函数

函数原型:

#include<stdio.h>
FILE *freopen(const char *filename, const char *mode, FILE *stream);
  • filename:需要重定向到的文件名或文件路径。
  • mode:代表文件访问权限的字符串,如"r"(只读)、"w"(只写)、"a"(追加)等,这些模式与fopen函数中的模式相同。
  • stream:需要被重定向的文件流指针,通常使用标准流文件(stdin、stdout、stderr)的指针。

返回值

  • 如果成功,freopen函数返回一个新的文件指针,该指针指向被重新打开(或重定向)的文件。
  • 如果失败,返回NULL,并设置全局变量errno为相应的错误代码。

 代码示例:

#include<stdio.h>int main()
{FILE* out = freopen("out.log","w",stdout);printf("%s\n","hello everybody!!!!!");fprintf(stdout,"how are u?");return 0;
}

将标准输出重定向至out.log文件

注意

如果我们实在要用printf或者fprintf去生成日志的话,最好还是加上这些信息,__FILE__ __LINE__ __FUNCTION__, __DATE__, __TIME__。

当然我们一定要明白,printf设计到文件,这会引起IO中断,因此执行printf比一般的指令的效率要低很多。

Log4cpp组件

step 1 : 安装log4cpp
  •   log4cpp的官网是:Log for C++ Project
wget https://nchc.dl.sourceforge.net/project/log4cpp/log4cpp-1.1.x%20%28new%29/log4cpp-1.1/log4cpp-1.1.3.tar.gztar xzvf log4cpp-1.1.3.tar.gzcd log4cpp./configure --prefix=/home/xiaomai/shared_bike/third/ --build=arm-linuxmakemake install

其中./configure --prefix=....后面的路径指的是log4cpp的安装位置,

我们将log4cpp安装至第三方文件夹third

在shared_bike/third/lib文件夹下创建log4cpp文件夹,用来存放log4cpp对应的库文件

外面还剩一个pkgconfig暂时没用

 

step 2 : 包含头文件
#include <log4cpp/Category.hh>
#include <log4cpp/FileAppender.hh>
#include <log4cpp/PatternLayout.hh>
#include <log4cpp/OstreamAppender.hh>
step 3 : 初始化日志输出的目的地(appenders)
//以root身份输出到std::cout
log4cpp::Appender* appender = new log4cpp::OstreamAppender("root", &std::cout);
//以root身份输出到log文件
log4cpp::Appender *appender = new log4cpp::FileAppender("root", "test.log");

appender有以下这些:

log4cpp::FileAppender // 输出到文件

log4cpp::RollingFileAppender // 输出到回卷文件,即当文件到达某个大小后回卷

log4cpp::OstreamAppender // 输出到一个ostream类

log4cpp::RemoteSyslogAppender // 输出到远程syslog服务器

log4cpp::StringQueueAppender // 内存队列

log4cpp::SyslogAppender // 本地syslog

log4cpp::Win32DebugAppender // 发送到缺省系统调试器

log4cpp::NTEventLogAppender // 发送到win 事件日志

  上文,我们说过日志输出到终端或者文件中实际上是很慢的,会引起IO中断,所以我们可以输出到内存里StringQueueAppender,然后从StringQueueAppender输出到其它地方,这样我们的线程执行是比较高效的。

step 4 : 设置日志输出的格式
log4cpp::PatternLayout *patternLayout = new log4cpp::PatternLayout();
patternLayout->setConversionPattern("%d [%p] - %m%n");
appender->setLayout(patternLayout);

日志输出格式控制有: 

%% - 单一的%符号  

%c - 分类

%d - 日期格式:日期格式字符后可能跟着一个用花括号括起来的日期格式说明符。例如,%d{%H:%M:%S,%l} 或 %d{%d %m %Y %H:%M:%S,%l}。如果没有给出日期格式说明符,则使用以下格式:“Wed Jan 02 02:03:55 1980”。日期格式说明符的语法与 ANSI C 函数 strftime 相同,但增加了一个说明符 %l,用于表示毫秒,并用零填充至三位数。

%m - 消息

%n - 换行符

%p - 优先级

%r - 表示自布局创建以来的毫秒数

%R - 表示自1970年1月1日以来的秒数

%u - 表示进程开始以来的时钟周期数

%x - 代表NDC(嵌套的诊断上下文),它是一种用于在多线程环境中跟踪用户特定信息的机制。

%t - 线程名称

默认情况下,PatternLayout的ConversionPattern设置为“%m%n”。

step 5 : 设置类别输出的(category)和日志优先级(priority)

代码示例:

log4cpp::Category &root = log4cpp::Category::getRoot();
root.setPriority(log4cpp::Priority::NOTICE);
root.addAppender(appender);

日志的级别总共有:NOTSET < DEBUG < INFO < NOTICE < WARN < ERROR < CRIT < ALERT < FATAL = EMERG。日志级别的意思是低于该级别的日志不会被记录。

step 6 : 定义一个宏

#define LOG(__level) log4cpp::Category::getRoot() << log4cpp::Priority::__level << __FILE__ << " " << __LINE__ << ": "

当然也可以使用Category定义的函数:

      

 /*** Log a message with the specified priority.* @param priority The priority of this log message.* @param stringFormat Format specifier for the string to write* in the log file.* @param ... The arguments for stringFormat**/virtual void log(Priority::Value priority, const char* stringFormat,...) throw();/*** Log a message with the specified priority.* @param priority The priority of this log message.* @param message string to write in the log file**/virtual void log(Priority::Value priority,const std::string& message) throw();void debug(const char* stringFormat, ...) throw();void debug(const std::string& message) throw();void info(const char* stringFormat, ...) throw();

step 7 : 使用宏定义记录日志
LOG(DEBUG) << "i am happy.";LOG(INFO)  << "oh, you happy, we happy.";LOG(NOTICE)<< "please do not contact me. ";LOG(WARN)  << "i am very busy now.";LOG(ERROR) << "oh, what happed?";

log4cpp实战

在log4cpp的使用过程中可以封装一个单例.

在实际工程上应用,我们是使用日志配置文件去控制日志记录的。接下来让我们先配置一个日志配置文件

1.在conf文件夹下创建配置文件log.conf
#定义Root category的属性
log4cpp.rootCategory=DEBUG, RootLog#定义RootLog属性
log4cpp.appender.RootLog=RollingFileAppender
log4cpp.appender.RootLog.layout=PatternLayout
#log4cpp.appender.RootLog.layout.ConversionPattern=%d{% m-%d %H:%M:%S %l} [%t][%p]%m%n
log4cpp.appender.RootLog.layout.ConversionPattern=%d{%m-%d %H:%M:%S %l} [%t][%p]%m%n
log4cpp.appender.RootLog.fileName=/var/log/qiniu_bike.log
log4cpp.appender.RootLog.maxFileSize=268435456 #256MB
log4cpp.appender.RootLog.fileNamePattern=qiniu_bike_%i.log
log4cpp.appender.RootLog.maxBackupIndex=256

2.对log4cpp进行封装

头文件:

logger.h

#ifndef DISTRIBUTED_LOGGER_H
#define DISTRIBUTED_LOGGER_H#include <iostream>
#include <string>#include <log4cpp/Category.hh>
#include <log4cpp/FileAppender.hh>
#include <log4cpp/OstreamAppender.hh>
#include <log4cpp/PatternLayout.hh>
#include <log4cpp/RemoteSyslogAppender.hh>
#include <log4cpp/PropertyConfigurator.hh>using namespace std;class Logger
{
public:bool init(const string& log_conf_file); //初始化 log.conf 文件 用 log4cpp的接口对 log.conf 文件进行读取、配置static Logger* instance() { return &instance_; }   //单例模式 在任何地方都只有一个对象log4cpp::Category* GetHandle() { return category_; }protected:static Logger instance_;    //单例模式对象log4cpp::Category* category_;   //通过该对象进行日志的操作
};#define LOG_INFO  Logger::instance()->GetHandle()->info     //info  为 category_ 对象的成员函数
#define LOG_DEBUG Logger::instance()->GetHandle()->debug    //debug 为 category_ 对象的成员函数
#define LOG_ERROR Logger::instance()->GetHandle()->error    //error 为 category_ 对象的成员函数
#define LOG_WARN  Logger::instance()->GetHandle()->warn     //warn  为 category_ 对象的成员函数#endif

解析:

1.Logger类中有一个静态单例,我们调用Logger的方法时使用这个静态单例就行

2.category_表示单例的日志类别,在init执行的时候对其进行初始化

3.instance()函数返回这个静态单例

4.宏定义了一些宏函数方便发送message

cpp文件:

logger.cpp

#include "logger.h"Logger Logger::instance_;   //静态成员需要在外部进行定义//初始化 log.conf 文件 用 log4cpp的接口对 log.conf 文件进行读取、配置
bool Logger::init(const string& log_conf_file)
{try{log4cpp::PropertyConfigurator::configure(log_conf_file);    //使用 log4cpp 的接口初始化 log.conf 文件}catch (log4cpp::ConfigureFailure& f)     //如果失败{cerr << " load log config file " << log_conf_file.c_str() << " failed with result : " << f.what() << endl;    // f.what() 函数可以获取错误原因return false;}//如果成功 执行以下代码category_ = &log4cpp::Category::getRoot();return true;
}

PS

1.对静态单例instance_进行了定义:

2.log4cpp::PropertyConfigurator::configure(log_conf_file)方法读取配置文件

 3.改动CMakeLists.txt

修改src/common下的CMakeLists.txt

如图所示,修改两个地方

4.修改main.cpp
#include"iniconfig.h"
#include"configdef.h"
#include"logger.h"int main(int argc,char** argv)
{if (argc != 3)//指定两个配置文件:服务器配置、项目日志配置{printf("Please input shbk<config file path> <log file config>!\n");return -1;}//初始化日志if (!Logger::instance()->init(std::string(argv[2]))){fprintf(stderr,"init log module failed.\n");return -2;}Iniconfig config;if (!config.loadfile(std::string(argv[1]))){/*printf("load project config %s failed.\n",argv[1]);*/LOG_ERROR("load project config % s failed.\n",argv[1]);//上面的代码相当于:Logger::instance()->GetHandle()->error(.......)return -3;}st_env_config conf_args = config.getconfig();LOG_INFO("[database] ip:%s port:%d user:%s pwd:%s db_name:%s [server] port:%d \n", conf_args.db_ip.c_str(), conf_args.db_port, conf_args.db_user.c_str(), conf_args.db_pwd.c_str(), conf_args.db_name.c_str(), conf_args.svr_port);/*printf("[database] ip:%s port:%d user:%s pwd:%s db_name:%s [server] port:%d \n",conf_args.db_ip.c_str(),conf_args.db_port,conf_args.db_user.c_str(),conf_args.db_pwd.c_str(),conf_args.db_name.c_str(),conf_args.svr_port);*/return 0;
}

 PS:

a.argc由原来的两个变为了3个: ./shared_bike <项目配置> <项目日志配置>

b.需要判断Logger是否初始化成功

c.Logger初始化前使用fprintf输出,Logger初始化成功后可以利用Logger的宏寒素进行输出

5.运行程序

在shared_bike/src文件夹下执行

​cmake .
make
sudo ./shared_bike ../conf/shared_bike.ini ../conf/log.conf
​

注意一定要在root权限下运行shared_bike,否则没有/var/log的写入权限。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3224779.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

LNMP搭建Discuz和Wordpress

1、LNMP L:linux操作系统 N&#xff1a;nginx展示前端页面web服务 M&#xff1a;mysql数据库&#xff0c;保存用户和密码&#xff0c;以及论坛相关的内容 P&#xff1a;php动态请求转发的中间件 数据库的作用&#xff1a; 登录时验证用户名和密码 创建用户和密码 发布和…

接口测试基础知识(url,http,接口测试流程)

接口测试的节点&#xff1a;在功能测试时&#xff0c;我们需要等待前端与后端都开发完成之后&#xff0c;才能进行界面的功能测试&#xff0c;接口测试则只需要在后端开发完成&#xff0c;即可进行接口测试&#xff0c;如下图表示&#xff1a; 接口测试的节点.png 学习路径.png…

【PTA天梯赛】L1-003 个位数统计(15分)

作者&#xff1a;指针不指南吗 专栏&#xff1a;算法刷题 &#x1f43e;或许会很慢&#xff0c;但是不可以停下来&#x1f43e; 文章目录 题目题解总结 题目 题目链接 题解 使用string把长度达1000位的数字存起来开一个代表个位数的数组 a[11]倒序计算最后一位&#xff0c;…

YOLOv5、v7、v8如何修改检测框文字颜色和大小

YOLOv5和YOLOv8默认的标签文字颜色为白色&#xff0c;但是在亮度较大的图片中文字不明显&#xff0c;就需要对标签文字的颜色进行修改 一、YOLOv5 打开X:\Anaconda\envs\your-env\Lib\site-packages\ultralytics\utils\plotting.py X代表你的anaconda安装的盘&#xff0c;yo…

【面试题】正向代理和反向代理的区别?

正向代理&#xff08;Forward Proxy&#xff09;和反向代理&#xff08;Reverse Proxy&#xff09;是两种常见的代理服务器类型&#xff0c;它们在网络通信中扮演着不同的角色&#xff0c;具有不同的功能和应用场景。 一、正向代理 1. 定义与位置 正向代理是位于客户端和目标…

修改服务器挂载目录

由于我们的项目通常需要挂载一个大容量的数据盘来存储文件数据&#xff0c;所以我们每台服务器都需要一个默认的挂载目录来存放这些数据&#xff0c;但是由于我们的误操作&#xff0c;导致挂载目录名字建错了&#xff0c;这时候后端就读不到挂载目录了&#xff0c;那我们我们的…

机器人三定律及伦理分析

全世界的机器人定律并没有一个统一的标准或体系&#xff0c;但是在科学文献中&#xff0c;最广为人知的是由科幻小说家阿西莫夫提出的“机器人三定律”。本文将以这些定律为基础&#xff0c;分析现有的机器人伦理和实际应用中的问题&#xff0c;给出若干实例&#xff0c;并对相…

从微软 Word 中提取数据

从 Microsoft Word 文档中提取数据可以通过编程来实现&#xff0c;有几种常见的方法&#xff0c;其中之一是使用 Python 和 python-docx 库。python-docx 是一个处理 .docx 文件&#xff08;Microsoft Word 文档&#xff09;的 Python 库&#xff0c;可以读取和操作 Word 文档的…

windows USB 设备驱动开发-USB带宽

本文讨论如何仔细管理 USB 带宽的指导。 每个 USB 客户端驱动程序都有责任最大程度地减少其使用的 USB 带宽&#xff0c;并尽快将未使用的带宽返回到可用带宽池。 在这里&#xff0c;我们认为USB 2.0 的速度是480Mbps、12Mbps、1.5Mbps&#xff0c;这分别对应高速、全速、低速…

第4章 课程发布:模块需求分析,课程预览(模板引擎 静态页面),课程审核,课程发布(分布式事务,页面静态化:熔断降级),课程搜索(es索引)

1 模块需求分析 1.1 模块介绍 课程信息编辑完毕即可发布课程&#xff0c;发布课程相当于一个确认操作&#xff0c;课程发布后学习者在网站可以搜索到课程&#xff0c;然后查看课程的详细信息&#xff0c;进一步选课、支付、在线学习。 下边是课程编辑与发布的整体流程&#…

Python数据处理必备:如何高效校验各种空值?

更多Python学习内容&#xff1a;ipengtao.com 在编程中&#xff0c;处理空值是一个常见且重要的任务。空值可能会导致程序异常&#xff0c;因此在进行数据处理时&#xff0c;必须确保数据的有效性。Python 提供了多种方法来处理不同数据对象的空值校验。本文将详细介绍如何对Py…

数据结构作业/2024/7/9

2>实现双向循环链表的创建、判空、尾插、遍历、尾删、销毁 fun.c #include "head.h" //1.双向循环链表的创建 doubleloop_ptr create_list() …

【自用】【高昆轮概率论与数理统计笔记】2.1 分布函数的概念与性质

不定期更新&#xff0c;前面的章节会在学完后补回来&#xff0c;重新学学概率&#xff0c;当年考研考的数学二&#xff0c;没有概率基础&#xff0c;想自己补补&#xff0c;视频课是高昆轮老师讲的浙大四版概率论教材的视频课&#xff0c;地址&#xff1a; 第一章&#xff1a;h…

类似评论、省市区这种具有层次结构的数据表怎么设计?

业务功能模块 评论、回复模块省市区表 设置一个给每个数据设置一个parent_id 例如&#xff1a; 某个视频下a写了条评论&#xff0c;那a的parent_id就是0;b回复了a&#xff0c;那b的parent_id就是a的id;c回复了b&#xff0c;那c的parent_id就是b的id; 这样&#xff0c;所有评论…

全终端自动化测试框架wyTest

突然有一些觉悟&#xff0c;程序猿不能只会吭哧吭哧的低头做事&#xff0c;应该学会怎么去展示自己&#xff0c;怎么去宣传自己&#xff0c;怎么把自己想做的事表述清楚。 于是&#xff0c;这两天一直在整理自己的作品&#xff0c;也为接下来的找工作多做点准备。接下来…

leetcode--从中序与后序遍历序列构造二叉树

leeocode地址&#xff1a;从中序与后序遍历序列构造二叉树 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵树的后序遍历&#xff0c;请你构造并返回这颗 二叉树 。 示例 1: 输入&#xff1a;inorder …

三级_网络技术_12_路由设计技术基础

1.R1、R2是一个自治系统中采用RIP路由协议的两个相邻路由器&#xff0c;R1的路由表如下图(a)所示&#xff0c;当R1收到R2发送的如下图(b)的(V.D)报文后&#xff0c;R1更新的4个路由表项中距离值从上到下依次为0、3、3、4 那么&#xff0c;①②③④可能的取值依次为()。 0、4、…

Git命令常规操作

目录 常用操作示意图 文件的状态变化周期 1. 创建文件 2. 修改原有文件 3. 删除原有文件 没有添加到暂存区的数据直接 rm 删除即可&#xff1a; 对于添加到暂存区的数据 文件或目录&#xff1a; 4. 重命名暂存区数据 5. 查看历史记录 6. 还原历史数据 恢复过程的原…

[安洵杯 2019]easy_serialize_php

源码&#xff1a; <?php$function $_GET[f];function filter($img){$filter_arr array(php,flag,php5,php4,fl1g);$filter /.implode(|,$filter_arr)./i;return preg_replace($filter,,$img); }if($_SESSION){unset($_SESSION); }$_SESSION["user"] guest; …

240709_昇思学习打卡-Day21-文本解码原理--以MindNLP为例

240709_昇思学习打卡-Day21-文本解码原理–以MindNLP为例 今天做根据前文预测下一个单词&#xff0c;仅作简单记录及注释。 一个文本序列的概率分布可以分解为每个词基于其上文的条件概率的乘积 &#x1d44a;_0:初始上下文单词序列&#x1d447;: 时间步当生成EOS标签时&a…