Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 97 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>3.0.0-SNAPSHOT</version>
<version>3.0.0-GH-2348-SNAPSHOT</version>

<name>Spring Data Redis</name>
<description>Spring Data module for Redis</description>
Expand All @@ -28,6 +29,11 @@
<multithreadedtc>1.01</multithreadedtc>
<netty>4.1.79.Final</netty>
<java-module-name>spring.data.redis</java-module-name>

<!-- Observability -->
<micrometer-docs-generator.inputPath>${project.basedir}</micrometer-docs-generator.inputPath>
<micrometer-docs-generator.inclusionPattern>.*</micrometer-docs-generator.inclusionPattern>
<micrometer-docs-generator.outputPath>${project.basedir}/target/</micrometer-docs-generator.outputPath>
</properties>

<scm>
Expand Down Expand Up @@ -157,6 +163,44 @@
<optional>true</optional>
</dependency>

<!-- Observability -->

<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-observation</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8-standalone</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-integration-test</artifactId>
<scope>test</scope>
</dependency>

<!-- CDI -->
<!-- Dependency order required to build against CDI 1.0 and test with CDI 2.0 -->

Expand Down Expand Up @@ -285,6 +329,57 @@
</configuration>
</plugin>

<plugin>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can simplify this section - check our docs https://micrometer.io/docs/observation#_documentation_building

<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>generate-metrics-metadata</id>
<phase>generate-resources</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>io.micrometer.docs.metrics.DocsFromSources</mainClass>
</configuration>
</execution>
<execution>
<id>generate-tracing-metadata</id>
<phase>generate-resources</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>io.micrometer.docs.spans.DocsFromSources
</mainClass>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-docs-generator-spans</artifactId>
<version>${micrometer-docs-generator}</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-docs-generator-metrics</artifactId>
<version>${micrometer-docs-generator}</version>
<type>jar</type>
</dependency>
</dependencies>
<configuration>
<includePluginDependencies>true</includePluginDependencies>
<arguments>
<argument>${micrometer-docs-generator.inputPath}</argument>
<argument>${micrometer-docs-generator.inclusionPattern}</argument>
<argument>${micrometer-docs-generator.outputPath}</argument>
</arguments>
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
Expand Down
1 change: 1 addition & 0 deletions src/main/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ include::{spring-data-commons-docs}/dependencies.adoc[leveloffset=+1]
include::reference/introduction.adoc[leveloffset=+1]
include::reference/why-sdr.adoc[leveloffset=+1]
include::reference/redis.adoc[leveloffset=+1]
include::reference/observability.adoc[leveloffset=+1]
include::reference/reactive-redis.adoc[leveloffset=+1]
include::reference/redis-cluster.adoc[leveloffset=+1]
include::reference/redis-repositories.adoc[leveloffset=+1]
Expand Down
38 changes: 38 additions & 0 deletions src/main/asciidoc/reference/observability.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[[redis.observability]]
== Observability

Getting insights from an application component about its operations, timing and relation to application code is crucial to understand latency.
Spring Data Redis ships with a Micrometer integration through the Lettuce driver to collect observations during Redis interaction.
Once the integration is set up, Micrometer will create meters and spans (for distributed tracing) for each Redis command.

To enable the integration, apply the following configuration to `LettuceClientConfiguration`:

[source,java]
----
@Configuration
class TracingConfig {

@Bean
public ClientResources clientResources(ObservationRegistry observationRegistry) {

return ClientResources.builder()
.tracing(new MicrometerTracingAdapter(observationRegistry, "my-redis-cache"))
.build();
}

@Bean
public LettuceConnectionFactory lettuceConnectionFactory(ClientResources clientResources) {

LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.clientResources(clientResources).build();
RedisConfiguration redisConfiguration = …;
return new LettuceConnectionFactory(redisConfiguration, clientConfig);
}
}
----

include::../../../../target/_conventions.adoc[]

include::../../../../target/_metrics.adoc[]

