このチュートリアルでは、Spring Bootプロジェクトでロギングシステムを接続して構成し、Filebeatを使用してログをELKに送信する方法について説明します。このガイドは、エントリーレベルの開発者を対象としています。
ロギングとその理由
私が最初にプログラマーとして働き始めたとき、私の先輩の1人は、「ログがなければ、何もありません」と繰り返すのが好きでした。実際、テストベンチでの最初のバグ、または産業環境での最悪の事態に直面した場合、最初に必要なのは、アプリケーションログとそれらへの簡単なアクセスです。アプリケーションの開発者はログ自体に責任があり、システムの動作がログに記録され、システムで何が起こっているのか、そして最も重要なことに、システムの何が悪いのかをいつでも理解できるようにする必要があります。
次の質問は、ログへのアクセスの利便性です。通常、ローカルテスト中は、アプリケーションコンソールとテストベンチ(サーバー上の特別なログファイル)にログが表示されます。毎回スタンドに接続し、必要なディレクトリを検索してそこからログファイルを読み取るのは便利で安全ですか?実践はノーを示しています、そしてこれはログへの便利なアクセスとそれらの中の重要な情報の検索を提供する多くの製品が解決するように設計されている2番目の問題です。今日は、そのような製品のグループの1つであるいわゆるELKスタック(Elasticsearch-Logstash-Kibana)について簡単に説明し、Filebeat(ログをELKに配信するための便利なメカニズムを提供するオープンソース製品)について詳しく説明します。
ELKについての3行
- Logstash-ログの受信、変更
- Elasticsearch-ストレージと検索
- きばな-ディスプレイ
Filebeatはそれと何の関係がありますか?
FilebeatはELKにデータ配信を提供し、アプリケーションの隣に展開されます。多くの場合、ログファイルや他のチャネルからデータを読み取るようにLogstashを構成するよりも便利です。
良いアプローチは、マイクロサービスのグループに共通のELKをデプロイし、マイクロサービスごとに独自のFilebeatをデプロイすることです(特に簡単ではないため)。これにより、マイクロサービスのログが読み取られ、共通のELKに送信されます。
このソリューションは、後でローカル環境に実装しようとします。
練習
Java 8
ApacheMaven3.6
Spring Boot 2.3.4.RELEASE
Docker
Spring Boot App
Spring Boot , . /
Spring Boot Spring Initalizr
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>6.4</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
- spring-boot-starter-web — ..
- logstash-logback-encoder —
- lombok — ,
pom.xml
Spring Boot :
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
:
@Slf4j
@Service
public class LogGenerator {
public void generate(int count) {
log.info("Start generating logs");
LongStream.range(0, count)
.forEach(i -> log.info("Log {}", i));
}
}
0 count
, :
@Slf4j
@RestController
@RequiredArgsConstructor
public class LogController {
private final LogGenerator generator;
@GetMapping("/generate")
public ResponseEntity test(@RequestParam(name = "count", defaultValue = "0") Integer count) {
log.info("Test request received with count: {}", count);
generator.generate(count);
return ResponseEntity.ok("Success!");
}
}
GET :
http://localhost:8080/generate?count=10
. resources logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d [%thread] %-5level %logger{35} - [%mdc] - %msg%n</pattern>
</encoder>
</appender>
<appender name="filebeatAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>./log/application.log</file>
<append>true</append>
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>./log/application.%d.%i.log.gz</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
</rollingPolicy>
</appender>
<root level="INFO">
<appender-ref ref="consoleAppender" />
<appender-ref ref="filebeatAppender" />
</root>
</configuration>
:
- consoleAppender —
- filebeatAppender — , LogstashEncoder logstash-logback-encoder
— JSON , Logstash. Logstash .
, ./log/application.log log . . maxFileSize
Filebeat .
:
@Slf4j
@Component
public class LogFilter extends OncePerRequestFilter {
private static final String REQUEST_ID = "requestId";
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String requestId = request.getHeader(REQUEST_ID);
if (requestId == null) {
requestId = UUID.randomUUID().toString();
}
MDC.put(REQUEST_ID, requestId);
try {
log.info("Started process request with {} : {}", REQUEST_ID, requestId);
filterChain.doFilter(request, response);
} finally {
MDC.clear();
}
}
}
, ( requestId), MDC (Mapped Diagnostic Context)
MDC.put(REQUEST_ID, requestId);
finally MDC
MDC.clear();
, , . Kibana .
, , :
mvn spring-boot:run
, application.log
curl "localhost:8080/generate?count=10"
Success!, application.log :
{
"@timestamp":"2020-10-17T22:39:45.595+03:00",
"@version":"1",
"message":"Writing [\"Success!\"]",
"logger_name":"org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor",
"thread_name":"http-nio-8080-exec-3",
"level":"INFO",
"level_value":10000,
"requestId":"77abe5ac-4458-4dc3-9f4e-a7320979e3ae"
}
Filebeat
Filebeat
7.9.2 macOS
.
Filebeat filebeat.xml
, inputs output:
inputs:
- enabled: true
encoding: utf-8
exclude_files: ['\.gz$']
json:
add_error_key: true
keys_under_root: true
overwrite_keys: true
paths:
- { }/*.log
scan_frequency: 10s
type: log
, Filebeat . :
- keys_under_root — json json, Filebeat Logstash
- overwrite_keys —
- add_error_key — Filebeat error.message error.type: json json .
output:
logstash:
hosts:
- localhost:5044
ssl:
certificate_authorities:
- { }/logstash-beats.crt
, Filebeat . Logstash ( )
ssl.certificate_authorities Logstash ( ), .
Filebeat, , .. ELK .
ELK . , docker ELK sebp/elk logstash-beats.crt. certificate_authorities filebeat.xml
docker-compose :
version: '3.7'
services:
elk:
image: sebp/elk
ports:
- "5601:5601" #kibana
- "9200:9200" #elastic
- "5044:5044" #logstash
ELK Filebeat , macOS :
./filebeat -e run
? , LogstashEncoder JSON application.log, Filebeat , Logstash. Kibana.
Kibana :
http://localhost:5601/
Discover:
:
Kibana index ELK . ! Filebeat , . :
curl "localhost:8080/generate?count=100"
:
:
. requestId MDC :
これで、インデックスの[検出]タブで、フィールドの表示を構成し、1つのリクエスト内のすべてのログが同じrequestIdによって結合されていることを確認できます。JSONフィールドを展開して、Filebeatから受信したメッセージの全文を表示できます。