Monday 30 December 2013

Use Qt Resource System to package resource files into binary

Why need Qt Resource System? It can help us to package all resource files(image, qml, etc.) into our app's executable. 
To do this, you need to follow below steps:
1. Create qrc file and configure it in pro file
I have resources.qrc file, in my gui.pro file I configure it like so: (see red words)
QT += qml quick
TARGET = gui
!android: !ios: !blackberry: qtHaveModule(widgets): QT += widgets
include(src/src.pri)
RESOURCES += \
    resources.qrc

HEADERS +=

2.  Edit resources.qrc file to contain all resource files in project folder
In my project folder, I have some qml files located under qmls folder and its sub folders, the resources.qrc is in the project folder. Some image files are under images folder. See my folder tree below:
├── gui.pro
├── images
│   ├── header.png
│   └── selectedrow.png
├── qmls
│   ├── a.qml
│   ├── main.qml
│   ├── menu.qml
│   ├── props
│   │   ├── input
│   │   │   └── b.qml
│   │   ├── output
│   │   └── processor
│   ├── README
├── resources.qrc
└── src
    ├── main.cpp
    └── src.pri
Add all these resource files into resources.qrc manually.
<!DOCTYPE RCC>
<RCC version="1.0">
  <qresource prefix="/">
    <file>qmls/main.qml</file>
    <file>qmls/menu.qml</file>
    <file>qmls/a.qml</file>
    <file>qmls/props/input/b.qml</file>
    <file>images/selectedrow.png</file>
    <file>images/header.png</file>
  </qresource>
</RCC>
All above configured resource files will be compiled into app executable.

3. In main.cpp, use URL to load main.qml file
     QQmlApplicationEngine engine(QUrl("qrc:/qmls/main.qml"));
