---
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
> 		<datasource-micrometer.version>2.1.0</datasource-micrometer.version>
48a50,57
> 		<dependency>
> 			<groupId>net.ttddyy.observation</groupId>
> 			<artifactId>datasource-micrometer-opentelemetry</artifactId>
> 		</dependency>
> 		<dependency>
> 			<groupId>net.ttddyy.observation</groupId>
> 			<artifactId>datasource-micrometer-spring-boot</artifactId>
> 		</dependency>
95a105,115
> 	<dependencyManagement>
> 		<dependencies>
> 			<dependency>
> 				<groupId>net.ttddyy.observation</groupId>
> 				<artifactId>datasource-micrometer-bom</artifactId>
> 				<version>${datasource-micrometer.version}</version>
> 				<type>pom</type>
> 				<scope>import</scope>
> 			</dependency>
> 		</dependencies>
> 	</dependencyManagement>
```

`dependencies`に`opentelemetry`も含まれる場合は、`datasource-micrometer-opentelemetry`も合わせて追加されます。

せっかくなので [こちらの記事](/entries/892) で紹介した、[OpenTelemetry Logback Appender の AutoConfiguration](https://github.com/making/otel-logs-autoconfigure) も追加します。

```xml
<dependency>
    <groupId>am.ik.spring.opentelemetry</groupId>
    <artifactId>otel-logs-autoconfigure</artifactId>
    <version>0.5.0</version>
</dependency>
```

簡単な API を実装します。

```java
cat <<EOF > 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<CounterResponse> 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 <<EOF >> 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 <<EOF > 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 から利用できるので、簡単に組み込むことができます。ぜひ、使ってみてください。
