

# 複数行またはスタックトレースの Amazon ECS ログメッセージの連結
<a name="firelens-concatanate-multiline"></a>

Fluent Bit バージョン 2.22.0 以降の AWS では、複数行フィルターが含まれています。複数行フィルターは、もともと 1 つのコンテキストに属していても、複数のレコードまたはログ行に分割されたログメッセージを連結するのに役立ちます。複数行フィルターの詳細については、「[Fluent Bit documentation](https://docs.fluentbit.io/manual/pipeline/filters/multiline-stacktrace)」(Fluent Bit ドキュメント) を参照してください。

以下は、分割されたログメッセージの一般的な例です。
+ スタックトレース 
+ 複数の行にログを出力するアプリケーション。
+ 指定されたランタイムの最大バッファサイズを超過した場合に、ログメッセージが分割されます GitHub の例、「[FireLens Example: Concatenate Partial/Split Container Logs](https://github.com/aws-samples/amazon-ecs-firelens-examples/tree/mainline/examples/fluent-bit/filter-multiline-partial-message-mode)」(FireLens の例:部分/分割コンテナログの連結) に従い、コンテナランタイムによって分割されたログメッセージを連結することができます。

## 必要な IAM 許可
<a name="iam-permissions"></a>

コンテナエージェントが Amazon ECR からコンテナイメージをプルし、コンテナがログを CloudWatch Logs にルーティングするために必要なIAM 許可を持っていることを確認する必要があります。

これらの許可には、次のロールが必要です。
+ タスク IAM ロール 
+ タスク実行 IAM ロールです。

次のアクセス許可が必要です。
+ `logs:CreateLogStream`
+ `logs:CreateLogGroup`
+ `logs:PutLogEvents`

## 複数行ログの設定を使用する場合を決定する
<a name="determine-filter"></a>

CloudWatch Logs コンソールで、デフォルトのログ設定で表示されるログスニペットの例を次に示します。`log` で始まる行を見て、複数行フィルタが必要かどうかを判断できます。コンテキストが同じ場合は、複数行ログ設定を使用できます。この例の場合、コンテキストは「com.myproject.model.MyProject」です。

```
2022-09-20T15:47:56:595-05-00                           {"container_id": "82ba37cada1d44d389b03e78caf74faa-EXAMPLE", "container_name": "example-app", "source=": "stdout", "log": ": "     at com.myproject.modele.(MyProject.badMethod.java:22)",
    {
      "container_id":  "82ba37cada1d44d389b03e78caf74faa-EXAMPLE",
      "container_name: ": "example-app",
      "source": "stdout",
      "log": ": "     at com.myproject.model.MyProject.badMethod(MyProject.java:22)",
      "ecs_cluster": "default",
      "ecs_task_arn": "arn:aws:region:123456789012:task/default/b23c940d29ed4714971cba72cEXAMPLE",
      "ecs_task_definition": "firelense-example-multiline:3"
     }
```

```
2022-09-20T15:47:56:595-05-00                           {"container_id": "82ba37cada1d44d389b03e78caf74faa-EXAMPLE", "container_name": "example-app", "stdout", "log": ": "     at com.myproject.modele.(MyProject.oneMoreMethod.java:18)",
    {
      "container_id":  "82ba37cada1d44d389b03e78caf74faa-EXAMPLE",
      "container_name: ": "example-app",
      "source": "stdout",
      "log": ": "     at com.myproject.model.MyProject.oneMoreMethod(MyProject.java:18)",
      "ecs_cluster": "default",
      "ecs_task_arn": "arn:aws:region:123456789012:task/default/b23c940d29ed4714971cba72cEXAMPLE,
      "ecs_task_definition": "firelense-example-multiline:3"
     }
```

複数行のログ設定を使用すると、出力は次の例のようになります。

```
2022-09-20T15:47:56:595-05-00                           {"container_id": "82ba37cada1d44d389b03e78caf74faa-EXAMPLE", "container_name": "example-app", "stdout",...
    {
      "container_id":  "82ba37cada1d44d389b03e78caf74faa-EXAMPLE",
      "container_name: ": "example-app",
      "source": "stdout",
      "log:    "September 20, 2022 06:41:48 Exception in thread \"main\" java.lang.RuntimeException: Something has gone wrong, aborting!\n    
    at com.myproject.module.MyProject.badMethod(MyProject.java:22)\n    at   
    at com.myproject.model.MyProject.oneMoreMethod(MyProject.java:18) com.myproject.module.MyProject.main(MyProject.java:6)",
      "ecs_cluster": "default",
      "ecs_task_arn": "arn:aws:region:123456789012:task/default/b23c940d29ed4714971cba72cEXAMPLE",
      "ecs_task_definition": "firelense-example-multiline:2"
     }
```

## オプションを解析して連結する
<a name="parse-multiline-log"></a>

ログを解析し、改行によって分割された行を連結するには、以下の 2 つのオプションのいずれかを使用します。
+ 同じメッセージに属する行を解析および連結するルールを含む独自のパーサーファイルを作成します。
+ Fluent Bit 組み込みパーサーを使用します。Fluent Bit 組み込みパーサーでサポートされている言語のリストについては、「[Fluent Bit ドキュメント](https://docs.fluentbit.io/manual/pipeline/filters/multiline-stacktrace)」を参照してください。

次のチュートリアルでは、各ユースケースの手順を説明します。この手順では、複数行を連結し、ログを Amazon CloudWatch に送信する方法について説明します。ログに別の宛先を指定できます。

### 例:作成したパーサーを使用する
<a name="customer-parser"></a>

このチュートリアルでは、次の手順を実行します。

1. Fluent Bit コンテナのイメージをビルドしてアップロードします。

1. 実行され、失敗し、複数行スタックトレースを生成するデモ複数行アプリケーションのイメージをビルドしてアップロードします。

1. タスク定義を作成して、タスクを起動する 

1. ログを表示して、複数の行にまたがるメッセージが連結されて表示されることを確認します。

**Fluent Bit コンテナのイメージをビルドしてアップロードします。**

このイメージには、正規表現を指定するパーサーファイルと、パーサーファイルを参照する設定ファイルが含まれます。

1. `FluentBitDockerImage` という名前のフォルダを作成します。

1. フォルダ内で、ログを解析し、同じメッセージに属する行を連結するためのルールを含むパーサーファイルを作成します。

   1. パーサーファイルに次のコンテンツを貼り付けます。

      ```
      [MULTILINE_PARSER]
          name          multiline-regex-test
          type          regex
          flush_timeout 1000
          #
          # Regex rules for multiline parsing
          # ---------------------------------
          #
          # configuration hints:
          #
          #  - first state always has the name: start_state
          #  - every field in the rule must be inside double quotes
          #
          # rules |   state name  | regex pattern                  | next state
          # ------|---------------|--------------------------------------------
          rule      "start_state"   "/(Dec \d+ \d+\:\d+\:\d+)(.*)/"  "cont"
          rule      "cont"          "/^\s+at.*/"                     "cont"
      ```

      正規表現パターンをカスタマイズするときは、正規表現エディタを使用して式をテストすることをお勧めします。

   1. `parsers_multiline.conf` という名前でファイルを保存します。

1. `FluentBitDockerImage` フォルダで、前のステップで作成したパーサーファイルを参照するカスタム設定ファイルを作成します。

   カスタム設定ファイルの詳細については、「*Amazon Elastic Container Service デベロッパーガイド*」の「[カスタム設定ファイルの指定](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/firelens-taskdef.html#firelens-taskdef-customconfig)」を参照してください。

   1. ファイルに次のコンテンツを貼り付けます。

      ```
      [SERVICE]
          flush                 1
          log_level             info
          parsers_file          /parsers_multiline.conf
          
      [FILTER]
          name                  multiline
          match                 *
          multiline.key_content log
          multiline.parser      multiline-regex-test
      ```
**注記**  
パーサーの絶対パスを使用する必要があります。

   1. `extra.conf` という名前でファイルを保存します。

1. `FluentBitDockerImage` フォルダで、Fluent Bit イメージと、作成したパーサーファイルと設定ファイルを使用して Dockerfile を作成します。

   1. ファイルに次のコンテンツを貼り付けます。

      ```
      FROM public.ecr.aws/aws-observability/aws-for-fluent-bit:latest
      
      ADD parsers_multiline.conf /parsers_multiline.conf
      ADD extra.conf /extra.conf
      ```

   1. `Dockerfile` という名前でファイルを保存します。

1. Dockerfile を使用して、パーサーとカスタム設定ファイルが含まれたカスタム Fluent Bit イメージをビルドします。
**注記**  
このファイルパスが FireLens によって使用される `/fluent-bit/etc/fluent-bit.conf` の場合を除いて、Dockerイメージの任意の場所にパーサーファイルと構成ファイルを配置できます。

   1. イメージを構築します: `docker build -t fluent-bit-multiline-image.`

      どこ: `fluent-bit-multiline-image` この例のイメージの名前です。

   1. 次を実行して、イメージが正しく作成されたことを確認します: `docker images —filter reference=fluent-bit-multiline-image` 

      成功すると、出力にイメージと `latest` タグが表示されます。

1. カスタムの Fluent Bit イメージを Amazon Elastic Container Registry にアップロードします。

   1. イメージを保存する Amazon ECR リポジトリを作成します: `aws ecr create-repository --repository-name fluent-bit-multiline-repo --region us-east-1`

      どこ: `fluent-bit-multiline-repo` は、リポジトリの名前です。`us-east-1` はこの例のリージョンです。

      出力には、新しいリポジトリの詳細が表示されます。

   1. 前のステップの `docker tag fluent-bit-multiline-image repositoryUri` の値で `repositoryUri` イメージにタグを付けます。

      例:`docker tag fluent-bit-multiline-image xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/fluent-bit-multiline-repo` 

   1. Docker イメージを実行して、正しく実行されたことを確認します: `docker images —filter reference=repositoryUri`

      出力では、リポジトリ名が fluent-bit-multiline-repo から `repositoryUri` に変わります。

   1. `aws ecr get-login-password` コマンドを実行し、認証先のレジストリ ID を指定して Amazon ECR を認証します: `aws ecr get-login-password | docker login --username AWS --password-stdin registry ID.dkr.ecr.region.amazonaws.com` 

      例:`ecr get-login-password | docker login --username AWS --password-stdin xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com`

      正常にログインメッセージが表示されます。

   1. Amazon ECR にイメージをプッシュします: `docker push registry ID.dkr.ecr.region.amazonaws.com/repository name` 

      例:`docker push xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/fluent-bit-multiline-repo`

**デモ複数行アプリケーションのイメージをビルドしてアップロードする**

このイメージには、アプリケーションを実行する Python スクリプトファイルと、サンプルログファイルが含まれます。

タスクを実行すると、アプリケーションは実行をシミュレートし、失敗してスタックトレースを作成します。

1. `multiline-app` という名前のフォルダを作成します: `mkdir multiline-app` 

1. Python スクリプトファイルを作成します。

   1. `multiline-app` フォルダで、ファイルを作成して、`main.py` という名前を付けます。

   1. ファイルに次のコンテンツを貼り付けます。

      ```
      import os
      import time
      file1 = open('/test.log', 'r')
      Lines = file1.readlines()
       
      count = 0
      
      for i in range(10):
          print("app running normally...")
          time.sleep(1)
      
      # Strips the newline character
      for line in Lines:
          count += 1
          print(line.rstrip())
      print(count)
      print("app terminated.")
      ```

   1. `main.py` ファイルを保存します。

1. サンプルのログファイルを作成します。

   1. `multiline-app` フォルダで、ファイルを作成して、`test.log` という名前を付けます。

   1. ファイルに次のコンテンツを貼り付けます。

      ```
      single line...
      Dec 14 06:41:08 Exception in thread "main" java.lang.RuntimeException: Something has gone wrong, aborting!
          at com.myproject.module.MyProject.badMethod(MyProject.java:22)
          at com.myproject.module.MyProject.oneMoreMethod(MyProject.java:18)
          at com.myproject.module.MyProject.anotherMethod(MyProject.java:14)
          at com.myproject.module.MyProject.someMethod(MyProject.java:10)
          at com.myproject.module.MyProject.main(MyProject.java:6)
      another line...
      ```

   1. `test.log` ファイルを保存します。

1. `multiline-app` フォルダで Dockerfile を作成します。

   1. ファイルに次のコンテンツを貼り付けます。

      ```
      FROM public.ecr.aws/amazonlinux/amazonlinux:latest
      ADD test.log /test.log
      
      RUN yum upgrade -y && yum install -y python3
      
      WORKDIR /usr/local/bin
      
      COPY main.py .
      
      CMD ["python3", "main.py"]
      ```

   1. `Dockerfile` ファイルを保存します。

1. Dockerfile を使用して、イメージをビルドします。

   1. イメージを構築します: `docker build -t multiline-app-image `

      どこ: `multiline-app-image` この例のイメージの名前です。

   1. 次を実行して、イメージが正しく作成されたことを確認します: `docker images —filter reference=multiline-app-image` 

      成功すると、出力にイメージと `latest` タグが表示されます。

1. イメージを Amazon Elastic コンテナレジストリにアップロードします。

   1. イメージを保存する Amazon ECR リポジトリを作成します: `aws ecr create-repository --repository-name multiline-app-repo --region us-east-1`

      どこ: `multiline-app-repo` は、リポジトリの名前です。`us-east-1` はこの例のリージョンです。

      出力には、新しいリポジトリの詳細が表示されます。次のステップで必要になるため、`repositoryUri` の値を書きとめておきます。

   1. 前のステップの `docker tag multiline-app-image repositoryUri` の値で `repositoryUri` イメージにタグを付けます。

      例:`docker tag multiline-app-image xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/multiline-app-repo` 

   1. Docker イメージを実行して、正しく実行されたことを確認します: `docker images —filter reference=repositoryUri`

      出力では、リポジトリ名が `multiline-app-repo` から `repositoryUri` に変更されます。

   1. Amazon ECR にイメージをプッシュします: `docker push aws_account_id.dkr.ecr.region.amazonaws.com/repository name` 

      例:`docker push xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/multiline-app-repo`

**タスク定義を作成して、タスクを実行する**

1. ファイル名 `multiline-task-definition.json` でタスク定義ファイルを作成します。

1. `multiline-task-definition.json` ファイルに次のコンテンツを貼り付けます。

   ```
   {
       "family": "firelens-example-multiline",
       "taskRoleArn": "task role ARN,
       "executionRoleArn": "execution role ARN",
       "containerDefinitions": [
           {
               "essential": true,
               "image": "aws_account_id.dkr.ecr.us-east-1.amazonaws.com/fluent-bit-multiline-image:latest",
               "name": "log_router",
               "firelensConfiguration": {
                   "type": "fluentbit",
                   "options": {
                       "config-file-type": "file",
                       "config-file-value": "/extra.conf"
                   }
               },
               "memoryReservation": 50
           },
           {
               "essential": true,
               "image": "aws_account_id.dkr.ecr.us-east-1.amazonaws.com/multiline-app-image:latest",
               "name": "app",
               "logConfiguration": {
                   "logDriver": "awsfirelens",
                   "options": {
                       "Name": "cloudwatch_logs",
                       "region": "us-east-1",
                       "log_group_name": "multiline-test/application",
                       "auto_create_group": "true",
                       "log_stream_prefix": "multiline-"
                   }
               },
               "memoryReservation": 100
           }
       ],
       "requiresCompatibilities": ["FARGATE"],
       "networkMode": "awsvpc",
       "cpu": "256",
       "memory": "512"
   }
   ```

   `multiline-task-definition.json` タスク定義で以下を置き換えます:

   1. `task role ARN`

      タスクロールの ARN を検索するには、IAM コンソールに移動します。**ロール**を選択し、作成した `ecs-task-role-for-firelens` タスクロールを検索します。ロールを選択し、**概要**セクションに表示される **ARN** をコピーします。

   1. `execution role ARN`

      実行ロールの ARN を検索するには、IAM コンソールに移動します。**ロール**を選択し、`ecsTaskExecutionRole` ロールを検索します。ロールを選択し、**概要**セクションに表示される **ARN** をコピーします。

   1. `aws_account_id`

      `aws_account_id` を検索するには、AWS マネジメントコンソール にログインします。右上のユーザー名を選択し、アカウント ID をコピーします。

   1. `us-east-1`

      必要に応じてリージョンを置換します。

1. タスク定義ファイルを登録します: `aws ecs register-task-definition --cli-input-json file://multiline-task-definition.json --region region` 

1. コンソールを[https://console.aws.amazon.com/ecs/v2](https://console.aws.amazon.com/ecs/v2)で開きます。

1. ナビゲーションペインで、[**タスク定義**] を選択し、上記のタスク定義の最初の行でタスク定義をこのファミリに登録したため、`firelens-example-multiline` ファミリを選択します。

1. 最新バージョンを選択します。

1. **[デプロイ]**、**[タスクを実行]** を選択します。

1. **[タスクを実行]** ページの **[クラスター]** でクラスターを選択し、**[ネットワーク]** の **[サブネット]** で、タスクに使用できるサブネットを選択します。

1. **[作成]** を選択します。

**Amazon CloudWatch の複数行のログメッセージが連結されて表示されることを確認する**

1. CloudWatch コンソールの [https://console.aws.amazon.com/cloudwatch/](https://console.aws.amazon.com/cloudwatch/) を開いてください。

1. ナビゲーションペインで、[**Logs (ログ)**] を展開して、[**Log groups (ロググループ)**] を選択します。

1. `multiline-test/applicatio` ロググループを選択します。

1. ログを選択します。メッセージを表示します。パーサーファイル内のルールに一致する行は連結され、1 つのメッセージとして表示されます。

   次のログスニペットは、単一の Java スタックトレースイベントで連結された行を示しています。

   ```
   {
       "container_id": "xxxxxx",
       "container_name": "app",
       "source": "stdout",
       "log": "Dec 14 06:41:08 Exception in thread \"main\" java.lang.RuntimeException: Something has gone wrong, aborting!\n    at com.myproject.module.MyProject.badMethod(MyProject.java:22)\n    at com.myproject.module.MyProject.oneMoreMethod(MyProject.java:18)\n    at com.myproject.module.MyProject.anotherMethod(MyProject.java:14)\n    at com.myproject.module.MyProject.someMethod(MyProject.java:10)\n    at com.myproject.module.MyProject.main(MyProject.java:6)",
       "ecs_cluster": "default",
       "ecs_task_arn": "arn:aws:ecs:us-east-1:xxxxxxxxxxxx:task/default/xxxxxx",
       "ecs_task_definition": "firelens-example-multiline:2"
   }
   ```

   次のログスニペットは、複数行のログメッセージを連結するように設定されていない Amazon ECS コンテナを実行する場合に、同じメッセージが 1 行でどのように表示されるかを示しています。

   ```
   {
       "log": "Dec 14 06:41:08 Exception in thread \"main\" java.lang.RuntimeException: Something has gone wrong, aborting!",
       "container_id": "xxxxxx-xxxxxx",
       "container_name": "app",
       "source": "stdout",
       "ecs_cluster": "default",
       "ecs_task_arn": "arn:aws:ecs:us-east-1:xxxxxxxxxxxx:task/default/xxxxxx",
       "ecs_task_definition": "firelens-example-multiline:3"
   }
   ```

### 例: Fluent Bit 組み込みパーサーを使用します。
<a name="fluent-bit-parser"></a>

このチュートリアルでは、次の手順を実行します。

1. Fluent Bit コンテナのイメージをビルドしてアップロードします。

1. 実行され、失敗し、複数行スタックトレースを生成するデモ複数行アプリケーションのイメージをビルドしてアップロードします。

1. タスク定義を作成して、タスクを起動する 

1. ログを表示して、複数の行にまたがるメッセージが連結されて表示されることを確認します。

**Fluent Bit コンテナのイメージをビルドしてアップロードします。**

このイメージには、Fluent Bit パーサーを参照する設定ファイルが含まれています。

1. `FluentBitDockerImage` という名前のフォルダを作成します。

1. `FluentBitDockerImage` フォルダで、Fluent Bit 組み込みパーサーファイルを参照するカスタム設定ファイルを作成します。

   カスタム設定ファイルの詳細については、「*Amazon Elastic Container Service デベロッパーガイド*」の「[カスタム設定ファイルの指定](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/firelens-taskdef.html#firelens-taskdef-customconfig)」を参照してください。

   1. ファイルに次のコンテンツを貼り付けます。

      ```
      [FILTER]
          name                  multiline
          match                 *
          multiline.key_content log
          multiline.parser      go
      ```

   1. `extra.conf` という名前でファイルを保存します。

1. `FluentBitDockerImage` フォルダで、Fluent Bit イメージと、作成したパーサーファイルと設定ファイルを使用して Dockerfile を作成します。

   1. ファイルに次のコンテンツを貼り付けます。

      ```
      FROM public.ecr.aws/aws-observability/aws-for-fluent-bit:latest
      ADD extra.conf /extra.conf
      ```

   1. `Dockerfile` という名前でファイルを保存します。

1. Dockerfile を使用して、カスタム設定ファイルを含めてカスタム Fluent Bit イメージをビルドします。
**注記**  
このファイルパスが FireLens によって使用される `/fluent-bit/etc/fluent-bit.conf` の場合を除いて、Docker イメージの任意の場所に設定ファイルを配置できます。

   1. イメージを構築します: `docker build -t fluent-bit-multiline-image.`

      どこ: `fluent-bit-multiline-image` この例のイメージの名前です。

   1. 次を実行して、イメージが正しく作成されたことを確認します: `docker images —filter reference=fluent-bit-multiline-image` 

      成功すると、出力にイメージと `latest` タグが表示されます。

1. カスタムの Fluent Bit イメージを Amazon Elastic Container Registry にアップロードします。

   1. イメージを保存する Amazon ECR リポジトリを作成します: `aws ecr create-repository --repository-name fluent-bit-multiline-repo --region us-east-1`

      どこ: `fluent-bit-multiline-repo` は、リポジトリの名前です。`us-east-1` はこの例のリージョンです。

      出力には、新しいリポジトリの詳細が表示されます。

   1. 前のステップの `docker tag fluent-bit-multiline-image repositoryUri` の値で `repositoryUri` イメージにタグを付けます。

      例:`docker tag fluent-bit-multiline-image xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/fluent-bit-multiline-repo` 

   1. Docker イメージを実行して、正しく実行されたことを確認します: `docker images —filter reference=repositoryUri`

      出力では、リポジトリ名が fluent-bit-multiline-repo から `repositoryUri` に変わります。

   1. `aws ecr get-login-password` コマンドを実行し、認証先のレジストリ ID を指定して Amazon ECR を認証します: `aws ecr get-login-password | docker login --username AWS --password-stdin registry ID.dkr.ecr.region.amazonaws.com` 

      例:`ecr get-login-password | docker login --username AWS --password-stdin xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com`

      正常にログインメッセージが表示されます。

   1. Amazon ECR にイメージをプッシュします: `docker push registry ID.dkr.ecr.region.amazonaws.com/repository name` 

      例:`docker push xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/fluent-bit-multiline-repo`

**デモ複数行アプリケーションのイメージをビルドしてアップロードする**

このイメージには、アプリケーションを実行する Python スクリプトファイルと、サンプルログファイルが含まれます。

1. `multiline-app` という名前のフォルダを作成します: `mkdir multiline-app` 

1. Python スクリプトファイルを作成します。

   1. `multiline-app` フォルダで、ファイルを作成して、`main.py` という名前を付けます。

   1. ファイルに次のコンテンツを貼り付けます。

      ```
      import os
      import time
      file1 = open('/test.log', 'r')
      Lines = file1.readlines()
       
      count = 0
      
      for i in range(10):
          print("app running normally...")
          time.sleep(1)
      
      # Strips the newline character
      for line in Lines:
          count += 1
          print(line.rstrip())
      print(count)
      print("app terminated.")
      ```

   1. `main.py` ファイルを保存します。

1. サンプルのログファイルを作成します。

   1. `multiline-app` フォルダで、ファイルを作成して、`test.log` という名前を付けます。

   1. ファイルに次のコンテンツを貼り付けます。

      ```
      panic: my panic
      
      goroutine 4 [running]:
      panic(0x45cb40, 0x47ad70)
        /usr/local/go/src/runtime/panic.go:542 +0x46c fp=0xc42003f7b8 sp=0xc42003f710 pc=0x422f7c
      main.main.func1(0xc420024120)
        foo.go:6 +0x39 fp=0xc42003f7d8 sp=0xc42003f7b8 pc=0x451339
      runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003f7e0 sp=0xc42003f7d8 pc=0x44b4d1
      created by main.main
        foo.go:5 +0x58
      
      goroutine 1 [chan receive]:
      runtime.gopark(0x4739b8, 0xc420024178, 0x46fcd7, 0xc, 0xc420028e17, 0x3)
        /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc420053e30 sp=0xc420053e00 pc=0x42503c
      runtime.goparkunlock(0xc420024178, 0x46fcd7, 0xc, 0x1000f010040c217, 0x3)
        /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc420053e70 sp=0xc420053e30 pc=0x42512e
      runtime.chanrecv(0xc420024120, 0x0, 0xc420053f01, 0x4512d8)
        /usr/local/go/src/runtime/chan.go:506 +0x304 fp=0xc420053f20 sp=0xc420053e70 pc=0x4046b4
      runtime.chanrecv1(0xc420024120, 0x0)
        /usr/local/go/src/runtime/chan.go:388 +0x2b fp=0xc420053f50 sp=0xc420053f20 pc=0x40439b
      main.main()
        foo.go:9 +0x6f fp=0xc420053f80 sp=0xc420053f50 pc=0x4512ef
      runtime.main()
        /usr/local/go/src/runtime/proc.go:185 +0x20d fp=0xc420053fe0 sp=0xc420053f80 pc=0x424bad
      runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc420053fe8 sp=0xc420053fe0 pc=0x44b4d1
      
      goroutine 2 [force gc (idle)]:
      runtime.gopark(0x4739b8, 0x4ad720, 0x47001e, 0xf, 0x14, 0x1)
        /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc42003e768 sp=0xc42003e738 pc=0x42503c
      runtime.goparkunlock(0x4ad720, 0x47001e, 0xf, 0xc420000114, 0x1)
        /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc42003e7a8 sp=0xc42003e768 pc=0x42512e
      runtime.forcegchelper()
        /usr/local/go/src/runtime/proc.go:238 +0xcc fp=0xc42003e7e0 sp=0xc42003e7a8 pc=0x424e5c
      runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003e7e8 sp=0xc42003e7e0 pc=0x44b4d1
      created by runtime.init.4
        /usr/local/go/src/runtime/proc.go:227 +0x35
      
      goroutine 3 [GC sweep wait]:
      runtime.gopark(0x4739b8, 0x4ad7e0, 0x46fdd2, 0xd, 0x419914, 0x1)
        /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc42003ef60 sp=0xc42003ef30 pc=0x42503c
      runtime.goparkunlock(0x4ad7e0, 0x46fdd2, 0xd, 0x14, 0x1)
        /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc42003efa0 sp=0xc42003ef60 pc=0x42512e
      runtime.bgsweep(0xc42001e150)
        /usr/local/go/src/runtime/mgcsweep.go:52 +0xa3 fp=0xc42003efd8 sp=0xc42003efa0 pc=0x419973
      runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003efe0 sp=0xc42003efd8 pc=0x44b4d1
      created by runtime.gcenable
        /usr/local/go/src/runtime/mgc.go:216 +0x58
      one more line, no multiline
      ```

   1. `test.log` ファイルを保存します。

1. `multiline-app` フォルダで Dockerfile を作成します。

   1. ファイルに次のコンテンツを貼り付けます。

      ```
      FROM public.ecr.aws/amazonlinux/amazonlinux:latest
      ADD test.log /test.log
      
      RUN yum upgrade -y && yum install -y python3
      
      WORKDIR /usr/local/bin
      
      COPY main.py .
      
      CMD ["python3", "main.py"]
      ```

   1. `Dockerfile` ファイルを保存します。

1. Dockerfile を使用して、イメージをビルドします。

   1. イメージを構築します: `docker build -t multiline-app-image `

      どこ: `multiline-app-image` この例のイメージの名前です。

   1. 次を実行して、イメージが正しく作成されたことを確認します: `docker images —filter reference=multiline-app-image` 

      成功すると、出力にイメージと `latest` タグが表示されます。

1. イメージを Amazon Elastic コンテナレジストリにアップロードします。

   1. イメージを保存する Amazon ECR リポジトリを作成します: `aws ecr create-repository --repository-name multiline-app-repo --region us-east-1`

      どこ: `multiline-app-repo` は、リポジトリの名前です。`us-east-1` はこの例のリージョンです。

      出力には、新しいリポジトリの詳細が表示されます。次のステップで必要になるため、`repositoryUri` の値を書きとめておきます。

   1. 前のステップの `docker tag multiline-app-image repositoryUri` の値で `repositoryUri` イメージにタグを付けます。

      例:`docker tag multiline-app-image xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/multiline-app-repo` 

   1. Docker イメージを実行して、正しく実行されたことを確認します: `docker images —filter reference=repositoryUri`

      出力では、リポジトリ名が `multiline-app-repo` から `repositoryUri` に変更されます。

   1. Amazon ECR にイメージをプッシュします: `docker push aws_account_id.dkr.ecr.region.amazonaws.com/repository name` 

      例:`docker push xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/multiline-app-repo`

**タスク定義を作成して、タスクを実行する**

1. ファイル名 `multiline-task-definition.json` でタスク定義ファイルを作成します。

1. `multiline-task-definition.json` ファイルに次のコンテンツを貼り付けます。

   ```
   {
       "family": "firelens-example-multiline",
       "taskRoleArn": "task role ARN,
       "executionRoleArn": "execution role ARN",
       "containerDefinitions": [
           {
               "essential": true,
               "image": "aws_account_id.dkr.ecr.us-east-1.amazonaws.com/fluent-bit-multiline-image:latest",
               "name": "log_router",
               "firelensConfiguration": {
                   "type": "fluentbit",
                   "options": {
                       "config-file-type": "file",
                       "config-file-value": "/extra.conf"
                   }
               },
               "memoryReservation": 50
           },
           {
               "essential": true,
               "image": "aws_account_id.dkr.ecr.us-east-1.amazonaws.com/multiline-app-image:latest",
               "name": "app",
               "logConfiguration": {
                   "logDriver": "awsfirelens",
                   "options": {
                       "Name": "cloudwatch_logs",
                       "region": "us-east-1",
                       "log_group_name": "multiline-test/application",
                       "auto_create_group": "true",
                       "log_stream_prefix": "multiline-"
                   }
               },
               "memoryReservation": 100
           }
       ],
       "requiresCompatibilities": ["FARGATE"],
       "networkMode": "awsvpc",
       "cpu": "256",
       "memory": "512"
   }
   ```

   `multiline-task-definition.json` タスク定義で以下を置き換えます:

   1. `task role ARN`

      タスクロールの ARN を検索するには、IAM コンソールに移動します。**ロール**を選択し、作成した `ecs-task-role-for-firelens` タスクロールを検索します。ロールを選択し、**概要**セクションに表示される **ARN** をコピーします。

   1. `execution role ARN`

      実行ロールの ARN を検索するには、IAM コンソールに移動します。**ロール**を選択し、`ecsTaskExecutionRole` ロールを検索します。ロールを選択し、**概要**セクションに表示される **ARN** をコピーします。

   1. `aws_account_id`

      `aws_account_id` を検索するには、AWS マネジメントコンソール にログインします。右上のユーザー名を選択し、アカウント ID をコピーします。

   1. `us-east-1`

      必要に応じてリージョンを置換します。

1. タスク定義ファイルを登録します: `aws ecs register-task-definition --cli-input-json file://multiline-task-definition.json --region us-east-1` 

1. コンソールを[https://console.aws.amazon.com/ecs/v2](https://console.aws.amazon.com/ecs/v2)で開きます。

1. ナビゲーションペインで、[**タスク定義**] を選択し、上記のタスク定義の最初の行でタスク定義をこのファミリに登録したため、`firelens-example-multiline` ファミリを選択します。

1. 最新バージョンを選択します。

1. **[デプロイ]**、**[タスクを実行]** を選択します。

1. **[タスクを実行]** ページの **[クラスター]** でクラスターを選択し、**[ネットワーク]** の **[サブネット]** で、タスクに使用できるサブネットを選択します。

1. **[作成]** を選択します。

**Amazon CloudWatch の複数行のログメッセージが連結されて表示されることを確認する**

1. CloudWatch コンソールの [https://console.aws.amazon.com/cloudwatch/](https://console.aws.amazon.com/cloudwatch/) を開いてください。

1. ナビゲーションペインで、[**Logs (ログ)**] を展開して、[**Log groups (ロググループ)**] を選択します。

1. `multiline-test/applicatio` ロググループを選択します。

1. ログを選択し、メッセージを表示します。パーサーファイル内のルールに一致する行は連結され、1 つのメッセージとして表示されます。

   次のログスニペットは、単一の Go スタックトレースイベントで連結された行を示しています。

   ```
   {
       "log": "panic: my panic\n\ngoroutine 4 [running]:\npanic(0x45cb40, 0x47ad70)\n  /usr/local/go/src/runtime/panic.go:542 +0x46c fp=0xc42003f7b8 sp=0xc42003f710 pc=0x422f7c\nmain.main.func1(0xc420024120)\n  foo.go:6 +0x39 fp=0xc42003f7d8 sp=0xc42003f7b8 pc=0x451339\nruntime.goexit()\n  /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003f7e0 sp=0xc42003f7d8 pc=0x44b4d1\ncreated by main.main\n  foo.go:5 +0x58\n\ngoroutine 1 [chan receive]:\nruntime.gopark(0x4739b8, 0xc420024178, 0x46fcd7, 0xc, 0xc420028e17, 0x3)\n  /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc420053e30 sp=0xc420053e00 pc=0x42503c\nruntime.goparkunlock(0xc420024178, 0x46fcd7, 0xc, 0x1000f010040c217, 0x3)\n  /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc420053e70 sp=0xc420053e30 pc=0x42512e\nruntime.chanrecv(0xc420024120, 0x0, 0xc420053f01, 0x4512d8)\n  /usr/local/go/src/runtime/chan.go:506 +0x304 fp=0xc420053f20 sp=0xc420053e70 pc=0x4046b4\nruntime.chanrecv1(0xc420024120, 0x0)\n  /usr/local/go/src/runtime/chan.go:388 +0x2b fp=0xc420053f50 sp=0xc420053f20 pc=0x40439b\nmain.main()\n  foo.go:9 +0x6f fp=0xc420053f80 sp=0xc420053f50 pc=0x4512ef\nruntime.main()\n  /usr/local/go/src/runtime/proc.go:185 +0x20d fp=0xc420053fe0 sp=0xc420053f80 pc=0x424bad\nruntime.goexit()\n  /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc420053fe8 sp=0xc420053fe0 pc=0x44b4d1\n\ngoroutine 2 [force gc (idle)]:\nruntime.gopark(0x4739b8, 0x4ad720, 0x47001e, 0xf, 0x14, 0x1)\n  /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc42003e768 sp=0xc42003e738 pc=0x42503c\nruntime.goparkunlock(0x4ad720, 0x47001e, 0xf, 0xc420000114, 0x1)\n  /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc42003e7a8 sp=0xc42003e768 pc=0x42512e\nruntime.forcegchelper()\n  /usr/local/go/src/runtime/proc.go:238 +0xcc fp=0xc42003e7e0 sp=0xc42003e7a8 pc=0x424e5c\nruntime.goexit()\n  /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003e7e8 sp=0xc42003e7e0 pc=0x44b4d1\ncreated by runtime.init.4\n  /usr/local/go/src/runtime/proc.go:227 +0x35\n\ngoroutine 3 [GC sweep wait]:\nruntime.gopark(0x4739b8, 0x4ad7e0, 0x46fdd2, 0xd, 0x419914, 0x1)\n  /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc42003ef60 sp=0xc42003ef30 pc=0x42503c\nruntime.goparkunlock(0x4ad7e0, 0x46fdd2, 0xd, 0x14, 0x1)\n  /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc42003efa0 sp=0xc42003ef60 pc=0x42512e\nruntime.bgsweep(0xc42001e150)\n  /usr/local/go/src/runtime/mgcsweep.go:52 +0xa3 fp=0xc42003efd8 sp=0xc42003efa0 pc=0x419973\nruntime.goexit()\n  /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003efe0 sp=0xc42003efd8 pc=0x44b4d1\ncreated by runtime.gcenable\n  /usr/local/go/src/runtime/mgc.go:216 +0x58",
       "container_id": "xxxxxx-xxxxxx",
       "container_name": "app",
       "source": "stdout",
       "ecs_cluster": "default",
       "ecs_task_arn": "arn:aws:ecs:us-east-1:xxxxxxxxxxxx:task/default/xxxxxx",
       "ecs_task_definition": "firelens-example-multiline:2"
   }
   ```

   次のログスニペットは、複数行のログメッセージを連結するように設定されていない ECS コンテナを実行する場合に、同じイベントがどのように表示されるかを示しています。ログフィールドには 1 行が含まれます。

   ```
   {
       "log": "panic: my panic",
       "container_id": "xxxxxx-xxxxxx",
       "container_name": "app",
       "source": "stdout",
       "ecs_cluster": "default",
       "ecs_task_arn": "arn:aws:ecs:us-east-1:xxxxxxxxxxxx:task/default/xxxxxx",
       "ecs_task_definition": "firelens-example-multiline:3"
   ```

**注記**  
ログが標準出力ではなくログファイルに移動する場合は、フィルターではなく [Tail 入力プラグイン](https://docs.fluentbit.io/manual/pipeline/inputs/tail#multiline-support)で `multiline.parser` および `multiline.key_content` 構成パラメーターを指定することをお勧めします。