4. In qml files, use relative path to load other resource files, for example:
    Button {
        id: saveMenu
        anchors.left: openMenu.right
        text: "Save"
        iconSource: "../images/selectedrow.png"

That's all. When compiling your app, Qt will create a qrc_resources.cpp file which contain static C++ array to contain the compressed data from resource files. Below is code snippet from my project's qrc_resources.cpp file.
#include <QtCore/qglobal.h>
static const unsigned char qt_resource_data[] = {
  // /home/dean/work/arcgis-3d-scene-builder/source/builder/gui/qmls/main.qml
  0x0,0x0,0x2,0x38,
  0x0,
  0x0,0xa,0x71,0x78,0x9c,0xdd,0x55,0xdf,0x8f,0x9a,0x40,0x10,0x7e,0xe7,0xaf,0x98,
  0xf8,0xd8,0x26,0x4,0xb1,0xb5,0x17,0x12,0xd3,0xf4,0x4c,0xd3,0x36,0x69,0x93,0xb3,
  0x5e,0xda,0xbe,0xae,0xcb,0xa8,0x9b,0x2e,0x2c,0xdd,0x1d,0xe2,0x99,0x8b,0xff,0x7b,
  0x17,0x4,0x14,0x1,0x83,0xda,0x7b,0xe9,0x3e,0x0,0x3b,0xf3,0xed,0xfc,0xf8,0x76,
  0x66,0x10,0x51,0xa2,0x34,0xc1,0x8c,0x66,0xa9,0xe0,0xbf,0xc1,0x77,0x87,0x8e,0xa8,
...

Sunday 29 December 2013

Use rectangles to create basic layout

Rectangle is a basic Qml type, I will introduce how to embed two rectangles in one rectangle in this article. First, let's see the screenshot:

The red rectangle has two children, one is blue, another one is green.
Create a test.qml file like so:
import QtQuick 2.0
Rectangle {
    Rectangle {
        id: r1
        anchors.left: parent.left
        anchors.leftMargin: 5
        anchors.top: parent.top
        anchors.topMargin: 5
        width: 107
        height: 309
        color: "red"
        Rectangle {
            id: r11
            anchors.left: parent.left
            anchors.leftMargin: 5
            anchors.top: r1.top
            anchors.topMargin: 5
            width: 90
            height: 94
            color: "blue"
        }
        Rectangle {
            id: r12
            anchors.left: parent.left
            anchors.leftMargin: 5
            anchors.top: r11.bottom
            anchors.topMargin: 5
            width: 90
            height: 94
            color: "green"
        }
    }
}
The outermost rectangle can be ignored.
Now run the test.qml using following command:
~/Qt5.2.0/5.2.0/gcc_64/bin/qmlscene ./test.qml 
You will see the above diagram.
Note, do not forget to set width of rectangle, otherwise you will not able to see the inner rectangles.

Friday 27 December 2013

use boost log step 11

Today, I will apply rotation log here. To use rotation, the backend needs to be replaced by text_file_backend instead of text_ostream_backend.
Use keywords to set the following attributes when the backend is constructed.
file_name, rotation_size, time_based_rotation and min_free_space

Code in the logger.cc file
#include "logger.h"
void InitLog() {
  typedef sinks::synchronous_sink<sinks::text_file_backend> TextSink;
 
  // init sink1
  boost::shared_ptr<sinks::text_file_backend> backend1 = boost::make_shared<sinks::text_file_backend>(
 keywords::file_name = "sign_%Y-%m-%d_%H-%M-%S.%N.log",
 keywords::rotation_size = 10 * 1024 * 1024,
 keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0),
 keywords::min_free_space = 30 * 1024 * 1024);
  boost::shared_ptr<TextSink> sink1(new TextSink(backend1));
  sink1->set_formatter (
expr::format("[%1%]<%2%>(%3%): %4%")
% expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
% expr::attr<sign_severity_level>("Severity")
% expr::attr<attrs::current_thread_id::value_type >("ThreadID")
% expr::smessage
);
  sink1->set_filter(expr::attr<sign_severity_level>("Severity") >= warning);
  logging::core::get()->add_sink(sink1);

  // init sink2
  boost::shared_ptr<sinks::text_file_backend> backend2 = boost::make_shared<sinks::text_file_backend>(
 keywords::file_name = "sign_%Y-%m-%d.csv",
 keywords::rotation_size = 100 * 1024 * 1024,
 keywords::time_based_rotation = sinks::file::rotation_at_time_point(boost::gregorian::greg_day(1), 0, 0, 0),
 keywords::min_free_space = 30 * 1024 * 1024);
  backend2->auto_flush(true);
  boost::shared_ptr<TextSink> sink2(new TextSink(backend2));
  sink2->set_formatter (
expr::format("%1%,%2%")
% expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
% expr::smessage
);
  sink2->set_filter(expr::attr<sign_severity_level>("Severity") == report);
  logging::core::get()->add_sink(sink2);
  logging::add_common_attributes();
  logging::core::get()->add_global_attribute("ThreadID", attrs::current_thread_id());
}
logger.h file, it is not changed.
#include <boost/log/sinks.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/utility/setup/formatter_parser.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/severity_feature.hpp>
#include <fstream>
namespace logging = boost::log;
namespace attrs = boost::log::attributes;
namespace src = boost::log::sources;
namespace sinks = boost::log::sinks;
namespace expr = boost::log::expressions;
namespace keywords = boost::log::keywords;
enum sign_severity_level {
  trace,
  debug,
  info,
  warning,
  error,
  fatal,
  report
};
void InitLog();
BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(my_logger, src::severity_logger_mt<sign_severity_level>)
Note, sink1 and sink2 use different rotation log policy.
sink2 generate a new log file on the 1-st of every month

use boost log step 10

There are still something need to be improved in the demo of step 9.
First, I move all statements related to boost log into logger.h. This logger.h could be included in any app which wants to use boost log library. Just need to modify the code of InitLog function if you want different sinks.
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/thread/thread.hpp>
#include <boost/log/core.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/common.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/sinks.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/utility/setup/formatter_parser.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/severity_feature.hpp>
#include <fstream>
namespace logging = boost::log;
namespace attrs = boost::log::attributes;
namespace src = boost::log::sources;
namespace sinks = boost::log::sinks;
namespace expr = boost::log::expressions;
namespace keywords = boost::log::keywords;
enum sign_severity_level {
  trace,
  debug,
  info,
  warning,
  error,
  fatal,
  report
};
void InitLog() {
  typedef sinks::synchronous_sink<sinks::text_ostream_backend> TextSink;

  // init sink1
  boost::shared_ptr<sinks::text_ostream_backend> backend1 = boost::make_shared<sinks::text_ostream_backend>();
  backend1->add_stream(boost::shared_ptr<std::ostream>(new std::ofstream("sign.log")));
  boost::shared_ptr<TextSink> sink1(new TextSink(backend1));
  sink1->set_formatter (
expr::format("[%1%]<%2%>(%3%): %4%")
% expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
% expr::attr<sign_severity_level>("Severity")
% expr::attr<attrs::current_thread_id::value_type >("ThreadID")
% expr::smessage
);
  sink1->set_filter(expr::attr<sign_severity_level>("Severity") >= warning);
  logging::core::get()->add_sink(sink1);

  // init sink2
  boost::shared_ptr<sinks::text_ostream_backend> backend2 = boost::make_shared<sinks::text_ostream_backend>();
  backend2->auto_flush(true);
  backend2->add_stream(boost::shared_ptr<std::ostream>(new std::ofstream("sign.csv")));
  boost::shared_ptr<TextSink> sink2(new TextSink(backend2));
  sink2->set_formatter (
expr::format("%1%,%2%,%3%")
% expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
% expr::attr<sign_severity_level>("Severity")
% expr::smessage
);
  sink2->set_filter(expr::attr<sign_severity_level>("Severity") == report);
  logging::core::get()->add_sink(sink2);

  logging::add_common_attributes();
  logging::core::get()->add_global_attribute("ThreadID", attrs::current_thread_id());
}
BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(my_logger, src::severity_logger_mt<sign_severity_level>)
Second, prevent app from crashing
See the official article:http://www.boost.org/doc/libs/1_55_0/libs/log/doc/html/log/rationale/why_crash_on_term.html
I use 2nd solution, add one line in main function before return statement.
#include "logger.h"
int main(int, char*[]) {
  InitLog();
  src::severity_logger_mt<sign_severity_level>& lg = my_logger::get();
  BOOST_LOG_SEV(lg, trace) << "A trace severity message";
  BOOST_LOG_SEV(lg, debug) << "A debug severity message";
  BOOST_LOG_SEV(lg, info) << "An informational severity message";
  BOOST_LOG_SEV(lg, warning) << "A warning severity message";
  BOOST_LOG_SEV(lg, error) << "An error severity message";
  BOOST_LOG_SEV(lg, fatal) << "A fatal severity message";
  BOOST_LOG_SEV(lg, report) << "A report severity message";
  logging::core::get()->remove_all_sinks();
  return 0;
}
Next time, I will apply rotation log to both sink objects.



use boost log step 9

When using the code from step 8 in my real app. I did not see any log records in sign.csv file even if I wait more than one hour. I need a way to flush the log immediately for testing. How to do this?
The boost log backend classes provide a method called auto_flush, by calling this method, you will make boost log write log for you without any latency. But it will interfere with the performance. You need to make a decision according to your actual situation.
I changed some code from step 8 below.
First, I create backend object explicitly, then create a sink object on it.
  typedef sinks::synchronous_sink<sinks::text_ostream_backend> TextSink;
 
  // init sink1
  boost::shared_ptr<sinks::text_ostream_backend> backend1 = boost::make_shared<sinks::text_ostream_backend>();
  backend1->add_stream(boost::shared_ptr<std::ostream>(new std::ofstream("sign.log")));
  boost::shared_ptr<TextSink> sink1(new TextSink(backend1));
  sink1->set_formatter (
expr::format("[%1%]<%2%>(%3%): %4%")
% expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
% expr::attr<sign_severity_level>("Severity")
% expr::attr<attrs::current_thread_id::value_type >("ThreadID")
% expr::smessage
);
  sink1->set_filter(expr::attr<sign_severity_level>("Severity") >= warning);
  logging::core::get()->add_sink(sink1);
Second, I set auto_flush to true on sink2 object's backend.
  boost::shared_ptr<sinks::text_ostream_backend> backend2 = boost::make_shared<sinks::text_ostream_backend>();
  backend2->auto_flush(true)
I have tested it in my app. The sink2 writes log for me immediately now, and the sink1 has some latency.
The full code of main.cc is below, logger.h has not been modified.
#include <iostream>
#include "core/server.h"
#include "business/sign.h"
#include <booster/shared_ptr.h>
#include <boost/make_shared.hpp>
#include <boost/filesystem.hpp>
#include "util/config_error.h"
#include "util/config.h"
#include "util/my_app.h"
using namespace std;
void LoadConfig(string const& xml_path) {
  boost::filesystem::path config_file(xml_path);
  if (!boost::filesystem::exists(config_file)) {
    cout << "The configuration file path specified by paramerter doens't exit, file path:" << xml_path << endl;
    throw ConfigError("The configuration file path specified by paramerter doens't exit,file path:" + xml_path);
  }
  Configuration* config = new Configuration(xml_path);
  MyApp& app = AppHolder::Instance();
  app.set_config(config);
}
#include "util/logger.h"
/*
  void InitLog() {
  boost::log::register_simple_formatter_factory< boost::log::trivial::severity_level, char >("Severity");
  logging::add_file_log(
  keywords::file_name = AppHolder::Instance().config().log_folder + "/sign_%Y-%m-%d_%H-%M-%S.%N.log",
  keywords::rotation_size = 10 * 1024 * 1024,
  keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0),
  keywords::format = "[%TimeStamp%]<%Severity%>(%ThreadID%): %Message%",
  keywords::min_free_space = 3 * 1024 * 1024);
  logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::debug);
  }*/
void InitLog() {
  typedef sinks::synchronous_sink<sinks::text_ostream_backend> TextSink;
  // init sink1
  boost::shared_ptr<sinks::text_ostream_backend> backend1 = boost::make_shared<sinks::text_ostream_backend>();
  backend1->add_stream(boost::shared_ptr<std::ostream>(new std::ofstream("sign.log")));
  boost::shared_ptr<TextSink> sink1(new TextSink(backend1));
  sink1->set_formatter (
expr::format("[%1%]<%2%>(%3%): %4%")
% expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
% expr::attr<sign_severity_level>("Severity")
% expr::attr<attrs::current_thread_id::value_type >("ThreadID")
% expr::smessage
);
  sink1->set_filter(expr::attr<sign_severity_level>("Severity") >= warning);
  logging::core::get()->add_sink(sink1);
  // init sink2
  boost::shared_ptr<sinks::text_ostream_backend> backend2 = boost::make_shared<sinks::text_ostream_backend>();
  backend2->auto_flush(true);
  backend2->add_stream(boost::shared_ptr<std::ostream>(new std::ofstream("sign.csv")));
  boost::shared_ptr<TextSink> sink2(new TextSink(backend2));
  sink2->set_formatter (
expr::format("%1%,%3%")
% expr::format_date_time<boost::posix_time::ptime>("TimeStamp", "%Y-%m-%d %H:%M:%S")
% expr::smessage
);
  sink2->set_filter(expr::attr<sign_severity_level>("Severity") == report);
  logging::core::get()->add_sink(sink2);

  logging::add_common_attributes();
  logging::core::get()->add_global_attribute("ThreadID", attrs::current_thread_id());
}

int main(int argc, char ** argv) {
  try {
    //check argument
    string xml_path;
    if (argc != 2) {
      cout << "The 1st parameter doesn't exist, data_service quits now";
      return 1;
    } else {
      xml_path = argv[1];
    }
    LoadConfig(xml_path);
    InitLog();
    logging::add_common_attributes();
    src::severity_logger_mt<sign_severity_level>& lg = my_logger::get();
    //src::severity_logger< severity_level > lg;
    BOOST_LOG_SEV(lg, info) << "thread id: " << this_thread::get_id() << " Initialization succeeded";
    io_service iosev;
    Configuration & config = AppHolder::Instance().config();
    tcp::endpoint listen_endpoint(tcp::v4(), config.listen_port);
    Server<Sign> server(iosev, listen_endpoint, config.thread_number);
    server.Run();
  } catch(std::exception const& ex) {
    cout << "thread id: " << this_thread::get_id() << " Caught an exception: " << ex.what() << endl;
  }
}

Thursday 26 December 2013

use boost log step 8

In a real project, I need to log in different cpp or cc files, usually it is required in different threads.
In previous post, write log needs a logger object. We need the logger to be a global variable and thread-safe accessible in a real app.
Official document is located at:http://www.boost.org/doc/libs/1_55_0/libs/log/doc/html/log/detailed/sources.html#log.detailed.sources.global_storage

Now I modified my demo code.

1. create a new file called logger.h, add just one line:
BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(my_logger, src::severity_logger_mt<sign_severity_level>)

2. in main.cc, include required header files, and use my_logger::get() to retrieve the reference of logger object.
#include <fstream>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/thread/thread.hpp>

#include <boost/log/core.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/common.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/sinks.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/utility/setup/formatter_parser.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/severity_feature.hpp>
namespace logging = boost::log;
namespace attrs = boost::log::attributes;
namespace src = boost::log::sources;
namespace sinks = boost::log::sinks;
namespace expr = boost::log::expressions;
namespace keywords = boost::log::keywords;
enum sign_severity_level {
  trace,
  debug,
  info,
  warning,
  error,
  fatal,
  report
};
void InitLog() {
  typedef sinks::synchronous_sink<sinks::text_ostream_backend> text_sink;
  boost::shared_ptr< text_sink > sink1 = boost::make_shared<text_sink>();
  sink1->locked_backend()->add_stream(
     boost::make_shared<std::ofstream>("sign.log"));
  sink1->set_formatter (
expr::format("[%1%]<%2%>(%3%): %4%")
% expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
% expr::attr<sign_severity_level>("Severity")
% expr::attr<attrs::current_thread_id::value_type >("ThreadID")
% expr::smessage
);
  logging::core::get()->add_sink(sink1);
  sink1->set_filter(expr::attr<sign_severity_level>("Severity") >= warning);
  boost::shared_ptr< text_sink > sink2 = boost::make_shared< text_sink >();
  sink2->locked_backend()->add_stream(
     boost::make_shared< std::ofstream >("sign.csv"));
  sink2->set_formatter (
expr::format("%1%,%2%,%3%")
% expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
% expr::attr<sign_severity_level>("Severity")
% expr::smessage
);
  logging::core::get()->add_sink(sink2);
  sink2->set_filter(expr::attr<sign_severity_level>("Severity") == report);
  logging::add_common_attributes();
  logging::core::get()->add_global_attribute("ThreadID", attrs::current_thread_id());
}
#include "logger.h"
int main(int, char*[]) {
  InitLog();
  src::severity_logger_mt<sign_severity_level>& lg = my_logger::get();
  BOOST_LOG_SEV(lg, trace) << "A trace severity message";
  BOOST_LOG_SEV(lg, debug) << "A debug severity message";
  BOOST_LOG_SEV(lg, info) << "An informational severity message";
  BOOST_LOG_SEV(lg, warning) << "A warning severity message";
  BOOST_LOG_SEV(lg, error) << "An error severity message";
  BOOST_LOG_SEV(lg, fatal) << "A fatal severity message";
  BOOST_LOG_SEV(lg, report) << "A report severity message";
  return 0;
}
That's all. 
Inside the macro, it defines a struct for you, also implements a get function. The singleton logger will be created at the first time you call get function.
Below is the code inside macros:
./sources/global_logger_storage.hpp:185
//! The macro declares a global logger that will be default-constructed
#define BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(tag_name, logger)\
    BOOST_LOG_INLINE_GLOBAL_LOGGER_INIT(tag_name, logger)\
    {\
        return logger_type();\
    }

//! The macro declares a global logger with a custom initialization
#define BOOST_LOG_INLINE_GLOBAL_LOGGER_INIT(tag_name, logger)\
    BOOST_LOG_GLOBAL_LOGGER(tag_name, logger)\
    inline BOOST_LOG_GLOBAL_LOGGER_INIT(tag_name, logger)
//! The macro forward-declares a global logger with a custom initialization
#define BOOST_LOG_GLOBAL_LOGGER(tag_name, logger)\
    struct tag_name\
    {\
        typedef logger logger_type;\
        enum registration_line_t { registration_line = __LINE__ };\
        static const char* registration_file() { return __FILE__; }\
        static logger_type construct_logger();\
        static inline logger_type& get()\
        {\
            return ::boost::log::sources::aux::logger_singleton< tag_name >::get();\
        }\
    };
//! The macro defines a global logger initialization routine
#define BOOST_LOG_GLOBAL_LOGGER_INIT(tag_name, logger)\
    tag_name::logger_type tag_name::construct_logger()
 It generates the real C++ code below:
    struct my_logger
    {
        typedef src::logger_mt logger_type;
        enum registration_line_t { registration_line = __LINE__ };
        static const char* registration_file() { return __FILE__; }
        static logger_type construct_logger();
        static inline logger_type& get()
        {
            return ::boost::log::sources::aux::logger_singleton<my_logger>::get();
        }
    };
    inline my_logger::logger_type my_logger::construct_logger()

Wednesday 25 December 2013

Assemble different qml files in one Qt quick app

Use qml to define GUI, we can separate the whole GUI into different parts. One main.qml file indicates the whole layout, and the other qml files define partial GUI.

I create a project called assemble, the main.qml is defined as following:
import QtQuick 2.1
import QtQuick.Window 2.1
import QtQuick.Controls 1.1
import QtQuick.XmlListModel 2.0
Window {
    title: "Assemble Demo"
    width: 538 + frame.margins * 2
    height: 360 + frame.margins * 2
    ToolBar {
        id: toolbar
        width: parent.width
        Text {
            text: "Assemble Demo"
        }
    }
    SystemPalette {id: syspal}
    color: syspal.window
    Rectangle {
        id: r1
        anchors.top: toolbar.bottom
        anchors.right: parent.right
        anchors.left: parent.left
        anchors.bottom:  parent.bottom
        anchors.margins: 16
        TabView {
            id:frame
            focus:true
            property int margins: Qt.platform.os === "osx" ? 16 : 0
            height: 120
            anchors.right: parent.right
            anchors.left: parent.left
            anchors.margins: margins
            Tab {
                title: "Tab1"
                source: "menu.qml"
            }
            Tab {
                title: "Tab2"
                source: "menu.qml"
            }
        }
    }
}
Note, in above two Tab elements, I use source attribute to include external menu.qml file.
Below is menu.qml file:
import QtQuick 2.0
import QtQuick.Controls 1.1
Rectangle {
    Button {
        id: createMenu
        text: "Create"
    }
    Button {
        id: loadMenu
        anchors.left: createMenu.right
        text: "Load"
    }
}

Also, need to add two qml files into resources.qrc.
Now have a look at the screenshot.


use boost log step 7

Keep moving on step 6. This time I will show how to customize severity level. I have two sinks in step 6. I hope my sink2 will only record the log whose  level is equal to report. Below is how to complete this.
1. Comment namespace logging::trivial
//using namespace logging::trivial;
I will not use default severity level from trivial now.

2. Define my own enum
 enum sign_severity_level {
  trace,
  debug,
  info,
  warning,
  error,
  fatal,
  report
};
3. Update format for two sinks like so:
  sink1->set_formatter (
expr::format("[%1%]<%2%>(%3%): %4%")
% expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
% expr::attr<sign_severity_level>("Severity")
% expr::attr<attrs::current_thread_id::value_type >("ThreadID")
% expr::smessage
);
4. Create  severity_logger with new enum
src::severity_logger<sign_severity_level> lg;
5. Change the filter of sink2
   sink2->set_filter(expr::attr<sign_severity_level>("Severity") == report);

The following is full example code:
 #include <fstream>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/thread/thread.hpp>
#include <boost/log/attributes/scoped_attribute.hpp>
namespace logging = boost::log;
namespace src = boost::log::sources;
namespace expr = boost::log::expressions;
namespace sinks = boost::log::sinks;
namespace keywords = boost::log::keywords;
namespace attrs = boost::log::attributes;
//using namespace logging::trivial;
enum sign_severity_level {
  trace,
  debug,
  info,
  warning,
  error,
  fatal,
  report
};
void InitLog() {
  typedef sinks::synchronous_sink<sinks::text_ostream_backend> text_sink;
  boost::shared_ptr< text_sink > sink1 = boost::make_shared<text_sink>();
  sink1->locked_backend()->add_stream(
     boost::make_shared< std::ofstream >("sign.log"));
  sink1->set_formatter (
expr::format("[%1%]<%2%>(%3%): %4%")
% expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
% expr::attr<sign_severity_level>("Severity")
% expr::attr<attrs::current_thread_id::value_type >("ThreadID")
% expr::smessage
);
  logging::core::get()->add_sink(sink1);
  sink1->set_filter(expr::attr<sign_severity_level>("Severity") >= warning);
  boost::shared_ptr< text_sink > sink2 = boost::make_shared< text_sink >();
  sink2->locked_backend()->add_stream(
     boost::make_shared< std::ofstream >("sign.csv"));
  sink2->set_formatter (
expr::format("%1%,%2%,%3%")
% expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
% expr::attr<sign_severity_level>("Severity")
% expr::smessage
);
  logging::core::get()->add_sink(sink2);
  sink2->set_filter(expr::attr<sign_severity_level>("Severity") == report);
  logging::add_common_attributes();
  //  BOOST_LOG_SCOPED_THREAD_TAG("ThreadID", boost::this_thread::get_id());
  logging::core::get()->add_global_attribute("ThreadID", attrs::current_thread_id());
}
int main(int, char*[]) {
  InitLog();
  src::severity_logger<sign_severity_level> lg;
  BOOST_LOG_SEV(lg, trace) << "A trace severity message";
  BOOST_LOG_SEV(lg, debug) << "A debug severity message";
  BOOST_LOG_SEV(lg, info) << "An informational severity message";
  BOOST_LOG_SEV(lg, warning) << "A warning severity message";
  BOOST_LOG_SEV(lg, error) << "An error severity message";
  BOOST_LOG_SEV(lg, fatal) << "A fatal severity message";
  BOOST_LOG_SEV(lg, report) << "A report severity message";
  return 0;
}

use boost log step 6

This time I will show how to use two sinks in one app. These two sinks have different filter, generate two log files with different formats.
The full code is listed below:
#include <fstream>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/thread/thread.hpp>
#include <boost/log/attributes/scoped_attribute.hpp>
namespace logging = boost::log;
namespace src = boost::log::sources;
namespace expr = boost::log::expressions;
namespace sinks = boost::log::sinks;
namespace keywords = boost::log::keywords;
using namespace logging::trivial;
void InitLog() {
  typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
  boost::shared_ptr< text_sink > sink1 = boost::make_shared< text_sink >();
  sink1->locked_backend()->add_stream(
    boost::make_shared< std::ofstream >("sign.log"));
  sink1->set_formatter (
      expr::format("[%1%]<%2%>(%3%): %4%")
      % expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
      % logging::trivial::severity
      % expr::attr<boost::log::attributes::current_thread_id::value_type >("ThreadID")
      % expr::smessage
      );
  logging::core::get()->add_sink(sink1);
  sink1->set_filter(expr::attr< severity_level >("Severity") >= warning);
  boost::shared_ptr< text_sink > sink2 = boost::make_shared< text_sink >();
  sink2->locked_backend()->add_stream(
    boost::make_shared< std::ofstream >("sign.csv"));
  sink2->set_formatter (
      expr::format("%1%,%2%,%3%")
      % expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
      % logging::trivial::severity
      % expr::smessage
      );
  logging::core::get()->add_sink(sink2);
  sink2->set_filter(expr::attr< severity_level >("Severity") < warning);
  logging::add_common_attributes();
  BOOST_LOG_SCOPED_THREAD_TAG("ThreadID", boost::this_thread::get_id());
}
int main(int, char*[]) {
  InitLog();
  src::severity_logger<severity_level> lg;
  BOOST_LOG_SEV(lg, trace) << "A trace severity message";
  BOOST_LOG_SEV(lg, debug) << "A debug severity message";
  BOOST_LOG_SEV(lg, info) << "An informational severity message";
  BOOST_LOG_SEV(lg, warning) << "A warning severity message";
  BOOST_LOG_SEV(lg, error) << "An error severity message";
  BOOST_LOG_SEV(lg, fatal) << "A fatal severity message";
  return 0;
}
I created sink1 & sink2 in above example. sink1 outputs sign.log file and sink2 creates sign.csv file.
All logs whose severity is greater than or equal to warning will appear in sign.log file, all logs less than warning will be recorded into sign.csv file with csv format.
Run this example and watch the result of two files:

$ cat sign.csv
2013-12-25 16:31:27,trace,A trace severity message
2013-12-25 16:31:27,debug,A debug severity message
2013-12-25 16:31:27,info,An informational severity message 
$ cat sign.log
[2013-12-25 16:31:27]<warning>(0x00007f64b5cf3740): A warning severity message
[2013-12-25 16:31:27]<error>(0x00007f64b5cf3740): An error severity message
[2013-12-25 16:31:27]<fatal>(0x00007f64b5cf3740): A fatal severity message

Tuesday 24 December 2013

Make a Qt Quick UI project

I introduced how to create a Qt Quick project using Qt Creator, it's a basic project. But most of time, I want the Qt UI designed by qml will be kept separate from C++ code. UI designer edits the .qml files, C++ code loads them in runtime, then binds events and handles events like HTML.
This article, I will show how to do this.

First, let's design a qml file which represents a simple tab view window.
In main.qml file, add below qml statements:
import QtQuick 2.1
import QtQuick.Window 2.1
import QtQuick.Controls 1.1
import QtQuick.XmlListModel 2.0
Window {
    width: 538 + frame.margins * 2
    height: 360 + frame.margins * 2
    ToolBar {
        id: toolbar
        width: parent.width
    }
    SystemPalette {id: syspal}
    color: syspal.window

    Rectangle {
        anchors.top: toolbar.bottom
        anchors.right: parent.right
        anchors.left: parent.left
        anchors.bottom:  parent.bottom
        anchors.margins: 8
        TabView {
            id:frame
            focus:true
            property int margins: Qt.platform.os === "osx" ? 16 : 0
            height: parent.height - 34
            anchors.right: parent.right
            anchors.left: parent.left
            anchors.margins: margins
            Tab {
                title: "Home"
            }
            Tab {
                title: "Edit"
            }
            Tab {
                title: "View"
            }
            Tab {
                title: "Help"
            }
        }
    }
}
We can use qmlscene tool for testing qml file. Execute the following command
qmlscene main.qml
qmlscene: could not find a Qt installation of ''
I see the error because my Ubuntu have installed different versions of Qt. Need to point to the full path of my qmlscene like this:
 ~/Qt5.2.0/5.2.0/gcc_64/bin/qmlscene main.qml

A window pops up now.

Second, create a Qt C++ project to load qml file.
My project folder tree looks like so:
.
├── gui.pro
├── images
│   ├── header.png
│   └── selectedrow.png
├── main.qml
├── resources.qrc
└── src
    ├── main.cpp
    └── src.pri

the gui,pro file has content as following
QT += qml quick
TARGET = gui
!android: !ios: !blackberry: qtHaveModule(widgets): QT += widgets
include(src/src.pri)
OTHER_FILES += \
    main.qml
RESOURCES += \
    resources.qrc
HEADERS +=
the resources.qrc file, to get more information about qrc, see http://doc-snapshot.qt-project.org/qdoc/resources.html
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/">
  <file>main.qml</file>
  <file>images/selectedrow.png</file>
  <file>images/header.png</file>
</qresource>
</RCC>
the src.pri file
SOURCES += \
    $$PWD/main.cpp
 Ok. have  a look at the key file: src/main.cpp
#include <QtQml>
#include <QtQuick/QQuickView>
#include <QtCore/QString>
#ifdef QT_WIDGETS_LIB
#include <QtWidgets/QApplication>
#else
#include <QtGui/QGuiApplication>
#endif

#ifdef QT_WIDGETS_LIB
#define Application QApplication
#else
#define Application QGuiApplication
#endif
int main(int argc, char *argv[])
{
    Application app(argc, argv);
    QQmlApplicationEngine engine(QUrl("qrc:/main.qml"));
    QObject *topLevel = engine.rootObjects().value(0);
    QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
    if ( !window ) {
        qWarning("Error: Your root item has to be a Window.");
        return -1;
    }
    window->show();
    return app.exec();
}
In the main function, Create QmlAppliationEngine to load qml file, then get top level QObject from engine and show it as a window object.

Finally, you can open the gui.pro with Qt Creator and run it. Alternatively, you can run qmake to generate Makfile and make it.

Monday 23 December 2013

Getting Started Programming with Qt Quick

There is an article on official site which describes how to develop a Qt Quick app, 
http://qt-project.org/doc/qt-5/gettingstartedqml.html#building-a-text-editor
But it's not the real doc for newer, I read through it for a few minutes, still do not know how to begin my first project. Which file should I put my following descriptive language lines into?
Rectangle {
    id: simpleButton
    ...

    MouseArea {
        id: buttonMouseArea

        // Anchor all sides of the mouse area to the rectangle's anchors
        anchors.fill: parent
        // onClicked handles valid mouse button clicks
        onClicked: console.log(buttonLabel.text + " clicked")
    }
}
And how to set up a Qt Quick project? Any thing must be included in this project?
Nothing, the official article only talked about some code snippets. You have to find a way to integrate all these parts by yourself? So, it's article, but not written for new guys.

Then, I tried my way. First, I create a new Qt project using VS 2012 with QT add-on, select QML & Quick options when creating project. This project has some *.ui files, in this kind of *.ui file, I see XML definition, not above JSON style. I am new to QML, but this project obviously is not what I want.

OK. Let's have a try with Qt Creator. Using this IDE, I can create a real Quick project now, just choose Qt Quick Application at the beginning. My project name is called helloworld.
In the main.cpp file,
#include <QtGui/QGuiApplication>
#include "qtquick2applicationviewer.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QtQuick2ApplicationViewer viewer;
viewer.setMainQmlFile(QStringLiteral("qml/helloworld/main.qml"));
viewer.showExpanded();
return app.exec();
}
QGuiApplication is the main class for QT GUI app. Doc is here:http://qt-project.org/doc/qt-5.0/qtgui/qguiapplication.html#details

