Skywalkingとは
Skywalkingは、特にマイクロサービス、クラウドネイティブ、およびコンテナ化されたアプリケーションを対象としたオープンソースの観測プラットフォームです。このプラットフォームは、分散システムのトレーシング、モニタリング、診断機能を提供します。
JDKバージョンとの対応関係
SkyWalking 8.xはJDK 1.8以上、9.xはJDK 11以上のバージョンが必要です。
Skywalkingのダウンロード
公式サイトからAPMとAgentsをダウンロードできます:https://skywalking.apache.org/downloads/
古いバージョンのAPMや特定のJavaバージョン向けのAgentsは以下のURLから入手可能です:
- 他のバージョンのAPM: https://archive.apache.org/dist/skywalking/
- 他のJavaバージョンのAgents: https://archive.apache.org/dist/skywalking/java-agent/
本記事では、APM 8.9.1とAgents 8.9.0を使用します。
データストア
Skywalkingは複数のデータストアオプションをサポートしています:
- H2(デフォルト:再起動時にデータが失われる)
- Elasticsearch(最も一般的な選択肢)
- MySQL
- TiDB
OAPの設定ファイル(config/application.yml)でストレージを選択します。以下はElasticsearchの設定例です:
storage:
selector: ${SW_STORAGE:elasticsearch}
elasticsearch:
clusterNodes: ${SW_STORAGE_ES_CLUSTER_NODES:localhost:9200}
user: ${SW_ES_USER:""}
password: ${SW_ES_PASSWORD:""}
indexShardsNumber: ${SW_STORAGE_ES_INDEX_SHARDS_NUMBER:1}
indexReplicasNumber: ${SW_STORAGE_ES_INDEX_REPLICAS_NUMBER:1}
Skywalkingの起動
APMディレクトリ内のbinフォルダに移動し、startup.batを管理者権限で実行します。これにより、次の2つのプロセスが開始されます:
- Skywalking-Collector: gRPC/HTTP経由でクライアントからの情報を収集します。デフォルトポートはgRPC 11800, HTTP 12800。
- Skywalking-Webapp: 管理画面。デフォルトポートは8080。
ブラウザでhttp://localhost:8080/にアクセスすると管理画面が表示されます。
プローブの配置
Agents 8.9.0をプロジェクトに配置します。
IDEAでのプローブの配置
VM optionsに次のパラメータを追加します:
-javaagent:D:\path\to\skywalking-agent.jar -Dskywalking.agent.service_name=your-service-name -Dskywalking.collector.backend_service=127.0.0.1:11800
コマンドラインでの起動
java -javaagent:D:\path\to\skywalking-agent.jar -Dskywalking.agent.service_name=your-service-name -Dskywalking.collector.backend_service=localhost:11800 -jar your-app.jar
Linux環境でのシェルスクリプトによる起動
#!/bin/bash AGENT_PATH="/path/to/skywalking-agent" JAR_PATH="/path/to/your-app.jar" SERVICE_NAME="your-service-name" COLLECTOR_BACKEND_SERVICE="localhost:11800" JAVA_AGENT="-javaagent:$AGENT_PATH/skywalking-agent.jar -Dskywalking.agent.service_name=$SERVICE_NAME -Dskywalking.collector.backend_service=$COLLECTOR_BACKEND_SERVICE" java $JAVA_AGENT -jar $JAR_PATH
Spring Bootの起動
IDEAから探針を設定してSpring Bootを起動すると、コンソールに接続ログが出力され、SkywalkingのUIでも確認できます。
ログの構成
まず、Maven依存関係を追加します:
<dependency> <groupId>org.apache.skywalking</groupId> <artifactId>apm-toolkit-logback-1.x</artifactId> <version>9.0.0</version> </dependency>
次に、resourcesディレクトリ下にlogback-spring.xmlを作成します:
<configuration debug="false">
<property name="LOG_HOME" value="D:/logs/"></property>
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
<pattern>%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} [%X{tid}] %clr([%-10.10thread]){faint} %clr(%-5level) %clr(%-50.50logger{50}:%-3L){cyan} %clr(-){faint} %msg%n</pattern>
</layout>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
入出力パラメータの取得
Agent設定での実現
Agentの設定ファイル(agent.config)で次の設定を有効にします:
plugin.tomcat.collect_http_params=true plugin.springmvc.collect_http_params=true plugin.httpclient.collect_http_params=true
ただし、この方法ではGETリクエストのパラメータのみ取得可能で、POSTリクエストのパラメータは取得できません。
TraceとFilterを使った実現
Maven依存関係を追加します:
<dependency> <groupId>org.apache.skywalking</groupId> <artifactId>apm-toolkit-trace</artifactId> <version>9.0.0</version> </dependency>
次に、HttpFilterとContentCachingRequestWrapperを使用して、リクエストとレスポンスの内容をログに出力するフィルターを作成します:
package com.example.springbootfull.quartztest.Filter;
import lombok.extern.slf4j.Slf4j;
import org.apache.skywalking.apm.toolkit.trace.ActiveSpan;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
@Slf4j
@Component
public class ApmHttpInfo extends HttpFilter {
private static final Set<String> IGNORED_HEADERS = new HashSet<>();
static {
Set<String> ignoredHeaders = new HashSet<>();
ignoredHeaders.addAll(
java.util.Arrays.asList(
"Content-Type",
"User-Agent",
"Accept",
"Cache-Control",
"Postman-Token",
"Host",
"Accept-Encoding",
"Connection",
"Content-Length"
).stream()
.map(String::toUpperCase)
.collect(Collectors.toList())
);
IGNORED_HEADERS = ignoredHeaders;
}
@Override
public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
try {
filterChain.doFilter(requestWrapper, responseWrapper);
} finally {
try {
StringBuilder sb = new StringBuilder("curl")
.append(" -X ").append(request.getMethod())
.append(" ").append(request.getRequestURL().toString());
if (StringUtils.hasLength(request.getQueryString())) {
sb.append("?").append(request.getQueryString());
}
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
if (!IGNORED_HEADERS.contains(headerName.toUpperCase())) {
sb.append(" -H '").append(headerName).append(": ").append(request.getHeader(headerName)).append("'");
}
}
String body = new String(requestWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);
if (StringUtils.hasLength(body)) {
sb.append(" -d '").append(body).append("'");
}
ActiveSpan.tag("input", sb.toString());
String responseBody = new String(responseWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);
ActiveSpan.tag("output", responseBody);
} catch (Exception e) {
log.warn("fail to build http log", e);
} finally {
responseWrapper.copyBodyToResponse();
}
}
}
}
これにより、GETとPOSTリクエストの入出力パラメータをログに出力できます。