Skip to content

Commit 75f07cd

Browse files
mp911dechristophstrobl
authored andcommitted
Introduce contextual Observer and improved KeyName utils.
Original Pull Request: #5020
1 parent 767077a commit 75f07cd

File tree

5 files changed

+517
-166
lines changed

5 files changed

+517
-166
lines changed

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

Lines changed: 3 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,12 @@
1515
*/
1616
package org.springframework.data.mongodb.observability;
1717

18-
import com.mongodb.ConnectionString;
19-
import com.mongodb.ServerAddress;
20-
import com.mongodb.connection.ConnectionDescription;
21-
import com.mongodb.connection.ConnectionId;
22-
import com.mongodb.event.CommandStartedEvent;
2318
import io.micrometer.common.KeyValues;
19+
2420
import org.springframework.util.Assert;
2521
import org.springframework.util.ObjectUtils;
2622

27-
import static org.springframework.data.mongodb.observability.MongoObservation.LowCardinalityCommandKeyNames.*;
23+
import com.mongodb.event.CommandStartedEvent;
2824

2925
/**
3026
* Default {@link MongoHandlerObservationConvention} implementation.
@@ -43,44 +39,7 @@ public KeyValues getLowCardinalityKeyValues(MongoHandlerContext context) {
4339
throw new IllegalStateException("not command started event present");
4440
}
4541

46-
ConnectionString connectionString = context.getConnectionString();
47-
String connectionStringValue = connectionString != null ? connectionString.getConnectionString() : null;
48-
String username = connectionString != null ? connectionString.getUsername() : null;
49-
50-
String transport = null, peerName = null, peerPort =null, clusterId = null;
51-
ConnectionDescription connectionDescription = context.getCommandStartedEvent().getConnectionDescription();
52-
if (connectionDescription != null) {
53-
ServerAddress serverAddress = connectionDescription.getServerAddress();
54-
55-
if (serverAddress != null) {
56-
transport = "IP.TCP";
57-
peerName = serverAddress.getHost();
58-
peerPort = String.valueOf(serverAddress.getPort());
59-
}
60-
61-
ConnectionId connectionId = connectionDescription.getConnectionId();
62-
if (connectionId != null) {
63-
clusterId = connectionId.getServerId().getClusterId().getValue();
64-
}
65-
}
66-
67-
return KeyValues.of(
68-
DB_SYSTEM.withValue("mongodb"),
69-
MONGODB_COMMAND.withValue(context.getCommandName()),
70-
DB_CONNECTION_STRING.withOptionalValue(connectionStringValue),
71-
DB_USER.withOptionalValue(username),
72-
DB_NAME.withOptionalValue(context.getDatabaseName()),
73-
MONGODB_COLLECTION.withOptionalValue(context.getCollectionName()),
74-
NET_TRANSPORT.withOptionalValue(transport),
75-
NET_PEER_NAME.withOptionalValue(peerName),
76-
NET_PEER_PORT.withOptionalValue(peerPort),
77-
MONGODB_CLUSTER_ID.withOptionalValue(clusterId)
78-
);
79-
}
80-
81-
@Override
82-
public KeyValues getHighCardinalityKeyValues(MongoHandlerContext context) {
83-
return KeyValues.empty();
42+
return MongoObservation.LowCardinality.observe(context).toKeyValues();
8443
}
8544

8645
@Override
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mongodb.observability;
17+
18+
import io.micrometer.common.KeyValue;
19+
import io.micrometer.common.docs.KeyName;
20+
21+
import java.util.Objects;
22+
import java.util.function.Function;
23+
import java.util.function.Predicate;
24+
25+
import org.jspecify.annotations.Nullable;
26+
27+
import org.springframework.util.StringUtils;
28+
29+
/**
30+
* Value object representing an observation key name for MongoDB operations. It allows easier transformation to
31+
* {@link KeyValue} and {@link KeyName}.
32+
*
33+
* @author Mark Paluch
34+
*/
35+
record MongoKeyName<C>(String name, boolean required, Function<C, @Nullable Object> valueFunction) implements KeyName {
36+
37+
/**
38+
* Creates a required {@link MongoKeyName} along with a contextual value function to extract the value from the
39+
* context. The value defaults to {@link KeyValue#NONE_VALUE} if the contextual value function returns
40+
* {@literal null}.
41+
*
42+
* @param name
43+
* @param valueFunction
44+
* @return
45+
* @param <C>
46+
*/
47+
public static <C> MongoKeyName<C> required(String name, Function<C, @Nullable Object> valueFunction) {
48+
return required(name, valueFunction, Objects::nonNull);
49+
}
50+
51+
/**
52+
* Creates a required {@link MongoKeyName} along with a contextual value function to extract the value from the
53+
* context. The value defaults to {@link KeyValue#NONE_VALUE} if the contextual value function returns {@literal null}
54+
* or an empty {@link String}.
55+
*
56+
* @param name
57+
* @param valueFunction
58+
* @return
59+
* @param <C>
60+
*/
61+
public static <C> MongoKeyName<C> requiredString(String name, Function<C, @Nullable String> valueFunction) {
62+
return required(name, valueFunction, StringUtils::hasText);
63+
}
64+
65+
/**
66+
* Creates a required {@link MongoKeyName} along with a contextual value function to extract the value from the
67+
* context. The value defaults to {@link KeyValue#NONE_VALUE} if the contextual value function returns
68+
* {@literal null}.
69+
*
70+
* @param name
71+
* @param valueFunction
72+
* @param hasValue predicate to determine if the value is present.
73+
* @return
74+
* @param <C>
75+
*/
76+
public static <C, V extends @Nullable Object> MongoKeyName<C> required(String name, Function<C, V> valueFunction,
77+
Predicate<V> hasValue) {
78+
return new MongoKeyName<>(name, true, c -> {
79+
V value = valueFunction.apply(c);
80+
return hasValue.test(value) ? value : null;
81+
});
82+
}
83+
84+
/**
85+
* Creates a required {@link MongoKeyValue} with a constant value.
86+
*
87+
* @param name
88+
* @param value
89+
* @return
90+
*/
91+
public static MongoKeyValue just(String name, String value) {
92+
return new MongoKeyName<>(name, false, it -> value).withValue(value);
93+
}
94+
95+
/**
96+
* Create a new {@link MongoKeyValue} with a given value.
97+
*
98+
* @param value value for key
99+
* @return
100+
*/
101+
@Override
102+
public MongoKeyValue withValue(String value) {
103+
return new MongoKeyValue(this, value);
104+
}
105+
106+
/**
107+
* Create a new {@link MongoKeyValue} from the context. If the context is {@literal null}, the value will be
108+
* {@link KeyValue#NONE_VALUE}.
109+
*
110+
* @param context
111+
* @return
112+
*/
113+
public MongoKeyValue valueOf(@Nullable C context) {
114+
115+
Object value = context != null ? valueFunction.apply(context) : null;
116+
return new MongoKeyValue(this, value == null ? KeyValue.NONE_VALUE : value.toString());
117+
}
118+
119+
/**
120+
* Create a new absent {@link MongoKeyValue} with the {@link KeyValue#NONE_VALUE} as value.
121+
*
122+
* @return
123+
*/
124+
public MongoKeyValue absent() {
125+
return new MongoKeyValue(this, KeyValue.NONE_VALUE);
126+
}
127+
128+
@Override
129+
public boolean isRequired() {
130+
return required;
131+
}
132+
133+
@Override
134+
public String asString() {
135+
return name;
136+
}
137+
138+
@Override
139+
public String toString() {
140+
return "Key: " + asString();
141+
}
142+
143+
/**
144+
* Value object representing an observation key and value for MongoDB operations. It allows easier transformation to
145+
* {@link KeyValue} and {@link KeyName}.
146+
*/
147+
static class MongoKeyValue implements KeyName, KeyValue {
148+
149+
private final KeyName keyName;
150+
private final String value;
151+
152+
MongoKeyValue(KeyName keyName, String value) {
153+
this.keyName = keyName;
154+
this.value = value;
155+
}
156+
157+
@Override
158+
public String getKey() {
159+
return keyName.asString();
160+
}
161+
162+
@Override
163+
public String getValue() {
164+
return value;
165+
}
166+
167+
@Override
168+
public String asString() {
169+
return getKey();
170+
}
171+
172+
@Override
173+
public String toString() {
174+
return getKey() + "=" + getValue();
175+
}
176+
}
177+
178+
}

0 commit comments

Comments
 (0)