友好的开源项目,不仅仅是上传代码,更是开源设计思路,开箱即用,让开发者无痛/快速的获得新思路,新方法和新工具。为了让
GuiLite 对所有开发者都足够友善,我们又作了一些更新。

Log4cpp 是C++开源日志库,为 C++
应用程序开发中提供了日志的追踪和调试功能,基于 LGPL 开源协议,移植自
java 的日志项目 log4j, 并在 api 上保持了一致性。

在使用了
RTKLIB开源包自带的
rtkplot.exe后,知道了它所具有的功能,就想着如何模仿它做出一个
demo。一开始看的是之前下载的 2.4.2版本的 RTKLIB,里面是使用
Delphi开发的。由于我现在对 Qt比较熟悉,所以想着使用 Qt框架来开发这个
demo。在看源码的过程中,阴差阳错之间又去官网上重新下载了一次源码包,结果发现最新的
2.4.3版本里面就带有相关 App程序的 Qt实现。这样的话,就可以直接阅读现成的
Qt源码了。不过首先需要解决的问题是,要尝试将所给的源代码编译成功。

解释核心:大家反应最为强烈的代码注释问题,得到了比较大的改善,增加了如下文档更新:

1. 环境

  • windows 7 64b + Qt5.7 + MinGW530
  • log4cpp: 1.1.3

1.直接在 Qt
creator中打开rtkplot_qt.pro文件,配置项目属性,进入正常的工作界面。直接进行调试,则会报出如下错误。

  1. 完成 display.cpp
    的函数注释;该文件,负责对显示设备的适配和GUI的移植;
  2. 完成 surface.cpp 的函数注释;该文件,负责底层渲染及图层管理;
  3. 完成 wnd.cpp
    的函数注释;该文件,负责基本窗口的管理和基本功能的实现;

2. 下载

  • 下载地址:
  • log4cpp主页:

图片 1

相信这3个文件的代码注释,能帮助大家快速领会 GuiLite 核心运作机制。

3.特点

  • 可扩展,提供了多种记录方式:文件、命令行、内存、win事件日志…
  • 可以动态控制日志级别,进行动态调整
  • 多语言支持,C(log4c), C++(log4cpp/log4cplus), java(log4j),
    python(log4p))
  • 平台无关性,源码编译后可以运行在各大主流操作系统上

图 1

简化移植:为了再次提高移植效率,我们作了以下调整:

4. 编译

打开工程后,可以发现包含了多个工程编译项目,不过基本都是基于VS的项目,不是我想要的,需要手动转化成
Qt 项目,也就是编程 pro 形式。

拷贝源码目录下的 include 和 Src 文件夹到自己新建的工程中,主要使用这两部分,其余的先不管。

编写 pro 文件

QT       = core

TEMPLATE = lib
TARGET = log4cpp
INCLUDEPATH += $$PWD/.. 
               $$PWD/../../boost/ 
DEPENDPATH += $$PWD/..

DEFINES += LOG4CPP_HAVE_BOOST

SOURCES += 
    Appender.cpp 
    AppenderSkeleton.cpp 
    AppendersFactory.cpp 
    ......

HEADERS += 

win32:{ LIBS += -L -lwsock32 -lws2_32   }