QtQuick2ApplicationViewer is generated by project wizard automatically. It inherits from QQuickView, we do not need to modify it. It offers a useful function called setMainQmlFile. The qml file is generated by project wizard too, it defines the GUI on main window.
import QtQuick 2.0

Rectangle {
    width: 360
    height: 360
    Text {
        text: qsTr("Hello World")
        anchors.centerIn: parent
    }
    MouseArea {
        anchors.fill: parent
        onClicked: {
            Qt.quit();
        }
    }
}


qtquick2applicationviewer.pri file was created automatically too. Below is some description about this file. Do not modify this too.
# checksum 0x21c9 version 0x90005
# This file was generated by the Qt Quick 2 Application wizard of Qt Creator.
# The code below adds the QtQuick2ApplicationViewer to the project and handles
# the activation of QML debugging.
# It is recommended not to modify this file, since newer versions of Qt Creator
# may offer an updated version of it.

qml file is JSON style, very easy to read and understand. Define a rectangle which includes two elements: text & mouse area.

Also, the project wizard has created project file: helloworld.pro.

Run it, a helloworld window will popup.
This is a real getting started doc for new people.



Monday 16 December 2013

Write hello word Qt app on Ubuntu

Without using Qt Creator IDE, I just use my favorite editor - Emacs and command lines to create a hello wold project.

