The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

ログの一元管理(Graylog、Logstash、Fluentd)

このガイドでは、Graylog、Logstash(Elastic StackやELK - Elasticsearch、Logstash、Kibanaの内部)、Fluentd(EFK - Elasticsearch、Fluentd、Kibanaの内部)のような集中型ログ管理システムにログを送信する方法を説明します。

ログを一元化管理する方法はたくさんあります(Kubernetesを使用している場合、最も簡単な方法はコンソールにログを記録して、クラスター管理者にクラスター内のログマネージャーを統合するように依頼することです)。このガイドでは、TCPまたはUDPを使用してGraylog Extended Log Format (GELF)でログを送信できる quarkus-logging-gelf エクステンションを使用して外部ツールにログを送信する方法を説明します。

quarkus-logging-gelf エクステンションは、Quarkusが使用するロギングバックエンド(jboss-logmanager)にGELFログハンドラーを追加します。デフォルトでは無効になっており、有効にした場合も別のハンドラーも使用している場合(デフォルトではコンソールハンドラーが有効)、ログは両方のハンドラーに送信されます。

前提条件

このガイドを完成させるには、以下が必要です:

  • 約15分

  • IDE

  • JDK 11+ がインストールされ、 JAVA_HOME が適切に設定されていること

  • Apache Maven 3.8.1+

  • Docker and Docker Compose or Podman, and Docker Compose

  • 使用したい場合、 Quarkus CLI

  • ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること

アプリケーション例

以下の例はすべて、以下の手順で作成できる同じアプリケーションの例を基にしています。

quarkus-logging-gelf のエクステンションを持つアプリケーションを作成します。以下のコマンドで作成することができます:

CLI
quarkus create app org.acme:gelf-logging \
    --extension=resteasy-reactive,logging-gelf \
    --no-code
cd gelf-logging

Gradleプロジェクトを作成するには、 --gradle または --gradle-kotlin-dsl オプションを追加します。

Quarkus CLIのインストール方法については、Quarkus CLIガイドをご参照ください。

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:2.11.1.Final:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=gelf-logging \
    -Dextensions="resteasy-reactive,logging-gelf" \
    -DnoCode
cd gelf-logging

Gradleプロジェクトを作成するには、 -DbuildTool=gradle または -DbuildTool=gradle-kotlin-dsl オプションを追加します。

すでにQuarkusプロジェクトが設定されている場合は、プロジェクトのベースディレクトリーで以下のコマンドを実行することで、プロジェクトに logging-gelf エクステンションを追加することができます:

CLI
quarkus extension add 'logging-gelf'
Maven
./mvnw quarkus:add-extension -Dextensions="logging-gelf"
Gradle
./gradlew addExtension --extensions="logging-gelf"

これにより、ビルドファイルに以下の依存関係が追加されます:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-logging-gelf</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-logging-gelf")

デモのために、文をログに記録するだけのエンドポイントを作成します。これをアプリケーション内で行う必要はありません。

import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.GET;
import javax.ws.rs.Path;

import org.jboss.logging.Logger;

@Path("/gelf-logging")
@ApplicationScoped
public class GelfLoggingResource {
    private static final Logger LOG = Logger.getLogger(GelfLoggingResource.class);

    @GET
    public void log() {
        LOG.info("Some useful log message");
    }

}

GELF ログハンドラーを構成して、ポート 12201 の外部 UDP エンドポイントにログを送信します:

quarkus.log.handler.gelf.enabled=true
quarkus.log.handler.gelf.host=localhost
quarkus.log.handler.gelf.port=12201

ログをGraylogに送信

Graylogにログを送信するには、まずGraylogスタックを構成するコンポーネントを起動する必要があります:

  • MongoDB

  • Elasticsearch

  • Graylog

これは、以下の docker-compose.yml ファイルから、 docker-compose up -d で起動できます:

version: '3.2'

services:
  elasticsearch:
    image: docker.io/elastic/elasticsearch:7.16.3
    ports:
      - "9200:9200"
    environment:
      ES_JAVA_OPTS: "-Xms512m -Xmx512m"
      discovery.type: "single-node"
    networks:
      - graylog

  mongo:
    image: mongo:4.0
    networks:
      - graylog

  graylog:
    image: graylog/graylog:4.3.0
    ports:
      - "9000:9000"
      - "12201:12201/udp"
      - "1514:1514"
    environment:
      GRAYLOG_HTTP_EXTERNAL_URI: "http://127.0.0.1:9000/"
      # CHANGE ME (must be at least 16 characters)!
      GRAYLOG_PASSWORD_SECRET: "forpasswordencryption"
      # Password: admin
      GRAYLOG_ROOT_PASSWORD_SHA2: "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918"
    networks:
      - graylog
    depends_on:
      - elasticsearch
      - mongo

