Browse Source

Polishing.

Update javadoc and connection string rendering.
4.4.x
Christoph Strobl 4 months ago
parent
commit
b5664de0bd
No known key found for this signature in database
GPG Key ID: E6054036D0C37A4B
  1. 5
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoKeyName.java
  2. 56
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoObservation.java
  3. 17
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/Observer.java
  4. 66
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/observability/MongoObservationUnitTests.java

5
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoKeyName.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2025 the original author or authors. * Copyright 2025-present the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -30,6 +30,7 @@ import org.springframework.util.StringUtils;
* {@link KeyValue} and {@link KeyName}. * {@link KeyValue} and {@link KeyName}.
* *
* @author Mark Paluch * @author Mark Paluch
* @since 4.4.9
*/ */
record MongoKeyName<C>(String name, boolean required, Function<C, Object> valueFunction) implements KeyName { record MongoKeyName<C>(String name, boolean required, Function<C, Object> valueFunction) implements KeyName {
@ -43,7 +44,7 @@ record MongoKeyName<C>(String name, boolean required, Function<C, Object> valueF
* @return * @return
* @param <C> * @param <C>
*/ */
public static <C> MongoKeyName<C> required(String name, Function<C, Object> valueFunction) { static <C> MongoKeyName<C> required(String name, Function<C, Object> valueFunction) {
return required(name, valueFunction, Objects::nonNull); return required(name, valueFunction, Objects::nonNull);
} }

56
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoObservation.java

@ -15,11 +15,17 @@
*/ */
package org.springframework.data.mongodb.observability; package org.springframework.data.mongodb.observability;
import static org.springframework.data.mongodb.observability.MongoKeyName.*; import static org.springframework.data.mongodb.observability.MongoKeyName.MongoKeyValue;
import static org.springframework.data.mongodb.observability.MongoKeyName.just;
import io.micrometer.common.docs.KeyName; import io.micrometer.common.docs.KeyName;
import io.micrometer.observation.docs.ObservationDocumentation; import io.micrometer.observation.docs.ObservationDocumentation;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.regex.Pattern;
import org.springframework.lang.Contract;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -86,7 +92,7 @@ enum MongoObservation implements ObservationDocumentation {
static MongoKeyName<ServerAddress> NET_PEER_PORT = MongoKeyName.required("net.peer.port", ServerAddress::getPort); static MongoKeyName<ServerAddress> NET_PEER_PORT = MongoKeyName.required("net.peer.port", ServerAddress::getPort);
static MongoKeyName<ConnectionString> DB_CONNECTION_STRING = MongoKeyName.requiredString("db.connection_string", static MongoKeyName<ConnectionString> DB_CONNECTION_STRING = MongoKeyName.requiredString("db.connection_string",
Object::toString); MongoObservation::connectionString);
static MongoKeyName<ConnectionString> DB_USER = MongoKeyName.requiredString("db.user", static MongoKeyName<ConnectionString> DB_USER = MongoKeyName.requiredString("db.user",
ConnectionString::getUsername); ConnectionString::getUsername);
@ -96,7 +102,7 @@ enum MongoObservation implements ObservationDocumentation {
* @param context the context to contribute from, can be {@literal null} if no context is available. * @param context the context to contribute from, can be {@literal null} if no context is available.
* @return the key value contributor providing low cardinality key names. * @return the key value contributor providing low cardinality key names.
*/ */
public static Observer observe(@Nullable MongoHandlerContext context) { static Observer observe(@Nullable MongoHandlerContext context) {
return Observer.fromContext(context, it -> { return Observer.fromContext(context, it -> {
@ -115,9 +121,51 @@ enum MongoObservation implements ObservationDocumentation {
* *
* @return the key names for low cardinality keys. * @return the key names for low cardinality keys.
*/ */
public static KeyName[] getKeyNames() { static KeyName[] getKeyNames() {
return observe(null).toKeyNames(); return observe(null).toKeyNames();
} }
} }
@Contract("null -> null")
static @Nullable String connectionString(@Nullable ConnectionString connectionString) {
if (connectionString == null) {
return null;
}
if (!StringUtils.hasText(connectionString.getUsername()) && connectionString.getPassword() == null) {
return connectionString.toString();
}
String target = renderPart(connectionString.toString(), "//", connectionString.getUsername());
if (connectionString.getPassword() != null) {
String rendered = renderPart(target, ":", new String(connectionString.getPassword()));
if (!rendered.equals(target)) {
target = rendered;
} else {
String protocol = connectionString.isSrvProtocol() ? "mongodb+srv" : "mongodb";
target = "%s://*****:*****@%s".formatted(protocol,
StringUtils.collectionToCommaDelimitedString(connectionString.getHosts()));
}
}
return target;
}
private static String renderPart(String source, String prefix, @Nullable String part) {
if (!StringUtils.hasText(part)) {
return source;
}
String intermediate = source.replaceFirst(prefix + Pattern.quote(part), "%s*****".formatted(prefix));
if (!intermediate.equals(source)) {
return intermediate;
}
String encoded = URLEncoder.encode(part, StandardCharsets.UTF_8);
return source.replaceFirst(prefix + Pattern.quote(encoded), "%s*****".formatted(prefix));
}
} }

17
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/Observer.java

@ -30,6 +30,7 @@ import org.springframework.lang.Nullable;
* observability systems. * observability systems.
* *
* @author Mark Paluch * @author Mark Paluch
* @since 4.4.9
*/ */
class Observer { class Observer {
@ -40,7 +41,7 @@ class Observer {
* *
* @return a new {@link Observer}. * @return a new {@link Observer}.
*/ */
public static Observer create() { static Observer create() {
return new Observer(); return new Observer();
} }
@ -53,7 +54,7 @@ class Observer {
* @return the stateful {@link Observer}. * @return the stateful {@link Observer}.
* @param <C> context type. * @param <C> context type.
*/ */
public static <C> Observer fromContext(@Nullable C context, Consumer<? super ContextualObserver<C>> consumer) { static <C> Observer fromContext(@Nullable C context, Consumer<? super ContextualObserver<C>> consumer) {
Observer contributor = create(); Observer contributor = create();
@ -68,7 +69,7 @@ class Observer {
* @param keyValue * @param keyValue
* @return * @return
*/ */
public Observer contribute(MongoKeyName.MongoKeyValue keyValue) { Observer contribute(MongoKeyName.MongoKeyValue keyValue) {
keyValues.add(keyValue); keyValues.add(keyValue);
@ -83,7 +84,7 @@ class Observer {
* @return the nested contextual {@link ContextualObserver} that can contribute key-value tuples. * @return the nested contextual {@link ContextualObserver} that can contribute key-value tuples.
* @param <C> * @param <C>
*/ */
public <C> ContextualObserver<C> contextual(@Nullable C context) { <C> ContextualObserver<C> contextual(@Nullable C context) {
if (context == null) { if (context == null) {
return new EmptyContextualObserver<>(keyValues); return new EmptyContextualObserver<>(keyValues);
@ -92,15 +93,11 @@ class Observer {
return new DefaultContextualObserver<>(context, keyValues); return new DefaultContextualObserver<>(context, keyValues);
} }
public <T> ContextualObserver<T> empty(Class<T> targetType) { KeyValues toKeyValues() {
return new EmptyContextualObserver<>(this.keyValues);
}
public KeyValues toKeyValues() {
return KeyValues.of(keyValues); return KeyValues.of(keyValues);
} }
public KeyName[] toKeyNames() { KeyName[] toKeyNames() {
KeyName[] keyNames = new KeyName[keyValues.size()]; KeyName[] keyNames = new KeyName[keyValues.size()];

66
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/observability/MongoObservationUnitTests.java

@ -0,0 +1,66 @@
/*
* Copyright 2025-present 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.mongodb.observability;
import static org.assertj.core.api.Assertions.assertThat;
import java.nio.charset.StandardCharsets;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import com.mongodb.ConnectionString;
/**
* @author Christoph Strobl
*/
class MongoObservationUnitTests {
@ParameterizedTest // GH-5020
@MethodSource("connectionStrings")
void connectionStringRendering(ConnectionString source, String expected) {
assertThat(MongoObservation.connectionString(source)).isEqualTo(expected);
}
private static Stream<Arguments> connectionStrings() {
return Stream.of(Arguments.of(new ConnectionString(
"mongodb+srv://m0ngP%40oUser:m0ngP%40o@cluster0.example.mongodb.net/?retryWrites=true&w=majority"),
"mongodb+srv://*****:*****@cluster0.example.mongodb.net/?retryWrites=true&w=majority"), //
Arguments.of(new ConnectionString(
"mongodb://mongodb:m0ngP%40o@cluster0.example.mongodb.net,cluster1.example.com:1234/?retryWrites=true"),
"mongodb://*****:*****@cluster0.example.mongodb.net,cluster1.example.com:1234/?retryWrites=true"), //
Arguments.of(
new ConnectionString("mongodb://myDatabaseUser@cluster0.example.mongodb.net/?authMechanism=MONGODB-X509"),
"mongodb://*****@cluster0.example.mongodb.net/?authMechanism=MONGODB-X509"), //
Arguments.of(
new ConnectionString("mongodb+srv://myDatabaseUser:mongodb@cluster0.example.mongodb.net/?w=acknowledged"),
"mongodb+srv://*****:*****@cluster0.example.mongodb.net/?w=acknowledged"), //
Arguments.of(
new ConnectionString(
new String("mongodb://mongodb:mongodb@localhost:27017".getBytes(), StandardCharsets.US_ASCII)),
"mongodb://*****:*****@localhost:27017"),
Arguments.of(new ConnectionString("mongodb+srv://cluster0.example.mongodb.net/?retryWrites=true&w=majority"),
"mongodb+srv://cluster0.example.mongodb.net/?retryWrites=true&w=majority"), //
Arguments.of(
new ConnectionString(
"mongodb+srv://mongodb:mongodb@cluster0.example.mongodb.net/?retryWrites=true&w=majority"),
"mongodb+srv://*****:*****@cluster0.example.mongodb.net/?retryWrites=true&w=majority"));
}
}
Loading…
Cancel
Save