1. Make sure /usr/bin/qmake point to the one comes with installed Qt 5.2.0.
/usr/bin# mv qmake qmake_bk
/usr/bin# ln -s /home/likewise-open/CHN/shu6889/Qt5.2.0/5.2.0/gcc_64/bin/qmake ./qmake
2. Write code
create helloworld folder, add main.cc file
#include <QtWidgets/QApplication>
#include <QtWidgets/QLabel>
int main(int argc, char *argv[]) {
  QApplication app(argc, argv);
  QLabel hello("<center>Welcome to my first WikiHow Qt program</center>");
  hello.setWindowTitle("My First WikiHow Qt Program");
  hello.resize(400, 400);
  hello.show();
  return app.exec();
}
Above code comes from this article: http://www.wikihow.com/Create-Your-First-Qt-Program-on-Ubuntu-Linux. I changed include statements because I am using Qt 5.2.0.

3. Generate project file
In helloworld folder, run the following command:
qmake -project
Now, I get helloworld.pro file.
######################################################################
# Automatically generated by qmake (3.0) ?? 12? 16 15:49:40 2013
######################################################################
TEMPLATE = app
TARGET = helloworld
INCLUDEPATH += .
# Input
SOURCES += main.cc

4. Compile it
Just execute qmake without args again. Get Makefile file now.
qmake
make