networks:
  graylog:
    driver: bridge

次に、GraylogでUDP入力を作成する必要があります。これは、http://localhost:9000 の GraylogのWebコンソール(System → Input → Select GELF UDP)から、 またはAPI経由で行うことができます。

この curl の例では、Graylog (admin/admin) からのデフォルトのログインを使用し、GELF UDP タイプの新しい入力を作成します。

curl -H "Content-Type: application/json" -H "Authorization: Basic YWRtaW46YWRtaW4=" -H "X-Requested-By: curl" -X POST -v -d \
'{"title":"udp input","configuration":{"recv_buffer_size":262144,"bind_address":"0.0.0.0","port":12201,"decompress_size_limit":8388608},"type":"org.graylog2.inputs.gelf.udp.GELFUDPInput","global":true}' \
http://localhost:9000/api/system/inputs

アプリケーションを起動すると、Graylog内にログが届くのがわかるはずです。

Logstash / Elastic Stack (ELK)にログを送信

LogstashにはGELF形式を理解できるInputプラグインがデフォルトで入ってるので、まずこのプラグインを有効にするパイプラインを作成します。

$HOME/pipelines/gelf.conf に以下のファイルを作成します:

input {
  gelf {
    port => 12201
  }
}
output {
  stdout {}
  elasticsearch {
    hosts => ["http://elasticsearch:9200"]
  }
}

最後に、Elastic Stackを構成するコンポーネントを起動します:

  • Elasticsearch

  • Logstash

  • Kibana

これは、以下の docker-compose.yml ファイルから、 docker-compose up -d で起動できます:

# Launch Elasticsearch
version: '3.2'

services:
  elasticsearch:
    image: docker.io/elastic/elasticsearch:7.16.3
    ports:
      - "9200:9200"
      - "9300:9300"
    environment:
      ES_JAVA_OPTS: "-Xms512m -Xmx512m"
      discovery.type: "single-node"
    networks:
      - elk

  logstash:
    image: docker.io/elastic/logstash:7.16.3
    volumes:
      - source: $HOME/pipelines
        target: /usr/share/logstash/pipeline
        type: bind
    ports:
      - "12201:12201/udp"
      - "5000:5000"
      - "9600:9600"
    networks:
      - elk
    depends_on:
      - elasticsearch

  kibana:
    image: docker.io/elastic/kibana:7.16.3
    ports:
      - "5601:5601"
    networks:
      - elk
    depends_on:
      - elasticsearch

networks:
  elk:
    driver: bridge

アプリケーションを起動すると、Elastic Stack内にログが届いているのが確認できるはずです。 http://localhost:5601/ で利用できるKibanaを使って、ログにアクセスすることができます。

Fluentd(EFK)へのログ送信

まず、必要なプラグインである elasticsearch と input-elf を使って Fluentd イメージを作成します。 以下のDockerfileを使用して、 fluentd ディレクトリの中に作成します。

FROM fluent/fluentd:v1.3-debian
RUN ["gem", "install", "fluent-plugin-elasticsearch", "--version", "3.7.0"]
RUN ["gem", "install", "fluent-plugin-input-gelf", "--version", "0.3.1"]

イメージをビルドするか、docker-composeにビルドを任せることができます。

次に、$HOME/fluentd/fluent.conf の中にfluentd 設定ファイルを作成する必要があります

<source>
  type gelf
  tag example.gelf
  bind 0.0.0.0
  port 12201
</source>

<match example.gelf>
  @type elasticsearch
  host elasticsearch
  port 9200
  logstash_format true
</match>

最後に、EFKスタックを構成するコンポーネントを起動します:

  • Elasticsearch

  • Fluentd

  • Kibana

これは、以下の docker-compose.yml ファイルから、 docker-compose up -d で起動できます:

version: '3.2'

