Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 9d6a801

Browse files
feat: Suppress logging of deprecation warning for id() by default.
Signed-off-by: Michael Simons <michael@simons.ac>
1 parent 9b1bce5 commit 9d6a801

File tree

5 files changed

+189
-38
lines changed

5 files changed

+189
-38
lines changed

‎src/main/java/org/springframework/data/neo4j/core/Neo4jClient.java‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.Collection;
1919
import java.util.Map;
2020
import java.util.Optional;
21+
import java.util.concurrent.atomic.AtomicBoolean;
2122
import java.util.function.BiFunction;
2223
import java.util.function.Function;
2324
import java.util.function.Supplier;
@@ -44,6 +45,12 @@
4445
@API(status = API.Status.STABLE, since = "6.0")
4546
public interface Neo4jClient {
4647

48+
/**
49+
* This is a public API introduced to turn the logging of the infamous warning back on.
50+
* {@code The query used a deprecated function: `id`.}
51+
*/
52+
AtomicBoolean SUPPRESS_ID_DEPRECATIONS = new AtomicBoolean(true);
53+
4754
LogAccessor cypherLog = new LogAccessor(LogFactory.getLog("org.springframework.data.neo4j.cypher"));
4855
LogAccessor log = new LogAccessor(LogFactory.getLog(Neo4jClient.class));
4956

‎src/main/java/org/springframework/data/neo4j/core/ResultSummaries.java‎

Lines changed: 50 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,20 @@
1616
package org.springframework.data.neo4j.core;
1717

1818
import java.util.function.Consumer;
19+
import java.util.function.Predicate;
20+
import java.util.regex.Pattern;
1921
import java.util.stream.Collectors;
2022
import java.util.stream.Stream;
2123

2224
import org.apache.commons.logging.LogFactory;
23-
import org.neo4j.driver.NotificationCategory;
25+
import org.neo4j.driver.NotificationClassification;
26+
import org.neo4j.driver.NotificationSeverity;
2427
import org.neo4j.driver.summary.InputPosition;
2528
import org.neo4j.driver.summary.Notification;
2629
import org.neo4j.driver.summary.Plan;
2730
import org.neo4j.driver.summary.ResultSummary;
2831
import org.springframework.core.log.LogAccessor;
32+
import org.springframework.lang.Nullable;
2933

3034
/**
3135
* Utility class for dealing with result summaries.
@@ -46,6 +50,8 @@ final class ResultSummaries {
4650
private static final LogAccessor cypherSecurityNotificationLog = new LogAccessor(LogFactory.getLog("org.springframework.data.neo4j.cypher.security"));
4751
private static final LogAccessor cypherTopologyNotificationLog = new LogAccessor(LogFactory.getLog("org.springframework.data.neo4j.cypher.topology"));
4852

53+
private static final Pattern DEPRECATED_ID_PATTERN = Pattern.compile("(?im)The query used a deprecated function: `id`\\.");
54+
4955
/**
5056
* Does some post-processing on the giving result summary, especially logging all notifications
5157
* and potentially query plans.
@@ -65,48 +71,55 @@ private static void logNotifications(ResultSummary resultSummary) {
6571
return;
6672
}
6773

74+
boolean supressIdDeprecations = Neo4jClient.SUPPRESS_ID_DEPRECATIONS.getAcquire();
75+
Predicate<Notification> isDeprecationWarningForId;
76+
try {
77+
isDeprecationWarningForId = notification -> supressIdDeprecations
78+
&& notification.classification().orElse(NotificationClassification.UNRECOGNIZED)
79+
== NotificationClassification.DEPRECATION && DEPRECATED_ID_PATTERN.matcher(notification.description())
80+
.matches();
81+
} finally {
82+
Neo4jClient.SUPPRESS_ID_DEPRECATIONS.setRelease(supressIdDeprecations);
83+
}
84+
6885
String query = resultSummary.query().text();
6986
resultSummary.notifications()
70-
.forEach(notification -> {
71-
LogAccessor log = notification.category()
72-
.map(ResultSummaries::getLogAccessor)
73-
.orElse(Neo4jClient.cypherLog);
74-
Consumer<String> logFunction =
75-
switch (notification.severity()) {
76-
case "WARNING" -> log::warn;
77-
case "INFORMATION" -> log::info;
78-
default -> log::debug;
79-
};
87+
.stream().filter(Predicate.not(isDeprecationWarningForId))
88+
.forEach(notification -> notification.severityLevel().ifPresent(severityLevel -> {
89+
var category = notification.classification().orElse(null);
90+
91+
var logger = getLogAccessor(category);
92+
Consumer<String> logFunction;
93+
if (severityLevel == NotificationSeverity.WARNING) {
94+
logFunction = logger::warn;
95+
} else if (severityLevel == NotificationSeverity.INFORMATION) {
96+
logFunction = logger::info;
97+
} else if (severityLevel == NotificationSeverity.OFF) {
98+
logFunction = (String message) -> {
99+
};
100+
} else {
101+
logFunction = logger::debug;
102+
}
103+
80104
logFunction.accept(ResultSummaries.format(notification, query));
81-
});
105+
}));
82106
}
83107

84-
private static LogAccessor getLogAccessor(NotificationCategory category) {
85-
if (category == NotificationCategory.HINT) {
86-
return cypherHintNotificationLog;
87-
}
88-
if (category == NotificationCategory.DEPRECATION) {
89-
return cypherDeprecationNotificationLog;
90-
}
91-
if (category == NotificationCategory.PERFORMANCE) {
92-
return cypherPerformanceNotificationLog;
93-
}
94-
if (category == NotificationCategory.GENERIC) {
95-
return cypherGenericNotificationLog;
96-
}
97-
if (category == NotificationCategory.UNSUPPORTED) {
98-
return cypherUnsupportedNotificationLog;
99-
}
100-
if (category == NotificationCategory.UNRECOGNIZED) {
101-
return cypherUnrecognizedNotificationLog;
102-
}
103-
if (category == NotificationCategory.SECURITY) {
104-
return cypherSecurityNotificationLog;
105-
}
106-
if (category == NotificationCategory.TOPOLOGY) {
107-
return cypherTopologyNotificationLog;
108+
private static LogAccessor getLogAccessor(@Nullable NotificationClassification category) {
109+
if (category == null) {
110+
return Neo4jClient.cypherLog;
108111
}
109-
return Neo4jClient.cypherLog;
112+
return switch (category) {
113+
case HINT -> cypherHintNotificationLog;
114+
case DEPRECATION -> cypherDeprecationNotificationLog;
115+
case PERFORMANCE -> cypherPerformanceNotificationLog;
116+
case GENERIC -> cypherGenericNotificationLog;
117+
case UNSUPPORTED -> cypherUnsupportedNotificationLog;
118+
case UNRECOGNIZED -> cypherUnrecognizedNotificationLog;
119+
case SECURITY -> cypherSecurityNotificationLog;
120+
case TOPOLOGY -> cypherTopologyNotificationLog;
121+
default -> Neo4jClient.cypherLog;
122+
};
110123
}
111124

112125
/**

‎src/test/java/org/springframework/data/neo4j/integration/issues/pure_element_id/AbstractElementIdTestBase.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ static void assertThatLogMessageDoNotIndicateIDUsage(LogbackCapture logbackCaptu
8080
assertThat(formattedMessages)
8181
.noneMatch(s -> s.contains("Neo.ClientNotification.Statement.FeatureDeprecationWarning") ||
8282
s.contains("The query used a deprecated function. ('id' is no longer supported)") ||
83+
s.contains("The query used a deprecated function: `id`.") ||
8384
s.matches("(?s).*toString\\(id\\(.*")); // No deprecations are logged when deprecated function call is nested. Anzeige ist raus.
8485
}
8586
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
* Copyright 2011-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.neo4j.integration.misc;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.assertj.core.api.Assertions.assertThatCode;
20+
21+
import java.util.function.Predicate;
22+
23+
import org.junit.jupiter.api.Test;
24+
import org.junit.jupiter.api.condition.EnabledIf;
25+
import org.junit.jupiter.api.extension.ExtendWith;
26+
import org.neo4j.driver.Driver;
27+
import org.springframework.beans.factory.annotation.Autowired;
28+
import org.springframework.context.annotation.Bean;
29+
import org.springframework.context.annotation.ComponentScan;
30+
import org.springframework.context.annotation.Configuration;
31+
import org.springframework.data.neo4j.core.Neo4jClient;
32+
import org.springframework.data.neo4j.integration.bookmarks.DatabaseInitializer;
33+
import org.springframework.data.neo4j.test.LogbackCapture;
34+
import org.springframework.data.neo4j.test.LogbackCapturingExtension;
35+
import org.springframework.data.neo4j.test.Neo4jExtension;
36+
import org.springframework.data.neo4j.test.Neo4jImperativeTestConfiguration;
37+
import org.springframework.data.neo4j.test.Neo4jIntegrationTest;
38+
import org.springframework.data.neo4j.test.ServerVersion;
39+
import org.springframework.transaction.annotation.EnableTransactionManagement;
40+
41+
import ch.qos.logback.classic.Level;
42+
import ch.qos.logback.classic.Logger;
43+
44+
@Neo4jIntegrationTest
45+
@ExtendWith(LogbackCapturingExtension.class)
46+
class IdLoggingIT {
47+
48+
protected static Neo4jExtension.Neo4jConnectionSupport neo4jConnectionSupport;
49+
50+
@Configuration
51+
@EnableTransactionManagement
52+
@ComponentScan
53+
static class Config extends Neo4jImperativeTestConfiguration {
54+
55+
@Bean
56+
DatabaseInitializer databaseInitializer(Driver driver) {
57+
return new DatabaseInitializer(driver);
58+
}
59+
60+
@Bean
61+
public Driver driver() {
62+
return neo4jConnectionSupport.getDriver();
63+
}
64+
65+
@Override
66+
public boolean isCypher5Compatible() {
67+
return neo4jConnectionSupport.isCypher5SyntaxCompatible();
68+
}
69+
}
70+
71+
static boolean isGreaterThanOrEqualNeo4j5() {
72+
return neo4jConnectionSupport.getServerVersion().greaterThanOrEqual(ServerVersion.v5_0_0);
73+
}
74+
75+
@EnabledIf("isGreaterThanOrEqualNeo4j5")
76+
@Test
77+
void idWarningShouldBeSuppressed(LogbackCapture logbackCapture, @Autowired Neo4jClient neo4jClient) {
78+
79+
// Was not able to combine the autowiring of capture and the client here
80+
for (Boolean enabled : new Boolean[] {true, false, null}) {
81+
82+
Logger logger = (Logger) org.slf4j.LoggerFactory.getLogger("org.springframework.data.neo4j.cypher.deprecation");
83+
Level originalLevel = logger.getLevel();
84+
logger.setLevel(Level.DEBUG);
85+
86+
Boolean oldValue = null;
87+
if (enabled != null) {
88+
oldValue = Neo4jClient.SUPPRESS_ID_DEPRECATIONS.getAndSet(enabled);
89+
}
90+
91+
try {
92+
assertThatCode(() -> neo4jClient.query(
93+
"CREATE (n:XXXIdTest) RETURN id(n)").fetch().all()).doesNotThrowAnyException();
94+
Predicate<String> stringPredicate = msg -> msg.contains(
95+
"Neo.ClientNotification.Statement.FeatureDeprecationWarning");
96+
97+
if (enabled == null || enabled) {
98+
assertThat(logbackCapture.getFormattedMessages()).noneMatch(stringPredicate);
99+
} else {
100+
assertThat(logbackCapture.getFormattedMessages()).anyMatch(stringPredicate);
101+
}
102+
} finally {
103+
logbackCapture.clear();
104+
logger.setLevel(originalLevel);
105+
if (oldValue != null) {
106+
Neo4jClient.SUPPRESS_ID_DEPRECATIONS.set(oldValue);
107+
}
108+
}
109+
}
110+
}
111+
112+
@EnabledIf("isGreaterThanOrEqualNeo4j5")
113+
@Test
114+
void otherDeprecationsWarningsShouldNotBeSuppressed(LogbackCapture logbackCapture, @Autowired Neo4jClient neo4jClient) {
115+
116+
Logger logger = (Logger) org.slf4j.LoggerFactory.getLogger("org.springframework.data.neo4j.cypher.deprecation");
117+
Level originalLevel = logger.getLevel();
118+
logger.setLevel(Level.DEBUG);
119+
120+
try {
121+
assertThatCode(() -> neo4jClient.query(
122+
"MATCH (n) CALL {WITH n RETURN count(n) AS cnt} RETURN *").fetch().all()).doesNotThrowAnyException();
123+
assertThat(logbackCapture.getFormattedMessages())
124+
.anyMatch(msg -> msg.contains("Neo.ClientNotification.Statement.FeatureDeprecationWarning"))
125+
.anyMatch(msg -> msg.contains("CALL subquery without a variable scope clause is now deprecated. Use CALL (n) { ... }"));
126+
} finally {
127+
logger.setLevel(originalLevel);
128+
}
129+
}
130+
}

‎src/test/java/org/springframework/data/neo4j/test/LogbackCapture.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ void start() {
6161
this.listAppender.start();
6262
}
6363

64-
void clear() {
64+
publicvoid clear() {
6565
this.resetLogLevel();
6666
this.listAppender.list.clear();
6767
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /