JacksonとLombokでbooleanのフィールドをシリアライズする
表題の備忘録。
JacksonはJavaBeansのルールに従うため、boolean型のフィールドをシリアライズする際に、デフォルトの設定ではgetter名からisを取り除いてプロパティ名を推測する。そのため、以下のDemo1とDemo2はどちらも{"active":true}になる。
ObjectMapper objectMapper = new ObjectMapper(); System.out.println(objectMapper.writeValueAsString(new Demo1(true))); System.out.println(objectMapper.writeValueAsString(new Demo2(true)));
public class Demo1 { private final boolean active; public Demo1(boolean active) { this.active = active; } public boolean isActive() { return active; } } public class Demo2 { private final boolean isActive; public Demo2(boolean isActive) { this.isActive = isActive; } public boolean isActive() { return isActive; } }
Jacksonの設定を変えずにJSONのプロパティ名をisActiveにしたい場合は、@JsonPropertyを使うか、getter名をisIsActive()またはgetIsActive()にする必要がある。
public class Demo1 { private final boolean active; public Demo1(boolean active) { this.active = active; } @JsonProperty("isActive") public boolean isActive() { return active; } } public class Demo2 { private final boolean isActive; public Demo2(boolean isActive) { this.isActive = isActive; } public boolean isIsActive() { return isActive; } }
次にLombokを使った場合。
boolean型のフィールド名がisXxxのような場合、Lombokはgetter名にis prefixを付けないため、このisActiveフィールドのgetter名はisActiveになり、プロパティはactiveになる。
@Value public class Demo { boolean isActive; } ↓ public final class Demo { private final boolean isActive; public boolean isActive() { return isActive; } }
このクラスのisActiveフィールドに@JsonPropertyを追加した場合は、フィールドに@JsonPropertyが付与され、かつgetterも生成されるため、JSONのプロパティはisActiveとactiveの2つになる。(この現象はboolean型のみ発生するらしい)
@Value public class Demo { @JsonProperty("isActive") boolean isActive; } ↓ public final class Demo { @JsonProperty("isActive") private final boolean isActive; public boolean isActive() { return isActive; } }
上記の現象を回避するため、フィールドではなくgetterにのみ@JsonPropertyを付与したいので、@GetterのonMethodにアノテーションを指定する。
@Value public class Demo { @Getter(onMethod_ = {@JsonProperty("isActive")}) boolean isActive; } ↓ public final class Demo { private final boolean isActive; @JsonProperty("isActive") public boolean isActive() { return isActive; } }
ちなみに、Boolean型の場合はgetter名のprefixはgetになるため、このフィールドのgetter名はgetIsActive()になり、プロパティはisActiveになる。
@Value public class Demo { Boolean isActive; } ↓ public final class Demo { private final Boolean isActive; public Boolean getIsActive() { return isActive; } }
参考
CompletableFutureの備忘録
CompletableFutureの使い方をよく忘れるので備忘録。
Memo
staticメソッド
completedFuture
CompletableFuture<U> completedFuture(U value)
CompletableFuture.completedFuture("OK")
supplyAsync
CompletableFuture<U> supplyAsync(Supplier<U> supplier)
CompletableFuture.supplyAsync(() -> "OK")
runAsync
CompletableFuture<Void> runAsync(Runnable runnable)
CompletableFuture.runAsync(() -> System.out.println("OK"))
allOf
CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
戻り値はVoid。
CompletableFuture.allOf(CompletableFuture.runAsync(() -> System.out.println("OK")), CompletableFuture.runAsync(() -> System.out.println("OK")))
anyOf
CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
戻り値を返せる。
CompletableFuture.anyOf(CompletableFuture.completedFuture(1), CompletableFuture.completedFuture(2)) .thenAccept(System.out::println);
thenXxxx
thenRun
CompletableFuture<Void> thenRun(Runnable action)
Runnableを実行する。
CompletableFuture.runAsync(() -> System.out.println("OK")) .thenRun(() -> System.out.println("OK"));
thenAccept
CompletableFuture<Void> thenAccept(Consumer<? super T> action)
Consumerを実行する。
CompletableFuture.completedFuture("OK")
.thenAccept(System.out::println);
thenApply
CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
Functionを実行する。
CompletableFuture.completedFuture("OK") .thenApply(value -> value + "OK") .thenAccept(System.out::println);
thenCompose
CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn)
Functionを実行する。thenApplyとの違いは、CompletionStageを返す。
CompletableFuture.completedFuture("OK") .thenCompose(value -> CompletableFuture.completedFuture(value + "OK")) .thenAccept(System.out::println);
thenCombine
CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn)
2つの実行結果をBiFunctionで受け取る。
CompletableFuture.completedFuture("OK") .thenCombine(CompletableFuture.completedFuture("OK"), (value1, value2) -> value1 + value2) .thenAccept(System.out::println);
xxxxEither
runAfterEither
CompletableFuture<Void> runAfterEither(CompletionStage<?> other, Runnable action)
どちらかが実行された後で、Runnableを実行する。
CompletableFuture.runAsync(() -> System.out.println(1)) .runAfterEither(CompletableFuture.runAsync(() -> System.out.println(2)), () -> System.out.println("OK"));
acceptEither
CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other,
Consumer<? super T> action)
どちらかの実行結果をConsumerで受け取る。
CompletableFuture.completedFuture(1) .acceptEither(CompletableFuture.completedFuture(2), System.out::println);
applyToEither
CompletableFuture<U> applyToEither(CompletionStage<? extends T> other,
Function<? super T, U> fn)
どちらかの実行結果をFunctionで受け取る。
CompletableFuture.completedFuture(1) .applyToEither(CompletableFuture.completedFuture(2), value -> value) .thenAccept(System.out::println);
xxxxBoth
runAfterBoth
CompletableFuture<Void> runAfterBoth(CompletionStage<?> other, Runnable action)
両方が実行された後で、Runnableを実行する。
CompletableFuture.runAsync(() -> System.out.println(1)) .runAfterBoth(CompletableFuture.runAsync(() -> System.out.println(2)), () -> System.out.println("OK"));
thenAcceptBoth
CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action)
両方の実行結果をBiConsumerで受け取る。
CompletableFuture.completedFuture(1) .thenAcceptBoth(CompletableFuture.completedFuture(2), (value1, value2) -> System.out.println(value1 + value2));
例外ハンドリング
exceptionally
CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)
例外発生時にのみ呼ばれて、値を返せる。
CompletableFuture.failedFuture(new RuntimeException()) .exceptionally(throwable -> "Error") .thenAccept(System.out::println)
handle
CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn)
例外発生時に値を返せる。Tを受け取ってUを返すので、別の型を返すこともできる。
CompletableFuture.failedFuture(new RuntimeException()) .handle((value, throwable) -> { if (throwable == null) { return value; } else { return "Error"; } }) .thenAccept(System.out::println);
whenComplete
CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action)
例外発生時の処理を追加できるが、BiConsumerなので値は返せない。
CompletableFuture.failedFuture(new RuntimeException()) .whenComplete((value, throwable) -> { if (throwable == null) { System.out.println(value); } else { System.err.println("Error"); } });
参考
WiresharkでHTTPSを復号化する
Wiresharkでhttps通信を復号化するときの備忘録。
今回はMacOSのChromeとlocalhostのnginxでhttpsの通信をする。
準備
証明書
mkcertでlocalhostの証明書と秘密鍵を用意する。コマンドを実行するとlocalhost.pemとlocalhost-key.pemが作成されるので、これをnginxに設定する。
% brew install mkcert
% mkcert -install
% mkcert localhost
NGINX
localhost.conf
server {
listen 443 ssl;
server_name localhost;
root /usr/share/nginx/html;
ssl_certificate localhost.pem;
ssl_certificate_key localhost-key.pem;
}
compose.yaml
services:
nginx:
image: nginx:latest
ports:
- "443:443"
volumes:
- ./localhost.conf:/etc/nginx/conf.d/localhost.conf
- ./localhost-key.pem:/etc/nginx/localhost-key.pem
- ./localhost.pem:/etc/nginx/localhost.pem
Dockerでnginxを起動する。
% docker compose up
Google Chrome
環境変数SSLKEYLOGFILEかコマンドライン引数の--ssl-key-log-fileでSSL session keyのログファイルを指定して、コマンドラインからChromeを起動する。今回はコマンドライン引数でsslkey.logファイルを指定してChromeを起動する。
% /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --ssl-key-log-file=./sslkey.log
Wireshark
Preferences -> Protocols -> TLS から「(Pre)-Master-Secret log filename」にsslkey.logファイルを設定後、Wiresharkを再起動する。

