This is the third post of the “R2DBC Proxy Tips” mini blog series.

This post shows how to achieve observability - metrics and distributed tracing.

To implement observability, we need to capture the interactions with the R2DBC objects. ProxyMethodExecutionListener is suitable for this use case. This listener is an extension of ProxyExecutionListener and provides callbacks for all the methods defined in the R2DBC SPI classes.

For example, the beforeCreateOnConnectionFactory method is called back before the create method on ConnectionFactory. The afterExecuteOnStatement is invoked after the execute method on Statement.

By using these callback methods, you can easily add logic to populate metrics, start a new span, or take any action you want.

For example, the following listener populates metrics for the time taken to obtain a connection:

public class MyMetricsListener implements ProxyMethodExecutionListener {

	private MeterRegistry registry;

	public MyMetricsListener(MeterRegistry registry) {
		this.registry = registry;
	}

	@Override
	public void beforeCreateOnConnectionFactory(MethodExecutionInfo methodExecInfo) {
		Timer.Sample sample = Timer.start(this.registry);
		methodExecInfo.getValueStore().put("connectionCreate", sample);
	}

	@Override
	public void afterCreateOnConnectionFactory(MethodExecutionInfo methodExecInfo) {
		Timer.Sample sample = methodExecInfo.getValueStore()
				.get("connectionCreate", Timer.Sample.class);

		Timer timer = Timer
				.builder("r2dbc.connection")
				.description("Time to create(acquire) a connection")
				.tags("event", "create")
				.register(this.registry);

		sample.stop(timer);
	}

}

As mentioned in the second post, we use the ValueStore API to pass the sample object between methods.

MetricsExecutionListener and QueryTimeMetricsExecutionListener are other sample metrics listener implementations.

For distributed tracing, the implementation is similar to the above sample code for metrics. You can create spans at method invocations, pass them to ValueStore, and finish them in the appropriate method invocations.

This TracingExecutionListener example shows how to create spans by using the zipkin Tracer.

For Spring Cloud Sleuth users, we have good news. This commit enables out of the box support for R2DBC instrumentation.