--- title: Datasource MicrometerでSpring BootアプリのJDBC操作を計測する summary: この記事では、Spring BootでDatasource Micrometerを使いJDBC操作を計測し、SQLログ・トレース・メトリクスをGrafanaで可視化する方法を紹介します。 tags: ["OpenTelemetry", "Spring Boot", "Micrometer", "Java", "Logging", "Tracing", "Metrics", "Loki", "Grafana", "Tempo", "Prometheus"] categories: ["Programming", "Java", "net", "ttddyy", "observation"] date: 2026-01-26T05:30:03.578Z updated: 2026-02-04T01:29:22.737Z --- > [!NOTE] > 2026-02-04 datasource-micrometer-opentelemetryの説明を追加。 [Datasource Micrometer](https://github.com/jdbc-observations/datasource-micrometer) ([ドキュメントリンク](https://jdbc-observations.github.io/datasource-micrometer/docs/current/docs/html/)) は、JDBC の Proxy であり、JDBC の操作を Micrometer の [Observation API](https://docs.micrometer.io/micrometer/reference/observation.html) で計測して、Observability を高められます。Trace、Metrics だけでなく、SQL ログやスローログも取得可能です。現在は Micrometer とは独立した開発になっていますが、Micrometer と近い位置で開発されています。 Datasource Micrometer は 2025 年末あたりから [Spring Initializr](https://start.spring.io/) からも選択可能になり、便利に利用できるようになりました。Datasource Micrometer 1 系は Spring Boot 3.5 系で、Datasource Micrometer 2 系は Spring Boot 4 系で利用可能です。 ![image](https://s3.ik.am/ikam/_/1769391518407_pasted-image.png) 題材アプリとして、簡単な Counter API を作成します。次のコマンドでプロジェクトの雛形を作成します。 ```bash curl -s https://start.spring.io/starter.tgz \ -d artifactId=counter-api\ -d name=counter-api \ -d baseDir=counter-api \ -d packageName=com.example \ -d dependencies=web,jdbc,postgresql,actuator,configuration-processor,opentelemetry,datasource-micrometer,testcontainers \ -d type=maven-project \ -d applicationName=CounterApiApplication | tar -xzvf - cd counter-api ``` すでに Datasource Micrometer の dependency は設定済みです。`dependencies`に`datasource-micrometer`を含めない場合に比べて、以下の設定が追加されます。 ```diff 30a31 > 2.1.0 48a50,57 > > net.ttddyy.observation > datasource-micrometer-opentelemetry > > > net.ttddyy.observation > datasource-micrometer-spring-boot > 95a105,115 > > > > net.ttddyy.observation > datasource-micrometer-bom > ${datasource-micrometer.version} > pom > import > > > ``` `dependencies`に`opentelemetry`も含まれる場合は、`datasource-micrometer-opentelemetry`も合わせて追加されます。 せっかくなので [こちらの記事](/entries/892) で紹介した、[OpenTelemetry Logback Appender の AutoConfiguration](https://github.com/making/otel-logs-autoconfigure) も追加します。 ```xml am.ik.spring.opentelemetry otel-logs-autoconfigure 0.5.0 ``` 簡単な API を実装します。 ```java cat < src/main/java/com/example/CounterController.java package com.example; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.simple.JdbcClient; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController public class CounterController { private final JdbcClient jdbcClient; private final Logger logger = LoggerFactory.getLogger(this.getClass()); public CounterController(JdbcClient jdbcClient) { this.jdbcClient = jdbcClient; } @PostMapping(path = "/counter") @Transactional public CounterResponse increment(@RequestBody CounterRequest request) { CounterResponse counterResponse = this.jdbcClient.sql(""" INSERT INTO counters (entry_id, counter) VALUES (?, 1) ON CONFLICT (entry_id) DO UPDATE SET counter = counters.counter + 1 RETURNING entry_id, counter """).param(request.entryId()).query(CounterResponse.class).single(); logger.atInfo() .addKeyValue("entryId", counterResponse.entryId()) .addKeyValue("counter", counterResponse.counter()) .log("event=increment entryId={} counter={}", counterResponse.entryId(), counterResponse.counter()); return counterResponse; } @GetMapping(path = "/counter") public List getAll() { return this.jdbcClient.sql(""" SELECT entry_id, counter FROM counters ORDER BY counter DESC """).query(CounterResponse.class).list(); } public record CounterRequest(int entryId) { } public record CounterResponse(int entryId, long counter) { } } EOF ``` `application.properties` を設定します。Datasource Micrometer による SQL ログとスロークエリログの設定を行います。 ```properties cat <> src/main/resources/application.properties jdbc.datasource-proxy.json-format=true jdbc.datasource-proxy.logging=slf4j jdbc.datasource-proxy.multiline=false jdbc.datasource-proxy.query.enable-logging=true jdbc.datasource-proxy.slow-query.enable-logging=true jdbc.datasource-proxy.slow-query.threshold=5 logging.level.net.ttddyy.dsproxy.listener.logging.SLF4JQueryLoggingListener=debug management.opentelemetry.instrumentation.logback-appender.capture-experimental-attributes=true management.opentelemetry.instrumentation.logback-appender.capture-key-value-pair-attributes=true management.otlp.metrics.export.base-time-unit=seconds management.otlp.metrics.export.step=30s management.tracing.sampling.probability=1.0 spring.sql.init.mode=always EOF ``` 簡単なスキーマを定義します。 ```sql cat < src/main/resources/schema.sql CREATE TABLE IF NOT EXISTS counters ( entry_id BIGINT PRIMARY KEY, counter BIGINT NOT NULL ); EOF ``` 次のコマンドを実行すると、Testcontainers を使った PostgreSQL と [LGTM スタック](https://hub.docker.com/r/grafana/otel-lgtm) のローカル開発用コンテナが立ち上がります (`src/test/java/com/example/TestcontainersConfiguration.java` を確認してみてください)。DataSource の設定や OTLP エンドポイントの設定は自動で行われます。 ```bash ./mvnw spring-boot:test-run ``` 起動時に次のようなログが出力され、Grafana の URL がわかります。 ``` 2026-01-26T13:08:41.512+09:00 INFO 28654 --- [counter-api] [ main] [ ] tc.grafana/otel-lgtm:latest : Creating container for image: grafana/otel-lgtm:latest 2026-01-26T13:08:41.579+09:00 INFO 28654 --- [counter-api] [ main] [ ] tc.grafana/otel-lgtm:latest : Container grafana/otel-lgtm:latest is starting: e0bb34b52593b45152a1236b6766c8ba2b1d3949b5ade2f4126dfbe8bf681790 2026-01-26T13:08:46.762+09:00 INFO 28654 --- [counter-api] [ main] [ ] tc.grafana/otel-lgtm:latest : Container grafana/otel-lgtm:latest started in PT5.249929S 2026-01-26T13:08:46.762+09:00 INFO 28654 --- [counter-api] [ main] [ ] o.t.grafana.LgtmStackContainer : Access to the Grafana dashboard: http://localhost:35507 2026-01-26T13:08:46.862+09:00 INFO 28654 --- [counter-api] [ main] [ ] i.m.c.instrument.push.PushMeterRegistry : Publishing metrics for OtlpMeterRegistry every 30s to http://localhost:35511/v1/metrics with resource attributes {service.name=counter-api} ``` 適当にリクエストを送ります。 ```bash curl -s http://localhost:8080/counter --json '{"entryId":100}' curl -s http://localhost:8080/counter ``` 次のような SQL ログを確認できます。 ``` 2026-01-26T14:11:44.165+09:00 DEBUG 28654 --- [counter-api] [nio-8080-exec-1] [382b35f274086c23f3a45d4c94cfb785-6c507dd2b93e2c20] n.t.d.l.l.SLF4JQueryLoggingListener : {"name":"test", "connection":4, "time":2, "success":true, "type":"Prepared", "batch":false, "querySize":1, "batchSize":0, "query":["INSERT INTO counters (entry_id, counter)\nVALUES (?, 1)\nON CONFLICT (entry_id)\nDO UPDATE\n SET counter = counters.counter + 1\nRETURNING entry_id, counter\n"], "params":[["100"]]} 2026-01-26T14:11:44.169+09:00 INFO 28654 --- [counter-api] [nio-8080-exec-1] [382b35f274086c23f3a45d4c94cfb785-25067aeeaad66229] com.example.CounterController : event=increment entryId=100 counter=1 2026-01-26T14:11:45.923+09:00 DEBUG 28654 --- [counter-api] [nio-8080-exec-3] [1a250337750c5fbed850028f8fe5c636-72c7abbd0c14e831] n.t.d.l.l.SLF4JQueryLoggingListener : {"name":"test", "connection":5, "time":0, "success":true, "type":"Prepared", "batch":false, "querySize":1, "batchSize":0, "query":["SELECT entry_id, counter FROM counters ORDER BY counter DESC\n"], "params":[[]]} ``` 次に、以下のコマンドで [`vegeta`](https://github.com/tsenart/vegeta) を使い、負荷をかけてみます。 ```bash for round in $(seq 20); do echo "=== Round $round/20 ===" for i in $(seq 3000); do id=$((RANDOM % 50 + 1)) if [ $((RANDOM % 3)) -eq 0 ]; then echo '{"method":"GET","url":"http://localhost:8080/counter"}' else echo '{"method":"POST","url":"http://localhost:8080/counter","header":{"Content-Type":["application/json"]},"body":"'$(echo -n "{\"entryId\":$id}" | base64)'"}' fi done | vegeta attack -rate=100 -duration=30s -format=json | vegeta report done ``` ### Grafanaで確認 Grafana にアクセスします。Grafana の URL はログに出力されています。 ![image](https://s3.ik.am/ikam/_/1769400574216_pasted-image.png) #### Traces 左のメニューの Drilldown から **"Traces"** を選択します。 ![image](https://s3.ik.am/ikam/_/1769400767383_pasted-image.png) Span Rate の ◇ マーク (Exemplar、代表点) をクリックし、**"View trace"** リンクをクリックします。 ![image](https://s3.ik.am/ikam/_/1769400790970_pasted-image.png) そのリクエストの Trace View が表示されます。 ![image](https://s3.ik.am/ikam/_/1770167765909_pasted-image.png) **"connection"** Span をクリックすると、commitのタイミング/回数や時間を確認することができます。 ![image](https://s3.ik.am/ikam/_/1770168097447_pasted-image.png) **"INSERT ..."** Span をクリックすると、そのタイミングでの SQL を確認することができます。 ![image](https://s3.ik.am/ikam/_/1770168212581_pasted-image.png) `datasource-micrometer-opentelemetry`が追加されることにより、Attribute名は [OpenTelemetry semantic conventions](https://opentelemetry.io/docs/specs/semconv/) に準拠するようになりました。 **"Log"** ボタンをクリックしてみます。 ![image](https://s3.ik.am/ikam/_/1770168428219_pasted-image.png) この Trace におけるログを確認することができます。 ![image](https://s3.ik.am/ikam/_/1770168478407_pasted-image.png) #### Metrics 次に、左のメニューの Drilldown から **"Metrics"** を選択します。**"Prefix filters"** で **"jdbc"** を選択すると、表示を Datasource Micrometer が送信したメトリクスのみに絞ることができます。 ![image](https://s3.ik.am/ikam/_/1769402849540_pasted-image.png) 取得できるメトリクスは [こちらのドキュメント](https://jdbc-observations.github.io/datasource-micrometer/docs/current/docs/html/#observability-metrics) を参照してください。Connection の開始から終了までの時間、コミット・ロールバックの回数、クエリの実行時間、回数などが取得できます。 #### Logs 最後に、左のメニューの Drilldown から **"Logs"** を選択します。 ![image](https://s3.ik.am/ikam/_/1769400620829_pasted-image.png) **"counter-api"** で **"Show logs"** ボタンをクリックします。 ![image](https://s3.ik.am/ikam/_/1769400657018_pasted-image.png) 特定のログをクリックし、**"Links"** の **"Trace"** ボタンをクリックすると、 ![image](https://s3.ik.am/ikam/_/1769400710078_pasted-image.png) また、Trace View に飛ぶことができ、このログに関する SQL などを確認することもできます。 ![image](https://s3.ik.am/ikam/_/1769400730475_pasted-image.png) --- Datasource Micrometer を使い、Spring Boot アプリの JDBC 操作を計測する方法を紹介しました。Spring Initializr から利用できるので、簡単に組み込むことができます。ぜひ、使ってみてください。