Demo
コマンドラインから起動したChromeで https://localhost にアクセスすると、復号化されたHTTPSのパケットが確認できる。

参考
PrometheusのHTTP Service Discoveryを試す
PrometheusのHTTP Service Discoveryを試したときのメモ。
準備
Prometheus
DockerのPrometheusとNode exporter2台を使う。
prometheus.yml
global:
scrape_interval: 30s
scrape_configs:
- job_name: prometheus
static_configs:
- targets: ['host.docker.internal:9090']
- job_name: http-sd
http_sd_configs:
- url: 'http://host.docker.internal:8080/http-sd'
compose.yml
services:
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
node-exporter1:
image: quay.io/prometheus/node-exporter:latest
ports:
- "9101:9100"
node-exporter2:
image: quay.io/prometheus/node-exporter:latest
ports:
- "9102:9100"
PromethusとNod exporterを起動する。
% docker compose up
HTTP SD endpoint
HTTP SD endpointはspring bootで実装する。
build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.0.2'
id 'io.spring.dependency-management' version '1.1.0'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'io.micrometer:micrometer-registry-prometheus'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
application.yml
management:
endpoints:
web:
exposure:
include: prometheus
HttpSdController.java
ドキュメントに記載されているフォーマットで、Node exporter2台とこのspring bootアプリの情報を返す。
import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RestController; @RestController public class HttpSdController { private static final Logger logger = LoggerFactory.getLogger(HttpSdController.class); public record HttpSd(List<String> targets, Map<String, String> labels) {} @GetMapping("/http-sd") public List<HttpSd> httpSd(@RequestHeader("X-Prometheus-Refresh-Interval-Seconds") String refreshInterval) { logger.info("Refresh-Interval-Seconds: {}", refreshInterval); return List.of(new HttpSd(List.of("host.docker.internal:9101", "host.docker.internal:9102"), Map.of("job", "node-exporter")), new HttpSd(List.of("host.docker.internal:8080"), Map.of("job", "spring-boot", "__metrics_path__", "/actuator/prometheus"))); } }
具体的にはこんなJSONを返す。
[
{
"targets": [
"host.docker.internal:9101",
"host.docker.internal:9102"
],
"labels": {
"job": "node-exporter"
}
},
{
"targets": [
"host.docker.internal:8080"
],
"labels": {
"job": "spring-boot",
"__metrics_path__": "/actuator/prometheus"
}
}
]
動作確認
spring bootアプリを起動後、http://localhost:9090/targets にアクセスすると、SD endpointのtargetsがprometheusに反映されていることが確認できた。

