IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 项目日志/stdin stdout stderr/重定向/Log4cpp -> 正文阅读

[系统运维]项目日志/stdin stdout stderr/重定向/Log4cpp

stdin/stdout/stderr


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

Linux的本质就是一切皆文件,输入输出设备也是以文件形式存在和管理的。 ??
注意: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);
        //5秒后,全部直接打印
}

sleep(1);

for(int i = 0; i < 5; i++)
    {
        fprintf(stderr, "This is stderr[%d]", i);
        sleep(1);
        //每隔一秒打印一次
    }
    return 0;
}

加上换行符后:

#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]\n", i);
        sleep(1);
        
}

sleep(1);

for(int i = 0; i < 5; i++)
    {
        fprintf(stderr, "This is stderr[%d]\n", i);
        sleep(1);
    }


    return 0;
}

上面两端代码就是在打印的时候加上了"\n"而已,但是效果完全不一样。

思考:很多时候我们会用printf打印信息来调试程序,但是如果终端关掉了,那怎么显示printf的调试信息呢?

重定向

将日志不写控制台,写入文件中

#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;
}

编译好后(gcc -o test test.c),我们试试下面不同的运行方式: ./test > test.txt

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

标准输出和标准出错重定向到文件
./test > test.txt 2>&1

当然除了使用“>”重定向外,我们还可以试下“>>”,>>是以附加的方式(尾部追加)重定向到文件中,另外我们还可以在代码中实现重定向,
比如:

#include <stdio.h>
 
int main(void)
{
    FILE *out = freopen("stdout.txt", "w", stdout);
    printf("%s\n", "hello verybody!!!");
 
    return 0;
}

总的来说,stdin,stdout和stderr还是和终端有密切关系,通常在生产环境时,会将这3个流重定向到其它文件。
如果我们实在要用printf或者fprintf去生成日志的话,最好还是加上这些信息

__FILE__ __LINE__ __FUNCTION__, __DATE__, __TIME__

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

Log4cpp

在上文,我们使用重定向去输入日志到日志文件中,但是这样一种处理方式,还是有很多不足,比如没有办法输出到多个文件,没有办法设置日志文件大小,不太方便设置日志的级别,如果想远程输出到日志服务器怎么办呢?等等这些问题怎么解决呢?
??log4cpp就提供了很多的功能,帮助我们应用程序更方便地记录日志,OK,我们先来看一个简单的实现吧:

step 1 : 安装log4cpp

log4cpp的官网是:http://log4cpp.sourceforge.net/

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-1.1.3
./configure  --prefix=自己想安装到的 绝对路径/home/.../...  
make
make install

step 2 : 包含头文件

#include <log4cpp/Category.hh>
#include <log4cpp/FileAppender.hh>
#include <log4cpp/PatternLayout.hh>
#include <log4cpp/OstreamAppender.hh>

step 3 : 初始化日志输出的目的地(appenders)

// 输出到std::cout  "root" : 用户身份
log4cpp::Appender *appender = new log4cpp::OstreamAppender("root", &std::cout);
// 输出到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);

日志输出格式控制有:

PatternLayout supports following set of format characters:
%% - a single percent sign //转义 输出一个百分号
%c - the category  //日志类别 区分不同的模块
%d - the date\n Date format: The date format character may be followed by a date format specifier enclosed between braces. For example, %d{%\H:%M:%S,%l} or %d{%\d %m %Y %H:%\M:%S,%l}. If no date format specifier is given then the following format is used: "Wed Jan 02 02:03:55 1980". The date format specifier admits the same syntax as the ANSI C function strftime, with 1 addition. The addition is the specifier %l for milliseconds, padded with zeros to make 3 digits.
//时间  (时分秒)

%m - the message //消息
%n - the platform specific line separator  //换行(平台特定的线分隔符)
%p - the priority  //优先级
%r - milliseconds since this layout was created.  //该布局创建以来的毫秒数。
%R - seconds since Jan 1, 1970  //时间戳
%u - clock ticks since process start  //自进程启动以来,消耗的时钟
%x - the NDC
%t - thread name  //线程名
By default, ConversionPattern for PatternLayout is set to "%m%n". //默认