Got a link error: -lGL, install mesa
 apt-get install libgl1-mesa-dev
But I get a few link errors now
main.o: In function `main':
main.cc:(.text.startup+0x27): undefined reference to `QApplication::QApplication(int&, char**, int)'
main.cc:(.text.startup+0x56): undefined reference to `QLabel::QLabel(QString const&, QWidget*, QFlags<Qt::WindowType>)'
main.cc:(.text.startup+0x84): undefined reference to `QWidget::setWindowTitle(QString const&)'
main.cc:(.text.startup+0xa9): undefined reference to `QWidget::resize(QSize const&)'
main.cc:(.text.startup+0xb1): undefined reference to `QWidget::show()'
main.cc:(.text.startup+0xb6): undefined reference to `QApplication::exec()'
main.cc:(.text.startup+0xc1): undefined reference to `QLabel::~QLabel()'
main.cc:(.text.startup+0xc9): undefined reference to `QApplication::~QApplication()'
main.cc:(.text.startup+0xe5): undefined reference to `QApplication::~QApplication()'
main.cc:(.text.startup+0x106): undefined reference to `QLabel::~QLabel()'
main.cc:(.text.startup+0x118): undefined reference to `QLabel::~QLabel()'
collect2: error: ld returned 1 exit status
make: *** [helloworld] Error 1

