Add observability adapter for Lettuce.

We now provide MicrometerTracingAdapter to connect Lettuce to Micrometer Tracing.

Original pull request: #2439
Closes #2348
This commit is contained in:
Mark Paluch
2022-10-20 10:33:58 +02:00
parent 190b28cbbe
commit ccdd5d2d98
13 changed files with 1081 additions and 1 deletions

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.connection.lettuce.observability;
import static org.assertj.core.api.Assertions.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.ReactiveRedisConnection;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor;
import io.micrometer.tracing.test.SampleTestRunner;
import reactor.test.StepVerifier;
import reactor.util.context.Context;
/**
* Collection of tests that log metrics and tracing using the reactive API.
*
* @author Mark Paluch
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = TestConfig.class)
public class ReactiveIntegrationTests extends SampleTestRunner {
@Autowired LettuceConnectionFactory connectionFactory;
ReactiveIntegrationTests() {
super(SampleRunnerConfig.builder().build());
}
@Override
protected MeterRegistry createMeterRegistry() {
return TestConfig.METER_REGISTRY;
}
@Override
protected ObservationRegistry createObservationRegistry() {
return TestConfig.OBSERVATION_REGISTRY;
}
@Override
public SampleTestRunnerConsumer yourCode() {
return (tracer, meterRegistry) -> {
Observation intermediate = Observation.start("intermediate", createObservationRegistry());
ReactiveRedisConnection connection = connectionFactory.getReactiveConnection();
connection.ping().contextWrite(Context.of(ObservationThreadLocalAccessor.KEY, intermediate))
.as(StepVerifier::create).expectNext("PONG").verifyComplete();
intermediate.stop();
connection.close();
assertThat(tracer.getFinishedSpans()).isNotEmpty();
System.out.println(((SimpleMeterRegistry) meterRegistry).getMetersAsString());
};
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.connection.lettuce.observability;
import static org.assertj.core.api.Assertions.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.SettingsUtils;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.tracing.exporter.FinishedSpan;
import io.micrometer.tracing.test.SampleTestRunner;
/**
* Collection of tests that log metrics and tracing using the synchronous API.
*
* @author Mark Paluch
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = TestConfig.class)
public class SynchronousIntegrationTests extends SampleTestRunner {
@Autowired LettuceConnectionFactory connectionFactory;
SynchronousIntegrationTests() {
super(SampleRunnerConfig.builder().build());
}
@Override
protected MeterRegistry createMeterRegistry() {
return TestConfig.METER_REGISTRY;
}
@Override
protected ObservationRegistry createObservationRegistry() {
return TestConfig.OBSERVATION_REGISTRY;
}
@Override
public SampleTestRunnerConsumer yourCode() {
return (tracer, meterRegistry) -> {
RedisConnection connection = connectionFactory.getConnection();
connection.ping();
connection.close();
assertThat(tracer.getFinishedSpans()).isNotEmpty();
System.out.println(((SimpleMeterRegistry) meterRegistry).getMetersAsString());
assertThat(tracer.getFinishedSpans()).isNotEmpty();
for (FinishedSpan finishedSpan : tracer.getFinishedSpans()) {
assertThat(finishedSpan.getTags()).containsEntry("db.system", "redis")
.containsEntry("net.sock.peer.addr", SettingsUtils.getHost())
.containsEntry("net.sock.peer.port", "" + SettingsUtils.getPort());
assertThat(finishedSpan.getTags()).containsKeys("db.operation");
}
};
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.connection.lettuce.observability;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.SettingsUtils;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import io.lettuce.core.resource.ClientResources;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import io.micrometer.observation.ObservationRegistry;
/**
* @author Mark Paluch
*/
@Configuration
class TestConfig {
static final MeterRegistry METER_REGISTRY = new SimpleMeterRegistry();
static final ObservationRegistry OBSERVATION_REGISTRY = ObservationRegistry.create();
static {
OBSERVATION_REGISTRY.observationConfig().observationHandler(new DefaultMeterObservationHandler(METER_REGISTRY));
}
@Bean(destroyMethod = "shutdown")
ClientResources clientResources(ObservationRegistry observationRegistry) {
return ClientResources.builder().tracing(new MicrometerTracingAdapter(observationRegistry, "Redis", true))
.build();
}
@Bean
LettuceConnectionFactory connectionFactory(ClientResources clientResources) {
LettuceClientConfiguration clientConfiguration = LettuceClientConfiguration.builder()
.clientResources(clientResources).build();
return new LettuceConnectionFactory(SettingsUtils.standaloneConfiguration(), clientConfiguration);
}
@Bean
ObservationRegistry registry() {
return OBSERVATION_REGISTRY;
}
}