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

news2024/9/25 15:28:07

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.gz

tar xzvf log4cpp-1.1.3.tar.gz

cd log4cpp

./configure --prefix=/home/xiaomai/shared_bike/third/ --build=arm-linux

make

make 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://www.coloradmin.cn/o/1914815.html

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

相关文章

Hum Brain Mapp:青春期早期的灰质流失可以用白质生长来解释吗?

摘要 关于大脑发育的一个基本谜题是&#xff0c;为什么儿童进入青春期时&#xff0c;灰质(GM)体积明显减少&#xff0c;而白质(WM)体积明显增加。一种流行的理论认为&#xff0c;由于被修剪的突触太小而不足以影响脑灰质体积&#xff0c;因此大脑总体积保持稳定&#xff0c;而…

从0-1搭建一个web项目(页面布局详解)详解

本章分析页面布局详解详解 ObJack-Admin一款基于 Vue3.3、TypeScript、Vite3、Pinia、Element-Plus 开源的后台管理框架。在一定程度上节省您的开发效率。另外本项目还封装了一些常用组件、hooks、指令、动态路由、按钮级别权限控制等功能。感兴趣的小伙伴可以访问源码点个赞 地…

10个图源二维码分享及使用方法

我们曾在《8个图源二维码分享及使用方法》一文中&#xff0c;为你分享了8个图源二维码。 现在在此基础之上新增两个图源二维码&#xff0c;共享10个。 如果你需要这些图源&#xff0c;请在文末查看领取方式。 新增了哪两个图源 增加的两个图源分别是全球10m等高线地图和全球…

【HTML入门】第八课 - 链接的学习(二)

我们上一节学习了&#xff0c;链接的基本知识&#xff0c;有锚点&#xff0c;还有鼠标上移的title属性的作用&#xff0c;这一节&#xff0c;我们继续说链接的知识点。 目录 1 跳转本项目的网页 1.1 修改html文件名 1.2 新建index1.html文件 1.3 修改index1.html文件 1.4…

随身WiFi市场乱象横生,随身WiFi测评最好的格行随身WiFi如何引领变革?

在当今随身WiFi市场乱象频发、内卷严重的背景下&#xff0c;消费者对于产品的性能与商家是否会后台割韭菜依旧存疑&#xff0c;尤其是“随身WiFi到底卡不卡&#xff1f;”的问题&#xff0c;成为了广大消费者关注的重点。然而&#xff0c;在众多品牌中&#xff0c;格行随身WiFi…

浅谈开源项目对于我编程之路的影响

开源项目有哪些机遇与挑战&#xff1f; 随着全球经济和科技环境的快速变化&#xff0c;开源软件项目的蓬勃发展成为了开发者社区的热门话题。越来越多的开发者和企业选择参与开源项目&#xff0c;以推动技术创新和实现协作共赢。你如何看待当前开源项目的发展趋势&#xff1f;…

昇思25天学习打卡营第22天 | Shufflenet图像分类

ShuffleNet图像分类 当前案例不支持在GPU设备上静态图模式运行&#xff0c;其他模式运行皆支持。 ShuffleNet网络介绍 ShuffleNetV1是旷视科技提出的一种计算高效的CNN模型&#xff0c;和MobileNet, SqueezeNet等一样主要应用在移动端&#xff0c;所以模型的设计目标就是利用有…

分布式架构演进之路

文章目录 1 相关概念1.1 基本概念1.2 评价指标 2 架构演进2.1 单机架构2.2 应用数据分离架构2.3 应用服务集群架构2.4 读写分离/主从分离架构2.5 冷热分离架构&#xff08;缓存&#xff09;2.5 分库分表2.6 微服务架构 3 本章总结 1 相关概念 在正式引入架构演进之前&#xff…

不入耳耳机哪个品牌好便宜学生、不入耳式蓝牙耳机推荐

开放式耳机相较于传统的入耳式耳机&#xff0c;极大地提升了用户的听觉享受和佩戴时的持久舒适度。然而&#xff0c;如何找到一款性价比高、品质优良的开放式耳机也是一个不小的问题。不入耳耳机哪个品牌好便宜学生&#xff1f;为了帮助大家更好地做出选择&#xff0c;我结合自…

第5章 IT服务部署实施