Add this line into helloworld.pro file
 QT += widgets
make it again. It works.
Run the helloworld binary, see Qt window now.

Sunday 15 December 2013

Install Qt 5.2 on Ubuntu 13.10

This article on Qt official site is old. Today, I try to install Qt 5.2.0 on my Ubuntu 13.10. Below is my steps:

1. Download Qt 5.2.0 for Linux
wget http://download.qt-project.org/official_releases/qt/5.2/5.2.0/qt-linux-opensource-5.2.0-x86_64-offline.run

2.  Give the downloaded file execute permission, then execute it
chmod +x ./qt-linux-opensource-5.2.0-x86_64-offline.run
./qt-linux-opensource-5.2.0-x86_64-offline.run
You will see one setup dialog, following the setup wizard.



accept the license agreement, and press Next button


Press install button, wait a few seconds.


After installation, press Window button, enter Qt in search window, click Qt Creator, you will see the Qt Creator IDE

Because I have g++ & mesa installed, so nothing left needs to do.


Monday 9 December 2013

newlisp accesses MongoDB via C++ dynamic library

I want to let my newlisp app access MongoDB server. There is C driver for MongoDB, but I have no time to learn C driver, just want to finish my job ASAP.
Below is my way, create a dynamic library using C++, also link to MongoDB C++ driver. But export C function using extern "C" statement.

One thing needs to note, MongoDB C++ driver must be compiled using -fPIC option.

First, create db.h file
extern "C" void CheckOnline();
extern "C" void Init(char * mongo_uri);

edit db.cc file
#include "db.h"
#include <stdlib.h>
#include <ctype.h>
#include <iostream>
#include "mongo_session_factory.h"
#include "mongo_session.h"
#include <mongo/bson/bsonobj.h>
#include <mongo/bson/bsonobjbuilder.h>
using namespace std;
class Config {
public:
  string mongo_uri;
};
Config config;
void SetOffline(mongo::DBClientBase& con, mongo::OID const& id) {
  mongo::BSONObjBuilder condition;
  condition.append("_id", id);
  mongo::BSONObjBuilder update_field;
  update_field.append("status", "offline");
  mongo::BSONObjBuilder set_field;
  set_field.append("$set", update_field.obj());
  con.update("kaimei.display", mongo::Query(condition.obj()), set_field.obj());
}
/**
 * Check every sign status
 */
void CheckOnline() {
  cout << config.mongo_uri << endl;
  std::shared_ptr<MongoSession> session = MongoSessionFactory::GetSession(config.mongo_uri);
  mongo::DBClientBase & con = session->get_db_client_base();
  mongo::BSONObjBuilder condition;
  std::unique_ptr<mongo::DBClientCursor> cursor = con.query("kaimei.display", mongo::Query(condition.obj()));
  while (cursor->more()) {
    mongo::BSONObj record = cursor->next();
    uint64_t t1 = record.getField("last_active_time").numberLong();
    timeval tv;
    gettimeofday(&tv,NULL);
    if (tv.tv_sec - t1 > 300) {
      SetOffline(con, record.getField("_id").OID());
    }
  }
}

void Init(char * mongo_uri) {
  config.mongo_uri = mongo_uri;
}
in include and src folders, there are some helper classes called MongoSessionFactory and MongoSesion. They are useful to connect to MongoDB server(Replicaset or Single server)

Ok, build it, see build.lsp file
#!/usr/bin/newlisp
(if (file? "db.so")
    (delete-file "db.so"))
(if (directory? "objects")
    (exec "rm -rf objects"))
(make-dir "objects")
(exec "c++ -g -fPIC -std=c++11 -I./include  -Wall -o ./objects/mongo_session_factory.cc.o -c ./src/mongo_session_factory.cc")
(exec "c++ -g -fPIC -std=c++11 -I./include  -Wall -o ./objects/mongo_session.cc.o -c ./src/mongo_session.cc")
(exec "c++ -g -fPIC -std=c++11 -I./include  -Wall -o ./objects/db.cc.o -c ./src/db.cc")
(exec "c++ -g -fPIC -std=c++11 ./objects/mongo_session_factory.cc.o ./objects/mongo_session.cc.o ./objects/db.cc.o -shared -o db.so -rdynamic -lpthread -lmongoclient -lboost_system -lboost_thread -lboost_filesystem")
(exit)

After building succeeds, write a check.lsp to use the db.so library.
#!/usr/bin/newlisp 