http_sd_config.refresh_intervalのデフォルトは1mなので、1分ごとにprometheusがSD endpointにアクセスしていることも確認できた。

参考
MongoDBのクエリの備忘録 (Element)
準備
使用するMongoDBはこちらと同じ。
MongoDB Shellで接続して、マニュアルと同じテストデータを挿入する。
test> db.inventory.insertMany([ ... { item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] }, ... { item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] }, ... { item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] }, ... { item: "planner", qty: 75, tags: ["blank", "red"], dim_cm: [ 22.85, 30 ] }, ... { item: "postcard", qty: 45, tags: ["blue"], dim_cm: [ 10, 15.25 ] } ... ]); { acknowledged: true, insertedIds: { '0': ObjectId("63945c181ce8aac5f4f6e632"), '1': ObjectId("63945c181ce8aac5f4f6e633"), '2': ObjectId("63945c181ce8aac5f4f6e634"), '3': ObjectId("63945c181ce8aac5f4f6e635"), '4': ObjectId("63945c181ce8aac5f4f6e636") } }
テストデータを変更して、"notebook"のqty fieldを削除、"paper"のqtyにnullを設定する。
test> db.inventory.updateOne({item: "notebook"}, {$unset: {qty: ""}}) { acknowledged: true, insertedId: null, matchedCount: 1, modifiedCount: 1, upsertedCount: 0 } test> db.inventory.updateOne({item: "paper"}, {$set: {qty: undefined}}) { acknowledged: true, insertedId: null, matchedCount: 1, modifiedCount: 1, upsertedCount: 0 }
Element Query Operators
exists
qty fieldがあるdocumentを取得
(valueがnullのdocumentも含む)
test> db.inventory.find({qty: {$exists: true}}) [ { _id: ObjectId("63945c181ce8aac5f4f6e632"), item: 'journal', qty: 25, tags: [ 'blank', 'red' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("63945c181ce8aac5f4f6e634"), item: 'paper', qty: null, tags: [ 'red', 'blank', 'plain' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("63945c181ce8aac5f4f6e635"), item: 'planner', qty: 75, tags: [ 'blank', 'red' ], dim_cm: [ 22.85, 30 ] }, { _id: ObjectId("63945c181ce8aac5f4f6e636"), item: 'postcard', qty: 45, tags: [ 'blue' ], dim_cm: [ 10, 15.25 ] } ]
qty fieldがないdocumentを取得
test> db.inventory.find({qty: {$exists: false}}) [ { _id: ObjectId("63945c181ce8aac5f4f6e633"), item: 'notebook', tags: [ 'red', 'blank' ], dim_cm: [ 14, 21 ] } ]
type
qtyのvalueがnumberのdocumentを取得
test> db.inventory.find({qty: {$type: "number"}}) [ { _id: ObjectId("63945c181ce8aac5f4f6e632"), item: 'journal', qty: 25, tags: [ 'blank', 'red' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("63945c181ce8aac5f4f6e635"), item: 'planner', qty: 75, tags: [ 'blank', 'red' ], dim_cm: [ 22.85, 30 ] }, { _id: ObjectId("63945c181ce8aac5f4f6e636"), item: 'postcard', qty: 45, tags: [ 'blue' ], dim_cm: [ 10, 15.25 ] } ]
qtyのvalueがnullのdocumentを取得
test> db.inventory.find({qty: {$type: "null"}}) [ { _id: ObjectId("63945c181ce8aac5f4f6e634"), item: 'paper', qty: null, tags: [ 'red', 'blank', 'plain' ], dim_cm: [ 14, 21 ] } ]
参考
MongoDBのクエリの備忘録 (Array)
準備
使用するMongoDBはこちらと同じ。
MongoDB Shellで接続して、マニュアルと同じテストデータを挿入する。
test> db.inventory.insertMany([ ... { item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] }, ... { item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] }, ... { item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] }, ... { item: "planner", qty: 75, tags: ["blank", "red"], dim_cm: [ 22.85, 30 ] }, ... { item: "postcard", qty: 45, tags: ["blue"], dim_cm: [ 10, 15.25 ] } ... ]); { acknowledged: true, insertedIds: { '0': ObjectId("634137309de97ee89b8130a8"), '1': ObjectId("634137309de97ee89b8130a9"), '2': ObjectId("634137309de97ee89b8130aa"), '3': ObjectId("634137309de97ee89b8130ab"), '4': ObjectId("634137309de97ee89b8130ac") } }
Array Query Operators
all
指定した全ての要素を含むarrayを取得
redとblankを含むtags
test> db.inventory.find( { tags: { $all: ["red", "blank"] } } ) [ { _id: ObjectId("634137309de97ee89b8130a8"), item: 'journal', qty: 25, tags: [ 'blank', 'red' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("634137309de97ee89b8130a9"), item: 'notebook', qty: 50, tags: [ 'red', 'blank' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("634137309de97ee89b8130aa"), item: 'paper', qty: 100, tags: [ 'red', 'blank', 'plain' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("634137309de97ee89b8130ab"), item: 'planner', qty: 75, tags: [ 'blank', 'red' ], dim_cm: [ 22.85, 30 ] } ]
このクエリと同じ
test> db.inventory.find( { $and: [ { tags: "red" }, { tags: "blank" } ] } ) [ { _id: ObjectId("634137309de97ee89b8130a8"), item: 'journal', qty: 25, tags: [ 'blank', 'red' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("634137309de97ee89b8130a9"), item: 'notebook', qty: 50, tags: [ 'red', 'blank' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("634137309de97ee89b8130aa"), item: 'paper', qty: 100, tags: [ 'red', 'blank', 'plain' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("634137309de97ee89b8130ab"), item: 'planner', qty: 75, tags: [ 'blank', 'red' ], dim_cm: [ 22.85, 30 ] } ]
これらのクエリは完全一致
test> db.inventory.find( { tags: { $all: [["red", "blank"]] } } ) [ { _id: ObjectId("634137309de97ee89b8130a9"), item: 'notebook', qty: 50, tags: [ 'red', 'blank' ], dim_cm: [ 14, 21 ] } ]
test> db.inventory.find( { $and: [ { tags: ["red", "blank"] } ] } ) [ { _id: ObjectId("634137309de97ee89b8130a9"), item: 'notebook', qty: 50, tags: [ 'red', 'blank' ], dim_cm: [ 14, 21 ] } ]
test> db.inventory.find( { tags: ["red", "blank"] } ) [ { _id: ObjectId("634137309de97ee89b8130a9"), item: 'notebook', qty: 50, tags: [ 'red', 'blank' ], dim_cm: [ 14, 21 ] } ]
elemMatch
指定した全てのクエリに一致する要素を少なくとも1つは含んでいるarrayを取得
dim_cmでvalue > 15 and value < 20 は15.25のみ
test> db.inventory.find( { dim_cm: { $elemMatch: { $gt: 15, $lt: 20 } } } ) [ { _id: ObjectId("634137309de97ee89b8130ac"), item: 'postcard', qty: 45, tags: [ 'blue' ], dim_cm: [ 10, 15.25 ] } ]
これは、arrayのいずれかの要素が指定したクエリのいずれかに一致する場合
dim_cm: [ 22.85, 30 ] のみ20未満の要素がないので該当しない
test> db.inventory.find( { dim_cm: { $gt: 15, $lt: 20 } } ) [ { _id: ObjectId("634137309de97ee89b8130a8"), item: 'journal', qty: 25, tags: [ 'blank', 'red' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("634137309de97ee89b8130a9"), item: 'notebook', qty: 50, tags: [ 'red', 'blank' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("634137309de97ee89b8130aa"), item: 'paper', qty: 100, tags: [ 'red', 'blank', 'plain' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("634137309de97ee89b8130ac"), item: 'postcard', qty: 45, tags: [ 'blue' ], dim_cm: [ 10, 15.25 ] } ]
size
指定したサイズのarrayを取得
test> db.inventory.find( { tags: { $size: 1 } } ) [ { _id: ObjectId("634137309de97ee89b8130ac"), item: 'postcard', qty: 45, tags: [ 'blue' ], dim_cm: [ 10, 15.25 ] } ] test> db.inventory.find({ tags: { $size: 2 } } ) [ { _id: ObjectId("634137309de97ee89b8130a8"), item: 'journal', qty: 25, tags: [ 'blank', 'red' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("634137309de97ee89b8130a9"), item: 'notebook', qty: 50, tags: [ 'red', 'blank' ], dim_cm: [ 14, 21 ] }, { _id: ObjectId("634137309de97ee89b8130ab"), item: 'planner', qty: 75, tags: [ 'blank', 'red' ], dim_cm: [ 22.85, 30 ] } ] test> db.inventory.find({ tags: { $size: 3 } } ) [ { _id: ObjectId("634137309de97ee89b8130aa"), item: 'paper', qty: 100, tags: [ 'red', 'blank', 'plain' ], dim_cm: [ 14, 21 ] } ]
Array Index Position
クエリの条件にarrayのindexを指定する場合
test> db.inventory.find( { "dim_cm.0": { $gt: 15 } } ) [ { _id: ObjectId("634137309de97ee89b8130ab"), item: 'planner', qty: 75, tags: [ 'blank', 'red' ], dim_cm: [ 22.85, 30 ] } ] test> db.inventory.find( { "dim_cm.1": { $lt: 20 } } ) [ { _id: ObjectId("634137309de97ee89b8130ac"), item: 'postcard', qty: 45, tags: [ 'blue' ], dim_cm: [ 10, 15.25 ] } ]
参考
jfrコマンドの備忘録
Memo
準備
JFRファイルのサイズを大きくしたいので、大量のJFRイベントを書き込むデモアプリを用意する。
Main.java
import java.time.LocalDateTime; import jdk.jfr.Category; import jdk.jfr.Description; import jdk.jfr.Event; import jdk.jfr.Label; import jdk.jfr.Name; public class Main { @Name("hirakida.Hello") @Label("Hello") static class HelloEvent extends Event { @Label("Message") String message; @Label("DateTime") LocalDateTime dateTime; } public static void main(String[] args) { while (true) { HelloEvent event = new HelloEvent(); event.message = "Hello!"; event.dateTime = LocalDateTime.now(); event.begin(); event.commit(); } } }
JFRを有効にして、デモアプリを実行する。
% sdk use java 17.0.4.1-tem % java -XX:StartFlightRecording:filename=recording.jfr Main.java [1.240s][info][jfr,startup] Started recording 1. No limit specified, using maxsize=250MB as default. [1.240s][info][jfr,startup] [1.240s][info][jfr,startup] Use jcmd 6058 JFR.dump name=1 to copy recording data to file.
コマンド
flight recordingの内容を表示する。
% jfr print --events hirakida.Hello recording.jfr hirakida.Hello { startTime = 14:53:33.505 duration = 0.000011 ms message = "Hello!" eventThread = "main" (javaThreadId = 1) stackTrace = [ Main.main(String[]) line: 29 jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Object, Object[]) line: 77 jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43 java.lang.reflect.Method.invoke(Object, Object[]) line: 568 ... ] } ...
JSON形式
% jfr print --json --events hirakida.Hello recording.jfr { "recording": { "events": [{ "type": "hirakida.Hello", "values": { "startTime": "2022-09-17T14:53:33.505442889+09:00", "duration": "PT0.000000011S", "eventThread": { "osName": "main", "osThreadId": 6659, "javaName": "main", "javaThreadId": 1, "group": { "parent": { "parent": null, "name": "system" }, "name": "main" } }, ...
metadata
イベントの情報を表示する。
% jfr metadata recording.jfr class boolean { } class byte { } class char { } class double { } class float { } class int { } class long { } class short { } @Name("java.lang.Class") @Label("Java Class") class Class { @Label("Class Loader") ClassLoader classLoader; @Label("Name") String name; @Label("Package") Package package; @Label("Access Modifiers") int modifiers; @Label("Hidden") boolean hidden; } ...
disassemble
JFRファイルを分割する。
% mkdir out % jfr disassemble --output out recording.jfr Examining recording /Users/hirakida/work/recording.jfr ... File consists of 19 chunks. The recording will be split into 4 files Writing /Users/hirakida/work/out/recording_0.jfr ... 66784534 Writing /Users/hirakida/work/out/recording_1.jfr ... 66796485 Writing /Users/hirakida/work/out/recording_2.jfr ... 66693161 Writing /Users/hirakida/work/out/recording_3.jfr ... 50546118
assemble
JFRファイルを統合する。
% jfr assemble out assemble.jfr Assembling files... /Users/hirakida/work/out/recording_0.jfr /Users/hirakida/work/out/recording_1.jfr /Users/hirakida/work/out/recording_2.jfr /Users/hirakida/work/out/recording_3.jfr Finished.
summary
サマリーを表示する。
% jfr summary recording.jfr Version: 2.1 Chunks: 19 Start: 2022-09-17 05:53:33 (UTC) Duration: 19 s Event Type Count Size (bytes) ================================================================== hirakida.Hello 11686763 245434096 jdk.BooleanFlag 9975 304950 jdk.NativeLibrary 9728 858382 jdk.ModuleExport 9063 96786 jdk.GCPhaseParallel 6668 177786 jdk.ActiveSetting 6308 194047 jdk.SystemProcess 4645 373916 jdk.LongFlag 3762 125020 jdk.ModuleRequire 2869 28690 jdk.UnsignedLongFlag 2774 94696 jdk.ObjectAllocationSample 2428 38397 ...