第5章 IT服务部署实施 5.1 概述 IT服务部署实施是衔接IT服务规划设计与IT服务运营的中间阶段&#xff0c;负责对服务组件进行客户化&#xff0c;并在充分满足客户要求的前提下&#xff0c;使用标准化的方法管理人员、资源、技术和过程&#xff0c;包括计划、实施和管理生产环…

【排序 - 快速排序】

快速排序&#xff08;Quick Sort&#xff09;是一种高效的排序算法&#xff0c;它基于分治&#xff08;Divide and Conquer&#xff09;的策略。这种排序算法的核心思想是选择一个基准元素&#xff0c;将数组分割成两部分&#xff0c;使得左边的元素都小于等于基准元素&#xf…

二分查找算法【折半查找算法】

二分查找算法 二分查找算法&#xff0c;也称为折半查找&#xff0c;是一种在有序数组中查找特定元素的高效算法。它的工作原理是通过不断地将搜索区间减半来缩小目标值可能存在的范围&#xff0c;直至找到目标值或确定目标值不存在于数组中。二分查找的关键在于每次比较都能排…

【堆 优先队列】1354. 多次求和构造目标数组

本文涉及知识点 堆 优先队列 LeetCode1354. 多次求和构造目标数组 给你一个整数数组 target 。一开始&#xff0c;你有一个数组 A &#xff0c;它的所有元素均为 1 &#xff0c;你可以执行以下操作&#xff1a; 令 x 为你数组里所有元素的和 选择满足 0 < i < target.…

瓦罗兰特游戏帧数低怎么办 瓦罗兰特游戏帧率提不上去怎么解决

瓦罗兰特是一款由拳头游戏&#xff08;Riot Games&#xff09;开发的5v5英雄射击游戏。结合了MOBA元素&#xff0c;每个角色都拥有四个独特的技能&#xff1b;提供了多种游戏模式&#xff0c;如5V5战术射击等&#xff1b;角色和皮肤设计丰富。游戏中&#xff0c;玩家将扮演各具…

《梦醒蝶飞:释放Excel函数与公式的力量》10.3 IMABS函数

第一节 10.3 IMABS函数 10.3.1 函数简介 IMABS函数是Excel中的一个工程函数&#xff0c;用于计算复数的绝对值&#xff08;模&#xff09;。在工程和科学计算中&#xff0c;复数的模是一个重要的概念&#xff0c;表示复数在复平面上到原点的距离。 10.3.2 语法&#xff1a; …

idea控制台乱码问题解决教程

注&#xff1a;按顺序来操作&#xff0c;完成后要重启软件 方案一&#xff1a;修改Tomcat的编码设置 在Tomcat的VM options中添加了-Dfile.encodingUTF-8 方案二&#xff1a;修改IDEA的编码设置 File->Settings->Editor->File Encodings 将Global Encoding、Proj…

顶顶通呼叫中心中间件-打电话没声音检查步骤(mod_cti基于FreeSWITCH)

顶顶通呼叫中心中间件-电话没声音检查步骤(mod_cti基于FreeSWITH) 检查步骤 1、检查配置文件 检查配置文件&#xff1a;打开ccadmin -> 配置文件 -> vars -> external_ip$${local_ip_v4}看一下这个有没有配置正确的外网IP&#xff0c;如果没有配置正确就需要配置正…

【C++】开源:drogon-web框架配置使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍drogon-web框架配置使用。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c;…

神经网络构成、优化、常用函数+激活函数

Iris分类 数据集介绍&#xff0c;共有数据150组&#xff0c;每组包括长宽等4个输入特征&#xff0c;同时给出输入特征对应的Iris类别&#xff0c;分别用0&#xff0c;1&#xff0c;2表示。 从sklearn包datasets读入数据集。 from sklearn import darasets from pandas impor…

Puppeteer 是什么以及如何在网络抓取中使用它 | 2024 完整指南

网页抓取已经成为任何处理网页数据提取的人都必须掌握的一项重要技能。无论你是开发者、数据科学家还是希望从网站收集信息的爱好者&#xff0c;Puppeteer都是你可以使用的最强大工具之一。本完整指南将深入探讨什么是Puppeteer以及如何有效地在网页抓取中使用它。 Puppeteer简…