You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rocketmq.apache.org by yu...@apache.org on 2018/03/02 08:07:43 UTC

[rocketmq] branch develop updated: [ROCKETMQ-367] Add logging component. (#224)

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

yukon pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/rocketmq.git


The following commit(s) were added to refs/heads/develop by this push:
     new 305f571  [ROCKETMQ-367] Add logging component. (#224)
305f571 is described below

commit 305f5717c6fe4ad6596433673d27c4831b382d1e
Author: lindzh <li...@163.com>
AuthorDate: Fri Mar 2 16:07:41 2018 +0800

    [ROCKETMQ-367] Add logging component. (#224)
---
 logging/pom.xml                                    |   50 +
 .../rocketmq/logging/InnerLoggerFactory.java       |  478 ++++++++
 .../apache/rocketmq/logging/InternalLogger.java    |   63 +
 .../rocketmq/logging/InternalLoggerFactory.java    |   93 ++
 .../rocketmq/logging/Slf4jLoggerFactory.java       |  153 +++
 .../apache/rocketmq/logging/inner/Appender.java    |  228 ++++
 .../org/apache/rocketmq/logging/inner/Layout.java  |   39 +
 .../org/apache/rocketmq/logging/inner/Level.java   |  156 +++
 .../org/apache/rocketmq/logging/inner/Logger.java  |  467 ++++++++
 .../rocketmq/logging/inner/LoggingBuilder.java     | 1230 ++++++++++++++++++++
 .../rocketmq/logging/inner/LoggingEvent.java       |  121 ++
 .../apache/rocketmq/logging/inner/SysLogger.java   |   89 ++
 .../org/apache/rocketmq/logging/package-info.java  |   35 +
 .../apache/rocketmq/logging/BasicloggerTest.java   |   70 ++
 .../rocketmq/logging/InnerLoggerFactoryTest.java   |   92 ++
 .../rocketmq/logging/InternalLoggerTest.java       |   72 ++
 .../rocketmq/logging/Slf4jLoggerFactoryTest.java   |   80 ++
 .../rocketmq/logging/inner/AppenderTest.java       |  160 +++
 .../apache/rocketmq/logging/inner/LayoutTest.java  |   54 +
 .../apache/rocketmq/logging/inner/LevelTest.java   |   37 +
 .../logging/inner/LoggerRepositoryTest.java        |   49 +
 .../apache/rocketmq/logging/inner/LoggerTest.java  |  115 ++
 .../rocketmq/logging/inner/LoggingBuilderTest.java |  113 ++
 .../logging/inner/MessageFormatterTest.java        |   40 +
 logging/src/test/resources/logback_test.xml        |   46 +
 pom.xml                                            |    6 +
 26 files changed, 4136 insertions(+)

diff --git a/logging/pom.xml b/logging/pom.xml
new file mode 100644
index 0000000..baad6ad
--- /dev/null
+++ b/logging/pom.xml
@@ -0,0 +1,50 @@
+<!--
+  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.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.apache.rocketmq</groupId>
+        <artifactId>rocketmq-all</artifactId>
+        <version>4.3.0-SNAPSHOT</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>jar</packaging>
+    <artifactId>rocketmq-logging</artifactId>
+    <name>rocketmq-logging ${project.version}</name>
+
+    <properties>
+        <maven.compiler.source>1.6</maven.compiler.source>
+        <maven.compiler.target>1.6</maven.compiler.target>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/logging/src/main/java/org/apache/rocketmq/logging/InnerLoggerFactory.java b/logging/src/main/java/org/apache/rocketmq/logging/InnerLoggerFactory.java
new file mode 100644
index 0000000..7714640
--- /dev/null
+++ b/logging/src/main/java/org/apache/rocketmq/logging/InnerLoggerFactory.java
@@ -0,0 +1,478 @@
+/*
+ * 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.rocketmq.logging;
+
+import org.apache.rocketmq.logging.inner.Logger;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class InnerLoggerFactory extends InternalLoggerFactory {
+
+    @Override
+    protected InternalLogger getLoggerInstance(String name) {
+        return new InnerLogger(name);
+    }
+
+    @Override
+    protected String getLoggerType() {
+        return LOGGER_INNER;
+    }
+
+    @Override
+    protected void shutdown() {
+        Logger.getRepository().shutdown();
+    }
+
+    public static class InnerLogger implements InternalLogger {
+
+        private Logger logger;
+
+        public InnerLogger(String name) {
+            logger = Logger.getLogger(name);
+        }
+
+        @Override
+        public String getName() {
+            return logger.getName();
+        }
+
+        @Override
+        public void debug(String var1) {
+            logger.debug(var1);
+        }
+
+        @Override
+        public void debug(String var1, Throwable var2) {
+            logger.debug(var1, var2);
+        }
+
+        @Override
+        public void info(String var1) {
+            logger.info(var1);
+        }
+
+        @Override
+        public void info(String var1, Throwable var2) {
+            logger.info(var1, var2);
+        }
+
+        @Override
+        public void warn(String var1) {
+            logger.warn(var1);
+        }
+
+        @Override
+        public void warn(String var1, Throwable var2) {
+            logger.warn(var1, var2);
+        }
+
+        @Override
+        public void error(String var1) {
+            logger.error(var1);
+        }
+
+        @Override
+        public void error(String var1, Throwable var2) {
+            logger.error(var1, var2);
+        }
+
+        @Override
+        public void debug(String var1, Object var2) {
+            FormattingTuple format = MessageFormatter.format(var1, var2);
+            logger.debug(format.getMessage(), format.getThrowable());
+        }
+
+        @Override
+        public void debug(String var1, Object var2, Object var3) {
+            FormattingTuple format = MessageFormatter.format(var1, var2, var3);
+            logger.debug(format.getMessage(), format.getThrowable());
+        }
+
+        @Override
+        public void debug(String var1, Object... var2) {
+            FormattingTuple format = MessageFormatter.arrayFormat(var1, var2);
+            logger.debug(format.getMessage(), format.getThrowable());
+        }
+
+        @Override
+        public void info(String var1, Object var2) {
+            FormattingTuple format = MessageFormatter.format(var1, var2);
+            logger.info(format.getMessage(), format.getThrowable());
+        }
+
+        @Override
+        public void info(String var1, Object var2, Object var3) {
+            FormattingTuple format = MessageFormatter.format(var1, var2, var3);
+            logger.info(format.getMessage(), format.getThrowable());
+        }
+
+        @Override
+        public void info(String var1, Object... var2) {
+            FormattingTuple format = MessageFormatter.arrayFormat(var1, var2);
+            logger.info(format.getMessage(), format.getThrowable());
+        }
+
+        @Override
+        public void warn(String var1, Object var2) {
+            FormattingTuple format = MessageFormatter.format(var1, var2);
+            logger.warn(format.getMessage(), format.getThrowable());
+        }
+
+        @Override
+        public void warn(String var1, Object... var2) {
+            FormattingTuple format = MessageFormatter.arrayFormat(var1, var2);
+            logger.warn(format.getMessage(), format.getThrowable());
+        }
+
+        @Override
+        public void warn(String var1, Object var2, Object var3) {
+            FormattingTuple format = MessageFormatter.format(var1, var2, var3);
+            logger.warn(format.getMessage(), format.getThrowable());
+        }
+
+        @Override
+        public void error(String var1, Object var2) {
+            FormattingTuple format = MessageFormatter.format(var1, var2);
+            logger.warn(format.getMessage(), format.getThrowable());
+        }
+
+        @Override
+        public void error(String var1, Object var2, Object var3) {
+            FormattingTuple format = MessageFormatter.format(var1, var2, var3);
+            logger.warn(format.getMessage(), format.getThrowable());
+        }
+
+        @Override
+        public void error(String var1, Object... var2) {
+            FormattingTuple format = MessageFormatter.arrayFormat(var1, var2);
+            logger.warn(format.getMessage(), format.getThrowable());
+        }
+
+        public Logger getLogger() {
+            return logger;
+        }
+    }
+
+
+    public static class FormattingTuple {
+        private String message;
+        private Throwable throwable;
+        private Object[] argArray;
+
+        public FormattingTuple(String message) {
+            this(message, null, null);
+        }
+
+        public FormattingTuple(String message, Object[] argArray, Throwable throwable) {
+            this.message = message;
+            this.throwable = throwable;
+            if (throwable == null) {
+                this.argArray = argArray;
+            } else {
+                this.argArray = trimmedCopy(argArray);
+            }
+
+        }
+
+        static Object[] trimmedCopy(Object[] argArray) {
+            if (argArray != null && argArray.length != 0) {
+                int trimemdLen = argArray.length - 1;
+                Object[] trimmed = new Object[trimemdLen];
+                System.arraycopy(argArray, 0, trimmed, 0, trimemdLen);
+                return trimmed;
+            } else {
+                throw new IllegalStateException("non-sensical empty or null argument array");
+            }
+        }
+
+        public String getMessage() {
+            return this.message;
+        }
+
+        public Object[] getArgArray() {
+            return this.argArray;
+        }
+
+        public Throwable getThrowable() {
+            return this.throwable;
+        }
+    }
+
+    public static class MessageFormatter {
+
+        public MessageFormatter() {
+        }
+
+        public static FormattingTuple format(String messagePattern, Object arg) {
+            return arrayFormat(messagePattern, new Object[]{arg});
+        }
+
+        public static FormattingTuple format(String messagePattern, Object arg1, Object arg2) {
+            return arrayFormat(messagePattern, new Object[]{arg1, arg2});
+        }
+
+        static Throwable getThrowableCandidate(Object[] argArray) {
+            if (argArray != null && argArray.length != 0) {
+                Object lastEntry = argArray[argArray.length - 1];
+                return lastEntry instanceof Throwable ? (Throwable) lastEntry : null;
+            } else {
+                return null;
+            }
+        }
+
+        public static FormattingTuple arrayFormat(String messagePattern, Object[] argArray) {
+            Throwable throwableCandidate = getThrowableCandidate(argArray);
+            if (messagePattern == null) {
+                return new FormattingTuple(null, argArray, throwableCandidate);
+            } else if (argArray == null) {
+                return new FormattingTuple(messagePattern);
+            } else {
+                int i = 0;
+                StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50);
+
+                int len;
+                for (len = 0; len < argArray.length; ++len) {
+                    int j = messagePattern.indexOf("{}", i);
+                    if (j == -1) {
+                        if (i == 0) {
+                            return new FormattingTuple(messagePattern, argArray, throwableCandidate);
+                        }
+
+                        sbuf.append(messagePattern.substring(i, messagePattern.length()));
+                        return new FormattingTuple(sbuf.toString(), argArray, throwableCandidate);
+                    }
+
+                    if (isEscapeDelimeter(messagePattern, j)) {
+                        if (!isDoubleEscaped(messagePattern, j)) {
+                            --len;
+                            sbuf.append(messagePattern.substring(i, j - 1));
+                            sbuf.append('{');
+                            i = j + 1;
+                        } else {
+                            sbuf.append(messagePattern.substring(i, j - 1));
+                            deeplyAppendParameter(sbuf, argArray[len], null);
+                            i = j + 2;
+                        }
+                    } else {
+                        sbuf.append(messagePattern.substring(i, j));
+                        deeplyAppendParameter(sbuf, argArray[len], null);
+                        i = j + 2;
+                    }
+                }
+
+                sbuf.append(messagePattern.substring(i, messagePattern.length()));
+                if (len < argArray.length - 1) {
+                    return new FormattingTuple(sbuf.toString(), argArray, throwableCandidate);
+                } else {
+                    return new FormattingTuple(sbuf.toString(), argArray, null);
+                }
+            }
+        }
+
+        static boolean isEscapeDelimeter(String messagePattern, int delimeterStartIndex) {
+            if (delimeterStartIndex == 0) {
+                return false;
+            } else {
+                char potentialEscape = messagePattern.charAt(delimeterStartIndex - 1);
+                return potentialEscape == 92;
+            }
+        }
+
+        static boolean isDoubleEscaped(String messagePattern, int delimeterStartIndex) {
+            return delimeterStartIndex >= 2 && messagePattern.charAt(delimeterStartIndex - 2) == 92;
+        }
+
+        private static void deeplyAppendParameter(StringBuilder sbuf, Object o, Map<Object[], Object> seenMap) {
+            if (o == null) {
+                sbuf.append("null");
+            } else {
+                if (!o.getClass().isArray()) {
+                    safeObjectAppend(sbuf, o);
+                } else if (o instanceof boolean[]) {
+                    booleanArrayAppend(sbuf, (boolean[]) o);
+                } else if (o instanceof byte[]) {
+                    byteArrayAppend(sbuf, (byte[]) o);
+                } else if (o instanceof char[]) {
+                    charArrayAppend(sbuf, (char[]) o);
+                } else if (o instanceof short[]) {
+                    shortArrayAppend(sbuf, (short[]) o);
+                } else if (o instanceof int[]) {
+                    intArrayAppend(sbuf, (int[]) o);
+                } else if (o instanceof long[]) {
+                    longArrayAppend(sbuf, (long[]) o);
+                } else if (o instanceof float[]) {
+                    floatArrayAppend(sbuf, (float[]) o);
+                } else if (o instanceof double[]) {
+                    doubleArrayAppend(sbuf, (double[]) o);
+                } else {
+                    objectArrayAppend(sbuf, (Object[]) o, seenMap);
+                }
+
+            }
+        }
+
+        private static void safeObjectAppend(StringBuilder sbuf, Object o) {
+            try {
+                String t = o.toString();
+                sbuf.append(t);
+            } catch (Throwable var3) {
+                System.err.println("RocketMQ InnerLogger: Failed toString() invocation on an object of type [" + o.getClass().getName() + "]");
+                var3.printStackTrace();
+                sbuf.append("[FAILED toString()]");
+            }
+
+        }
+
+        private static void objectArrayAppend(StringBuilder sbuf, Object[] a, Map<Object[], Object> seenMap) {
+            if (seenMap == null) {
+                seenMap = new HashMap<Object[], Object>();
+            }
+            sbuf.append('[');
+            if (!seenMap.containsKey(a)) {
+                seenMap.put(a, null);
+                int len = a.length;
+
+                for (int i = 0; i < len; ++i) {
+                    deeplyAppendParameter(sbuf, a[i], seenMap);
+                    if (i != len - 1) {
+                        sbuf.append(", ");
+                    }
+                }
+
+                seenMap.remove(a);
+            } else {
+                sbuf.append("...");
+            }
+
+            sbuf.append(']');
+        }
+
+        private static void booleanArrayAppend(StringBuilder sbuf, boolean[] a) {
+            sbuf.append('[');
+            int len = a.length;
+
+            for (int i = 0; i < len; ++i) {
+                sbuf.append(a[i]);
+                if (i != len - 1) {
+                    sbuf.append(", ");
+                }
+            }
+
+            sbuf.append(']');
+        }
+
+        private static void byteArrayAppend(StringBuilder sbuf, byte[] a) {
+            sbuf.append('[');
+            int len = a.length;
+
+            for (int i = 0; i < len; ++i) {
+                sbuf.append(a[i]);
+                if (i != len - 1) {
+                    sbuf.append(", ");
+                }
+            }
+
+            sbuf.append(']');
+        }
+
+        private static void charArrayAppend(StringBuilder sbuf, char[] a) {
+            sbuf.append('[');
+            int len = a.length;
+
+            for (int i = 0; i < len; ++i) {
+                sbuf.append(a[i]);
+                if (i != len - 1) {
+                    sbuf.append(", ");
+                }
+            }
+
+            sbuf.append(']');
+        }
+
+        private static void shortArrayAppend(StringBuilder sbuf, short[] a) {
+            sbuf.append('[');
+            int len = a.length;
+
+            for (int i = 0; i < len; ++i) {
+                sbuf.append(a[i]);
+                if (i != len - 1) {
+                    sbuf.append(", ");
+                }
+            }
+
+            sbuf.append(']');
+        }
+
+        private static void intArrayAppend(StringBuilder sbuf, int[] a) {
+            sbuf.append('[');
+            int len = a.length;
+
+            for (int i = 0; i < len; ++i) {
+                sbuf.append(a[i]);
+                if (i != len - 1) {
+                    sbuf.append(", ");
+                }
+            }
+
+            sbuf.append(']');
+        }
+
+        private static void longArrayAppend(StringBuilder sbuf, long[] a) {
+            sbuf.append('[');
+            int len = a.length;
+
+            for (int i = 0; i < len; ++i) {
+                sbuf.append(a[i]);
+                if (i != len - 1) {
+                    sbuf.append(", ");
+                }
+            }
+
+            sbuf.append(']');
+        }
+
+        private static void floatArrayAppend(StringBuilder sbuf, float[] a) {
+            sbuf.append('[');
+            int len = a.length;
+
+            for (int i = 0; i < len; ++i) {
+                sbuf.append(a[i]);
+                if (i != len - 1) {
+                    sbuf.append(", ");
+                }
+            }
+
+            sbuf.append(']');
+        }
+
+        private static void doubleArrayAppend(StringBuilder sbuf, double[] a) {
+            sbuf.append('[');
+            int len = a.length;
+
+            for (int i = 0; i < len; ++i) {
+                sbuf.append(a[i]);
+                if (i != len - 1) {
+                    sbuf.append(", ");
+                }
+            }
+
+            sbuf.append(']');
+        }
+    }
+}
diff --git a/logging/src/main/java/org/apache/rocketmq/logging/InternalLogger.java b/logging/src/main/java/org/apache/rocketmq/logging/InternalLogger.java
new file mode 100644
index 0000000..fae69dd
--- /dev/null
+++ b/logging/src/main/java/org/apache/rocketmq/logging/InternalLogger.java
@@ -0,0 +1,63 @@
+/*
+ * 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.rocketmq.logging;
+
+public interface InternalLogger {
+
+    String getName();
+
+    void debug(String var1);
+
+    void debug(String var1, Object var2);
+
+    void debug(String var1, Object var2, Object var3);
+
+    void debug(String var1, Object... var2);
+
+    void debug(String var1, Throwable var2);
+
+    void info(String var1);
+
+    void info(String var1, Object var2);
+
+    void info(String var1, Object var2, Object var3);
+
+    void info(String var1, Object... var2);
+
+    void info(String var1, Throwable var2);
+
+    void warn(String var1);
+
+    void warn(String var1, Object var2);
+
+    void warn(String var1, Object... var2);
+
+    void warn(String var1, Object var2, Object var3);
+
+    void warn(String var1, Throwable var2);
+
+    void error(String var1);
+
+    void error(String var1, Object var2);
+
+    void error(String var1, Object var2, Object var3);
+
+    void error(String var1, Object... var2);
+
+    void error(String var1, Throwable var2);
+}
diff --git a/logging/src/main/java/org/apache/rocketmq/logging/InternalLoggerFactory.java b/logging/src/main/java/org/apache/rocketmq/logging/InternalLoggerFactory.java
new file mode 100644
index 0000000..ec176ce
--- /dev/null
+++ b/logging/src/main/java/org/apache/rocketmq/logging/InternalLoggerFactory.java
@@ -0,0 +1,93 @@
+/*
+ * 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.rocketmq.logging;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+public abstract class InternalLoggerFactory {
+
+    public static final String LOGGER_SLF4J = "slf4j";
+
+    public static final String LOGGER_INNER = "inner";
+
+    public static final String DEFAULT_LOGGER = LOGGER_SLF4J;
+
+    private static String loggerType = null;
+
+    private static ConcurrentHashMap<String, InternalLoggerFactory> loggerFactoryCache = new ConcurrentHashMap<String, InternalLoggerFactory>();
+
+    public static InternalLogger getLogger(Class clazz) {
+        return getLogger(clazz.getName());
+    }
+
+    public static InternalLogger getLogger(String name) {
+        return getLoggerFactory().getLoggerInstance(name);
+    }
+
+    private static InternalLoggerFactory getLoggerFactory() {
+        InternalLoggerFactory internalLoggerFactory = null;
+        if (loggerType != null) {
+            internalLoggerFactory = loggerFactoryCache.get(loggerType);
+        }
+        if (internalLoggerFactory == null) {
+            internalLoggerFactory = loggerFactoryCache.get(DEFAULT_LOGGER);
+        }
+        if (internalLoggerFactory == null) {
+            internalLoggerFactory = loggerFactoryCache.get(LOGGER_INNER);
+        }
+        if (internalLoggerFactory == null) {
+            throw new RuntimeException("[RocketMQ] Logger init failed, please check logger");
+        }
+        return internalLoggerFactory;
+    }
+
+    public static void setCurrentLoggerType(String type) {
+        loggerType = type;
+    }
+
+    static {
+        try {
+            new Slf4jLoggerFactory();
+        } catch (Throwable e) {
+            //ignore
+        }
+        try {
+            new InnerLoggerFactory();
+        } catch (Throwable e) {
+            //ignore
+        }
+    }
+
+    public InternalLoggerFactory() {
+        doRegister();
+    }
+
+    protected void doRegister() {
+        String loggerType = getLoggerType();
+        if (loggerFactoryCache.get(loggerType) != null) {
+            return;
+        }
+        loggerFactoryCache.put(loggerType, this);
+    }
+
+    protected abstract void shutdown();
+
+    protected abstract InternalLogger getLoggerInstance(String name);
+
+    protected abstract String getLoggerType();
+}
diff --git a/logging/src/main/java/org/apache/rocketmq/logging/Slf4jLoggerFactory.java b/logging/src/main/java/org/apache/rocketmq/logging/Slf4jLoggerFactory.java
new file mode 100644
index 0000000..1a24684
--- /dev/null
+++ b/logging/src/main/java/org/apache/rocketmq/logging/Slf4jLoggerFactory.java
@@ -0,0 +1,153 @@
+/*
+ * 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.rocketmq.logging;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Slf4jLoggerFactory extends InternalLoggerFactory {
+
+    @Override
+    protected String getLoggerType() {
+        return InternalLoggerFactory.LOGGER_SLF4J;
+    }
+
+    @Override
+    protected InternalLogger getLoggerInstance(String name) {
+        return new Slf4jLogger(name);
+    }
+
+    @Override
+    protected void shutdown() {
+
+    }
+
+    public static class Slf4jLogger implements InternalLogger {
+
+        private Logger logger = null;
+
+        public Slf4jLogger(String name) {
+            logger = LoggerFactory.getLogger(name);
+        }
+
+        @Override
+        public String getName() {
+            return logger.getName();
+        }
+
+        @Override
+        public void debug(String s) {
+            logger.debug(s);
+        }
+
+        @Override
+        public void debug(String s, Object o) {
+            logger.debug(s, o);
+        }
+
+        @Override
+        public void debug(String s, Object o, Object o1) {
+            logger.debug(s, o, o1);
+        }
+
+        @Override
+        public void debug(String s, Object... objects) {
+            logger.debug(s, objects);
+        }
+
+        @Override
+        public void debug(String s, Throwable throwable) {
+            logger.debug(s, throwable);
+        }
+
+        @Override
+        public void info(String s) {
+            logger.info(s);
+        }
+
+        @Override
+        public void info(String s, Object o) {
+            logger.info(s, o);
+        }
+
+        @Override
+        public void info(String s, Object o, Object o1) {
+            logger.info(s, o, o1);
+        }
+
+        @Override
+        public void info(String s, Object... objects) {
+            logger.info(s, objects);
+        }
+
+        @Override
+        public void info(String s, Throwable throwable) {
+            logger.info(s, throwable);
+        }
+
+        @Override
+        public void warn(String s) {
+            logger.warn(s);
+        }
+
+        @Override
+        public void warn(String s, Object o) {
+            logger.warn(s, o);
+        }
+
+        @Override
+        public void warn(String s, Object... objects) {
+            logger.warn(s, objects);
+        }
+
+        @Override
+        public void warn(String s, Object o, Object o1) {
+            logger.warn(s, o, o1);
+        }
+
+        @Override
+        public void warn(String s, Throwable throwable) {
+            logger.warn(s, throwable);
+        }
+
+        @Override
+        public void error(String s) {
+            logger.error(s);
+        }
+
+        @Override
+        public void error(String s, Object o) {
+            logger.error(s, o);
+        }
+
+        @Override
+        public void error(String s, Object o, Object o1) {
+            logger.error(s, o, o1);
+        }
+
+        @Override
+        public void error(String s, Object... objects) {
+            logger.error(s, objects);
+        }
+
+        @Override
+        public void error(String s, Throwable throwable) {
+            logger.error(s, throwable);
+        }
+    }
+}
diff --git a/logging/src/main/java/org/apache/rocketmq/logging/inner/Appender.java b/logging/src/main/java/org/apache/rocketmq/logging/inner/Appender.java
new file mode 100755
index 0000000..c061563
--- /dev/null
+++ b/logging/src/main/java/org/apache/rocketmq/logging/inner/Appender.java
@@ -0,0 +1,228 @@
+/*
+ * 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.rocketmq.logging.inner;
+
+
+import java.io.InterruptedIOException;
+import java.util.Enumeration;
+import java.util.Vector;
+
+public abstract class Appender {
+
+    public static final int CODE_WRITE_FAILURE = 1;
+    public static final int CODE_FLUSH_FAILURE = 2;
+    public static final int CODE_CLOSE_FAILURE = 3;
+    public static final int CODE_FILE_OPEN_FAILURE = 4;
+
+    public final static String LINE_SEP = System.getProperty("line.separator");
+
+    boolean firstTime = true;
+
+    protected Layout layout;
+
+    protected String name;
+
+    protected boolean closed = false;
+
+    public void activateOptions() {
+    }
+
+    abstract protected void append(LoggingEvent event);
+
+    public void finalize() {
+        try {
+            super.finalize();
+        } catch (Throwable throwable) {
+            SysLogger.error("Finalizing appender named [" + name + "]. error", throwable);
+        }
+        if (this.closed) {
+            return;
+        }
+
+        SysLogger.debug("Finalizing appender named [" + name + "].");
+        close();
+    }
+
+    public Layout getLayout() {
+        return layout;
+    }
+
+    public final String getName() {
+        return this.name;
+    }
+
+    public synchronized void doAppend(LoggingEvent event) {
+        if (closed) {
+            SysLogger.error("Attempted to append to closed appender named [" + name + "].");
+            return;
+        }
+        this.append(event);
+    }
+
+    public void setLayout(Layout layout) {
+        this.layout = layout;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public abstract void close();
+
+    public void handleError(String message, Exception e, int errorCode) {
+        if (e instanceof InterruptedIOException || e instanceof InterruptedException) {
+            Thread.currentThread().interrupt();
+        }
+        if (firstTime) {
+            SysLogger.error(message + " code:" + errorCode, e);
+            firstTime = false;
+        }
+    }
+
+    public void handleError(String message) {
+        if (firstTime) {
+            SysLogger.error(message);
+            firstTime = false;
+        }
+    }
+
+
+    public interface AppenderPipeline {
+
+        void addAppender(Appender newAppender);
+
+        Enumeration getAllAppenders();
+
+        Appender getAppender(String name);
+
+        boolean isAttached(Appender appender);
+
+        void removeAllAppenders();
+
+        void removeAppender(Appender appender);
+
+        void removeAppender(String name);
+    }
+
+
+    public static class AppenderPipelineImpl implements AppenderPipeline {
+
+
+        protected Vector<Appender> appenderList;
+
+        public void addAppender(Appender newAppender) {
+            if (newAppender == null) {
+                return;
+            }
+
+            if (appenderList == null) {
+                appenderList = new Vector<Appender>(1);
+            }
+            if (!appenderList.contains(newAppender)) {
+                appenderList.addElement(newAppender);
+            }
+        }
+
+        public int appendLoopOnAppenders(LoggingEvent event) {
+            int size = 0;
+            Appender appender;
+
+            if (appenderList != null) {
+                size = appenderList.size();
+                for (int i = 0; i < size; i++) {
+                    appender = appenderList.elementAt(i);
+                    appender.doAppend(event);
+                }
+            }
+            return size;
+        }
+
+        public Enumeration getAllAppenders() {
+            if (appenderList == null) {
+                return null;
+            } else {
+                return appenderList.elements();
+            }
+        }
+
+        public Appender getAppender(String name) {
+            if (appenderList == null || name == null) {
+                return null;
+            }
+
+            int size = appenderList.size();
+            Appender appender;
+            for (int i = 0; i < size; i++) {
+                appender = appenderList.elementAt(i);
+                if (name.equals(appender.getName())) {
+                    return appender;
+                }
+            }
+            return null;
+        }
+
+        public boolean isAttached(Appender appender) {
+            if (appenderList == null || appender == null) {
+                return false;
+            }
+
+            int size = appenderList.size();
+            Appender a;
+            for (int i = 0; i < size; i++) {
+                a = appenderList.elementAt(i);
+                if (a == appender) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public void removeAllAppenders() {
+            if (appenderList != null) {
+                int len = appenderList.size();
+                for (int i = 0; i < len; i++) {
+                    Appender a = appenderList.elementAt(i);
+                    a.close();
+                }
+                appenderList.removeAllElements();
+                appenderList = null;
+            }
+        }
+
+        public void removeAppender(Appender appender) {
+            if (appender == null || appenderList == null) {
+                return;
+            }
+            appenderList.removeElement(appender);
+        }
+
+        public void removeAppender(String name) {
+            if (name == null || appenderList == null) {
+                return;
+            }
+            int size = appenderList.size();
+            for (int i = 0; i < size; i++) {
+                if (name.equals((appenderList.elementAt(i)).getName())) {
+                    appenderList.removeElementAt(i);
+                    break;
+                }
+            }
+        }
+
+    }
+}
diff --git a/logging/src/main/java/org/apache/rocketmq/logging/inner/Layout.java b/logging/src/main/java/org/apache/rocketmq/logging/inner/Layout.java
new file mode 100644
index 0000000..7ea3561
--- /dev/null
+++ b/logging/src/main/java/org/apache/rocketmq/logging/inner/Layout.java
@@ -0,0 +1,39 @@
+/*
+ * 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.rocketmq.logging.inner;
+
+public abstract class Layout {
+
+    public abstract String format(LoggingEvent event);
+
+    public String getContentType() {
+        return "text/plain";
+    }
+
+    public String getHeader() {
+        return null;
+    }
+
+    public String getFooter() {
+        return null;
+    }
+
+
+    abstract public boolean ignoresThrowable();
+
+}
\ No newline at end of file
diff --git a/logging/src/main/java/org/apache/rocketmq/logging/inner/Level.java b/logging/src/main/java/org/apache/rocketmq/logging/inner/Level.java
new file mode 100755
index 0000000..487682c
--- /dev/null
+++ b/logging/src/main/java/org/apache/rocketmq/logging/inner/Level.java
@@ -0,0 +1,156 @@
+/*
+ * 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.rocketmq.logging.inner;
+
+import java.io.Serializable;
+
+public class Level implements Serializable {
+
+    transient int level;
+    transient String levelStr;
+    transient int syslogEquivalent;
+
+    public final static int OFF_INT = Integer.MAX_VALUE;
+    public final static int ERROR_INT = 40000;
+    public final static int WARN_INT = 30000;
+    public final static int INFO_INT = 20000;
+    public final static int DEBUG_INT = 10000;
+    public final static int ALL_INT = Integer.MIN_VALUE;
+
+
+    private static final String ALL_NAME = "ALL";
+
+    private static final String DEBUG_NAME = "DEBUG";
+
+    private static final String INFO_NAME = "INFO";
+
+    private static final String WARN_NAME = "WARN";
+
+    private static final String ERROR_NAME = "ERROR";
+
+    private static final String OFF_NAME = "OFF";
+
+    final static public Level OFF = new Level(OFF_INT, OFF_NAME, 0);
+
+    final static public Level ERROR = new Level(ERROR_INT, ERROR_NAME, 3);
+
+    final static public Level WARN = new Level(WARN_INT, WARN_NAME, 4);
+
+    final static public Level INFO = new Level(INFO_INT, INFO_NAME, 6);
+
+    final static public Level DEBUG = new Level(DEBUG_INT, DEBUG_NAME, 7);
+
+    final static public Level ALL = new Level(ALL_INT, ALL_NAME, 7);
+
+    static final long serialVersionUID = 3491141966387921974L;
+
+    protected Level(int level, String levelStr, int syslogEquivalent) {
+        this.level = level;
+        this.levelStr = levelStr;
+        this.syslogEquivalent = syslogEquivalent;
+    }
+
+    public static Level toLevel(String sArg) {
+        return toLevel(sArg, Level.DEBUG);
+    }
+
+    public static Level toLevel(int val) {
+        return toLevel(val, Level.DEBUG);
+    }
+
+    public static Level toLevel(int val, Level defaultLevel) {
+        switch (val) {
+            case ALL_INT:
+                return ALL;
+            case DEBUG_INT:
+                return Level.DEBUG;
+            case INFO_INT:
+                return Level.INFO;
+            case WARN_INT:
+                return Level.WARN;
+            case ERROR_INT:
+                return Level.ERROR;
+            case OFF_INT:
+                return OFF;
+            default:
+                return defaultLevel;
+        }
+    }
+
+    public static Level toLevel(String sArg, Level defaultLevel) {
+        if (sArg == null) {
+            return defaultLevel;
+        }
+        String s = sArg.toUpperCase();
+
+        if (s.equals(ALL_NAME)) {
+            return Level.ALL;
+        }
+        if (s.equals(DEBUG_NAME)) {
+            return Level.DEBUG;
+        }
+        if (s.equals(INFO_NAME)) {
+            return Level.INFO;
+        }
+        if (s.equals(WARN_NAME)) {
+            return Level.WARN;
+        }
+        if (s.equals(ERROR_NAME)) {
+            return Level.ERROR;
+        }
+        if (s.equals(OFF_NAME)) {
+            return Level.OFF;
+        }
+
+        if (s.equals(INFO_NAME)) {
+            return Level.INFO;
+        }
+        return defaultLevel;
+    }
+
+
+    public boolean equals(Object o) {
+        if (o instanceof Level) {
+            Level r = (Level) o;
+            return this.level == r.level;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        int result = level;
+        result = 31 * result + (levelStr != null ? levelStr.hashCode() : 0);
+        result = 31 * result + syslogEquivalent;
+        return result;
+    }
+
+    public boolean isGreaterOrEqual(Level r) {
+        return level >= r.level;
+    }
+
+    final public String toString() {
+        return levelStr;
+    }
+
+    public final int toInt() {
+        return level;
+    }
+
+}
diff --git a/logging/src/main/java/org/apache/rocketmq/logging/inner/Logger.java b/logging/src/main/java/org/apache/rocketmq/logging/inner/Logger.java
new file mode 100755
index 0000000..470ed41
--- /dev/null
+++ b/logging/src/main/java/org/apache/rocketmq/logging/inner/Logger.java
@@ -0,0 +1,467 @@
+/*
+ * 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.rocketmq.logging.inner;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+
+public class Logger implements Appender.AppenderPipeline {
+
+    private static final String FQCN = Logger.class.getName();
+
+    private static final DefaultLoggerRepository REPOSITORY = new DefaultLoggerRepository(new RootLogger(Level.DEBUG));
+
+    public static LoggerRepository getRepository() {
+        return REPOSITORY;
+    }
+
+    private String name;
+
+    volatile private Level level;
+
+    volatile private Logger parent;
+
+    Appender.AppenderPipelineImpl appenderPipeline;
+
+    private boolean additive = true;
+
+    private Logger(String name) {
+        this.name = name;
+    }
+
+    static public Logger getLogger(String name) {
+        return getRepository().getLogger(name);
+    }
+
+    static public Logger getLogger(Class clazz) {
+        return getRepository().getLogger(clazz.getName());
+    }
+
+    public static Logger getRootLogger() {
+        return getRepository().getRootLogger();
+    }
+
+    synchronized public void addAppender(Appender newAppender) {
+        if (appenderPipeline == null) {
+            appenderPipeline = new Appender.AppenderPipelineImpl();
+        }
+        appenderPipeline.addAppender(newAppender);
+    }
+
+    public void callAppenders(LoggingEvent event) {
+        int writes = 0;
+
+        for (Logger logger = this; logger != null; logger = logger.parent) {
+            synchronized (logger) {
+                if (logger.appenderPipeline != null) {
+                    writes += logger.appenderPipeline.appendLoopOnAppenders(event);
+                }
+                if (!logger.additive) {
+                    break;
+                }
+            }
+        }
+
+        if (writes == 0) {
+            getRepository().emitNoAppenderWarning(this);
+        }
+    }
+
+    synchronized void closeNestedAppenders() {
+        Enumeration enumeration = this.getAllAppenders();
+        if (enumeration != null) {
+            while (enumeration.hasMoreElements()) {
+                Appender a = (Appender) enumeration.nextElement();
+                if (a instanceof Appender.AppenderPipeline) {
+                    a.close();
+                }
+            }
+        }
+    }
+
+    public void debug(Object message) {
+        if (getRepository().isDisabled(Level.DEBUG_INT)) {
+            return;
+        }
+        if (Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel())) {
+            forcedLog(FQCN, Level.DEBUG, message, null);
+        }
+    }
+
+
+    public void debug(Object message, Throwable t) {
+        if (getRepository().isDisabled(Level.DEBUG_INT)) {
+            return;
+        }
+        if (Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel())) {
+            forcedLog(FQCN, Level.DEBUG, message, t);
+        }
+    }
+
+
+    public void error(Object message) {
+        if (getRepository().isDisabled(Level.ERROR_INT)) {
+            return;
+        }
+        if (Level.ERROR.isGreaterOrEqual(this.getEffectiveLevel())) {
+            forcedLog(FQCN, Level.ERROR, message, null);
+        }
+    }
+
+    public void error(Object message, Throwable t) {
+        if (getRepository().isDisabled(Level.ERROR_INT)) {
+            return;
+        }
+        if (Level.ERROR.isGreaterOrEqual(this.getEffectiveLevel())) {
+            forcedLog(FQCN, Level.ERROR, message, t);
+        }
+
+    }
+
+
+    protected void forcedLog(String fqcn, Level level, Object message, Throwable t) {
+        callAppenders(new LoggingEvent(fqcn, this, level, message, t));
+    }
+
+
+    synchronized public Enumeration getAllAppenders() {
+        if (appenderPipeline == null) {
+            return null;
+        } else {
+            return appenderPipeline.getAllAppenders();
+        }
+    }
+
+    synchronized public Appender getAppender(String name) {
+        if (appenderPipeline == null || name == null) {
+            return null;
+        }
+
+        return appenderPipeline.getAppender(name);
+    }
+
+    public Level getEffectiveLevel() {
+        for (Logger c = this; c != null; c = c.parent) {
+            if (c.level != null) {
+                return c.level;
+            }
+        }
+        return null;
+    }
+
+    public final String getName() {
+        return name;
+    }
+
+    final public Level getLevel() {
+        return this.level;
+    }
+
+
+    public void info(Object message) {
+        if (getRepository().isDisabled(Level.INFO_INT)) {
+            return;
+        }
+        if (Level.INFO.isGreaterOrEqual(this.getEffectiveLevel())) {
+            forcedLog(FQCN, Level.INFO, message, null);
+        }
+    }
+
+    public void info(Object message, Throwable t) {
+        if (getRepository().isDisabled(Level.INFO_INT)) {
+            return;
+        }
+        if (Level.INFO.isGreaterOrEqual(this.getEffectiveLevel())) {
+            forcedLog(FQCN, Level.INFO, message, t);
+        }
+    }
+
+    public boolean isAttached(Appender appender) {
+        return appender != null && appenderPipeline != null && appenderPipeline.isAttached(appender);
+    }
+
+    synchronized public void removeAllAppenders() {
+        if (appenderPipeline != null) {
+            appenderPipeline.removeAllAppenders();
+            appenderPipeline = null;
+        }
+    }
+
+    synchronized public void removeAppender(Appender appender) {
+        if (appender == null || appenderPipeline == null) {
+            return;
+        }
+        appenderPipeline.removeAppender(appender);
+    }
+
+    synchronized public void removeAppender(String name) {
+        if (name == null || appenderPipeline == null) {
+            return;
+        }
+        appenderPipeline.removeAppender(name);
+    }
+
+    public void setAdditivity(boolean additive) {
+        this.additive = additive;
+    }
+
+    public void setLevel(Level level) {
+        this.level = level;
+    }
+
+    public void warn(Object message) {
+        if (getRepository().isDisabled(Level.WARN_INT)) {
+            return;
+        }
+
+        if (Level.WARN.isGreaterOrEqual(this.getEffectiveLevel())) {
+            forcedLog(FQCN, Level.WARN, message, null);
+        }
+    }
+
+    public void warn(Object message, Throwable t) {
+        if (getRepository().isDisabled(Level.WARN_INT)) {
+            return;
+        }
+        if (Level.WARN.isGreaterOrEqual(this.getEffectiveLevel())) {
+            forcedLog(FQCN, Level.WARN, message, t);
+        }
+    }
+
+    public interface LoggerRepository {
+
+        boolean isDisabled(int level);
+
+        void setLogLevel(Level level);
+
+        void emitNoAppenderWarning(Logger cat);
+
+        Level getLogLevel();
+
+        Logger getLogger(String name);
+
+        Logger getRootLogger();
+
+        Logger exists(String name);
+
+        void shutdown();
+
+        Enumeration getCurrentLoggers();
+    }
+
+    public static class ProvisionNode extends Vector<Logger> {
+
+        ProvisionNode(Logger logger) {
+            super();
+            addElement(logger);
+        }
+    }
+
+    public static class DefaultLoggerRepository implements LoggerRepository {
+
+        final Hashtable<CategoryKey,Object> ht = new Hashtable<CategoryKey,Object>();
+        Logger root;
+
+        int logLevelInt;
+        Level logLevel;
+
+        boolean emittedNoAppenderWarning = false;
+
+        public DefaultLoggerRepository(Logger root) {
+            this.root = root;
+            setLogLevel(Level.ALL);
+        }
+
+        public void emitNoAppenderWarning(Logger cat) {
+            if (!this.emittedNoAppenderWarning) {
+                SysLogger.warn("No appenders could be found for logger (" + cat.getName() + ").");
+                SysLogger.warn("Please initialize the logger system properly.");
+                this.emittedNoAppenderWarning = true;
+            }
+        }
+
+        public Logger exists(String name) {
+            Object o = ht.get(new CategoryKey(name));
+            if (o instanceof Logger) {
+                return (Logger) o;
+            } else {
+                return null;
+            }
+        }
+
+        public void setLogLevel(Level l) {
+            if (l != null) {
+                logLevelInt = l.level;
+                logLevel = l;
+            }
+        }
+
+        public Level getLogLevel() {
+            return logLevel;
+        }
+
+
+        public Logger getLogger(String name) {
+            CategoryKey key = new CategoryKey(name);
+            Logger logger;
+
+            synchronized (ht) {
+                Object o = ht.get(key);
+                if (o == null) {
+                    logger = makeNewLoggerInstance(name);
+                    ht.put(key, logger);
+                    updateParents(logger);
+                    return logger;
+                } else if (o instanceof Logger) {
+                    return (Logger) o;
+                } else if (o instanceof ProvisionNode) {
+                    logger = makeNewLoggerInstance(name);
+                    ht.put(key, logger);
+                    updateChildren((ProvisionNode) o, logger);
+                    updateParents(logger);
+                    return logger;
+                } else {
+                    return null;
+                }
+            }
+        }
+
+        public Logger makeNewLoggerInstance(String name) {
+            return new Logger(name);
+        }
+
+        public Enumeration getCurrentLoggers() {
+            Vector<Logger> loggers = new Vector<Logger>(ht.size());
+
+            Enumeration elems = ht.elements();
+            while (elems.hasMoreElements()) {
+                Object o = elems.nextElement();
+                if (o instanceof Logger) {
+                    Logger logger = (Logger)o;
+                    loggers.addElement(logger);
+                }
+            }
+            return loggers.elements();
+        }
+
+
+        public Logger getRootLogger() {
+            return root;
+        }
+
+        public boolean isDisabled(int level) {
+            return logLevelInt > level;
+        }
+
+
+        public void shutdown() {
+            Logger root = getRootLogger();
+            root.closeNestedAppenders();
+
+            synchronized (ht) {
+                Enumeration cats = this.getCurrentLoggers();
+                while (cats.hasMoreElements()) {
+                    Logger c = (Logger) cats.nextElement();
+                    c.closeNestedAppenders();
+                }
+                root.removeAllAppenders();
+            }
+        }
+
+
+        private void updateParents(Logger cat) {
+            String name = cat.name;
+            int length = name.length();
+            boolean parentFound = false;
+
+            for (int i = name.lastIndexOf('.', length - 1); i >= 0;
+                 i = name.lastIndexOf('.', i - 1)) {
+                String substr = name.substring(0, i);
+
+                CategoryKey key = new CategoryKey(substr);
+                Object o = ht.get(key);
+                if (o == null) {
+                    ht.put(key, new ProvisionNode(cat));
+                } else if (o instanceof Logger) {
+                    parentFound = true;
+                    cat.parent = (Logger) o;
+                    break;
+                } else if (o instanceof ProvisionNode) {
+                    ((ProvisionNode) o).addElement(cat);
+                } else {
+                    Exception e = new IllegalStateException("unexpected object type " + o.getClass() + " in ht.");
+                    e.printStackTrace();
+                }
+            }
+            if (!parentFound) {
+                cat.parent = root;
+            }
+        }
+
+        private void updateChildren(ProvisionNode pn, Logger logger) {
+            final int last = pn.size();
+
+            for (int i = 0; i < last; i++) {
+                Logger l = pn.elementAt(i);
+                if (!l.parent.name.startsWith(logger.name)) {
+                    logger.parent = l.parent;
+                    l.parent = logger;
+                }
+            }
+        }
+
+        private class CategoryKey {
+
+            String name;
+            int hashCache;
+
+            CategoryKey(String name) {
+                this.name = name;
+                hashCache = name.hashCode();
+            }
+
+            final public int hashCode() {
+                return hashCache;
+            }
+
+            final public boolean equals(Object o) {
+                if (this == o) {
+                    return true;
+                }
+
+                if (o != null && o instanceof CategoryKey) {
+                    CategoryKey cc = (CategoryKey) o;
+                    return name.equals(cc.name);
+                } else {
+                    return false;
+                }
+            }
+        }
+
+    }
+
+    public static class RootLogger extends Logger {
+
+        public RootLogger(Level level) {
+            super("root");
+            setLevel(level);
+        }
+    }
+}
diff --git a/logging/src/main/java/org/apache/rocketmq/logging/inner/LoggingBuilder.java b/logging/src/main/java/org/apache/rocketmq/logging/inner/LoggingBuilder.java
new file mode 100644
index 0000000..469cb52
--- /dev/null
+++ b/logging/src/main/java/org/apache/rocketmq/logging/inner/LoggingBuilder.java
@@ -0,0 +1,1230 @@
+/*
+ * 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.rocketmq.logging.inner;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FilterWriter;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.text.MessageFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+public class LoggingBuilder {
+
+    public static final String SYSTEM_OUT = "System.out";
+    public static final String SYSTEM_ERR = "System.err";
+
+    public static final String LOGGING_ENCODING = "rocketmq.logging.inner.encoding";
+    public static final String ENCODING = System.getProperty(LOGGING_ENCODING, "UTF-8");
+
+    public static AppenderBuilder newAppenderBuilder() {
+        return new AppenderBuilder();
+    }
+
+    public static class AppenderBuilder {
+        private AsyncAppender asyncAppender;
+
+        private Appender appender = null;
+
+        private AppenderBuilder() {
+
+        }
+
+        public AppenderBuilder withLayout(Layout layout) {
+            appender.setLayout(layout);
+            return this;
+        }
+
+        public AppenderBuilder withName(String name) {
+            appender.setName(name);
+            return this;
+        }
+
+        public AppenderBuilder withConsoleAppender(String target) {
+            ConsoleAppender consoleAppender = new ConsoleAppender();
+            consoleAppender.setTarget(target);
+            consoleAppender.activateOptions();
+            this.appender = consoleAppender;
+            return this;
+        }
+
+        public AppenderBuilder withFileAppender(String file) {
+            FileAppender appender = new FileAppender();
+            appender.setFile(file);
+            appender.setAppend(true);
+            appender.setBufferedIO(false);
+            appender.setEncoding(ENCODING);
+            appender.setImmediateFlush(true);
+            appender.activateOptions();
+            this.appender = appender;
+            return this;
+        }
+
+        public AppenderBuilder withRollingFileAppender(String file, String maxFileSize, int maxFileIndex) {
+            RollingFileAppender appender = new RollingFileAppender();
+            appender.setFile(file);
+            appender.setAppend(true);
+            appender.setBufferedIO(false);
+            appender.setEncoding(ENCODING);
+            appender.setImmediateFlush(true);
+            appender.setMaximumFileSize(Integer.parseInt(maxFileSize));
+            appender.setMaxBackupIndex(maxFileIndex);
+            appender.activateOptions();
+            this.appender = appender;
+            return this;
+        }
+
+        public AppenderBuilder withDailyFileRollingAppender(String file, String datePattern) {
+            DailyRollingFileAppender appender = new DailyRollingFileAppender();
+            appender.setFile(file);
+            appender.setAppend(true);
+            appender.setBufferedIO(false);
+            appender.setEncoding(ENCODING);
+            appender.setImmediateFlush(true);
+            appender.setDatePattern(datePattern);
+            appender.activateOptions();
+            this.appender = appender;
+            return this;
+        }
+
+        public AppenderBuilder withAsync(boolean blocking, int buffSize) {
+            AsyncAppender asyncAppender = new AsyncAppender();
+            asyncAppender.setBlocking(blocking);
+            asyncAppender.setBufferSize(buffSize);
+            this.asyncAppender = asyncAppender;
+            return this;
+        }
+
+        public Appender build() {
+            if (appender == null) {
+                throw new RuntimeException("please specify appender first");
+            }
+            if (asyncAppender != null) {
+                asyncAppender.addAppender(appender);
+                return asyncAppender;
+            } else {
+                return appender;
+            }
+        }
+    }
+
+    public static class AsyncAppender extends Appender implements Appender.AppenderPipeline {
+
+        public static final int DEFAULT_BUFFER_SIZE = 128;
+
+        private final List<LoggingEvent> buffer = new ArrayList<LoggingEvent>();
+
+        private final Map<String, DiscardSummary> discardMap = new HashMap<String, DiscardSummary>();
+
+        private int bufferSize = DEFAULT_BUFFER_SIZE;
+
+        private final AppenderPipelineImpl appenderPipeline;
+
+        private final Thread dispatcher;
+
+        private boolean blocking = true;
+
+        public AsyncAppender() {
+            appenderPipeline = new AppenderPipelineImpl();
+
+            dispatcher = new Thread(new Dispatcher(this, buffer, discardMap, appenderPipeline));
+
+            dispatcher.setDaemon(true);
+
+            dispatcher.setName("AsyncAppender-Dispatcher-" + dispatcher.getName());
+            dispatcher.start();
+        }
+
+        public void addAppender(final Appender newAppender) {
+            synchronized (appenderPipeline) {
+                appenderPipeline.addAppender(newAppender);
+            }
+        }
+
+        public void append(final LoggingEvent event) {
+            if ((dispatcher == null) || !dispatcher.isAlive() || (bufferSize <= 0)) {
+                synchronized (appenderPipeline) {
+                    appenderPipeline.appendLoopOnAppenders(event);
+                }
+
+                return;
+            }
+
+            event.getThreadName();
+            event.getRenderedMessage();
+
+            synchronized (buffer) {
+                while (true) {
+                    int previousSize = buffer.size();
+
+                    if (previousSize < bufferSize) {
+                        buffer.add(event);
+
+                        if (previousSize == 0) {
+                            buffer.notifyAll();
+                        }
+
+                        break;
+                    }
+
+                    boolean discard = true;
+                    if (blocking
+                        && !Thread.interrupted()
+                        && Thread.currentThread() != dispatcher) {
+                        try {
+                            buffer.wait();
+                            discard = false;
+                        } catch (InterruptedException e) {
+                            Thread.currentThread().interrupt();
+                        }
+                    }
+                    if (discard) {
+                        String loggerName = event.getLoggerName();
+                        DiscardSummary summary = discardMap.get(loggerName);
+
+                        if (summary == null) {
+                            summary = new DiscardSummary(event);
+                            discardMap.put(loggerName, summary);
+                        } else {
+                            summary.add(event);
+                        }
+
+                        break;
+                    }
+                }
+            }
+        }
+
+        public void close() {
+
+            synchronized (buffer) {
+                closed = true;
+                buffer.notifyAll();
+            }
+
+            try {
+                dispatcher.join();
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                SysLogger.error(
+                    "Got an InterruptedException while waiting for the "
+                        + "dispatcher to finish.", e);
+            }
+
+            synchronized (appenderPipeline) {
+                Enumeration iter = appenderPipeline.getAllAppenders();
+                if (iter != null) {
+                    while (iter.hasMoreElements()) {
+                        Object next = iter.nextElement();
+                        if (next instanceof Appender) {
+                            ((Appender) next).close();
+                        }
+                    }
+                }
+            }
+        }
+
+        public Enumeration getAllAppenders() {
+            synchronized (appenderPipeline) {
+                return appenderPipeline.getAllAppenders();
+            }
+        }
+
+        public Appender getAppender(final String name) {
+            synchronized (appenderPipeline) {
+                return appenderPipeline.getAppender(name);
+            }
+        }
+
+        public boolean isAttached(final Appender appender) {
+            synchronized (appenderPipeline) {
+                return appenderPipeline.isAttached(appender);
+            }
+        }
+
+        public void removeAllAppenders() {
+            synchronized (appenderPipeline) {
+                appenderPipeline.removeAllAppenders();
+            }
+        }
+
+        public void removeAppender(final Appender appender) {
+            synchronized (appenderPipeline) {
+                appenderPipeline.removeAppender(appender);
+            }
+        }
+
+        public void removeAppender(final String name) {
+            synchronized (appenderPipeline) {
+                appenderPipeline.removeAppender(name);
+            }
+        }
+
+        public void setBufferSize(final int size) {
+            if (size < 0) {
+                throw new NegativeArraySizeException("size");
+            }
+
+            synchronized (buffer) {
+                bufferSize = (size < 1) ? 1 : size;
+                buffer.notifyAll();
+            }
+        }
+
+        public int getBufferSize() {
+            return bufferSize;
+        }
+
+        public void setBlocking(final boolean value) {
+            synchronized (buffer) {
+                blocking = value;
+                buffer.notifyAll();
+            }
+        }
+
+        public boolean getBlocking() {
+            return blocking;
+        }
+
+        private final class DiscardSummary {
+
+            private LoggingEvent maxEvent;
+
+            private int count;
+
+            public DiscardSummary(final LoggingEvent event) {
+                maxEvent = event;
+                count = 1;
+            }
+
+            public void add(final LoggingEvent event) {
+                if (event.getLevel().toInt() > maxEvent.getLevel().toInt()) {
+                    maxEvent = event;
+                }
+                count++;
+            }
+
+            public LoggingEvent createEvent() {
+                String msg =
+                    MessageFormat.format(
+                        "Discarded {0} messages due to full event buffer including: {1}",
+                        count, maxEvent.getMessage());
+
+                return new LoggingEvent(
+                    "AsyncAppender.DONT_REPORT_LOCATION",
+                    Logger.getLogger(maxEvent.getLoggerName()),
+                    maxEvent.getLevel(),
+                    msg,
+                    null);
+            }
+        }
+
+        private class Dispatcher implements Runnable {
+
+            private final AsyncAppender parent;
+
+            private final List<LoggingEvent> buffer;
+
+            private final Map<String, DiscardSummary> discardMap;
+
+            private final AppenderPipelineImpl appenderPipeline;
+
+            public Dispatcher(
+                final AsyncAppender parent, final List<LoggingEvent> buffer, final Map<String, DiscardSummary> discardMap,
+                final AppenderPipelineImpl appenderPipeline) {
+
+                this.parent = parent;
+                this.buffer = buffer;
+                this.appenderPipeline = appenderPipeline;
+                this.discardMap = discardMap;
+            }
+
+            public void run() {
+                boolean isActive = true;
+
+                try {
+                    while (isActive) {
+                        LoggingEvent[] events = null;
+
+                        synchronized (buffer) {
+                            int bufferSize = buffer.size();
+                            isActive = !parent.closed;
+
+                            while ((bufferSize == 0) && isActive) {
+                                buffer.wait();
+                                bufferSize = buffer.size();
+                                isActive = !parent.closed;
+                            }
+
+                            if (bufferSize > 0) {
+                                events = new LoggingEvent[bufferSize + discardMap.size()];
+                                buffer.toArray(events);
+
+                                int index = bufferSize;
+                                Collection<DiscardSummary> values = discardMap.values();
+                                for (DiscardSummary value : values) {
+                                    events[index++] = value.createEvent();
+                                }
+
+                                buffer.clear();
+                                discardMap.clear();
+
+                                buffer.notifyAll();
+                            }
+                        }
+                        if (events != null) {
+                            for (LoggingEvent event : events) {
+                                synchronized (appenderPipeline) {
+                                    appenderPipeline.appendLoopOnAppenders(event);
+                                }
+                            }
+                        }
+                    }
+                } catch (InterruptedException ex) {
+                    Thread.currentThread().interrupt();
+                }
+            }
+        }
+    }
+
+    private static class QuietWriter extends FilterWriter {
+
+        protected Appender appender;
+
+        public QuietWriter(Writer writer, Appender appender) {
+            super(writer);
+            this.appender = appender;
+        }
+
+        public void write(String string) {
+            if (string != null) {
+                try {
+                    out.write(string);
+                } catch (Exception e) {
+                    appender.handleError("Failed to write [" + string + "].", e,
+                        Appender.CODE_WRITE_FAILURE);
+                }
+            }
+        }
+
+        public void flush() {
+            try {
+                out.flush();
+            } catch (Exception e) {
+                appender.handleError("Failed to flush writer,", e,
+                    Appender.CODE_FLUSH_FAILURE);
+            }
+        }
+    }
+
+    public static class WriterAppender extends Appender {
+
+
+        protected boolean immediateFlush = true;
+
+        protected String encoding;
+
+
+        protected QuietWriter qw;
+
+        public WriterAppender() {
+
+        }
+
+        public void setImmediateFlush(boolean value) {
+            immediateFlush = value;
+        }
+
+
+        public boolean getImmediateFlush() {
+            return immediateFlush;
+        }
+
+        public void activateOptions() {
+        }
+
+
+        public void append(LoggingEvent event) {
+            if (!checkEntryConditions()) {
+                return;
+            }
+            subAppend(event);
+        }
+
+        protected boolean checkEntryConditions() {
+            if (this.closed) {
+                SysLogger.warn("Not allowed to write to a closed appender.");
+                return false;
+            }
+
+            if (this.qw == null) {
+                handleError("No output stream or file set for the appender named [" +
+                    name + "].");
+                return false;
+            }
+
+            if (this.layout == null) {
+                handleError("No layout set for the appender named [" + name + "].");
+                return false;
+            }
+            return true;
+        }
+
+        public synchronized void close() {
+            if (this.closed) {
+                return;
+            }
+            this.closed = true;
+            writeFooter();
+            reset();
+        }
+
+        protected void closeWriter() {
+            if (qw != null) {
+                try {
+                    qw.close();
+                } catch (IOException e) {
+                    handleError("Could not close " + qw, e, CODE_CLOSE_FAILURE);
+                }
+            }
+        }
+
+        protected OutputStreamWriter createWriter(OutputStream os) {
+            OutputStreamWriter retval = null;
+
+            String enc = getEncoding();
+            if (enc != null) {
+                try {
+                    retval = new OutputStreamWriter(os, enc);
+                } catch (IOException e) {
+                    SysLogger.warn("Error initializing output writer.");
+                    SysLogger.warn("Unsupported encoding?");
+                }
+            }
+            if (retval == null) {
+                retval = new OutputStreamWriter(os);
+            }
+            return retval;
+        }
+
+        public String getEncoding() {
+            return encoding;
+        }
+
+        public void setEncoding(String value) {
+            encoding = value;
+        }
+
+
+        public synchronized void setWriter(Writer writer) {
+            reset();
+            this.qw = new QuietWriter(writer, this);
+            writeHeader();
+        }
+
+        protected void subAppend(LoggingEvent event) {
+            this.qw.write(this.layout.format(event));
+
+            if (layout.ignoresThrowable()) {
+                String[] s = event.getThrowableStr();
+                if (s != null) {
+                    for (String s1 : s) {
+                        this.qw.write(s1);
+                        this.qw.write(LINE_SEP);
+                    }
+                }
+            }
+
+            if (shouldFlush(event)) {
+                this.qw.flush();
+            }
+        }
+
+        protected void reset() {
+            closeWriter();
+            this.qw = null;
+        }
+
+        protected void writeFooter() {
+            if (layout != null) {
+                String f = layout.getFooter();
+                if (f != null && this.qw != null) {
+                    this.qw.write(f);
+                    this.qw.flush();
+                }
+            }
+        }
+
+        protected void writeHeader() {
+            if (layout != null) {
+                String h = layout.getHeader();
+                if (h != null && this.qw != null) {
+                    this.qw.write(h);
+                }
+            }
+        }
+
+        protected boolean shouldFlush(final LoggingEvent event) {
+            return event != null && immediateFlush;
+        }
+    }
+
+
+    public static class FileAppender extends WriterAppender {
+
+        protected boolean fileAppend = true;
+
+        protected String fileName = null;
+
+        protected boolean bufferedIO = false;
+
+        protected int bufferSize = 8 * 1024;
+
+        public FileAppender() {
+        }
+
+        public FileAppender(Layout layout, String filename, boolean append)
+            throws IOException {
+            this.layout = layout;
+            this.setFile(filename, append, false, bufferSize);
+        }
+
+        public void setFile(String file) {
+            fileName = file.trim();
+        }
+
+        public boolean getAppend() {
+            return fileAppend;
+        }
+
+        public String getFile() {
+            return fileName;
+        }
+
+        public void activateOptions() {
+            if (fileName != null) {
+                try {
+                    setFile(fileName, fileAppend, bufferedIO, bufferSize);
+                } catch (IOException e) {
+                    handleError("setFile(" + fileName + "," + fileAppend + ") call failed.",
+                        e, CODE_FILE_OPEN_FAILURE);
+                }
+            } else {
+                SysLogger.warn("File option not set for appender [" + name + "].");
+                SysLogger.warn("Are you using FileAppender instead of ConsoleAppender?");
+            }
+        }
+
+        protected void closeFile() {
+            if (this.qw != null) {
+                try {
+                    this.qw.close();
+                } catch (IOException e) {
+                    if (e instanceof InterruptedIOException) {
+                        Thread.currentThread().interrupt();
+                    }
+                    SysLogger.error("Could not close " + qw, e);
+                }
+            }
+        }
+
+        public boolean getBufferedIO() {
+            return this.bufferedIO;
+        }
+
+        public int getBufferSize() {
+            return this.bufferSize;
+        }
+
+        public void setAppend(boolean flag) {
+            fileAppend = flag;
+        }
+
+        public void setBufferedIO(boolean bufferedIO) {
+            this.bufferedIO = bufferedIO;
+            if (bufferedIO) {
+                immediateFlush = false;
+            }
+        }
+
+        public void setBufferSize(int bufferSize) {
+            this.bufferSize = bufferSize;
+        }
+
+        public synchronized void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize)
+            throws IOException {
+            SysLogger.debug("setFile called: " + fileName + ", " + append);
+
+            if (bufferedIO) {
+                setImmediateFlush(false);
+            }
+
+            reset();
+            FileOutputStream ostream;
+            try {
+                ostream = new FileOutputStream(fileName, append);
+            } catch (FileNotFoundException ex) {
+                String parentName = new File(fileName).getParent();
+                if (parentName != null) {
+                    File parentDir = new File(parentName);
+                    if (!parentDir.exists() && parentDir.mkdirs()) {
+                        ostream = new FileOutputStream(fileName, append);
+                    } else {
+                        throw ex;
+                    }
+                } else {
+                    throw ex;
+                }
+            }
+            Writer fw = createWriter(ostream);
+            if (bufferedIO) {
+                fw = new BufferedWriter(fw, bufferSize);
+            }
+            this.setQWForFiles(fw);
+            this.fileName = fileName;
+            this.fileAppend = append;
+            this.bufferedIO = bufferedIO;
+            this.bufferSize = bufferSize;
+            writeHeader();
+            SysLogger.debug("setFile ended");
+        }
+
+        protected void setQWForFiles(Writer writer) {
+            this.qw = new QuietWriter(writer, this);
+        }
+
+        protected void reset() {
+            closeFile();
+            this.fileName = null;
+            super.reset();
+        }
+    }
+
+
+    public static class RollingFileAppender extends FileAppender {
+
+        protected long maxFileSize = 10 * 1024 * 1024;
+
+        protected int maxBackupIndex = 1;
+
+        private long nextRollover = 0;
+
+        public RollingFileAppender() {
+            super();
+        }
+
+        public int getMaxBackupIndex() {
+            return maxBackupIndex;
+        }
+
+        public long getMaximumFileSize() {
+            return maxFileSize;
+        }
+
+        public void rollOver() {
+            File target;
+            File file;
+
+            if (qw != null) {
+                long size = ((CountingQuietWriter) qw).getCount();
+                SysLogger.debug("rolling over count=" + size);
+                nextRollover = size + maxFileSize;
+            }
+            SysLogger.debug("maxBackupIndex=" + maxBackupIndex);
+
+            boolean renameSucceeded = true;
+            if (maxBackupIndex > 0) {
+                file = new File(fileName + '.' + maxBackupIndex);
+                if (file.exists()) {
+                    renameSucceeded = file.delete();
+                }
+
+                for (int i = maxBackupIndex - 1; i >= 1 && renameSucceeded; i--) {
+                    file = new File(fileName + "." + i);
+                    if (file.exists()) {
+                        target = new File(fileName + '.' + (i + 1));
+                        SysLogger.debug("Renaming file " + file + " to " + target);
+                        renameSucceeded = file.renameTo(target);
+                    }
+                }
+
+                if (renameSucceeded) {
+                    target = new File(fileName + "." + 1);
+
+                    this.closeFile(); // keep windows happy.
+
+                    file = new File(fileName);
+                    SysLogger.debug("Renaming file " + file + " to " + target);
+                    renameSucceeded = file.renameTo(target);
+
+                    if (!renameSucceeded) {
+                        try {
+                            this.setFile(fileName, true, bufferedIO, bufferSize);
+                        } catch (IOException e) {
+                            if (e instanceof InterruptedIOException) {
+                                Thread.currentThread().interrupt();
+                            }
+                            SysLogger.error("setFile(" + fileName + ", true) call failed.", e);
+                        }
+                    }
+                }
+            }
+
+            if (renameSucceeded) {
+                try {
+                    this.setFile(fileName, false, bufferedIO, bufferSize);
+                    nextRollover = 0;
+                } catch (IOException e) {
+                    if (e instanceof InterruptedIOException) {
+                        Thread.currentThread().interrupt();
+                    }
+                    SysLogger.error("setFile(" + fileName + ", false) call failed.", e);
+                }
+            }
+        }
+
+        public synchronized void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize)
+            throws IOException {
+            super.setFile(fileName, append, this.bufferedIO, this.bufferSize);
+            if (append) {
+                File f = new File(fileName);
+                ((CountingQuietWriter) qw).setCount(f.length());
+            }
+        }
+
+        public void setMaxBackupIndex(int maxBackups) {
+            this.maxBackupIndex = maxBackups;
+        }
+
+        public void setMaximumFileSize(long maxFileSize) {
+            this.maxFileSize = maxFileSize;
+        }
+
+        protected void setQWForFiles(Writer writer) {
+            this.qw = new CountingQuietWriter(writer, this);
+        }
+
+        protected void subAppend(LoggingEvent event) {
+            super.subAppend(event);
+            if (fileName != null && qw != null) {
+                long size = ((CountingQuietWriter) qw).getCount();
+                if (size >= maxFileSize && size >= nextRollover) {
+                    rollOver();
+                }
+            }
+        }
+
+        protected class CountingQuietWriter extends QuietWriter {
+
+            protected long count;
+
+            public CountingQuietWriter(Writer writer, Appender appender) {
+                super(writer, appender);
+            }
+
+            public void write(String string) {
+                try {
+                    out.write(string);
+                    count += string.length();
+                } catch (IOException e) {
+                    appender.handleError("Write failure.", e, Appender.CODE_WRITE_FAILURE);
+                }
+            }
+
+            public long getCount() {
+                return count;
+            }
+
+            public void setCount(long count) {
+                this.count = count;
+            }
+
+        }
+    }
+
+
+    public static class DailyRollingFileAppender extends FileAppender {
+
+        static final int TOP_OF_TROUBLE = -1;
+        static final int TOP_OF_MINUTE = 0;
+        static final int TOP_OF_HOUR = 1;
+        static final int HALF_DAY = 2;
+        static final int TOP_OF_DAY = 3;
+        static final int TOP_OF_WEEK = 4;
+        static final int TOP_OF_MONTH = 5;
+
+
+        /**
+         * The date pattern. By default, the pattern is set to
+         * "'.'yyyy-MM-dd" meaning daily rollover.
+         */
+        private String datePattern = "'.'yyyy-MM-dd";
+
+        private String scheduledFilename;
+
+        private long nextCheck = System.currentTimeMillis() - 1;
+
+        Date now = new Date();
+
+        SimpleDateFormat sdf;
+
+        RollingCalendar rc = new RollingCalendar();
+
+        final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT");
+
+
+        public void setDatePattern(String pattern) {
+            datePattern = pattern;
+        }
+
+        public String getDatePattern() {
+            return datePattern;
+        }
+
+        public void activateOptions() {
+            super.activateOptions();
+            if (datePattern != null && fileName != null) {
+                now.setTime(System.currentTimeMillis());
+                sdf = new SimpleDateFormat(datePattern);
+                int type = computeCheckPeriod();
+                printPeriodicity(type);
+                rc.setType(type);
+                File file = new File(fileName);
+                scheduledFilename = fileName + sdf.format(new Date(file.lastModified()));
+
+            } else {
+                SysLogger.error("Either File or DatePattern options are not set for appender [" + name + "].");
+            }
+        }
+
+        void printPeriodicity(int type) {
+            switch (type) {
+                case TOP_OF_MINUTE:
+                    SysLogger.debug("Appender [" + name + "] to be rolled every minute.");
+                    break;
+                case TOP_OF_HOUR:
+                    SysLogger.debug("Appender [" + name + "] to be rolled on top of every hour.");
+                    break;
+                case HALF_DAY:
+                    SysLogger.debug("Appender [" + name + "] to be rolled at midday and midnight.");
+                    break;
+                case TOP_OF_DAY:
+                    SysLogger.debug("Appender [" + name + "] to be rolled at midnight.");
+                    break;
+                case TOP_OF_WEEK:
+                    SysLogger.debug("Appender [" + name + "] to be rolled at start of week.");
+                    break;
+                case TOP_OF_MONTH:
+                    SysLogger.debug("Appender [" + name + "] to be rolled at start of every month.");
+                    break;
+                default:
+                    SysLogger.warn("Unknown periodicity for appender [" + name + "].");
+            }
+        }
+
+        int computeCheckPeriod() {
+            RollingCalendar rollingCalendar = new RollingCalendar(gmtTimeZone, Locale.getDefault());
+            // set sate to 1970-01-01 00:00:00 GMT
+            Date epoch = new Date(0);
+            if (datePattern != null) {
+                for (int i = TOP_OF_MINUTE; i <= TOP_OF_MONTH; i++) {
+                    SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern);
+                    simpleDateFormat.setTimeZone(gmtTimeZone);
+                    String r0 = simpleDateFormat.format(epoch);
+                    rollingCalendar.setType(i);
+                    Date next = new Date(rollingCalendar.getNextCheckMillis(epoch));
+                    String r1 = simpleDateFormat.format(next);
+                    if (r0.equals(r1)) {
+                        return i;
+                    }
+                }
+            }
+            return TOP_OF_TROUBLE;
+        }
+
+        void rollOver() throws IOException {
+
+            if (datePattern == null) {
+                handleError("Missing DatePattern option in rollOver().");
+                return;
+            }
+
+            String datedFilename = fileName + sdf.format(now);
+
+            if (scheduledFilename.equals(datedFilename)) {
+                return;
+            }
+            this.closeFile();
+
+            File target = new File(scheduledFilename);
+            if (target.exists() && !target.delete()) {
+                SysLogger.error("Failed to delete [" + scheduledFilename + "].");
+            }
+
+            File file = new File(fileName);
+            boolean result = file.renameTo(target);
+            if (result) {
+                SysLogger.debug(fileName + " -> " + scheduledFilename);
+            } else {
+                SysLogger.error("Failed to rename [" + fileName + "] to [" + scheduledFilename + "].");
+            }
+
+            try {
+                this.setFile(fileName, true, this.bufferedIO, this.bufferSize);
+            } catch (IOException e) {
+                handleError("setFile(" + fileName + ", true) call failed.");
+            }
+            scheduledFilename = datedFilename;
+        }
+
+        protected void subAppend(LoggingEvent event) {
+            long n = System.currentTimeMillis();
+            if (n >= nextCheck) {
+                now.setTime(n);
+                nextCheck = rc.getNextCheckMillis(now);
+                try {
+                    rollOver();
+                } catch (IOException ioe) {
+                    if (ioe instanceof InterruptedIOException) {
+                        Thread.currentThread().interrupt();
+                    }
+                    SysLogger.error("rollOver() failed.", ioe);
+                }
+            }
+            super.subAppend(event);
+        }
+    }
+
+    private static class RollingCalendar extends GregorianCalendar {
+        private static final long serialVersionUID = -3560331770601814177L;
+
+        int type = DailyRollingFileAppender.TOP_OF_TROUBLE;
+
+        RollingCalendar() {
+            super();
+        }
+
+        RollingCalendar(TimeZone tz, Locale locale) {
+            super(tz, locale);
+        }
+
+        void setType(int type) {
+            this.type = type;
+        }
+
+        public long getNextCheckMillis(Date now) {
+            return getNextCheckDate(now).getTime();
+        }
+
+        public Date getNextCheckDate(Date now) {
+            this.setTime(now);
+
+            switch (type) {
+                case DailyRollingFileAppender.TOP_OF_MINUTE:
+                    this.set(Calendar.SECOND, 0);
+                    this.set(Calendar.MILLISECOND, 0);
+                    this.add(Calendar.MINUTE, 1);
+                    break;
+                case DailyRollingFileAppender.TOP_OF_HOUR:
+                    this.set(Calendar.MINUTE, 0);
+                    this.set(Calendar.SECOND, 0);
+                    this.set(Calendar.MILLISECOND, 0);
+                    this.add(Calendar.HOUR_OF_DAY, 1);
+                    break;
+                case DailyRollingFileAppender.HALF_DAY:
+                    this.set(Calendar.MINUTE, 0);
+                    this.set(Calendar.SECOND, 0);
+                    this.set(Calendar.MILLISECOND, 0);
+                    int hour = get(Calendar.HOUR_OF_DAY);
+                    if (hour < 12) {
+                        this.set(Calendar.HOUR_OF_DAY, 12);
+                    } else {
+                        this.set(Calendar.HOUR_OF_DAY, 0);
+                        this.add(Calendar.DAY_OF_MONTH, 1);
+                    }
+                    break;
+                case DailyRollingFileAppender.TOP_OF_DAY:
+                    this.set(Calendar.HOUR_OF_DAY, 0);
+                    this.set(Calendar.MINUTE, 0);
+                    this.set(Calendar.SECOND, 0);
+                    this.set(Calendar.MILLISECOND, 0);
+                    this.add(Calendar.DATE, 1);
+                    break;
+                case DailyRollingFileAppender.TOP_OF_WEEK:
+                    this.set(Calendar.DAY_OF_WEEK, getFirstDayOfWeek());
+                    this.set(Calendar.HOUR_OF_DAY, 0);
+                    this.set(Calendar.MINUTE, 0);
+                    this.set(Calendar.SECOND, 0);
+                    this.set(Calendar.MILLISECOND, 0);
+                    this.add(Calendar.WEEK_OF_YEAR, 1);
+                    break;
+                case DailyRollingFileAppender.TOP_OF_MONTH:
+                    this.set(Calendar.DATE, 1);
+                    this.set(Calendar.HOUR_OF_DAY, 0);
+                    this.set(Calendar.MINUTE, 0);
+                    this.set(Calendar.SECOND, 0);
+                    this.set(Calendar.MILLISECOND, 0);
+                    this.add(Calendar.MONTH, 1);
+                    break;
+                default:
+                    throw new IllegalStateException("Unknown periodicity type.");
+            }
+            return getTime();
+        }
+    }
+
+    public static class ConsoleAppender extends WriterAppender {
+
+        protected String target = SYSTEM_OUT;
+
+        public ConsoleAppender() {
+        }
+
+        public void setTarget(String value) {
+            String v = value.trim();
+
+            if (SYSTEM_OUT.equalsIgnoreCase(v)) {
+                target = SYSTEM_OUT;
+            } else if (SYSTEM_ERR.equalsIgnoreCase(v)) {
+                target = SYSTEM_ERR;
+            } else {
+                targetWarn(value);
+            }
+        }
+
+        public String getTarget() {
+            return target;
+        }
+
+        void targetWarn(String val) {
+            SysLogger.warn("[" + val + "] should be System.out or System.err.");
+            SysLogger.warn("Using previously set target, System.out by default.");
+        }
+
+        public void activateOptions() {
+            if (target.equals(SYSTEM_ERR)) {
+                setWriter(createWriter(System.err));
+            } else {
+                setWriter(createWriter(System.out));
+            }
+            super.activateOptions();
+        }
+
+        protected final void closeWriter() {
+
+        }
+    }
+
+    public static LayoutBuilder newLayoutBuilder() {
+        return new LayoutBuilder();
+    }
+
+    public static class LayoutBuilder {
+
+        private Layout layout;
+
+        public LayoutBuilder withSimpleLayout() {
+            layout = new SimpleLayout();
+            return this;
+        }
+
+        public LayoutBuilder withDefaultLayout() {
+            layout = new DefaultLayout();
+            return this;
+        }
+
+        public Layout build() {
+            if (layout == null) {
+                layout = new SimpleLayout();
+            }
+            return layout;
+        }
+    }
+
+    public static class SimpleLayout extends Layout {
+
+        @Override
+        public String format(LoggingEvent event) {
+
+            StringBuilder sb = new StringBuilder();
+            sb.append(event.getLevel().toString());
+            sb.append(" - ");
+            sb.append(event.getRenderedMessage());
+            sb.append("\r\n");
+            return sb.toString();
+        }
+
+        @Override
+        public boolean ignoresThrowable() {
+            return false;
+        }
+    }
+
+
+    /**
+     * %d{yyy-MM-dd HH:mm:ss,SSS} %p %c{1}%L - %m%n
+     */
+    public static class DefaultLayout extends Layout {
+        @Override
+        public String format(LoggingEvent event) {
+
+            StringBuilder sb = new StringBuilder();
+            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,sss");
+            String format = simpleDateFormat.format(new Date(event.timeStamp));
+            sb.append(format);
+            sb.append(" ");
+            sb.append(event.getLevel());
+            sb.append(" ");
+            sb.append(event.getLoggerName());
+            sb.append(" - ");
+            sb.append(event.getMessage());
+            String[] throwableStr = event.getThrowableStr();
+            if (throwableStr != null) {
+                sb.append("\r\n");
+                for (String s : throwableStr) {
+                    sb.append(s);
+                    sb.append("\r\n");
+                }
+            }
+            sb.append("\r\n");
+            return sb.toString();
+        }
+
+        @Override
+        public boolean ignoresThrowable() {
+            return false;
+        }
+    }
+}
diff --git a/logging/src/main/java/org/apache/rocketmq/logging/inner/LoggingEvent.java b/logging/src/main/java/org/apache/rocketmq/logging/inner/LoggingEvent.java
new file mode 100644
index 0000000..1b3e955
--- /dev/null
+++ b/logging/src/main/java/org/apache/rocketmq/logging/inner/LoggingEvent.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.rocketmq.logging.inner;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.LineNumberReader;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
+
+public class LoggingEvent implements java.io.Serializable {
+
+    transient public final String fqnOfCategoryClass;
+
+    transient private Object message;
+
+    transient private Level level;
+
+    transient private Logger logger;
+
+    private String renderedMessage;
+
+    private String threadName;
+
+    public final long timeStamp;
+
+    private Throwable throwable;
+
+    public LoggingEvent(String fqnOfCategoryClass, Logger logger,
+                        Level level, Object message, Throwable throwable) {
+        this.fqnOfCategoryClass = fqnOfCategoryClass;
+        this.message = message;
+        this.logger = logger;
+        this.throwable = throwable;
+        this.level = level;
+        timeStamp = System.currentTimeMillis();
+    }
+
+    public Object getMessage() {
+        if (message != null) {
+            return message;
+        } else {
+            return getRenderedMessage();
+        }
+    }
+
+    public String getRenderedMessage() {
+        if (renderedMessage == null && message != null) {
+            if (message instanceof String) {
+                renderedMessage = (String) message;
+            } else {
+                renderedMessage = message.toString();
+            }
+        }
+        return renderedMessage;
+    }
+
+    public String getThreadName() {
+        if (threadName == null) {
+            threadName = (Thread.currentThread()).getName();
+        }
+        return threadName;
+    }
+
+    public Level getLevel() {
+        return level;
+    }
+
+    public String getLoggerName() {
+        return logger.getName();
+    }
+
+    public String[] getThrowableStr() {
+        if (throwable == null) {
+            return null;
+        }
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        try {
+            throwable.printStackTrace(pw);
+        } catch (RuntimeException ex) {
+            SysLogger.warn("InnerLogger print stack trace error", ex);
+        }
+        pw.flush();
+        LineNumberReader reader = new LineNumberReader(
+            new StringReader(sw.toString()));
+        ArrayList<String> lines = new ArrayList<String>();
+        try {
+            String line = reader.readLine();
+            while (line != null) {
+                lines.add(line);
+                line = reader.readLine();
+            }
+        } catch (IOException ex) {
+            if (ex instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            lines.add(ex.toString());
+        }
+        String[] tempRep = new String[lines.size()];
+        lines.toArray(tempRep);
+        return tempRep;
+    }
+}
diff --git a/logging/src/main/java/org/apache/rocketmq/logging/inner/SysLogger.java b/logging/src/main/java/org/apache/rocketmq/logging/inner/SysLogger.java
new file mode 100755
index 0000000..b6d1049
--- /dev/null
+++ b/logging/src/main/java/org/apache/rocketmq/logging/inner/SysLogger.java
@@ -0,0 +1,89 @@
+/*
+ * 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.rocketmq.logging.inner;
+
+public class SysLogger {
+
+    protected static boolean debugEnabled = false;
+
+    private static boolean quietMode = false;
+
+    private static final String PREFIX = "RocketMQLog: ";
+    private static final String ERR_PREFIX = "RocketMQLog:ERROR ";
+    private static final String WARN_PREFIX = "RocketMQLog:WARN ";
+
+    public static void setInternalDebugging(boolean enabled) {
+        debugEnabled = enabled;
+    }
+
+    public static void debug(String msg) {
+        if (debugEnabled && !quietMode) {
+            System.out.printf("%s", PREFIX + msg);
+        }
+    }
+
+    public static void debug(String msg, Throwable t) {
+        if (debugEnabled && !quietMode) {
+            System.out.printf("%s", PREFIX + msg);
+            if (t != null) {
+                t.printStackTrace(System.out);
+            }
+        }
+    }
+
+    public static void error(String msg) {
+        if (quietMode) {
+            return;
+        }
+        System.err.println(ERR_PREFIX + msg);
+    }
+
+    public static void error(String msg, Throwable t) {
+        if (quietMode) {
+            return;
+        }
+
+        System.err.println(ERR_PREFIX + msg);
+        if (t != null) {
+            t.printStackTrace();
+        }
+    }
+
+    public static void setQuietMode(boolean quietMode) {
+        SysLogger.quietMode = quietMode;
+    }
+
+    public static void warn(String msg) {
+        if (quietMode) {
+            return;
+        }
+
+        System.err.println(WARN_PREFIX + msg);
+    }
+
+    public static void warn(String msg, Throwable t) {
+        if (quietMode) {
+            return;
+        }
+
+        System.err.println(WARN_PREFIX + msg);
+        if (t != null) {
+            t.printStackTrace();
+        }
+    }
+}
diff --git a/logging/src/main/java/org/apache/rocketmq/logging/package-info.java b/logging/src/main/java/org/apache/rocketmq/logging/package-info.java
new file mode 100644
index 0000000..7cb0645
--- /dev/null
+++ b/logging/src/main/java/org/apache/rocketmq/logging/package-info.java
@@ -0,0 +1,35 @@
+/*
+ * 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.rocketmq.logging;
+
+/*
+ This package is a minimal logger on the basis of Apache Log4j without
+ file configuration and pattern layout configuration. Main forked files are
+ followed as below:
+ 1. LoggingEvent
+ 2. Logger
+ 3. Layout
+ 4. Level
+ 5. AsyncAppender
+ 6. FileAppender
+ 7. RollingFileAppender
+ 8. DailyRollingFileAppender
+ 9. ConsoleAppender
+
+ For more information about Apache Log4j, please go to https://github.com/apache/log4j.
+ */
\ No newline at end of file
diff --git a/logging/src/test/java/org/apache/rocketmq/logging/BasicloggerTest.java b/logging/src/test/java/org/apache/rocketmq/logging/BasicloggerTest.java
new file mode 100644
index 0000000..28496dd
--- /dev/null
+++ b/logging/src/test/java/org/apache/rocketmq/logging/BasicloggerTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.rocketmq.logging;
+
+import org.apache.rocketmq.logging.inner.Level;
+import org.apache.rocketmq.logging.inner.Logger;
+import org.apache.rocketmq.logging.inner.LoggingEvent;
+import org.junit.After;
+import org.junit.Before;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+public class BasicLoggerTest {
+
+    protected Logger logger = Logger.getLogger("test");
+
+    protected LoggingEvent loggingEvent;
+
+    protected String loggingDir = System.getProperty("user.home") + "/logs/rocketmq-test";
+
+    @Before
+    public void createLoggingEvent() {
+        loggingEvent = new LoggingEvent(Logger.class.getName(), logger, Level.INFO,
+            "junit test error", new RuntimeException("createLogging error"));
+    }
+
+    public String readFile(String file) throws IOException {
+        StringBuilder stringBuilder = new StringBuilder();
+        FileInputStream fileInputStream = new FileInputStream(file);
+        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));
+        String line = bufferedReader.readLine();
+        while (line != null) {
+            stringBuilder.append(line);
+            stringBuilder.append("\r\n");
+            line = bufferedReader.readLine();
+        }
+        bufferedReader.close();
+        return stringBuilder.toString();
+    }
+
+    @After
+    public void clean() {
+        File file = new File(loggingDir);
+        if (file.exists()) {
+            File[] files = file.listFiles();
+            for (File file1 : files) {
+                file1.delete();
+            }
+        }
+    }
+}
diff --git a/logging/src/test/java/org/apache/rocketmq/logging/InnerLoggerFactoryTest.java b/logging/src/test/java/org/apache/rocketmq/logging/InnerLoggerFactoryTest.java
new file mode 100644
index 0000000..c47dba6
--- /dev/null
+++ b/logging/src/test/java/org/apache/rocketmq/logging/InnerLoggerFactoryTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.rocketmq.logging;
+
+import org.apache.rocketmq.logging.inner.Appender;
+import org.apache.rocketmq.logging.inner.Level;
+import org.apache.rocketmq.logging.inner.Logger;
+import org.apache.rocketmq.logging.inner.LoggingBuilder;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+public class InnerLoggerFactoryTest extends BasicLoggerTest {
+
+    private ByteArrayOutputStream byteArrayOutputStream;
+
+    public static final String LOGGER = "ConsoleLogger";
+
+    private PrintStream out;
+
+    @Before
+    public void initLogger() {
+        out = System.out;
+        byteArrayOutputStream = new ByteArrayOutputStream();
+        System.setOut(new PrintStream(byteArrayOutputStream));
+
+        Appender consoleAppender = LoggingBuilder.newAppenderBuilder()
+            .withConsoleAppender(LoggingBuilder.SYSTEM_OUT)
+            .withLayout(LoggingBuilder.newLayoutBuilder().withDefaultLayout().build()).build();
+
+        Logger consoleLogger = Logger.getLogger("ConsoleLogger");
+        consoleLogger.setAdditivity(false);
+        consoleLogger.addAppender(consoleAppender);
+        consoleLogger.setLevel(Level.INFO);
+    }
+
+    @After
+    public void fixConsole() {
+        System.setOut(out);
+    }
+
+    @Test
+    public void testInnerLoggerFactory() {
+        InternalLoggerFactory.setCurrentLoggerType(InternalLoggerFactory.LOGGER_INNER);
+
+        InternalLogger logger1 = InnerLoggerFactory.getLogger(LOGGER);
+        InternalLogger logger = InternalLoggerFactory.getLogger(LOGGER);
+
+        Assert.assertTrue(logger.getName().equals(logger1.getName()));
+
+        InternalLogger logger2 = InnerLoggerFactory.getLogger(InnerLoggerFactoryTest.class);
+        InnerLoggerFactory.InnerLogger logger3 = (InnerLoggerFactory.InnerLogger) logger2;
+
+        logger.info("innerLogger inner info Message");
+        logger.error("innerLogger inner error Message", new RuntimeException());
+        logger.debug("innerLogger inner debug message");
+        logger3.info("innerLogger info message");
+        logger3.error("logback error message");
+        logger3.info("info {}", "hahahah");
+        logger3.warn("warn {}", "hahahah");
+        logger3.warn("logger3 warn");
+        logger3.error("error {}", "hahahah");
+        logger3.debug("debug {}", "hahahah");
+
+        String content = new String(byteArrayOutputStream.toByteArray());
+        System.out.println(content);
+
+        Assert.assertTrue(content.contains("InnerLoggerFactoryTest"));
+        Assert.assertTrue(content.contains("info"));
+        Assert.assertTrue(content.contains("RuntimeException"));
+        Assert.assertTrue(!content.contains("debug"));
+    }
+}
diff --git a/logging/src/test/java/org/apache/rocketmq/logging/InternalLoggerTest.java b/logging/src/test/java/org/apache/rocketmq/logging/InternalLoggerTest.java
new file mode 100644
index 0000000..04b9f06
--- /dev/null
+++ b/logging/src/test/java/org/apache/rocketmq/logging/InternalLoggerTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.rocketmq.logging;
+
+import org.apache.rocketmq.logging.inner.Appender;
+import org.apache.rocketmq.logging.inner.Level;
+import org.apache.rocketmq.logging.inner.Logger;
+import org.apache.rocketmq.logging.inner.LoggingBuilder;
+import org.apache.rocketmq.logging.inner.SysLogger;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+
+public class InternalLoggerTest {
+
+
+    @Test
+    public void testInternalLogger() {
+        SysLogger.setQuietMode(false);
+        SysLogger.setInternalDebugging(true);
+        PrintStream out = System.out;
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        System.setOut(new PrintStream(byteArrayOutputStream));
+
+        Appender consoleAppender = LoggingBuilder.newAppenderBuilder()
+            .withConsoleAppender(LoggingBuilder.SYSTEM_OUT)
+            .withLayout(LoggingBuilder.newLayoutBuilder().withDefaultLayout().build()).build();
+
+
+        Logger consoleLogger = Logger.getLogger("ConsoleLogger");
+        consoleLogger.setAdditivity(false);
+        consoleLogger.addAppender(consoleAppender);
+        consoleLogger.setLevel(Level.INFO);
+
+        Logger.getRootLogger().addAppender(consoleAppender);
+
+        InternalLoggerFactory.setCurrentLoggerType(InternalLoggerFactory.LOGGER_INNER);
+        InternalLogger logger = InternalLoggerFactory.getLogger(InternalLoggerTest.class);
+        InternalLogger consoleLogger1 = InternalLoggerFactory.getLogger("ConsoleLogger");
+
+        consoleLogger1.warn("simple warn {}", 14555);
+
+        logger.info("testInternalLogger");
+        consoleLogger1.info("consoleLogger1");
+
+        System.setOut(out);
+        consoleAppender.close();
+
+        String result = new String(byteArrayOutputStream.toByteArray());
+        Assert.assertTrue(result.contains("consoleLogger1"));
+        Assert.assertTrue(result.contains("testInternalLogger"));
+    }
+
+}
diff --git a/logging/src/test/java/org/apache/rocketmq/logging/Slf4jLoggerFactoryTest.java b/logging/src/test/java/org/apache/rocketmq/logging/Slf4jLoggerFactoryTest.java
new file mode 100644
index 0000000..ba6ec3b
--- /dev/null
+++ b/logging/src/test/java/org/apache/rocketmq/logging/Slf4jLoggerFactoryTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.rocketmq.logging;
+
+import ch.qos.logback.classic.joran.JoranConfigurator;
+import ch.qos.logback.core.Context;
+import ch.qos.logback.core.joran.spi.JoranException;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.ILoggerFactory;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.URL;
+
+public class Slf4jLoggerFactoryTest extends BasicLoggerTest {
+
+    public static final String LOGGER = "Slf4jTestLogger";
+
+    @Before
+    public void initLogback() throws JoranException {
+        InternalLoggerFactory.setCurrentLoggerType(InternalLoggerFactory.LOGGER_SLF4J);
+        System.setProperty("loggingDir", loggingDir);
+        ILoggerFactory iLoggerFactory = LoggerFactory.getILoggerFactory();
+        JoranConfigurator joranConfigurator = new JoranConfigurator();
+        joranConfigurator.setContext((Context) iLoggerFactory);
+        URL logbackConfigFile = Slf4jLoggerFactoryTest.class.getClassLoader().getResource("logback_test.xml");
+        if (logbackConfigFile == null) {
+            throw new RuntimeException("can't find logback_test.xml");
+        } else {
+            joranConfigurator.doConfigure(logbackConfigFile);
+        }
+    }
+
+    @Test
+    public void testSlf4j() throws IOException {
+        InternalLogger logger1 = Slf4jLoggerFactory.getLogger(LOGGER);
+        InternalLogger logger = InternalLoggerFactory.getLogger(LOGGER);
+        Assert.assertTrue(logger.getName().equals(logger1.getName()));
+        InternalLogger logger2 = Slf4jLoggerFactory.getLogger(Slf4jLoggerFactoryTest.class);
+        Slf4jLoggerFactory.Slf4jLogger logger3 = (Slf4jLoggerFactory.Slf4jLogger) logger2;
+
+        String file = loggingDir + "/logback_test.log";
+
+        logger.info("logback slf4j info Message");
+        logger.error("logback slf4j error Message", new RuntimeException());
+        logger.debug("logback slf4j debug message");
+        logger3.info("logback info message");
+        logger3.error("logback error message");
+        logger3.info("info {}", "hahahah");
+        logger3.warn("warn {}", "hahahah");
+        logger3.warn("logger3 warn");
+        logger3.error("error {}", "hahahah");
+        logger3.debug("debug {}", "hahahah");
+        String content = readFile(file);
+        System.out.println(content);
+
+        Assert.assertTrue(content.contains("Slf4jLoggerFactoryTest"));
+        Assert.assertTrue(content.contains("info"));
+        Assert.assertTrue(content.contains("RuntimeException"));
+        Assert.assertTrue(!content.contains("debug"));
+    }
+
+}
diff --git a/logging/src/test/java/org/apache/rocketmq/logging/inner/AppenderTest.java b/logging/src/test/java/org/apache/rocketmq/logging/inner/AppenderTest.java
new file mode 100644
index 0000000..37ff8bd
--- /dev/null
+++ b/logging/src/test/java/org/apache/rocketmq/logging/inner/AppenderTest.java
@@ -0,0 +1,160 @@
+/*
+ * 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.rocketmq.logging.inner;
+
+import org.apache.rocketmq.logging.BasicLoggerTest;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+
+public class AppenderTest extends BasicLoggerTest {
+
+    @Test
+    public void testConsole() {
+        SysLogger.setQuietMode(false);
+        SysLogger.setInternalDebugging(true);
+        PrintStream out = System.out;
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        System.setOut(new PrintStream(byteArrayOutputStream));
+
+        Appender consoleAppender = LoggingBuilder.newAppenderBuilder()
+            .withConsoleAppender(LoggingBuilder.SYSTEM_OUT)
+            .withLayout(LoggingBuilder.newLayoutBuilder().withDefaultLayout().build()).build();
+
+        LoggingBuilder.ConsoleAppender consoleAppender1 = (LoggingBuilder.ConsoleAppender) consoleAppender;
+        String target = consoleAppender1.getTarget();
+        Assert.assertTrue(target.equals(LoggingBuilder.SYSTEM_OUT));
+
+        Layout layout = consoleAppender.getLayout();
+        Assert.assertTrue(layout instanceof LoggingBuilder.DefaultLayout);
+
+        Logger consoleLogger = Logger.getLogger("ConsoleLogger");
+        consoleLogger.setAdditivity(false);
+        consoleLogger.addAppender(consoleAppender);
+        consoleLogger.setLevel(Level.INFO);
+
+        Logger.getRootLogger().addAppender(consoleAppender);
+        Logger.getLogger(AppenderTest.class).info("this is a AppenderTest log");
+
+        Logger.getLogger("ConsoleLogger").info("console info Message");
+        Logger.getLogger("ConsoleLogger").error("console error Message", new RuntimeException());
+        Logger.getLogger("ConsoleLogger").debug("console debug message");
+        System.setOut(out);
+        consoleAppender.close();
+
+        String result = new String(byteArrayOutputStream.toByteArray());
+
+        Assert.assertTrue(result.contains("info"));
+        Assert.assertTrue(result.contains("RuntimeException"));
+        Assert.assertTrue(!result.contains("debug"));
+        Assert.assertTrue(result.contains("AppenderTest"));
+    }
+
+    @Test
+    public void testInnerFile() throws IOException {
+        String file = loggingDir + "/logger.log";
+
+        Logger fileLogger = Logger.getLogger("fileLogger");
+
+        Appender myappender = LoggingBuilder.newAppenderBuilder()
+            .withDailyFileRollingAppender(file, "'.'yyyy-MM-dd")
+            .withName("myappender")
+            .withLayout(LoggingBuilder.newLayoutBuilder().withDefaultLayout().build()).build();
+
+        fileLogger.addAppender(myappender);
+
+        Logger.getLogger("fileLogger").setLevel(Level.INFO);
+
+        Logger.getLogger("fileLogger").info("fileLogger info Message");
+        Logger.getLogger("fileLogger").error("fileLogger error Message", new RuntimeException());
+        Logger.getLogger("fileLogger").debug("fileLogger debug message");
+
+        myappender.close();
+
+        String content = readFile(file);
+
+        System.out.println(content);
+
+        Assert.assertTrue(content.contains("info"));
+        Assert.assertTrue(content.contains("RuntimeException"));
+        Assert.assertTrue(!content.contains("debug"));
+    }
+
+
+
+    @Test
+    public void asyncAppenderTest() {
+        Appender appender = LoggingBuilder.newAppenderBuilder().withAsync(false, 1024)
+            .withConsoleAppender(LoggingBuilder.SYSTEM_OUT)
+            .withLayout(LoggingBuilder.newLayoutBuilder().withDefaultLayout().build()).build();
+        Assert.assertTrue(appender instanceof LoggingBuilder.AsyncAppender);
+        LoggingBuilder.AsyncAppender asyncAppender = (LoggingBuilder.AsyncAppender) appender;
+        Assert.assertTrue(!asyncAppender.getBlocking());
+        Assert.assertTrue(asyncAppender.getBufferSize() > 0);
+    }
+
+    @Test
+    public void testWriteAppender() {
+        LoggingBuilder.WriterAppender writerAppender = new LoggingBuilder.WriterAppender();
+        writerAppender.setImmediateFlush(true);
+        Assert.assertTrue(writerAppender.getImmediateFlush());
+    }
+
+    @Test
+    public void testFileAppender() throws IOException {
+        LoggingBuilder.FileAppender fileAppender = new LoggingBuilder.FileAppender(
+            new LoggingBuilder.SimpleLayout(), loggingDir + "/simple.log", true);
+        fileAppender.setBufferSize(1024);
+        int bufferSize = fileAppender.getBufferSize();
+        boolean bufferedIO = fileAppender.getBufferedIO();
+        Assert.assertTrue(!bufferedIO);
+        Assert.assertTrue(bufferSize > 0);
+        Assert.assertTrue(fileAppender.getAppend());
+
+        LoggingBuilder.RollingFileAppender rollingFileAppender = new LoggingBuilder.RollingFileAppender();
+        rollingFileAppender.setImmediateFlush(true);
+        rollingFileAppender.setMaximumFileSize(1024 * 1024);
+        rollingFileAppender.setMaxBackupIndex(10);
+        rollingFileAppender.setAppend(true);
+        rollingFileAppender.setFile(loggingDir + "/rolling_file.log");
+        rollingFileAppender.setName("myRollingFileAppender");
+
+        rollingFileAppender.activateOptions();
+
+        Assert.assertTrue(rollingFileAppender.getMaximumFileSize() > 0);
+        Assert.assertTrue(rollingFileAppender.getMaxBackupIndex() == 10);
+    }
+
+    @Test
+    public void testDailyRollingAppender() {
+        LoggingBuilder.DailyRollingFileAppender dailyRollingFileAppender = new LoggingBuilder.DailyRollingFileAppender();
+        dailyRollingFileAppender.setFile(loggingDir + "/daily.log");
+        dailyRollingFileAppender.setName("dailyAppender");
+        dailyRollingFileAppender.setAppend(true);
+        dailyRollingFileAppender.setDatePattern("'.'yyyy-mm-dd");
+        String datePattern = dailyRollingFileAppender.getDatePattern();
+        Assert.assertTrue(datePattern != null);
+        dailyRollingFileAppender.activateOptions();
+    }
+
+}
+
+
diff --git a/logging/src/test/java/org/apache/rocketmq/logging/inner/LayoutTest.java b/logging/src/test/java/org/apache/rocketmq/logging/inner/LayoutTest.java
new file mode 100644
index 0000000..66ef18e
--- /dev/null
+++ b/logging/src/test/java/org/apache/rocketmq/logging/inner/LayoutTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.rocketmq.logging.inner;
+
+import org.apache.rocketmq.logging.BasicLoggerTest;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class LayoutTest extends BasicLoggerTest {
+
+    @Test
+    public void testSimpleLayout() {
+        Layout layout = LoggingBuilder.newLayoutBuilder().withSimpleLayout().build();
+        String format = layout.format(loggingEvent);
+        Assert.assertTrue(format.contains("junit"));
+    }
+
+    @Test
+    public void testDefaultLayout() {
+        Layout layout = LoggingBuilder.newLayoutBuilder().withDefaultLayout().build();
+        String format = layout.format(loggingEvent);
+        String contentType = layout.getContentType();
+        Assert.assertTrue(contentType.contains("text"));
+        Assert.assertTrue(format.contains("createLoggingEvent"));
+        Assert.assertTrue(format.contains("createLogging error"));
+        Assert.assertTrue(format.contains(Thread.currentThread().getName()));
+    }
+
+    @Test
+    public void testLogFormat() {
+        Layout innerLayout = LoggingBuilder.newLayoutBuilder().withDefaultLayout().build();
+
+        LoggingEvent loggingEvent = new LoggingEvent(Logger.class.getName(), logger, org.apache.rocketmq.logging.inner.Level.INFO,
+            "junit test error", null);
+        String format = innerLayout.format(loggingEvent);
+
+        System.out.println(format);
+    }
+}
diff --git a/logging/src/test/java/org/apache/rocketmq/logging/inner/LevelTest.java b/logging/src/test/java/org/apache/rocketmq/logging/inner/LevelTest.java
new file mode 100644
index 0000000..21667e1
--- /dev/null
+++ b/logging/src/test/java/org/apache/rocketmq/logging/inner/LevelTest.java
@@ -0,0 +1,37 @@
+/*
+ * 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.rocketmq.logging.inner;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class LevelTest {
+
+    @Test
+    public void levelTest() {
+        Level info = Level.toLevel("info");
+        Level error = Level.toLevel(3);
+        Assert.assertTrue(error != null && info != null);
+    }
+
+    @Test
+    public void loggerLevel(){
+        Level level = Logger.getRootLogger().getLevel();
+        Assert.assertTrue(level!=null);
+    }
+}
diff --git a/logging/src/test/java/org/apache/rocketmq/logging/inner/LoggerRepositoryTest.java b/logging/src/test/java/org/apache/rocketmq/logging/inner/LoggerRepositoryTest.java
new file mode 100644
index 0000000..6a56c20
--- /dev/null
+++ b/logging/src/test/java/org/apache/rocketmq/logging/inner/LoggerRepositoryTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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.rocketmq.logging.inner;
+
+import org.apache.rocketmq.logging.BasicLoggerTest;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Enumeration;
+
+public class LoggerRepositoryTest extends BasicLoggerTest {
+
+    @Test
+    public void testLoggerRepository() {
+        Logger.getRepository().setLogLevel(Level.INFO);
+
+        String file = loggingDir + "/repo.log";
+        Logger fileLogger = Logger.getLogger("repoLogger");
+
+        Appender myappender = LoggingBuilder.newAppenderBuilder()
+            .withDailyFileRollingAppender(file, "'.'yyyy-MM-dd")
+            .withName("repoAppender")
+            .withLayout(LoggingBuilder.newLayoutBuilder().withDefaultLayout().build()).build();
+
+        fileLogger.addAppender(myappender);
+        Logger.getLogger("repoLogger").setLevel(Level.INFO);
+        Logger repoLogger = Logger.getRepository().exists("repoLogger");
+        Assert.assertTrue(repoLogger != null);
+        Enumeration currentLoggers = Logger.getRepository().getCurrentLoggers();
+        Level logLevel = Logger.getRepository().getLogLevel();
+        Assert.assertTrue(logLevel.equals(Level.INFO));
+//        Logger.getRepository().shutdown();
+    }
+}
diff --git a/logging/src/test/java/org/apache/rocketmq/logging/inner/LoggerTest.java b/logging/src/test/java/org/apache/rocketmq/logging/inner/LoggerTest.java
new file mode 100644
index 0000000..4e738e2
--- /dev/null
+++ b/logging/src/test/java/org/apache/rocketmq/logging/inner/LoggerTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.rocketmq.logging.inner;
+
+import org.apache.rocketmq.logging.BasicLoggerTest;
+import org.apache.rocketmq.logging.InnerLoggerFactory;
+import org.apache.rocketmq.logging.InternalLogger;
+import org.apache.rocketmq.logging.InternalLoggerFactory;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+
+public class LoggerTest extends BasicLoggerTest {
+
+
+    @Before
+    public void init() {
+        InternalLoggerFactory.setCurrentLoggerType(InnerLoggerFactory.LOGGER_INNER);
+    }
+
+    @Test
+    public void testInnerConsoleLogger() throws IOException {
+        PrintStream out = System.out;
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        System.setOut(new PrintStream(byteArrayOutputStream));
+
+        Appender consoleAppender = LoggingBuilder.newAppenderBuilder()
+            .withConsoleAppender(LoggingBuilder.SYSTEM_OUT)
+            .withLayout(LoggingBuilder.newLayoutBuilder().withDefaultLayout().build()).build();
+
+        Logger.getLogger("ConsoleLogger").addAppender(consoleAppender);
+        Logger.getLogger("ConsoleLogger").setLevel(Level.INFO);
+
+        InternalLogger consoleLogger1 = InternalLoggerFactory.getLogger("ConsoleLogger");
+        consoleLogger1.info("console info Message");
+        consoleLogger1.error("console error Message", new RuntimeException());
+        consoleLogger1.debug("console debug message");
+
+        consoleLogger1.info("console {} test", "simple");
+        consoleLogger1.info("[WATERMARK] Send Queue Size: {} SlowTimeMills: {}", 1, 300);
+        consoleLogger1.info("new consumer connected, group: {} {} {} channel: {}", "mygroup", "orderly",
+            "broudcast", new RuntimeException("simple object"));
+
+        System.setOut(out);
+        consoleAppender.close();
+
+        String result = new String(byteArrayOutputStream.toByteArray());
+
+        System.out.println(result);
+
+        Assert.assertTrue(result.contains("info"));
+        Assert.assertTrue(result.contains("RuntimeException"));
+        Assert.assertTrue(result.contains("WATERMARK"));
+        Assert.assertTrue(result.contains("consumer"));
+        Assert.assertTrue(result.contains("broudcast"));
+        Assert.assertTrue(result.contains("simple test"));
+        Assert.assertTrue(!result.contains("debug"));
+    }
+
+    @Test
+    public void testInnerFileLogger() throws IOException {
+        String file = loggingDir + "/inner.log";
+
+        Logger fileLogger = Logger.getLogger("innerLogger");
+
+        Appender myappender = LoggingBuilder.newAppenderBuilder()
+            .withDailyFileRollingAppender(file, "'.'yyyy-MM-dd")
+            .withName("innerAppender")
+            .withLayout(LoggingBuilder.newLayoutBuilder().withDefaultLayout().build()).build();
+
+        fileLogger.addAppender(myappender);
+        fileLogger.setLevel(Level.INFO);
+
+        InternalLogger innerLogger = InternalLoggerFactory.getLogger("innerLogger");
+
+        innerLogger.info("fileLogger info Message");
+        innerLogger.error("fileLogger error Message", new RuntimeException());
+        innerLogger.debug("fileLogger debug message");
+
+        myappender.close();
+
+        String content = readFile(file);
+
+        System.out.println(content);
+
+        Assert.assertTrue(content.contains("info"));
+        Assert.assertTrue(content.contains("RuntimeException"));
+        Assert.assertTrue(!content.contains("debug"));
+    }
+
+    @After
+    public void close() {
+        InternalLoggerFactory.setCurrentLoggerType(null);
+    }
+}
diff --git a/logging/src/test/java/org/apache/rocketmq/logging/inner/LoggingBuilderTest.java b/logging/src/test/java/org/apache/rocketmq/logging/inner/LoggingBuilderTest.java
new file mode 100644
index 0000000..977e553
--- /dev/null
+++ b/logging/src/test/java/org/apache/rocketmq/logging/inner/LoggingBuilderTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.rocketmq.logging.inner;
+
+import org.apache.rocketmq.logging.BasicLoggerTest;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.PrintStream;
+
+public class LoggingBuilderTest extends BasicLoggerTest {
+
+    @Test
+    public void testConsole() {
+        PrintStream out = System.out;
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        System.setOut(new PrintStream(byteArrayOutputStream));
+
+        Appender consoleAppender = LoggingBuilder.newAppenderBuilder()
+            .withConsoleAppender(LoggingBuilder.SYSTEM_OUT)
+            .withLayout(LoggingBuilder.newLayoutBuilder().withDefaultLayout().build()).build();
+        consoleAppender.doAppend(loggingEvent);
+        String result = new String(byteArrayOutputStream.toByteArray());
+        System.setOut(out);
+
+        Assert.assertTrue(result.contains(loggingEvent.getMessage().toString()));
+
+    }
+
+    @Test
+    public void testFileAppender() throws InterruptedException {
+        String logFile = loggingDir + "/file.log";
+        Appender rollingFileAppender = LoggingBuilder.newAppenderBuilder().withAsync(false, 102400)
+            .withFileAppender(logFile).withLayout(LoggingBuilder.newLayoutBuilder().withDefaultLayout().build()).build();
+
+        for (int i = 0; i < 10; i++) {
+            rollingFileAppender.doAppend(loggingEvent);
+        }
+        rollingFileAppender.close();
+
+        File file = new File(logFile);
+        Assert.assertTrue(file.length() > 0);
+    }
+
+    @Test
+    public void testRollingFileAppender() throws InterruptedException {
+
+        String rollingFile = loggingDir + "/rolling.log";
+        Appender rollingFileAppender = LoggingBuilder.newAppenderBuilder().withAsync(false, 1024)
+            .withRollingFileAppender(rollingFile, "1024", 5)
+            .withLayout(LoggingBuilder.newLayoutBuilder().withDefaultLayout().build()).build();
+
+        for (int i = 0; i < 100; i++) {
+            rollingFileAppender.doAppend(loggingEvent);
+        }
+        rollingFileAppender.close();
+
+        int cc = 0;
+        for (int i = 0; i < 5; i++) {
+            File file;
+            if (i == 0) {
+                file = new File(rollingFile);
+            } else {
+                file = new File(rollingFile + "." + i);
+            }
+            if (file.exists() && file.length() > 0) {
+                cc += 1;
+            }
+        }
+        Assert.assertTrue(cc >= 2);
+    }
+
+    @Test
+    public void testDailyRollingFileAppender() throws InterruptedException {
+        String rollingFile = loggingDir + "/daily-rolling--222.log";
+        Appender rollingFileAppender = LoggingBuilder.newAppenderBuilder().withAsync(false, 1024)
+            .withDailyFileRollingAppender(rollingFile, "'.'yyyy-MM-dd_HH-mm-ss-SSS")
+            .withLayout(LoggingBuilder.newLayoutBuilder().withDefaultLayout().build()).build();
+
+        for (int i = 0; i < 100; i++) {
+            rollingFileAppender.doAppend(loggingEvent);
+        }
+
+        rollingFileAppender.close();
+
+        File file = new File(loggingDir);
+        String[] list = file.list(new FilenameFilter() {
+            @Override
+            public boolean accept(File dir, String name) {
+                return name.startsWith("daily-rolling--222.log");
+            }
+        });
+        Assert.assertTrue(list.length > 0);
+    }
+}
diff --git a/logging/src/test/java/org/apache/rocketmq/logging/inner/MessageFormatterTest.java b/logging/src/test/java/org/apache/rocketmq/logging/inner/MessageFormatterTest.java
new file mode 100644
index 0000000..5fa80ad
--- /dev/null
+++ b/logging/src/test/java/org/apache/rocketmq/logging/inner/MessageFormatterTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.rocketmq.logging.inner;
+
+
+import org.apache.rocketmq.logging.InnerLoggerFactory;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class MessageFormatterTest {
+
+    @Test
+    public void formatTest(){
+        InnerLoggerFactory.FormattingTuple logging = InnerLoggerFactory.MessageFormatter.format("this is {},and {}", "logging", 6546);
+        String message = logging.getMessage();
+        Assert.assertTrue(message.contains("logging"));
+
+        InnerLoggerFactory.FormattingTuple format = InnerLoggerFactory.MessageFormatter.format("cause exception {}", 143545, new RuntimeException());
+        String message1 = format.getMessage();
+        Throwable throwable = format.getThrowable();
+        System.out.println(message1);
+        Assert.assertTrue(throwable != null);
+    }
+
+}
diff --git a/logging/src/test/resources/logback_test.xml b/logging/src/test/resources/logback_test.xml
new file mode 100644
index 0000000..c1ab200
--- /dev/null
+++ b/logging/src/test/resources/logback_test.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+  -->
+
+<configuration>
+    <appender name="RocketmqClientAppender"
+              class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${loggingDir}/logback_test.log</file>
+        <append>true</append>
+        <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+            <fileNamePattern>${loggingDir}/logback_test.%i.log
+            </fileNamePattern>
+            <minIndex>1</minIndex>
+            <maxIndex>5</maxIndex>
+        </rollingPolicy>
+        <triggeringPolicy
+            class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+            <maxFileSize>10MB</maxFileSize>
+        </triggeringPolicy>
+        <encoder>
+            <pattern>%d{yyy-MM-dd HH:mm:ss,GMT+8} %p %t - %m%n</pattern>
+            <charset class="java.nio.charset.Charset">UTF-8</charset>
+        </encoder>
+    </appender>
+
+    <logger name="Slf4jTestLogger" additivity="false">
+        <level value="INFO"/>
+        <appender-ref ref="RocketmqClientAppender"/>
+    </logger>
+
+
+</configuration>
diff --git a/pom.xml b/pom.xml
index 4326b46..4be64a6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -125,6 +125,7 @@
         <module>test</module>
         <module>distribution</module>
         <module>openmessaging</module>
+        <module>logging</module>
     </modules>
 
     <build>
@@ -529,6 +530,11 @@
                 <version>${project.version}</version>
             </dependency>
             <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>rocketmq-logging</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
                 <groupId>org.slf4j</groupId>
                 <artifactId>slf4j-api</artifactId>
                 <version>1.7.7</version>

-- 
To stop receiving notification emails like this one, please contact
yukon@apache.org.