services:
  elasticsearch:
    image: docker.io/elastic/elasticsearch:7.16.3
    ports:
      - "9200:9200"
      - "9300:9300"
    environment:
      ES_JAVA_OPTS: "-Xms512m -Xmx512m"
      discovery.type: "single-node"
    networks:
      - efk

  fluentd:
    build: fluentd
    ports:
      - "12201:12201/udp"
    volumes:
      - source: $HOME/fluentd
        target: /fluentd/etc
        type: bind
    networks:
      - efk
    depends_on:
      - elasticsearch

  kibana:
    image: docker.io/elastic/kibana:7.16.3
    ports:
      - "5601:5601"
    networks:
      - efk
    depends_on:
      - elasticsearch

networks:
  efk:
    driver: bridge

アプリケーションを起動すると、EFK内にログが到着しているのが確認できるはずです: それらにアクセスするには、 http://localhost:5601/ で利用可能な Kibana を使用することができます。

GELFの代替:Syslogの使用

Syslog入力を使ってFluentdにログを送ることもできます。GELF入力とは対照的に、Syslog入力は1つのイベントで複数行のログをレンダリングすることができないため、Quarkusで実装されているGELF入力を使用することをお勧めします。

まず、elasticsearchプラグインでFluentdイメージを作成する必要があります。 以下の Dockerfile を使用することができます。このファイルは fluentd ディレクトリー内に作成する必要があります。

FROM fluent/fluentd:v1.3-debian
RUN ["gem", "install", "fluent-plugin-elasticsearch", "--version", "3.7.0"]

次に、fluentd設定ファイルを $HOME/fluentd/fluent.conf 内に作成する必要があります

<source>
  @type syslog
  port 5140
  bind 0.0.0.0
  message_format rfc5424
  tag system
</source>

<match **>
  @type elasticsearch
  host elasticsearch
  port 9200
  logstash_format true
</match>

次に、EFKスタックを構成するコンポーネントを起動します:

  • Elasticsearch

  • Fluentd

  • Kibana

これは、以下の docker-compose.yml ファイルから、 docker-compose up -d で起動できます:

version: '3.2'

services:
  elasticsearch:
    image: docker.io/elastic/elasticsearch:7.16.3
    ports:
      - "9200:9200"
      - "9300:9300"
    environment:
      ES_JAVA_OPTS: "-Xms512m -Xmx512m"
    networks:
      - efk

  fluentd:
    build: fluentd
    ports:
      - "5140:5140/udp"
    volumes:
      - source: $HOME/fluentd
        target: /fluentd/etc
        type: bind
    networks:
      - efk
    depends_on:
      - elasticsearch

  kibana:
    image: docker.io/elastic/kibana:7.16.3
    ports:
      - "5601:5601"
    networks:
      - efk
    depends_on:
      - elasticsearch

networks:
  efk:
    driver: bridge

最後に、Syslogを使用してEFKにログを送信するようにアプリケーションを設定します:

quarkus.log.syslog.enable=true
quarkus.log.syslog.endpoint=localhost:5140
quarkus.log.syslog.protocol=udp
quarkus.log.syslog.app-name=quarkus
quarkus.log.syslog.hostname=quarkus-test

アプリケーションを起動すると、EFK内にログが到着しているのが確認できるはずです: それらにアクセスするには、 http://localhost:5601/ で利用可能な Kibana を使用することができます。

Elasticsearch インデックスの検討

デフォルトでは、Elasticsearch は未知のフィールド(インデックス設定で無効になっていない場合)を、そのタイプを検出して自動的にマッピングすることに注意してください。これは、ログパラメータ(デフォルトで含まれている)を使用する場合や、MDC インクルード(デフォルトでは無効)を有効にする場合、最初のログがインデックス内のメッセージパラメータ(または MDC パラメータ)フィールドのタイプを定義するため、厄介なことになる可能性があります。

次のようなケースを想像してみてください:

LOG.info("some {} message {} with {} param", 1, 2, 3);
LOG.info("other {} message {} with {} param", true, true, true);

ログメッセージのパラメータを有効にすると、Elasticsearch に送信される最初のログメッセージは int 型の MessageParam0 パラメータが設定され、これにより integer 型のフィールドでインデックスが構成されます。2 番目のメッセージが Elasticsearch に届くと、ブーリアン値 true を持つ MessageParam0 パラメータが設定され、これによりインデックス作成エラーが発生します。

