输出重定向及log4cpp第三方库的使用
一、标准输入(stdin)、标准输出(stdout)和标准出错(stderr)
通常情况下,Linux/Unix每个程序在运行时会打开3个流分别用于标准输入(stdin,文件描述符为0)、标准输出(stdout,文件描述符为1)和标准出错(stderr,文件描述符为2)。通常他们会被连接到用户终端。这3个句柄的类型为指向FILE的指针。可被fprintf、fread等函数所使用。
Linux的本质就是一切皆文件,输入输出设备也是以文件的形式存在和管理的。
注意: stderr不进行缓存,stdout则进行行间缓存,具体可参考以下代码:
// 示例代码:不带换行符
#include <stdio.h>
#include <unistd.h>
int main() {
for(int i = 0; i < 5; ++i){
fprintf(stdout, "stdout No.%d", i);
sleep(1); // 休眠一秒
}
fprintf(stdout, "\n"); // 此处一起输出fprintf(stdout,"stdout No.%d);的结果
for(int i = 0; i < 5; ++i) {
fprintf(stderr, "stderr No.%d", i); // 此处每个一秒输出一次fprintf(stderr, "stderr No.%d", i);的结果
sleep(1);
}
return 0;
}
// 示例代码:带换行符
#include <stdio.h>
#include <unistd.h>
int main() {
for(int i = 0; i < 5; ++i){
fprintf(stdout, "stdout No.%d\n", i); // 此处每个一秒输出一次
sleep(1); // 休眠一秒
}
fprintf(stdout, "\n");
for(int i = 0; i < 5; ++i) {
fprintf(stderr, "stderr No.%d\n", i); // 此处每个一秒输出一次
sleep(1);
}
return 0;
}
二、输出重定向
- 运行程序时重定向
符号 | 含义 |
---|---|
> outfile | 标准输出重定向到文件(outfile) |
< infile | 标准输入重定向到文件(infile) |
< infile > outfile | 将标准输入重定向到文件(infile)、标注输出重定向到文件(outfile) |
> outfile 2>&1 | 标准输出和标准出错重定向到文件 |
>> outfile | 以追加的方式输出到文件 |
示例如下:
#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;
}
Shell下输入: ./test > out.txt
- 在代码中重定向
#include <stdio.h>
int main(int argc, char **argv) {
FILE *out = freopen("stdout.txt", "w", stdout);
printf("hello everyone!\n");
return 0;
}
总的来说,stdin,stdout,stderr和终端有密切关系。通常在生产环境下,会将这3个流重定向到其他文件。
打印日志时,通常还会加上如下信息:__ FILE __ 、 __ LINE __ 、 __ FUNCTION __ 、 __ DATE __ 、 __ TIME __ [此处为避免富文本,在__ 与 文字之间加了空格,实际使用时需要去掉]
提示:因为printf涉及到文件,这会引起IO中断。因此执行printf比执行一般指令的效率要低很多。
三、log4cpp的使用
我们使用重定向去输出日志到日志文件中,但是这样的方式还有很多不足,比如无法输出到多个文件、无法设置日志文件的大小、不方便设置日志的级别… log4cpp就提供了很多功能,帮助我们应用程序更方便地记录日志。
- log4cpp的安装
log4cpp链接:log4cpp
步骤如下
- 将权限切换至root
- 输入如下命令下载: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
- 输入如下命令配置log4cpp-1.1.3: ./configure
提示:默认安装至 /usr/local,如需安装到其他位置,则在运行configure时指定 --prefix=< location >,此处安装在项目文件加下的third目录中 [/home/myuser/project/third]- 输入如下命令编译文件:make
- 输入如下命令安装:make install
- 示例程序
文件 Logger.h
#ifndef _SHARE_BIKE_COMMON_LOGGER_H_
#define _SHARE_BIKE_COMMON_LOGGER_H_
#include <string>
#include <log4cpp/Category.hh>
class Logger {
public:
bool init(const std::string& log_conf_file);
static Logger* instance() { return &_instance; }
log4cpp::Category* GetHandle() { return _category; }
protected:
static Logger _instance; // 单例模式
log4cpp::Category* _category;
};
#define LOG_INFO Logger::instance()->GetHandle()->info
#define LOG_DEBUG Logger::instance()->GetHandle()->debug
#define LOG_ERROR Logger::instance()->GetHandle()->error
#define LOG_WARN Logger::instance()->GetHandle()->warn
#endif // !_SHARE_BIKE_COMMON_LOGGER_H_
文件 Logger.cpp
#include "Logger.h"
#include <iostream>
#include <log4cpp/FileAppender.hh> // 此处在CMakeLists.txt中指定包含目录
#include <log4cpp/OstreamAppender.hh>
#include <log4cpp/PatternLayout.hh>
#include <log4cpp/RemoteSyslogAppender.hh>
#include <log4cpp/PropertyConfigurator.hh>
Logger Logger::_instance;
bool Logger::init(const std::string& log_conf_file) {
try {
log4cpp::PropertyConfigurator::configure(log_conf_file);
}
catch (log4cpp::ConfigureFailure& f) {
std::cerr << "load log config file (" << log_conf_file.c_str() << ") failed with result:"
<< f.what() << std::endl;
return false;
}
_category = &log4cpp::Category::getRoot();
return true;
}
文件 main.cpp
#include "Logger.h"
int main(int argc, char** argv) {
if (argc < 2) {
printf("Please input share bike <log file config>.\n");
return -1;
}
if (!Logger::instance()->init(std::string(argv[2]))) {
fprintf(stderr, "init log module failed.\n");
return -2;
}
LOG_INFO("this is a INFO log.\n");
LOG_DEBUG("This is a DEBUG log.\n");
LOG_ERROR("This is an ERROR log.\n");
LOG_WARN("This is a WARN log.\n");
return 0;
}