Skip to content

Commit 7d1a327

Browse files
mp911dechristophstrobl
authored andcommitted
Introduce contextual Observer and improved KeyName utils.
Original Pull Request: #5020
1 parent 15709e0 commit 7d1a327

File tree

5 files changed

+515
-166
lines changed

5 files changed

+515
-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: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
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.springframework.lang.Nullable;
26+
import org.springframework.util.StringUtils;
27+
28+
/**
29+
* Value object representing an observation key name for MongoDB operations. It allows easier transformation to
30+
* {@link KeyValue} and {@link KeyName}.
31+
*
32+
* @author Mark Paluch
33+
*/
34+
record MongoKeyName<C>(String name, boolean required, Function<C, Object> valueFunction) implements KeyName {
35+
36+
/**
37+
* Creates a required {@link MongoKeyName} along with a contextual value function to extract the value from the
38+
* context. The value defaults to {@link KeyValue#NONE_VALUE} if the contextual value function returns
39+
* {@literal null}.
40+
*
41+
* @param name
42+
* @param valueFunction
43+
* @return
44+
* @param <C>
45+
*/
46+
public static <C> MongoKeyName<C> required(String name, Function<C, Object> valueFunction) {
47+
return required(name, valueFunction, Objects::nonNull);
48+
}
49+
50+
/**
51+
* Creates a required {@link MongoKeyName} along with a contextual value function to extract the value from the
52+
* context. The value defaults to {@link KeyValue#NONE_VALUE} if the contextual value function returns {@literal null}
53+
* or an empty {@link String}.
54+
*
55+
* @param name
56+
* @param valueFunction
57+
* @return
58+
* @param <C>
59+
*/
60+
public static <C> MongoKeyName<C> requiredString(String name, Function<C, String> valueFunction) {
61+
return required(name, valueFunction, StringUtils::hasText);
62+
}
63+
64+
/**
65+
* Creates a required {@link MongoKeyName} along with a contextual value function to extract the value from the
66+
* context. The value defaults to {@link KeyValue#NONE_VALUE} if the contextual value function returns
67+
* {@literal null}.
68+
*
69+
* @param name
70+
* @param valueFunction
71+
* @param hasValue predicate to determine if the value is present.
72+
* @return
73+
* @param <C>
74+
*/
75+
public static <C, V extends Object> MongoKeyName<C> required(String name, Function<C, V> valueFunction,
76+
Predicate<V> hasValue) {
77+
return new MongoKeyName<>(name, true, c -> {
78+
V value = valueFunction.apply(c);
79+
return hasValue.test(value) ? value : null;
80+
});
81+
}
82+
83+
/**
84+
* Creates a required {@link MongoKeyValue} with a constant value.
85+
*
86+
* @param name
87+
* @param value
88+
* @return
89+
*/
90+
public static MongoKeyValue just(String name, String value) {
91+
return new MongoKeyName<>(name, false, it -> value).withValue(value);
92+
}
93+
94+
/**
95+
* Create a new {@link MongoKeyValue} with a given value.
96+
*
97+
* @param value value for key
98+
* @return
99+
*/
100+
@Override
101+
public MongoKeyValue withValue(String value) {
102+
return new MongoKeyValue(this, value);
103+
}
104+
105+
/**
106+
* Create a new {@link MongoKeyValue} from the context. If the context is {@literal null}, the value will be
107+
* {@link KeyValue#NONE_VALUE}.
108+
*
109+
* @param context
110+
* @return
111+
*/
112+
public MongoKeyValue valueOf(@Nullable C context) {
113+
114+
Object value = context != null ? valueFunction.apply(context) : null;
115+
return new MongoKeyValue(this, value == null ? KeyValue.NONE_VALUE : value.toString());
116+
}
117+
118+
/**
119+
* Create a new absent {@link MongoKeyValue} with the {@link KeyValue#NONE_VALUE} as value.
120+
*
121+
* @return
122+
*/
123+
public MongoKeyValue absent() {
124+
return new MongoKeyValue(this, KeyValue.NONE_VALUE);
125+
}
126+
127+
@Override
128+
public boolean isRequired() {
129+
return required;
130+
}
131+
132+
@Override
133+
public String asString() {
134+
return name;
135+
}
136+
137+
@Override
138+
public String toString() {
139+
return "Key: " + asString();
140+
}
141+
142+
/**
143+
* Value object representing an observation key and value for MongoDB operations. It allows easier transformation to
144+
* {@link KeyValue} and {@link KeyName}.
145+
*/
146+
static class MongoKeyValue implements KeyName, KeyValue {
147+
148+
private final KeyName keyName;
149+
private final String value;
150+
151+
MongoKeyValue(KeyName keyName, String value) {
152+
this.keyName = keyName;
153+
this.value = value;
154+
}
155+
156+
@Override
157+
public String getKey() {
158+
return keyName.asString();
159+
}
160+
161+
@Override
162+
public String getValue() {
163+
return value;
164+
}
165+
166+
@Override
167+
public String asString() {
168+
return getKey();
169+
}
170+
171+
@Override
172+
public String toString() {
173+
return getKey() + "=" + getValue();
174+
}
175+
}
176+
177+
}

0 commit comments

Comments
 (0)