You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by al...@apache.org on 2022/07/10 12:26:33 UTC

[dubbo] branch 3.1 updated: [Feature] Adds feature of displaying FAQ link in logger output. (#10292)

This is an automated email from the ASF dual-hosted git repository.

albumenj pushed a commit to branch 3.1
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/3.1 by this push:
     new 6095d71062 [Feature] Adds feature of displaying FAQ link in logger output. (#10292)
6095d71062 is described below

commit 6095d71062bc6125167186ed0faa41d484251904
Author: Andy Cheung <wi...@users.noreply.github.com>
AuthorDate: Sun Jul 10 20:25:45 2022 +0800

    [Feature] Adds feature of displaying FAQ link in logger output. (#10292)
    
    * Add error type definition.
    
    * Add interface of error type aware logger.
    
    * Add fail-safe error type aware logger, and add getErrorTypeAwareLogger in logger factory.
    
    In order to obtain 'disabled' field in FailsafeLogger, a method called 'getDisabled()' in FailsafeLogger is also added.
    
    * Add corresponding test of FailsafeErrorTypeAwareLogger and LoggerFactory.
    
    * Add error code definition.
    
    * Add output of error code.
    
    * Per Project's request, remove ErrorType enumeration, and add parameters into logger methods.
    
    * Add error code checking logic.
    
    * Let matcher checks trimmed string instead of checking the original argument.
    
    * Remove debug, info, trace logging method per request.
---
 .../dubbo/common/logger/ErrorTypeAwareLogger.java  |  66 +++++++++++
 .../apache/dubbo/common/logger/LoggerFactory.java  |  22 ++++
 .../support/FailsafeErrorTypeAwareLogger.java      | 121 +++++++++++++++++++++
 .../common/logger/support/FailsafeLogger.java      |   4 +
 .../dubbo/common/logger/LoggerFactoryTest.java     |   8 ++
 .../support/FailsafeErrorTypeAwareLoggerTest.java  |  99 +++++++++++++++++
 6 files changed, 320 insertions(+)

diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/logger/ErrorTypeAwareLogger.java b/dubbo-common/src/main/java/org/apache/dubbo/common/logger/ErrorTypeAwareLogger.java
new file mode 100644
index 0000000000..fe9ebcc774
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/logger/ErrorTypeAwareLogger.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.dubbo.common.logger;
+
+/**
+ * Logger interface with the ability of displaying solution of different types of error.
+ */
+public interface ErrorTypeAwareLogger extends Logger {
+
+    /**
+     * Logs a message with warn log level.
+     *
+     * @param code error code
+     * @param cause error cause
+     * @param extendedInformation extended information
+     * @param msg log this message
+     */
+    void warn(String code, String cause, String extendedInformation, String msg);
+
+    /**
+     * Logs a message with warn log level.
+     *
+     * @param code error code
+     * @param cause error cause
+     * @param extendedInformation extended information
+     * @param msg log this message
+     * @param e log this cause
+     */
+    void warn(String code, String cause, String extendedInformation, String msg, Throwable e);
+
+    /**
+     * Logs a message with error log level.
+     *
+     * @param code error code
+     * @param cause error cause
+     * @param extendedInformation extended information
+     * @param msg log this message
+     */
+    void error(String code, String cause, String extendedInformation, String msg);
+
+    /**
+     * Logs a message with error log level.
+     *
+     * @param code error code
+     * @param cause error cause
+     * @param extendedInformation extended information
+     * @param msg log this message
+     * @param e log this cause
+     */
+    void error(String code, String cause, String extendedInformation, String msg, Throwable e);
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/logger/LoggerFactory.java b/dubbo-common/src/main/java/org/apache/dubbo/common/logger/LoggerFactory.java
index 9f787c51b2..91c0e60020 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/logger/LoggerFactory.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/logger/LoggerFactory.java
@@ -21,6 +21,7 @@ import org.apache.dubbo.common.logger.jdk.JdkLoggerAdapter;
 import org.apache.dubbo.common.logger.log4j.Log4jLoggerAdapter;
 import org.apache.dubbo.common.logger.log4j2.Log4j2LoggerAdapter;
 import org.apache.dubbo.common.logger.slf4j.Slf4jLoggerAdapter;
+import org.apache.dubbo.common.logger.support.FailsafeErrorTypeAwareLogger;
 import org.apache.dubbo.common.logger.support.FailsafeLogger;
 import org.apache.dubbo.rpc.model.FrameworkModel;
 
@@ -39,6 +40,7 @@ import java.util.concurrent.ConcurrentMap;
 public class LoggerFactory {
 
     private static final ConcurrentMap<String, FailsafeLogger> LOGGERS = new ConcurrentHashMap<>();
+    private static final ConcurrentMap<String, FailsafeErrorTypeAwareLogger> ERROR_TYPE_AWARE_LOGGERS = new ConcurrentHashMap<>();
     private static volatile LoggerAdapter LOGGER_ADAPTER;
 
     // search common-used logging frameworks
@@ -127,6 +129,26 @@ public class LoggerFactory {
         return LOGGERS.computeIfAbsent(key, k -> new FailsafeLogger(LOGGER_ADAPTER.getLogger(k)));
     }
 
+    /**
+     * Get error type aware logger by Class object.
+     *
+     * @param key the returned logger will be named after clazz
+     * @return error type aware logger
+     */
+    public static ErrorTypeAwareLogger getErrorTypeAwareLogger(Class<?> key) {
+        return ERROR_TYPE_AWARE_LOGGERS.computeIfAbsent(key.getName(), name -> new FailsafeErrorTypeAwareLogger(LOGGER_ADAPTER.getLogger(name)));
+    }
+
+    /**
+     * Get error type aware logger by a String key.
+     *
+     * @param key the returned logger will be named after key
+     * @return error type aware logger
+     */
+    public static ErrorTypeAwareLogger getErrorTypeAwareLogger(String key) {
+        return ERROR_TYPE_AWARE_LOGGERS.computeIfAbsent(key, k -> new FailsafeErrorTypeAwareLogger(LOGGER_ADAPTER.getLogger(k)));
+    }
+
     /**
      * Get logging level
      *
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/logger/support/FailsafeErrorTypeAwareLogger.java b/dubbo-common/src/main/java/org/apache/dubbo/common/logger/support/FailsafeErrorTypeAwareLogger.java
new file mode 100644
index 0000000000..afecb19986
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/logger/support/FailsafeErrorTypeAwareLogger.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.dubbo.common.logger.support;
+
+import org.apache.dubbo.common.Version;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.utils.NetUtils;
+
+import java.util.regex.Pattern;
+
+/**
+ * A fail-safe (ignoring exception thrown by logger) wrapper of error type aware logger.
+ */
+public class FailsafeErrorTypeAwareLogger extends FailsafeLogger implements ErrorTypeAwareLogger {
+
+    /**
+     * Mock address for formatting.
+     */
+    private static final String INSTRUCTIONS_URL = "https://dubbo.apache.org/faq/%d/%d";
+
+    private static final Pattern ERROR_CODE_PATTERN = Pattern.compile("\\d+-\\d+");
+
+    public FailsafeErrorTypeAwareLogger(Logger logger) {
+        super(logger);
+    }
+
+    private String appendContextMessageWithInstructions(String code, String cause, String extendedInformation, String msg) {
+        return " [DUBBO] " + msg + ", dubbo version: " + Version.getVersion() +
+            ", current host: " + NetUtils.getLocalHost() + ", error code: " + code +
+            ". This may be caused by " + cause + ", " +
+            "go to " + getErrorUrl(code) + " to find instructions. " + extendedInformation;
+    }
+
+    private String getErrorUrl(String code) {
+
+        String trimmedString = code.trim();
+
+        if (!ERROR_CODE_PATTERN.matcher(trimmedString).matches()) {
+            error("Invalid error code: " + code + ", the format of error code is: X-X (where X is a number).");
+            return "";
+        }
+
+        String[] segments = trimmedString.split("[-]");
+
+        int[] errorCodeSegments = new int[2];
+
+        try {
+            errorCodeSegments[0] = Integer.parseInt(segments[0]);
+            errorCodeSegments[1] = Integer.parseInt(segments[1]);
+        } catch (NumberFormatException numberFormatException) {
+            error("Invalid error code: " + code + ", the format of error code is: X-X (where X is a number).",
+                numberFormatException);
+        }
+
+        return String.format(INSTRUCTIONS_URL, errorCodeSegments[0], errorCodeSegments[1]);
+    }
+
+    @Override
+    public void warn(String code, String cause, String extendedInformation, String msg) {
+        if (getDisabled()) {
+            return;
+        }
+
+        try {
+            getLogger().warn(appendContextMessageWithInstructions(code, cause, extendedInformation, msg));
+        } catch (Throwable t) {
+        }
+    }
+
+    @Override
+    public void warn(String code, String cause, String extendedInformation, String msg, Throwable e) {
+        if (getDisabled()) {
+            return;
+        }
+
+        try {
+            getLogger().warn(appendContextMessageWithInstructions(code, cause, extendedInformation, msg), e);
+        } catch (Throwable t) {
+        }
+    }
+
+    @Override
+    public void error(String code, String cause, String extendedInformation, String msg) {
+        if (getDisabled()) {
+            return;
+        }
+
+        try {
+            getLogger().error(appendContextMessageWithInstructions(code, cause, extendedInformation, msg));
+        } catch (Throwable t) {
+        }
+    }
+
+    @Override
+    public void error(String code, String cause, String extendedInformation, String msg, Throwable e) {
+        if (getDisabled()) {
+            return;
+        }
+
+        try {
+            getLogger().error(appendContextMessageWithInstructions(code, cause, extendedInformation, msg), e);
+        } catch (Throwable t) {
+        }
+    }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/logger/support/FailsafeLogger.java b/dubbo-common/src/main/java/org/apache/dubbo/common/logger/support/FailsafeLogger.java
index ff6804bf70..66e369f83b 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/logger/support/FailsafeLogger.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/logger/support/FailsafeLogger.java
@@ -34,6 +34,10 @@ public class FailsafeLogger implements Logger {
         FailsafeLogger.disabled = disabled;
     }
 
+    static boolean getDisabled() {
+        return disabled;
+    }
+
     public Logger getLogger() {
         return logger;
     }
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/logger/LoggerFactoryTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/logger/LoggerFactoryTest.java
index c83765f444..70a7ce8693 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/logger/LoggerFactoryTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/logger/LoggerFactoryTest.java
@@ -68,4 +68,12 @@ public class LoggerFactoryTest {
 
         assertThat(logger1, is(logger2));
     }
+
+    @Test
+    public void shouldReturnSameErrorTypeAwareLogger() {
+        ErrorTypeAwareLogger logger1 = LoggerFactory.getErrorTypeAwareLogger(this.getClass().getName());
+        ErrorTypeAwareLogger logger2 = LoggerFactory.getErrorTypeAwareLogger(this.getClass().getName());
+
+        assertThat(logger1, is(logger2));
+    }
 }
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/logger/support/FailsafeErrorTypeAwareLoggerTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/logger/support/FailsafeErrorTypeAwareLoggerTest.java
new file mode 100644
index 0000000000..6eb3605556
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/logger/support/FailsafeErrorTypeAwareLoggerTest.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.dubbo.common.logger.support;
+
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Tests for FailsafeErrorTypeAwareLogger to test whether it 'ignores' exceptions thrown by logger or not.
+ */
+public class FailsafeErrorTypeAwareLoggerTest {
+    @Test
+    public void testFailsafeErrorTypeAwareForLoggingMethod() {
+        Logger failLogger = mock(Logger.class);
+        FailsafeErrorTypeAwareLogger failsafeLogger = new FailsafeErrorTypeAwareLogger(failLogger);
+
+        doThrow(new RuntimeException()).when(failLogger).error(anyString());
+        doThrow(new RuntimeException()).when(failLogger).warn(anyString());
+        doThrow(new RuntimeException()).when(failLogger).info(anyString());
+        doThrow(new RuntimeException()).when(failLogger).debug(anyString());
+        doThrow(new RuntimeException()).when(failLogger).trace(anyString());
+
+        failsafeLogger.error("1-1", "Registry center", "May be it's offline.", "error");
+        failsafeLogger.warn("1-1", "Registry center", "May be it's offline.", "warn");
+
+        doThrow(new RuntimeException()).when(failLogger).error(any(Throwable.class));
+        doThrow(new RuntimeException()).when(failLogger).warn(any(Throwable.class));
+        doThrow(new RuntimeException()).when(failLogger).info(any(Throwable.class));
+        doThrow(new RuntimeException()).when(failLogger).debug(any(Throwable.class));
+        doThrow(new RuntimeException()).when(failLogger).trace(any(Throwable.class));
+
+        failsafeLogger.error("1-1", "Registry center", "May be it's offline.", "error", new Exception("error"));
+        failsafeLogger.warn("1-1", "Registry center", "May be it's offline.", "warn", new Exception("warn"));
+    }
+
+    @Test
+    public void testSuccessLogger() {
+        Logger successLogger = mock(Logger.class);
+        FailsafeErrorTypeAwareLogger failsafeLogger = new FailsafeErrorTypeAwareLogger(successLogger);
+
+        failsafeLogger.error("1-1", "Registry center", "May be it's offline.", "error");
+        failsafeLogger.warn("1-1", "Registry center", "May be it's offline.", "warn");
+
+        verify(successLogger).error(anyString());
+        verify(successLogger).warn(anyString());
+
+        failsafeLogger.error("1-1", "Registry center", "May be it's offline.", "error", new Exception("error"));
+        failsafeLogger.warn("1-1", "Registry center", "May be it's offline.", "warn", new Exception("warn"));
+    }
+
+    @Test
+    public void testGetLogger() {
+        Assertions.assertThrows(RuntimeException.class, () -> {
+            Logger failLogger = mock(Logger.class);
+            FailsafeErrorTypeAwareLogger failsafeLogger = new FailsafeErrorTypeAwareLogger(failLogger);
+
+            doThrow(new RuntimeException()).when(failLogger).error(anyString());
+            failsafeLogger.getLogger().error("should get error");
+        });
+    }
+
+    @Test
+    public void testInstructionShownOrNot() {
+        LoggerFactory.setLoggerAdapter(FrameworkModel.defaultModel(), "jdk");
+
+        ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(FailsafeErrorTypeAwareLoggerTest.class);
+
+        logger.error("1-1", "Registry center", "May be it's offline.",
+            "error message", new Exception("error"));
+
+        logger.error("-1", "Registry center", "May be it's offline.",
+            "error message", new Exception("error"));
+    }
+}