Amazon FreeRTOSの使い方：Logging

# はじめに

Amazon FreeRTOSにあらかじめ用意されているデモプログラムを動かすことはできたものの、細かな使い方がわからなかったので、少しずつ調べています。この記事はログ出力について。

【今回の環境】

- Amazon FreeRTOS Version 202012.00
- ESP32-DevKitC-32D
- Toolchain v5.2.0
- Windows 10

【フォルダ構成】
こうなってます。

```
C:\IoT\
 └ ESP-FreeRTOS\
 　  ├ Tools\                 ... Toolkit
 　  ├ FreeRTOS\              ... Amazonからダウンロードしたもの
 　  │  └ libraries\
 　  │  　  ├ c_sdk\
 　  │  　  │  └ standard\
 　  │  　  │  　  └ common\
 　  │  　  └ logging\
 　  └ Projects\
 　  　  └ 00_test\           ... テストプログラム
 　  　  　  ├ CMakeLists.txt
 　  　  　  └ src\
 　  　  　  　  └ main.c
```

# 概要

色々なサンプルプログラムを見ると、主に以下３つのログ出力方法が使われているように思います。

- `vLoggingPrintf()`関数を使う方法
    - 関数宣言は以下のファイル。
        - `libraries\logging\include\iot_logging_task.h`
    - 渡されたメッセージはFreeRTOS KernelのQueueを介して順次出力される。
    - 事前に`xLoggingTaskInitialize()`関数で初期設定が必要。

- `LogError()`, `LogInfo()`等の#defineマクロを使う方法
    - FreeRTOSのドキュメントではこちらを紹介している。
        - https://www.freertos.org/logging.html
    - マクロ定義は以下のファイル。
        - `libraries\logging\include\logging_stack.h`
    - ビルド時に`LIBRARY_LOG_LEVEL`の値を変えることでログ出力レベルを変えられる。
    - 内部で`vLoggingPrintf()`関数を利用している。したがって、事前に`xLoggingTaskInitialize()`関数での初期化が必要。

- IotLogError(), IotLogInfo()等の#defineマクロを使う方法
    - AWSのドキュメントではこちらを紹介している。
        - https://docs.aws.amazon.com/ja_jp/freertos/latest/userguide/lib-logging.html
    - マクロ定義は以下のファイル。
        - `libraries\c_sdk\standard\common\include\iot_logging_setup.h`
    - ビルド時に`LIBRARY_LOG_LEVEL`の値を変えることでログ出力レベルを変えられる。
    - 内部で`vLoggingPrintf()`関数を利用している。したがって、事前に`xLoggingTaskInitialize()`関数での初期化が必要。

以降はそれぞれの使用例です。

# `vLoggingPrintf()`関数を使う場合

```CMakeList.txt
cmake_minimum_required(VERSION 3.13)

set(PROJ_NAME log_test)

project(${PROJ_NAME})

add_executable(${PROJ_NAME}
  src/main.c
  src/aws_demo.c
)

set(IDF_PROJECT_EXECUTABLE ${PROJ_NAME})

set(AFR_BOARD espressif.esp32_devkitc CACHE INTERNAL "")
add_subdirectory(../../FreeRTOS FreeRTOS)

target_link_libraries(${PROJ_NAME}
  PRIVATE
  AFR::logging
)
```
注：`vApplicationGetIdleTaskMemory()`と`vApplicationGetTimerTaskMemory()`の二つの関数定義は、`demos\demo_runner\aws_demo.c`を流用。ただし同ファイル中の以下二つの行は不要なのでコメントアウト。

- `#include "iot_config.h"`
- `#include "aws_clientcredential.h"`

```src\main.c
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"

#include "iot_logging_task.h"

#define mainLOGGING_MESSAGE_QUEUE_LENGTH    ( 32 )
#define mainLOGGING_TASK_STACK_SIZE         ( configMINIMAL_STACK_SIZE * 4 )

void app_main()
{
    xLoggingTaskInitialize( mainLOGGING_TASK_STACK_SIZE,
                            tskIDLE_PRIORITY + 5,
                            mainLOGGING_MESSAGE_QUEUE_LENGTH );

    for (int i = 1; i <= 5; i++) {
        vLoggingPrintf("Logging Test: %d\r\n", i);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

extern void esp_vApplicationTickHook();
void IRAM_ATTR vApplicationTickHook()
{
    esp_vApplicationTickHook();
}

extern void esp_vApplicationIdleHook();
void vApplicationIdleHook()
{
    esp_vApplicationIdleHook();
}

void vApplicationDaemonTaskStartupHook( void )
{
}
```

出力はこうなりました。

```
0 0 [main] Logging Test: 1
1 100 [main] Logging Test: 2
2 200 [main] Logging Test: 3
3 300 [main] Logging Test: 4
4 400 [main] Logging Test: 5
```

# `LogError()`, `LogInfo()`等の#defineマクロを使う場合

`vLoggingPrintf()`関数を使う場合との差分のみ記載します。

```src/main.c
#define LIBRARY_LOG_LEVEL   LOG_INFO
#define LIBRARY_LOG_NAME    ( "TEST" )

#include "iot_logging_task.h"
#include "logging_stack.h"

(...snip...)

    LogInfo( ( "Logging Test: %d\r\n", i ) );
```

出力はこうなりました。

```
0 0 [main] [INFO] [TEST] [main.c:21] 1 0 [main] Logging Test: 1
2 0 [main]
3 100 [main] [INFO] [TEST] [main.c:21] 4 100 [main] Logging Test: 2
5 100 [main]
6 200 [main] [INFO] [TEST] [main.c:21] 7 200 [main] Logging Test: 3
8 200 [main]
9 300 [main] [INFO] [TEST] [main.c:21] 10 300 [main] Logging Test: 4
11 300 [main]
12 400 [main] [INFO] [TEST] [main.c:21] 13 400 [main] Logging Test: 5
14 400 [main]
```

# `IotLogError()`, `IotLogInfo()`等の#defineマクロを使う場合

`vLoggingPrintf()`関数を使う場合との差分のみ記載します。

```src/main.c
#define LIBRARY_LOG_LEVEL   IOT_LOG_INFO
#define LIBRARY_LOG_NAME    ( "TEST" )

#include "iot_logging_setup.h"
#include "iot_logging_task.h"

(...snip...)

    IotLogInfo( "Logging Test: %d", i );
```

出力はこうなりました。

```
0 0 [main] [INFO ][TEST][0] Logging Test: 1
1 100 [main] [INFO ][TEST][1000] Logging Test: 2
2 200 [main] [INFO ][TEST][2000] Logging Test: 3
3 300 [main] [INFO ][TEST][3000] Logging Test: 4
4 400 [main] [INFO ][TEST][4000] Logging Test: 5
```