C++のLoggingライブラリ plog

札幌C++勉強会の [もくもく会#10](http://sapporocpp.connpass.com/event/26938)でもくもくと調べていたことをここに記す。本家Readme.mdを読んで、そのままメモっただけ。

# Plog - portable and simple log for C++
小さくまとまっているログ出力ライブラリ。

## インストール
header-onlyなのでgithubからダウンロードしてインクルードパス通すだけです。

```shell-session
$ git clone https://github.com/SergiusTheBest/plog.git
```

たとえばビルドツールにSConstructを使うなら

```py:SConstruct
 # SConstruct
 Program("hello_plog", "hello_plog.cpp", CPPPATH="plog/include")
```

```cpp:hello_plog.cpp
#include<plog/Log.h>

int main() {
    plog::init(plog::debug, "Output.txt");

    LOG_DEBUG << "Hello, plog!";
    return 0;
}
```

コンパイル

```shell-session
$ scons
```

出力はこんな感じになります

```text:Output.txt
2016-02-15 20:34:51.006 DEBUG [14279] [main@6] Hello, plog!
```

日付、ログタイプ(この例ではDEBUG)、行番号、メッセージがでます。
他にCSV形式とかカスタムフォーマットも作れるみたいです。

# Severityの指定

Severityを指定することで、指定したレベル以上のログだけを出すことができます。
Severityは次のものがあります。

```cpp
enum Severity
{
    none = 0,
    fatal = 1,
    error = 2,
    warning = 3,
    info = 4,
    debug = 5,
    verbose = 6
};
```

(Severityだとfatalの数値の方が大きくなるべきなんじゃないかなあ・・)

ログ出力時におけるSeverityレベルの指定方法は、次の３つです。

1. shortマクロ
2. longマクロ
3. 関数っぽく引数で指定

```cpp
#include<plog/Log.h>
int main() {
        plog::init(plog::debug, "Output.txt");
        LOGD << "Hello, plog!";             // 1. shortマクロ
        LOG_DEBUG << "Hello, plog!";        // 2. longマクロ
        LOG(plog::debug) << "Hello, plog!"; // 3. 関数っぽく引数で指定
        return 0;
}
```

DEBUG以外のSeverityを指定するときは、Longマクロなら(DEBUGも含めSeverityの順に)

```
LOG_VERBOSE, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_FATAL
```

で、これらに対応するShortマクロは

```
LOGV, LOGD, LOGI, LOGW, LOGE, LOGF
```

です。

出力したいSeverityのレベルはplogの初期化時に指定します。
例えば、

```cpp
#include<plog/Log.h>
int main() {
        plog::init(plog::warning, "Output.txt"); // warning以上を出力させる。
        LOGV << "verbose";
        LOGD << "debug";
        LOGI << "info";
        LOGW << "warning";
        LOGE << "error";
        LOGF << "fatal";
        return 0;
}
```

というコードを書くと、出力は

```txt:Output.txt
2016-02-15 21:32:12.920 WARN  [14455] [main@7] warning
2016-02-15 21:32:12.920 ERROR [14455] [main@8] error
2016-02-15 21:32:12.920 FATAL [14455] [main@9] fatal
```

となります。Severityがwarning以上のログだけが出ていますね。

## CSV形式での出力

出力ファイル名の拡張子を```.csv```にするとCSV形式で出力してくれます。ただし(デフォルトの)区切り文字はカンマではなくセミコロンです。
例

```cpp
plog::init(plog::warning, "Output.csv");
// 他は上記コードと同じ
```

```txt:Output.csv
Date;Time;Severity;TID;This;Function;Message
2016/02/15;21:38:51.297;WARN;14492;0;main@7;"warning"
2016/02/15;21:38:51.298;ERROR;14492;0;main@8;"error"
2016/02/15;21:38:51.298;FATAL;14492;0;main@9;"fatal"
```

ちなみに、拡張子を変えてフォーマットが変わるのは```.csv```だけです。

## 条件式でログ出力を切り替える

条件式付きでログ出力することができます。

```cpp
LOG_DEBUG_IF(cond) << "debug";         // longマクロ
LOGD_IF(cond) << "debug";              // shortマクロ
LOG_IF(plog::debug, cond) << "debug";  // 関数っぽく
```


plogのソース追っていくと結局

```cpp
if (condition) LOG_(instance, severity)
```

とif文に展開するマクロに落ちていたので、ありがたみがあるかは人それぞれな気もします。。

複数のログを出したいときは、スコープでまとめることができます。

```cpp
IF_LOG(plog::debug) // Severityがdebug以上のときはこのスコープ内を実行する
{
    for (int i = 0; i < vec.size(); ++i)
    {
        LOGD << "vec[" << i << "]: " << vec[i];
    }
}
```

やっぱりif文に展開するマクロに、、(ry

## 途中でログファイルに出力するSeverityレベルを変える場合

通常は初期化時にログの出力レベルを指定します
例

```cpp
plog::init(plog::warning, "Output.txt");
```

しかし、初期化時以外で指定したかったり、途中で変更したい時があるかもしれません。そういうときは、次のようにします。

```cpp
plog::get()->setMaxSeverity(plog::debug);
```

## ログをターミナル(コンソール)に出力したい場合。

ログの出力先をファイルではなく、標準出力にしたいことがあるかもしれません。そんな時は初期化でファイル名ではなく、```Appender```を指定します。コンソールに色付きで出力する```Appender```は次のように指定します。

```cpp
#include<plog/Log.h>
#include<plog/Appenders/ColorConsoleAppender.h>

int main() {
    static plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender;
    plog::init(plog::debug, &consoleAppender);
    LOGD << "debug message to stdout with color";
    return 0;
}
```
**注意！ このようにAppenderはstaticで宣言すべきです。** 
出力がカラーである必要がない場合は、```ConsoleAppender```もあります。

## 複数の出力先を指定したい場合

コンソールとファイルの両方に出力したい場合もあるかもしれません。そんな時は```Appender```を追加することができます。

```cpp
#include<plog/Log.h>
#include<plog/Appenders/ColorConsoleAppender.h>

int main() {
    static plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender;
    static plog::RollingFileAppender<plog::TxtFormatter> fileAppender("Output.txt");
    plog::init(plog::debug, &consoleAppender).addAppender(&fileAppender);
    LOGD << "debug message to stdout and file";
    return 0;
}
```

## その他

もくもく会の終了時刻になったためこれで終わり。