(set 'db-path "/path/to/your/db.so")
(import db-path "Init")
(import db-path "CheckOnline")
(Init "localhost:27017")
(CheckOnline)
(exit)

That's all. It works now.

Build MongoDB C++ driver with -fPIC

-fPIC is useful option when building c++ app using g++ compiler.
This page explains it: http://explainshell.com/explain?cmd=g%2B%2B+-fpic
In short, when compiling so lib, always using it will be a better idea.

For enabling this option when building MongoDB c++ 2.4.X driver, edit SConstruct file in line 81
    env.Append(CCFLAGS=["-O3", "-pthread", "-fPIC"])

Then run scons install to build it. 

Friday 6 December 2013

use boost log step 5

This time, I will use sink directly without the help of add_file_log function.
This example shows how to format log, register sink into core, write log to local file.  Full example code is below:
#include <fstream>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/thread/thread.hpp>
#include <boost/log/attributes/scoped_attribute.hpp>
namespace logging = boost::log;
namespace src = boost::log::sources;
namespace expr = boost::log::expressions;
namespace sinks = boost::log::sinks;
namespace keywords = boost::log::keywords;
void InitLog() {
  typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
  boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();
  sink->locked_backend()->add_stream(
    boost::make_shared< std::ofstream >("sign.log"));
  sink->set_formatter (
      expr::format("[%1%]<%2%>(%3%): %4%")
      % expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
      % logging::trivial::severity
      % expr::attr< boost::thread::id >("ThreadID")
      % expr::smessage
      );
  logging::core::get()->add_sink(sink);
  logging::add_common_attributes();
  BOOST_LOG_SCOPED_THREAD_TAG("ThreadID", boost::this_thread::get_id());
}
int main(int, char*[]) {
  InitLog();
  using namespace logging::trivial;
  src::severity_logger< severity_level > lg;
  BOOST_LOG_SEV(lg, trace) << "A trace severity message";
  BOOST_LOG_SEV(lg, debug) << "A debug severity message";
  BOOST_LOG_SEV(lg, info) << "An informational severity message";
  BOOST_LOG_SEV(lg, warning) << "A warning severity message";
  BOOST_LOG_SEV(lg, error) << "An error severity message";
  BOOST_LOG_SEV(lg, fatal) << "A fatal severity message";
  return 0;
}
The output is
[2013-12-06 18:02:28]<trace>(): A trace severity message
[2013-12-06 18:02:28]<debug>(): A debug severity message
[2013-12-06 18:02:28]<info>(): An informational severity message
[2013-12-06 18:02:28]<warning>(): A warning severity message
[2013-12-06 18:02:28]<error>(): An error severity message
[2013-12-06 18:02:28]<fatal>(): A fatal severity message

my build.lsp code is here
 #!/usr/bin/newlisp
(if (file? "main")
    (delete-file "main"))
(exec "c++ -g -std=c++11 -Wall -DBOOST_LOG_DYN_LINK -o ./main.o -c ./main.cc")
(exec "c++ -g -std=c++11 ./main.o -o main -rdynamic -lpthread -lboost_log -lboost_system -lboost_thread -lboost_filesystem")
(exit)

reset to one old commit on both local and remote computers

When using GitLab server, sometime I will reset to one old commit because I mess up in current commit.
On working computer, using this
git reset --hard 05a0753bd3e24922dd46bb6a04dc93dda9a9ec9f

Then try to push it to GitLab server, it doesn't work even if I push with --force option.
 remote: error: denying non-fast-forward refs/heads/develop (you should pull first)

Now the simplest way is to delete remote branch first, then push branch from local computer to GitLab server again.
git push origin :develop
git push origin develop

: means delete remote develop branch here.

Tuesday 3 December 2013

Try valgrind

Today, I will try valgrind, a tool to analyze c++ app.
Download the newest version:
wget http://valgrind.org/downloads/valgrind-3.9.0.tar.bz2
unzip it
tar jxvf valgrind-3.9.0.tar.bz2
 then enter the folder and compile it with root account
cd valgrind-3.9.0
./configure
make
make install
install required libs
apt-get install libc6-dbg

Then use valgrind to launch your app, e.g.
# valgrind --leak-check=yes --log-file=profile ./data_service_d ./config.xml &
You will get the below from profile file in current folder.
[1] 25076
root@vm8:/home/dist/tcp# ==25076== Memcheck, a memory error detector
==25076== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==25076== Using Valgrind-3.9.0 and LibVEX; rerun with -h for copyright info
==25076== Command: ./data_service_d ./config.xml
==25076== 

use boost log step 4

Now, I apply the boost log to my one of existing applications to replace CppCMS log. I use it to do the following jobs, the size of one single log file is limited less than 10 MB, and the free disk space must be 3GB.
To use this, there are some issues need to be known.
1. include correct header files

#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
namespace logging = boost::log;
namespace src = boost::log::sources;
namespace sinks = boost::log::sinks;
namespace keywords = boost::log::keywords;
2. register_simple_formatter_factory is required, otherwise you cannot see severity field in log file
void InitLog() {
  boost::log::register_simple_formatter_factory< boost::log::trivial::severity_level, char >("Severity");
  logging::add_file_log(
keywords::file_name = AppHolder::Instance().config().log_folder + "/sign_%Y-%m-%d_%H-%M-%S.%N.log",
keywords::rotation_size = 10 * 1024 * 1024,
keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0),
keywords::format = "[%TimeStamp%] (%Severity%) : %Message%",
keywords::min_free_space=3 * 1024 * 1024
);
  logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::debug);
}

3.  In main function, set attributes after calling InitLog like this:
    InitLog();
    logging::add_common_attributes();
    using namespace logging::trivial;
    src::severity_logger< severity_level > lg;
    BOOST_LOG_SEV(lg, info) << "thread id: " << this_thread::get_id() << " Initialization succeeded";

Now, have a look at the log files, each file size is less than 10M, and one new file will be created at midnight.

 -rw-r--r-- 1 root root  10M Dec  3 23:16 sign_2013-12-03_23-00-01.0.log
-rw-r--r-- 1 root root  10M Dec  3 23:36 sign_2013-12-03_23-16-55.1.log
-rw-r--r-- 1 root root  10M Dec  3 23:55 sign_2013-12-03_23-36-21.2.log
-rw-r--r-- 1 root root 2.4M Dec  4 00:00 sign_2013-12-03_23-55-33.3.log
-rw-r--r-- 1 root root  10M Dec  4 00:19 sign_2013-12-04_00-00-00.4.log
-rw-r--r-- 1 root root  10M Dec  4 00:38 sign_2013-12-04_00-19-30.5.log
-rw-r--r-- 1 root root  10M Dec  4 00:58 sign_2013-12-04_00-38-48.6.log
-rw-r--r-- 1 root root  10M Dec  4 01:17 sign_2013-12-04_00-58-06.7.log
-rw-r--r-- 1 root root  10M Dec  4 01:36 sign_2013-12-04_01-17-19.8.log
-rw-r--r-- 1 root root  10M Dec  4 01:56 sign_2013-12-04_01-36-45.9.log
-rw-r--r-- 1 root root  10M Dec  4 02:15 sign_2013-12-04_01-56-07.10.log

Look inside one log file:
[2013-Dec-04 10:17:45.728393] (debug) : object id: 529df217186f983f62ffa718 sent data: 01 30 30 30 43 44 31 4F 4B 31 30 03  sent size:12

4.  In other .cc files, just include one header file
 #include <boost/log/trivial.hpp>
And use BOOST_LOG_TRIVIAL() MACROS
BOOST_LOG_TRIVIAL(debug) << "object id: " << id_ << " sent data: " << PrintBytesAsHexString(*data, data->size()) << " sent size:" << data->size();

That's enough.
Inside boost log, there are many details need to be learned. I have not time for now to analyze it. Will write blogs about this when free in the future.

use boost log step 3

Now, I need to write log to local file. My example code is simpler than official doc.