step 4 : 设置类别输出的(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 5 : 定义一个宏

#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 6 : 使用宏定义记录日志

 	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示例:
log.conf

#定义Root category的属性
log4cpp.rootCategory=DEBUG, RootLog #级别大于等于DEBUG的都会输出

#定义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/shared_bike.log  #输出的文件名
log4cpp.appender.RootLog.maxFileSize=268435456 #256MB
log4cpp.appender.RootLog.fileNamePattern=shared_bike%i.log
log4cpp.appender.RootLog.maxBackupIndex=256  #大小

Logger.h

#ifndef __LOGGER_H__
#define __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* GetHadle() //获取
    {
        return category_;
    }    

protected:
    static Logger instance_;
    log4cpp::Category* category_;
};

#define LOG_INFO Logger::instance()->GetHadle()->info
#define LOG_DEBUG Logger::instance()->GetHadle()->debug
#define LOG_ERROR Logger::instance()->GetHadle()->error
#define LOG_WARN Logger::instance()->GetHadle()->warn

#endif

Logger.cpp

#include "Logger.h"

#include <iostream>
#include <log4cpp/FileAppender.hh>
#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;
}

src/CMakeLists.txt

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

#工程名
PROJECT(shared_bike)

#将指定目录添加到编译器的头文件搜索路径之下
INCLUDE_DIRECTORIES(../third/include)
INCLUDE_DIRECTORIES(./common)

#将指定目录添加到需要链接的库文件目录之下
LINK_DIRECTORIES(../third/lib/iniparser)
LINK_DIRECTORIES(../third/lib/log4cpp)
LINK_DIRECTORIES(./common)

#搜集所有在指定路径下的源文件, 将输出结果列表储存在指定的变量中
#内置变量: CMAKE_SOURCE_DIR 定义了顶级CMakeLists.txt 所在的文件夹, PROJECT_SOURCE_DIR 定义了包含最近的project()命令的CMakeLists.txt 所在的文件夹
aux_source_directory(${PROJECT_SOURCE_DIR}  SOURCE_FILES)

#使用给定的源文件,为工程引入一个可执行文件
ADD_EXECUTABLE(shared_bike ${SOURCE_FILES})

SET(CMAKE_CXX_FIAGS "${CMAKE_CXX_FIAGS} -rdynamic -Wall -m64 -pipe -std=c++0x -lrt -Wno-reorder -Wdeprecated-declarations")

TARGET_LINK_LIBRARIES(shared_bike iniparser)
#TARGET_LINK_LIBRARIES(shared_bike log4cpp)
target_link_libraries(shared_bike liblog4cpp.a)
TARGET_LINK_LIBRARIES(shared_bike pthread)
TARGET_LINK_LIBRARIES(shared_bike common)
#增加子目录
ADD_SUBDIRECTORY(common)

SET(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR})
#安装
INSTALL(TARGETS shared_bike DESTINATION bin)

src/common/CMakeLists.txt

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

#搜集所有在指定路径下的源文件, 将输出结果列表储存在指定的变量中
aux_source_directory(.  SOURCE_COMMON_FILES)

#add_library(<name> [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] source1 [source2 ...])
#(自定义的库名 ${由这些文件生成})
#构建库供他人模块使用
ADD_LIBRARY(common ${SOURCE_COMMON_FILES})

#用来显示的定义变量
SET(CMAKE_CXX_FIAGS "${CMAKE_CXX_FIAGS} -rdynamic -Wall -m64 -pipe -std=c++0x -lrt -Wno-reorder -Wdeprecated-declarations")

#将指定目录添加到编译器的头文件搜索路径之下
INCLUDE_DIRECTORIES(../../third/include)

#将指定目录添加到需要链接的库文件目录之下
LINK_DIRECTORIES(../../third/lib/iniparser)
LINK_DIRECTORIES(../../third/lib/log4cpp)

#将目标文件与库文件进行链接
TARGET_LINK_LIBRARIES(common iniparser)
TARGET_LINK_LIBRARIES(common log4cpp)
TARGET_LINK_LIBRARIES(common pthread)
TARGET_LINK_LIBRARIES(common dl)

输出对照:
在这里插入图片描述

时/分/秒/毫秒  [线程名] [优先级]

这里可以自定义:
在这里插入图片描述

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-03-03 16:53:58  更:2022-03-03 16:57:29 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 3:15:18-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码