上述引入了 `boost 库路径,并添加了wsock32库!否则编译会出现错误。

编译成功后会生成对应的库文件(静态库只有629KB):

图片 2

一开始以为是
VS2015在安装时出了问题,网上找的解决方案也都是说配置环境变量。其实不然,上图中已经说明是什么问题了,是
srcdebug目录下缺失 libRTKLib.a文件。

  1. 所有 HelloXXX 实例的 GuiLite 头文件由 24 个,降低到 1 个
    GuiLite.h(由 1h-1cpp.sh 脚本生成)
  2. HelloParticle、HelloWidgets、HelloGuiLite 支持 GuiLite
    源码级的调试(删除了 GuiLite 库,由GuiLite-xxx.cpp 源码代替 –
    该文件由 1h-1cpp.sh 脚本生成)
  3. 解决了个别 Ubuntu 虚拟机环境下,UI 无法显示的问题

5. 使用

  1. .a文件是
    Linux系统下的静态库文件,类似于 Windows系统下的
    .lib文件;同样的,Linux系统下的 .so文件是动态链接库文件,相当于
    Windos系统下的 .dll文件。另外,之前在阅读
    rtkplot_qt的源码时就发现,其实里面很多数据和算法的代码都是使用
    src下的相应代码,所以这些可视化程序是需要调用
    src库中的代码的。而调用方式就是通过将
    src中的源码编译成静态链接库,供这些可视化程序使用。这一点在rtkplot_qt.pro文件中是有所体现的,如下图所示。

虽然,GuiLite
可移植性的口碑一直很好;但降低文件/代码数量的事情,我们一直乐此不疲。

5.1 基本概念

  • Catgory (种类)
  • Appender (附加目的地)
  • Layout (布局)
  • Priority (优先级)
  • DNC (嵌套的诊断上下文)

图片 3

牵手 Qt:即使在 GuiLite 开发群,依然有很多 Qt 的粉丝,考虑到大家对 Qt
IDE 的钟爱,我们作了以下工作:

5.1.1 Category

Category 级别,优先级可以用来控制日志输出的数量,比如配置级别是 DEBUG
,则任意 log都可以被打印出来,如果配置级别是
ERROR,则只有高于这个级别的才能输出.

Category 真正完成了日志记录功能.

优先级: DEBUG < INFO < WARN < ERROR < FATAL

图 2

  1. 为 HelloGuiLite、HelloWidghets 增加了 Qt
    工程(BuildQt),大家以后可以在 Qt creator 的开发环境中对 GuiLite
    进行源码级开发和调试
  2. HelloGuiLite、HelloWidghets 可以编译为纯正的 Qt
    App,一些同学已经实现了 GuiLite 与 Qt
    原生接口的互相调用,制作了很多惊人的效果
  3. 虽然 HelloGuiLite、HelloWidghets 是普通 Qt
    App,但资源占用率,以及运行效率有着很大的提高

5.1.2 Appender

Appender(输出源) 负责将 layout 附加的消息
记录到某个输出设备上(比如:文件,socket).

多个 Appender 可以附加到一个 Category 上,这样一个消息就能同时输出到多个设备上

Appender设备:

  • log4cpp::IdsaAppender // 发送到IDS或者logger, 详细见
  • log4cpp::FileAppender // 输出到文件
  • log4cpp::RollingFileAppender //
    输出到回卷文件,即当文件到达某个大小后回卷
  • log4cpp::OstreamAppender // 输出到一个ostream类
  • log4cpp::RemoteSyslogAppender // 输出到远程syslog服务器
  • log4cpp::StringQueueAppender // 内存队列
  • log4cpp::SyslogAppender // 本地syslog
  • log4cpp::Win32DebugAppender // 发送到缺省系统调试器
  • log4cpp::NTEventLogAppender // 发送到win 事件日志

3.既然知道了问题是srcdebug目录下缺失
libRTKLib.a文件,那就想办法去找到libRTKLib.a文件并放置到该目录下即可。打开文件夹,发现 src目录下根本就没有
debug文件夹,所以我们就先新建一个 debug文件夹。接着在整个
RTKLIB源码包中搜索libRTKLib.a文件,结果是都没有找到。既然没有现成的,那就可能需要我们自己制作一个libRTKLib.a文件了。

感谢开发群中 Qt 大神的代码贡献,希望这种新方式,对大家改善现有 Qt
项目有所帮助,相信混合编程,能给大家带来更多的选择。

5.1.3 Layout

layout
类即布局,其实就是用来控制日志消息以怎么样的格式显示(这些开源库中类名比较怪,就不能直接点么,不就是日志格式么,搞一个
layout )。主要有一下几种格式:

  • log4cpp::BasicLayout 时间戳 + 优先级 + 类别 + NDC 标签 + 日志消息

  • log4cpp::PatternLayout 类似C语言中的 printf
    格式化输出,可以指定格式输出

  • log4cpp::SimpleLayout 优先级 + 日志信息

以上日志格式不太满意,所以需要对其进行升级(自定义),没有办法,世上很难遇上自己满意的东西,遇上喜欢的还要自定义。以下格式应该算是大部分人能接收到的格式:

[2017-12-03 15:10:50:393|ERROR : [fun: int main(int, char**)] [line:32] [time: "15:10:50.389" ] Msg: error test

一般的日志消息需要包含时间戳,消息类型,以及打印该消息的具体文件中函数名称、行数,消息内容。这样的结构基本可以满足日常开发需求。

继承关系:

图片 4

4.前面说了,libRTKLib.a文件就是将
src源码编译成的静态链接库文件,而
src文件夹下正好有一个src.pro文件。打开该文件,发现如下图所示代码

最后,也预告一下,GuiLite
即将带来新的实例,该实例支持:Windows,Linux,Qt
及单片机;具体效果如下。有兴趣的同学,请保持关注,谢谢

5.1.4 Priority

优先级在 Category中描述过.

图片 5

图片 6

5.1.5 DNC

暂时不了解

图 3

(文/开源中国)    

5.2 配置文件

使用 log4cpp
有两种方式,一种是自己手动编写配置步骤,比较繁琐,另一种是直接通过配置文件即可完成.

图片 7

上图中的代码就是说本次
Qt项目的目的是生成一个名为
RTKLib的静态链接库文件。不过需要注意的是,windows系统下生成的是
.lib文件!直接调试该项目,报出如下错误

5.3 使用

由于 log4cpp 类名较长,操作不便,所以进行二次分装比较方便

  • 读取配置文件
  • 实例化 category对象
  • 封装日志输出方法

图片 8

5.3.1 读取配置文件

bool Log4CppUtility::loadConfigFile(QString strConfigFile, QString strParentPath)
{
    try
    {
        QByteArray dataConfigFile = strConfigFile.toLocal8Bit ();
        QByteArray dataParentPath = strParentPath.toLocal8Bit ();

        log4cpp::PropertyConfigurator::configureEx ( dataConfigFile.data (),
                                                     dataParentPath.data ());
    }
    catch ( log4cpp::ConfigureFailure & e)
    {
        log4cpp::Category::getRoot ().warn (e.what ());
//        qDebug()<<"configureEx problem:"<<e.what ();
        return false;
    }

    return true;
}

图 4

5.3.2 实例化Category

bool Log4CppUtility::outputLog(xx,xxx,xxx,...)
{
    log4cpp::Category & category = strCategoryName.isEmpty ()?
                log4cpp::Category::getRoot ():
                log4cpp::Category::getInstance ( strCategoryName.toStdString () );
    //日志等级区分
    switch (Level) {
    case LP_EMERG:
    {
        category.emerg ( strMsg.toStdString () );
        bOutputMsg = category.isEmergEnabled ();
    }
        break;
    case LP_ALERT:
    {
        category.alert ( strMsg.toStdString () );
        bOutputMsg = category.isAlertEnabled ();
    }
        break;

     //其他类似......
}

再查看如下图所示的编译信息后,发现问题应该是编译参数“-Wno-unused-but-set-variable”
无效导致的。百度之后,该编译参数的原意是为了忽略掉那些设置了而没有使用到的参数,但是如下图所示的写法似乎有问题,应该改成图
7所示的写法。

5.3.1 日志输出方法

编写一个宏定义

#define LOGERROR(format, ...) Log4CppUtility::formateLog(__FILE__, __PRETTY_FUNCTION__, __LINE__, CATEGORYNAME, Log4CppUtility::LP_ERROR,  format, ##__VA_ARGS__);

//其他类似...

调用:

#include <QDebug>
#include <QCoreApplication>
#include "loginclib.h"

#include <QThread>

class MyThread:public QThread
{
public:
    MyThread()
    {
        m_nCount = 10000;
    }
    ~MyThread(){}

protected:
    virtual void run()
    {
        while (m_nCount)
        {
            LOGERROR("error:"+QString::number (m_nCount));
            m_nCount--;

            msleep (4);
        }
    }

private:
    int m_nCount;
};

int main(int argc, char *argv[])
{
    QCoreApplication app(argc ,argv);
    qDebug()<<"=====start test log=====";

    QString strFilePath = QCoreApplication::applicationDirPath ()+"/";

    QString logConfig = "LogProperty.conf";
    logConfig = strFilePath + logConfig;

    if ( !Log4CppUtility::loadConfigFile ( logConfig ))
    {
        qDebug()<<"set path error!";
        return 0;
    }

    MyThread thread;
    thread.start ();

    qDebug()<<"=====end test log=====";


    int ret = app.exec ();

    Log4CppUtility::shutDown ();

    return ret;
}

新建一个线程不断的输出日志到文件(这里只显示5条记录).

使用是很方便的:

LOGERROR("this is error info");

实际结果:

图片 9

 

图片 10


5编译参数“-Wno-unused-but-set-variable” 所导致的的错误

图片 11


6编译参数“-Wno-unused-but-set-variable” 原本的书写方式

图片 12


7编译参数“-Wno-unused-but-set-variable” 修改之后正确的书写方式

5.清除原项目后执行
qmake,再重新调试一次,即可成功运行,弹出如下所示的对话框。

图片 13

图 8

一开始我也很懵逼,咋会弹出这个界面。我知道这个界面就是相当于
C++控制台程序可以通过命令行给主函数 main传递参数,但是
src文件夹下根本就没有
main函数,里面都是函数的源码,并没有调用这些源码的函数。百度之后发现,这是因为该项目本身就是为了编译静态库文件,项目中没有可执行的主函数,所以才会弹出这个界面来选择可执行程序并输入参数。至于所需要的静态链接库文件libRTKLib.lib,已经生成在了build-src-Desktop_Qt_5_8_0_MSVC2015_64bit-Debugdebug目录下。将该文件拷贝到srcdebug目录下,在重新编译rtkplot_qt项目之前,需要将图
2中的代码改成下图所示。这是因为现在生成的静态库是
.lib文件,而源码的环境可能是 Linux系统,所以用的是
.a文件。需要对代码稍作修改。

图片 14

图 9

清除后执行
qmake,再重新调试,又会报出如下错误

图片 15

图 10

6.对于前两个错误,直接在
plotcmn.cpp文件中使用该函数报错的地方,再添加第三个参数
ndec就可以了。而第三个错误,则要将字符串指针数组
name重新定义成常量字符串指针数组(const
char*)。修改之后再次编译,又会报出如下错误。

图片 16

图 11

这个问题初看很奇怪,因为报“无法解析的外部符号”一般都是链接库出了问题,而我们之前已经添加了链接库文件libRTKLib.lib。一开始我不知道要自己编译libRTKLib.lib文件时,曾经把图
2中的代码直接注释掉,想试一下程序能不能运行,结果报了一大堆“无法解析的外部符号”的问题。而现在在添加了链接库之后还报了这个问题,不过只报了这两个“无法解析的外部符号”。难道是libRTKLib.lib里的确没有包含
input_tersus和 input_tersusf这两个函数?查找之后发现这两个函数是在
src/rcv/tersus.c文件中的,打开 src.pro文件,发现其
SOURCES中真的还就只缺失了tersus.c这个文件。在 SOURCES中添加完该文件后,重新编译
src项目的静态库文件。再将第二次得到的libRTKLib.lib替换掉
src/debug目录下的相应文件。

7.重新运行
rtkplot_qt项目,就可以成功运行啦。运行结果如下图所示

图片 17

图 12
rtkplot_qt成功运行后的主界面

Bingo!