include::../../../../target/_spans.adoc[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* 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 java.net.InetSocketAddress;
import java.util.Locale;

import org.springframework.data.redis.connection.lettuce.observability.RedisObservation.HighCardinalityCommandKeyNames;
import org.springframework.data.redis.connection.lettuce.observability.RedisObservation.LowCardinalityCommandKeyNames;

import io.lettuce.core.protocol.RedisCommand;
import io.lettuce.core.tracing.Tracing.Endpoint;
import io.micrometer.common.KeyValues;

/**
* Default {@link LettuceObservationConvention} implementation.
*
* @author Mark Paluch
* @since 3.0
*/
record DefaultLettuceObservationConvention(
boolean includeCommandArgsInSpanTags) implements LettuceObservationConvention {

@Override
public KeyValues getLowCardinalityKeyValues(LettuceObservationContext context) {

Endpoint ep = context.getRequiredEndpoint();
KeyValues keyValues = KeyValues.of(LowCardinalityCommandKeyNames.DATABASE_SYSTEM.withValue("redis"), //
LowCardinalityCommandKeyNames.REDIS_COMMAND.withValue(context.getRequiredCommand().getType().name()), //
LowCardinalityCommandKeyNames.REDIS_SERVER.withValue(ep.toString()));

if (ep instanceof SocketAddressEndpoint endpoint) {

if (endpoint.socketAddress()instanceof InetSocketAddress inet) {
keyValues = keyValues
.and(KeyValues.of(LowCardinalityCommandKeyNames.NET_PEER_NAME.withValue(inet.getHostString()),
LowCardinalityCommandKeyNames.NET_PEER_PORT.withValue("" + inet.getPort())));
} else {
keyValues = keyValues
.and(KeyValues.of(LowCardinalityCommandKeyNames.NET_PEER_ADDR.withValue(endpoint.toString())));
}
}

return keyValues;
}

@Override
public KeyValues getHighCardinalityKeyValues(LettuceObservationContext context) {

RedisCommand<?, ?, ?> command = context.getRequiredCommand();

if (includeCommandArgsInSpanTags) {

if (command.getArgs() != null) {
return KeyValues.of(HighCardinalityCommandKeyNames.STATEMENT
.withValue(command.getType().name() + " " + command.getArgs().toCommandString()));
}

return KeyValues.of(HighCardinalityCommandKeyNames.STATEMENT.withValue(command.getType().name()));
}

return KeyValues.empty();
}

@Override
public String getContextualName(LettuceObservationContext context) {
return context.getRequiredCommand().getType().name().toLowerCase(Locale.ROOT);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* 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.lang.Nullable;

import io.lettuce.core.protocol.RedisCommand;
import io.lettuce.core.tracing.Tracing.Endpoint;
import io.micrometer.observation.Observation;
import io.micrometer.observation.transport.Kind;
import io.micrometer.observation.transport.SenderContext;

/**
* Micrometer {@link Observation.Context} holding Lettuce contextual details.
*
* @author Mark Paluch
* @since 3.0
*/
class LettuceObservationContext extends SenderContext<Object> {

private volatile @Nullable RedisCommand<?, ?, ?> command;

private volatile @Nullable Endpoint endpoint;

public LettuceObservationContext(String serviceName) {
super((carrier, key, value) -> {}, Kind.CLIENT);
setRemoteServiceName(serviceName);
}

public RedisCommand<?, ?, ?> getRequiredCommand() {

RedisCommand<?, ?, ?> local = command;

if (local == null) {
throw new IllegalArgumentException("LettuceObservationContext is not associated with a Command");
}

return local;
}

public void setCommand(RedisCommand<?, ?, ?> command) {
this.command = command;
}

public Endpoint getRequiredEndpoint() {

Endpoint local = endpoint;

if (local == null) {
throw new IllegalArgumentException("LettuceObservationContext is not associated with a Endpoint");
}

return local;
}

public void setEndpoint(Endpoint endpoint) {
this.endpoint = endpoint;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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 io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationConvention;

/**
* {@link ObservationConvention} for {@link LettuceObservationContext}.
*
* @author Mark Paluch
* @since 3.0
*/
interface LettuceObservationConvention extends ObservationConvention<LettuceObservationContext> {

@Override
default boolean supportsContext(Observation.Context context) {
return context instanceof LettuceObservationContext;
}

}
Loading