この制限を回避するには、 quarkus.log.handler.gelf.include-log-message-parameters=false を設定して logging-gelf を介したログメッセージパラメータの送信を無効にするか、Elasticsearch インデックスにテキストまたはキーワードとしてこれらのフィールドを保存するように設定して、Elasticsearch は int/boolean から String への変換を自動的に行います。

Graylogについては以下のドキュメントを参照してください(ただし、他のセントラルロギングスタックにも同じ問題があります)。 カスタム インデックス マッピング

設定リファレンス

設定は通常の application.properties ファイルから行います。

ビルド時に固定される設定プロパティ - それ以外の設定プロパティは実行時に上書き可能

Configuration property

タイプ

デフォルト

Determine whether to enable the GELF logging handler

Environment variable: QUARKUS_LOG_HANDLER_GELF_ENABLED

boolean

false

Hostname/IP-Address of the Logstash/Graylog Host By default it uses UDP, prepend tcp: to the hostname to switch to TCP, example: "tcp:localhost"

Environment variable: QUARKUS_LOG_HANDLER_GELF_HOST

string

localhost

The port

Environment variable: QUARKUS_LOG_HANDLER_GELF_PORT

int

12201

GELF version: 1.0 or 1.1

Environment variable: QUARKUS_LOG_HANDLER_GELF_VERSION

string

1.1

Whether to post Stack-Trace to StackTrace field.

Environment variable: QUARKUS_LOG_HANDLER_GELF_EXTRACT_STACK_TRACE

boolean

true

Only used when extractStackTrace is true. A value of 0 will extract the whole stack trace. Any positive value will walk the cause chain: 1 corresponds with exception.getCause(), 2 with exception.getCause().getCause(), …​ Negative throwable reference walk the exception chain from the root cause side: -1 will extract the root cause, -2 the exception wrapping the root cause, …​

Environment variable: QUARKUS_LOG_HANDLER_GELF_STACK_TRACE_THROWABLE_REFERENCE

int

0

Whether to perform Stack-Trace filtering

Environment variable: QUARKUS_LOG_HANDLER_GELF_FILTER_STACK_TRACE

boolean

false

Java date pattern, see java.text.SimpleDateFormat

Environment variable: QUARKUS_LOG_HANDLER_GELF_TIMESTAMP_PATTERN

string

yyyy-MM-dd HH:mm:ss,SSS

The logging-gelf log level.

Environment variable: QUARKUS_LOG_HANDLER_GELF_LEVEL

Level

ALL

Name of the facility.

Environment variable: QUARKUS_LOG_HANDLER_GELF_FACILITY

string

jboss-logmanager

Whether to include all fields from the MDC.

Environment variable: QUARKUS_LOG_HANDLER_GELF_INCLUDE_FULL_MDC

boolean

false

Maximum message size (in bytes). If the message size is exceeded, the appender will submit the message in multiple chunks.

Environment variable: QUARKUS_LOG_HANDLER_GELF_MAXIMUM_MESSAGE_SIZE

int

8192

Include message parameters from the log event

Environment variable: QUARKUS_LOG_HANDLER_GELF_INCLUDE_LOG_MESSAGE_PARAMETERS

boolean

true

Include source code location

Environment variable: QUARKUS_LOG_HANDLER_GELF_INCLUDE_LOCATION

boolean

true

Origin hostname

Environment variable: QUARKUS_LOG_HANDLER_GELF_ORIGIN_HOST

string

Bypass hostname resolution. If you didn’t set the originHost property, and resolution is disabled, the value “unknown” will be used as hostname

Environment variable: QUARKUS_LOG_HANDLER_GELF_SKIP_HOSTNAME_RESOLUTION

boolean

false

Post additional fields

タイプ

デフォルト

Additional field value.

Environment variable: QUARKUS_LOG_HANDLER_GELF_ADDITIONAL_FIELD__FIELD_NAME__VALUE

string

required

Additional field type specification. Supported types: String, long, Long, double, Double and discover. Discover is the default if not specified, it discovers field type based on parseability.

Environment variable: QUARKUS_LOG_HANDLER_GELF_ADDITIONAL_FIELD__FIELD_NAME__TYPE

string

discover

このエクステンションは、 logstash-gelf ライブラリを使用しており、システムプロパティを介してより多くの設定オプションを使用できます。ドキュメントにはこちらからアクセスできます: https://logging.paluch.biz/ .