diff --git a/driver/clirr-ignored-differences.xml b/driver/clirr-ignored-differences.xml
index 424fb74730..40ad7e7178 100644
--- a/driver/clirr-ignored-differences.xml
+++ b/driver/clirr-ignored-differences.xml
@@ -409,4 +409,10 @@
org.neo4j.driver.BaseSession session(java.lang.Class, org.neo4j.driver.SessionConfig)
+
+ org/neo4j/driver/Logging
+ 7012
+ org.neo4j.driver.Logging javaPlatformLogging()
+
+
diff --git a/driver/src/main/java/module-info.java b/driver/src/main/java/module-info.java
index ba5b0b1ce9..4597e6458e 100644
--- a/driver/src/main/java/module-info.java
+++ b/driver/src/main/java/module-info.java
@@ -35,7 +35,7 @@
requires io.netty.buffer;
requires io.netty.codec;
requires io.netty.resolver;
- requires transitive java.logging;
+ requires static transitive java.logging;
requires transitive org.reactivestreams;
requires static micrometer.core;
requires static org.graalvm.nativeimage.builder;
diff --git a/driver/src/main/java/org/neo4j/driver/Logging.java b/driver/src/main/java/org/neo4j/driver/Logging.java
index 44b3cadc06..091132d415 100644
--- a/driver/src/main/java/org/neo4j/driver/Logging.java
+++ b/driver/src/main/java/org/neo4j/driver/Logging.java
@@ -24,6 +24,7 @@
import java.util.logging.Level;
import org.neo4j.driver.internal.logging.ConsoleLogging;
import org.neo4j.driver.internal.logging.JULogging;
+import org.neo4j.driver.internal.logging.JavaPlatformLogging;
import org.neo4j.driver.internal.logging.Slf4jLogging;
/**
@@ -33,6 +34,9 @@
*
{@link #slf4j() SLF4J logging} - uses available SLF4J binding (Logback, Log4j, etc.) fails when no SLF4J implementation is available. Uses
* application's logging configuration from XML or other type of configuration file. This logging method is the preferred one and relies on the SLF4J
* implementation available in the classpath or modulepath.
+ * {@link #javaPlatformLogging() Java Logging API} - uses {@link java.lang.System.Logger} created via
+ * {@link java.lang.System#getLogger(String)}. This logging method is suitable when application
+ * uses the java system logger api for logging and provides the appropriate implementation (Logback, Log4j, etc.)
* {@link #javaUtilLogging(Level) Java Logging API (JUL)} - uses {@link java.util.logging.Logger} created via
* {@link java.util.logging.Logger#getLogger(String)}. Global java util logging configuration applies. This logging method is suitable when application
* uses JUL for logging and explicitly configures it.
@@ -118,13 +122,27 @@ static Logging slf4j() {
return new Slf4jLogging();
}
+ /**
+ * Create logging implementation that uses {@link java.lang.System.Logger}.
+ *
+ * @return new logging implementation.
+ */
+ static Logging javaPlatformLogging() {
+ return new JavaPlatformLogging();
+ }
+
/**
* Create logging implementation that uses {@link java.util.logging}.
*
* @param level the log level.
* @return new logging implementation.
+ * @throws IllegalStateException if java.logging is not available.
*/
static Logging javaUtilLogging(Level level) {
+ IllegalStateException unavailabilityError = JULogging.checkAvailability();
+ if (unavailabilityError != null) {
+ throw unavailabilityError;
+ }
return new JULogging(level);
}
diff --git a/driver/src/main/java/org/neo4j/driver/internal/logging/JULogging.java b/driver/src/main/java/org/neo4j/driver/internal/logging/JULogging.java
index 58ee48908f..095d2bb757 100644
--- a/driver/src/main/java/org/neo4j/driver/internal/logging/JULogging.java
+++ b/driver/src/main/java/org/neo4j/driver/internal/logging/JULogging.java
@@ -42,4 +42,15 @@ public JULogging(Level loggingLevel) {
public Logger getLog(String name) {
return new JULogger(name, loggingLevel);
}
+
+ public static IllegalStateException checkAvailability() {
+ try {
+ Class.forName("java.util.logging.Logger");
+ return null;
+ } catch (Throwable error) {
+ return new IllegalStateException(
+ "java.util.logging is not available. Please add the java.logging module.",
+ error);
+ }
+ }
}
diff --git a/driver/src/main/java/org/neo4j/driver/internal/logging/JavaPlatformLogger.java b/driver/src/main/java/org/neo4j/driver/internal/logging/JavaPlatformLogger.java
new file mode 100644
index 0000000000..0c0b6d6104
--- /dev/null
+++ b/driver/src/main/java/org/neo4j/driver/internal/logging/JavaPlatformLogger.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * 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
+ *
+ * http://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.neo4j.driver.internal.logging;
+
+import org.neo4j.driver.Logger;
+
+import java.lang.System.Logger.Level;
+import java.util.Objects;
+
+public class JavaPlatformLogger implements Logger {
+ private final java.lang.System.Logger delegate;
+
+ public JavaPlatformLogger(System.Logger delegate) {
+ this.delegate = Objects.requireNonNull(delegate);
+ }
+
+ @Override
+ public void error(String message, Throwable cause) {
+ if (delegate.isLoggable(Level.ERROR)) {
+ delegate.log(Level.ERROR, message, cause);
+ }
+ }
+
+ @Override
+ public void info(String format, Object... params) {
+ if (delegate.isLoggable(Level.INFO)) {
+ delegate.log(Level.INFO, String.format(format, params));
+ }
+ }
+
+ @Override
+ public void warn(String format, Object... params) {
+ if (delegate.isLoggable(Level.WARNING)) {
+ delegate.log(Level.WARNING, String.format(format, params));
+ }
+ }
+
+ @Override
+ public void warn(String message, Throwable cause) {
+ if (delegate.isLoggable(Level.WARNING)) {
+ delegate.log(Level.WARNING, message, cause);
+ }
+ }
+
+ @Override
+ public void debug(String format, Object... params) {
+ if (isDebugEnabled()) {
+ delegate.log(Level.DEBUG, String.format(format, params));
+ }
+ }
+
+ @Override
+ public void debug(String message, Throwable throwable) {
+ if (isDebugEnabled()) {
+ delegate.log(Level.DEBUG, message, throwable);
+ }
+ }
+
+ @Override
+ public void trace(String format, Object... params) {
+ if (isTraceEnabled()) {
+ delegate.log(Level.TRACE, String.format(format, params));
+ }
+ }
+
+ @Override
+ public boolean isTraceEnabled() {
+ return delegate.isLoggable(Level.TRACE);
+ }
+
+ @Override
+ public boolean isDebugEnabled() {
+ return delegate.isLoggable(Level.DEBUG);
+ }
+}
diff --git a/driver/src/main/java/org/neo4j/driver/internal/logging/JavaPlatformLogging.java b/driver/src/main/java/org/neo4j/driver/internal/logging/JavaPlatformLogging.java
new file mode 100644
index 0000000000..19d9c9f903
--- /dev/null
+++ b/driver/src/main/java/org/neo4j/driver/internal/logging/JavaPlatformLogging.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * 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
+ *
+ * http://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.neo4j.driver.internal.logging;
+
+import org.neo4j.driver.Logger;
+import org.neo4j.driver.Logging;
+
+import java.io.Serializable;
+
+/**
+ * Internal implementation of the java platform logging module.
+ * This class should not be used directly. Please use {@link Logging#javaPlatformLogging()} factory method instead.
+ *
+ * @see Logging#javaPlatformLogging()
+ */
+public class JavaPlatformLogging implements Logging, Serializable {
+ private static final long serialVersionUID = -1145576859241657833L;
+
+ public JavaPlatformLogging() {
+ }
+
+ @Override
+ public Logger getLog(String name) {
+ return new JavaPlatformLogger(System.getLogger(name));
+ }
+}
diff --git a/driver/src/test/java/org/neo4j/driver/ConfigTest.java b/driver/src/test/java/org/neo4j/driver/ConfigTest.java
index e38515650c..408171cd6a 100644
--- a/driver/src/test/java/org/neo4j/driver/ConfigTest.java
+++ b/driver/src/test/java/org/neo4j/driver/ConfigTest.java
@@ -47,6 +47,7 @@
import org.neo4j.driver.internal.logging.DevNullLogging;
import org.neo4j.driver.internal.logging.JULogging;
import org.neo4j.driver.internal.logging.Slf4jLogging;
+import org.neo4j.driver.internal.logging.JavaPlatformLogging;
import org.neo4j.driver.net.ServerAddressResolver;
import org.neo4j.driver.testutil.TestUtil;
@@ -486,7 +487,7 @@ void shouldSerializeSerializableLogging() throws IOException, ClassNotFoundExcep
}
@ParameterizedTest
- @ValueSource(classes = {DevNullLogging.class, JULogging.class, ConsoleLogging.class, Slf4jLogging.class})
+ @ValueSource(classes = {DevNullLogging.class, JULogging.class, ConsoleLogging.class, Slf4jLogging.class, JavaPlatformLogging.class})
void officialLoggingProvidersShouldBeSerializable(Class extends Logging> loggingClass) {
assertTrue(Serializable.class.isAssignableFrom(loggingClass));
}
diff --git a/driver/src/test/java/org/neo4j/driver/internal/logging/JULoggingTest.java b/driver/src/test/java/org/neo4j/driver/internal/logging/JULoggingTest.java
new file mode 100644
index 0000000000..9b5cb2bd8d
--- /dev/null
+++ b/driver/src/test/java/org/neo4j/driver/internal/logging/JULoggingTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * 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
+ *
+ * http://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.neo4j.driver.internal.logging;
+
+import org.junit.jupiter.api.Test;
+import org.neo4j.driver.Logger;
+
+import java.util.logging.Level;
+
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.junit.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+class JULoggingTest {
+ @Test
+ void shouldCreateLoggers() {
+ JULogging logging = new JULogging(Level.ALL);
+
+ Logger logger = logging.getLog("My Log");
+
+ assertThat(logger, instanceOf(JULogger.class));
+ }
+
+ @Test
+ void shouldCheckIfAvailable() {
+ assertNull(JULogging.checkAvailability());
+ }
+}
diff --git a/driver/src/test/java/org/neo4j/driver/internal/logging/JavaPlatformLoggerTest.java b/driver/src/test/java/org/neo4j/driver/internal/logging/JavaPlatformLoggerTest.java
new file mode 100644
index 0000000000..05ce409e84
--- /dev/null
+++ b/driver/src/test/java/org/neo4j/driver/internal/logging/JavaPlatformLoggerTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * 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
+ *
+ * http://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.neo4j.driver.internal.logging;
+
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+
+class JavaPlatformLoggerTest {
+ private final Logger logger = Mockito.mock(Logger.class);
+ private final JavaPlatformLogger javaPlatformLogger = new JavaPlatformLogger(logger);
+
+ @Test
+ void shouldLogErrorWithMessageAndThrowable() {
+ Mockito.when(logger.isLoggable(Level.ERROR)).thenReturn(true);
+ String message = "Hello";
+ IllegalArgumentException error = new IllegalArgumentException("World");
+
+ javaPlatformLogger.error(message, error);
+
+ Mockito.verify(logger).log(Level.ERROR, message, error);
+ }
+
+ @Test
+ void shouldLogInfoWithMessageAndParams() {
+ Mockito.when(logger.isLoggable(Level.INFO)).thenReturn(true);
+ String message = "One %s, two %s, three %s";
+ Object[] params = {"111", "222", "333"};
+
+ javaPlatformLogger.info(message, params);
+
+ Mockito.verify(logger).log(Level.INFO, "One 111, two 222, three 333");
+ }
+
+ @Test
+ void shouldLogWarnWithMessageAndParams() {
+ Mockito.when(logger.isLoggable(Level.WARNING)).thenReturn(true);
+ String message = "C for %s, d for %s";
+ Object[] params = {"cat", "dog"};
+
+ javaPlatformLogger.warn(message, params);
+
+ Mockito.verify(logger).log(Level.WARNING, "C for cat, d for dog");
+ }
+
+ @Test
+ void shouldLogWarnWithMessageAndThrowable() {
+ Mockito.when(logger.isLoggable(Level.WARNING)).thenReturn(true);
+ String message = "Hello";
+ RuntimeException error = new RuntimeException("World");
+
+ javaPlatformLogger.warn(message, error);
+
+ Mockito.verify(logger).log(Level.WARNING, message, error);
+ }
+
+ @Test
+ void shouldLogDebugWithMessageAndParams() {
+ Mockito.when(logger.isLoggable(Level.DEBUG)).thenReturn(true);
+ String message = "Hello%s%s!";
+ Object[] params = {" ", "World"};
+
+ javaPlatformLogger.debug(message, params);
+
+ Mockito.verify(logger).log(Level.DEBUG, "Hello World!");
+ }
+
+ @Test
+ void shouldLogTraceWithMessageAndParams() {
+ Mockito.when(logger.isLoggable(Level.TRACE)).thenReturn(true);
+ String message = "I'll be %s!";
+ Object[] params = {"back"};
+
+ javaPlatformLogger.trace(message, params);
+
+ Mockito.verify(logger).log(Level.TRACE, "I'll be back!");
+ }
+
+ @Test
+ void shouldCheckIfDebugIsEnabled() {
+ Mockito.when(logger.isLoggable(Level.DEBUG)).thenReturn(false);
+ assertFalse(javaPlatformLogger.isDebugEnabled());
+
+ Mockito.when(logger.isLoggable(Level.DEBUG)).thenReturn(true);
+ assertTrue(javaPlatformLogger.isDebugEnabled());
+ }
+
+ @Test
+ void shouldCheckIfTraceIsEnabled() {
+ Mockito.when(logger.isLoggable(Level.TRACE)).thenReturn(false);
+ assertFalse(javaPlatformLogger.isTraceEnabled());
+
+ Mockito.when(logger.isLoggable(Level.TRACE)).thenReturn(true);
+ assertTrue(javaPlatformLogger.isTraceEnabled());
+ }
+}
diff --git a/driver/src/test/java/org/neo4j/driver/internal/logging/JavaPlatformLoggingTest.java b/driver/src/test/java/org/neo4j/driver/internal/logging/JavaPlatformLoggingTest.java
new file mode 100644
index 0000000000..6110b216b1
--- /dev/null
+++ b/driver/src/test/java/org/neo4j/driver/internal/logging/JavaPlatformLoggingTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * 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
+ *
+ * http://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.neo4j.driver.internal.logging;
+
+import org.junit.jupiter.api.Test;
+import org.neo4j.driver.Logger;
+
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.junit.MatcherAssert.assertThat;
+
+class JavaPlatformLoggingTest {
+ @Test
+ void shouldCreateLoggers() {
+ JavaPlatformLogging logging = new JavaPlatformLogging();
+
+ Logger logger = logging.getLog("My Log");
+
+ assertThat(logger, instanceOf(JavaPlatformLogger.class));
+ }
+
+}