#include <iostream>
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/utility/setup/file.hpp>
namespace logging = boost::log;
using namespace std;
void SetFilter1() {
  logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::info);
}
void SetFilter2() {
  logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::debug);
}
int main () {
  cout << "hello, world" << endl;
  logging::add_file_log("sample.log");
  SetFilter1();
  BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
  BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
  BOOST_LOG_TRIVIAL(info) << "An informational severity message";
  BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
  BOOST_LOG_TRIVIAL(error) << "An error severity message";
  BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";
 
  BOOST_LOG_TRIVIAL(info) << "--------------------" << endl;
  SetFilter2();
  BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
  BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
  BOOST_LOG_TRIVIAL(info) << "An informational severity message";
  BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
  BOOST_LOG_TRIVIAL(error) << "An error severity message";
  BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";
}
Do not forget to include <boost/log/utility/setup/file.hpp>, otherwise you will get compilation error like so:
no member named 'add_file_log' in namespace 'boost::log'

 Now, run the app, you will see sample file is created in current folder.
$ cat sample.log
An informational severity message
A warning severity message
An error severity message
A fatal severity message
--------------------
A debug severity message
An informational severity message
A warning severity message
An error severity message
A fatal severity message
Here we encounter a concept "sink", sink decides where the log info will be written. In the previous two articles, I didn't add sink. The boost log uses console as default sink. Now, because I call add_file_log function, so the default sink is replaced by the file sink now. To setup sink, you need to add it into core. Just one statement in above example:
logging::add_file_log("sample.log");
boost log has another concept-- source. This doc describes the architecture of boost log design.


The left side is log source, it collects the log info in our app. just like this statement
  BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
The right side is sink, it processes log info, decides where the log should be put, how to use it.
And the logging core interconnects them. 

Monday 2 December 2013

Use boost log step 2

This time, I will try filter. first code from official doc,

#include <iostream>
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
namespace logging = boost::log;
using namespace std;
void SetFilter() {
  logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::info);
}
int main () {
  cout << "hello, world" << endl;
  SetFilter();
  BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
  BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
  BOOST_LOG_TRIVIAL(info) << "An informational severity message";
  BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
  BOOST_LOG_TRIVIAL(error) << "An error severity message";
  BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";
}

When run the binary, output is changed, trac and debug level info were filtered out.
$ ./main
hello, world
[2013-12-03 15:34:47.343223] [0x000007f680e76774] [info]    An informational severity message
[2013-12-03 15:34:47.343324] [0x000007f680e76774] [warning] A warning severity message
[2013-12-03 15:34:47.343341] [0x000007f680e76774] [error]   An error severity message
[2013-12-03 15:34:47.343356] [0x000007f680e76774] [fatal]   A fatal severity message

But could we change the filter in runtime. Let's try. Change the code now:

#include <iostream>
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
namespace logging = boost::log;
using namespace std;
void SetFilter1() {
  logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::info);
}
void SetFilter2() {
  logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::debug);
}
int main () {
  cout << "hello, world" << endl;
  SetFilter1();
  BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
  BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
  BOOST_LOG_TRIVIAL(info) << "An informational severity message";
  BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
  BOOST_LOG_TRIVIAL(error) << "An error severity message";
  BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";
 
  cout << "--------------------" << endl;
  SetFilter2();
  BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
  BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
  BOOST_LOG_TRIVIAL(info) << "An informational severity message";
  BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
  BOOST_LOG_TRIVIAL(error) << "An error severity message";
  BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";
}

Ok, watch the result now.

$ ./main
hello, world
[2013-12-03 15:37:54.399513] [0x000007fd7709a374] [info]    An informational severity message
[2013-12-03 15:37:54.399612] [0x000007fd7709a374] [warning] A warning severity message
[2013-12-03 15:37:54.399630] [0x000007fd7709a374] [error]   An error severity message
[2013-12-03 15:37:54.399644] [0x000007fd7709a374] [fatal]   A fatal severity message
--------------------
[2013-12-03 15:37:54.399666] [0x000007fd7709a374] [debug]   A debug severity message
[2013-12-03 15:37:54.399680] [0x000007fd7709a374] [info]    An informational severity message
[2013-12-03 15:37:54.399693] [0x000007fd7709a374] [warning] A warning severity message
[2013-12-03 15:37:54.399706] [0x000007fd7709a374] [error]   An error severity message
[2013-12-03 15:37:54.399719] [0x000007fd7709a374] [fatal]   A fatal severity message

It works. That means we can change the log filter without redeploying app. In my TCP server, it can listen to another port for admin usage and receive special message for changing log filter.

Good feature! I like this.



Use boost log step1

I get used to log library from CppCMS, it's simple. Boost releases the official log library since 1.54 version. After reading some doc, I decide to change to boost::log because the following reasons:
1. There are about 2,000 devices which connect to my TCP server, the TCP server saves all detail information. For example, when the device logs into the server, when it quits, what message is sent by device or server. And the amount of devices will increase in the future. Thus cause a few very large log files are created.

2. We will analyze the log file for profiling the problems of TCP server, also find out how our customers use their devices. When a problem occurs, administrator should receive waning email. And the product people could see the user behavior analysis report on web page to help them improve our product.

Obviously, boost new log library is designed to do the above jobs. That's what I expected.

Here is a example, most of code from boost log document:
http://www.boost.org/doc/libs/1_55_0/libs/log/doc/html/log/tutorial.html#log.tutorial.trivial

I am using Ubuntu 13.10 installed clang 3.4, also I built boost 1.55 libraries with clang & c++11 option.
Refer to my build command below:

./bootstrap.sh --with-libraries=system,filesystem,log,thread --with-toolset=clang
./b2 toolset=clang cxxflags="-std=c++11"

Now, create a main.cc file, write some code like this:
#include <iostream>
#include <boost/log/trivial.hpp>

using namespace std;

int main () {
  cout << "hello, world" << endl;
  BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
  BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
  BOOST_LOG_TRIVIAL(info) << "An informational severity message";
  BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
  BOOST_LOG_TRIVIAL(error) << "An error severity message";
  BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";
}

Compile code and link it to binary:
c++ -g -std=c++11 -Wall -DBOOST_LOG_DYN_LINK -o ./main.o -c ./main.cc
c++ -g -std=c++11 ./main.o -o main -rdynamic -lpthread -lboost_log -lboost_system -lboost_thread -lboost_filesystem

Now, execute ./main binary, it outputs:
$ ./main
hello, world
[2013-12-03 14:35:14.309093] [0x000007f362c17d74] [trace]   A trace severity message
[2013-12-03 14:35:14.309208] [0x000007f362c17d74] [debug]   A debug severity message
[2013-12-03 14:35:14.309224] [0x000007f362c17d74] [info]    An informational severity message
[2013-12-03 14:35:14.309237] [0x000007f362c17d74] [warning] A warning severity message
[2013-12-03 14:35:14.309250] [0x000007f362c17d74] [error]   An error severity message
[2013-12-03 14:35:14.309262] [0x000007f362c17d74] [fatal]   A fatal severity message


By default, the BOOST_LOG_TRIVIAL outputs all information to console. Some people ran into the problem
 undefined reference to `boost::log::v2s_mt_posix::trivial::logger::get()'

Add -DBOOST_LOG_DYN_LINK to solve this.


Followers

Contributors