diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/CommandCenterLog.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/CommandCenterLog.java index d12624a3..8208c1c2 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/CommandCenterLog.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/CommandCenterLog.java @@ -15,36 +15,77 @@ */ package com.alibaba.csp.sentinel.log; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.Logger; +import java.util.Iterator; +import java.util.ServiceLoader; /** * Logger for command center. */ -public class CommandCenterLog extends LogBase { - - private static final Logger heliumRecordLog = Logger.getLogger("cspCommandCenterLog"); - private static final String FILE_NAME = "command-center.log"; - private static Handler logHandler = null; +public class CommandCenterLog { + private static com.alibaba.csp.sentinel.log.Logger log = null; static { - logHandler = makeLogger(FILE_NAME, heliumRecordLog); + ServiceLoader load = ServiceLoader.load(Logger.class); + Logger logger = null; + Iterator iterator = load.iterator(); + while (iterator.hasNext()) { + Logger next = iterator.next(); + LogTarget annotation = next.getClass().getAnnotation(LogTarget.class); + if (annotation == null) { + continue; + } + String value = annotation.value().name(); + if (value.equals(LogType.COMMAND_CENTER_LOG.name())) { + logger = next; + break; + } + } + // Use user implementations. + if (logger != null) { + log = logger; + } else { + // Use default implementations. + log = new CommandCenterLogLogging(); + } + } + + public static void info(String format, Object... arguments) { + log.info(format, arguments); + } + + public static void info(String msg, Throwable e) { + log.info(msg, e); + } + + public static void warn(String format, Object... arguments) { + log.warn(format, arguments); + } + + public static void warn(String msg, Throwable e) { + log.warn(msg, e); + } + + public static void trace(String format, Object... arguments) { + log.trace(format, arguments); + } + + public static void trace(String msg, Throwable e) { + log.trace(msg, e); } - public static void info(String detail, Object... params) { - log(heliumRecordLog, logHandler, Level.INFO, detail, params); + public static void debug(String format, Object... arguments) { + log.debug(format, arguments); } - public static void info(String detail, Throwable e) { - log(heliumRecordLog, logHandler, Level.INFO, detail, e); + public static void debug(String msg, Throwable e) { + log.debug(msg, e); } - public static void warn(String detail, Object... params) { - log(heliumRecordLog, logHandler, Level.WARNING, detail, params); + public static void error(String format, Object... arguments) { + log.error(format, arguments); } - public static void warn(String detail, Throwable e) { - log(heliumRecordLog, logHandler, Level.WARNING, detail, e); + public static void error(String msg, Throwable e) { + log.error(msg, e); } } diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/CommandCenterLogLogging.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/CommandCenterLogLogging.java new file mode 100644 index 00000000..84651511 --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/CommandCenterLogLogging.java @@ -0,0 +1,79 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://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 com.alibaba.csp.sentinel.log; + +import java.util.logging.Handler; +import java.util.logging.Logger; + +/** + * Default logger implementation. + * @author xue8 + */ +public class CommandCenterLogLogging extends LogBase implements com.alibaba.csp.sentinel.log.Logger { + private final Logger heliumRecordLog = Logger.getLogger("cspCommandCenterLog"); + private final String FILE_NAME = "command-center.log"; + private final Handler logHandler = makeLogger(FILE_NAME, heliumRecordLog); + + @Override + public void info(String format, Object... arguments) { + log(heliumRecordLog, logHandler, Level.INFO, format, arguments); + } + + @Override + public void info(String msg, Throwable e) { + log(heliumRecordLog, logHandler, Level.INFO, msg, e); + } + + @Override + public void warn(String format, Object... arguments) { + log(heliumRecordLog, logHandler, Level.WARNING, format, arguments); + } + + @Override + public void warn(String msg, Throwable e) { + log(heliumRecordLog, logHandler, Level.WARNING, msg, e); + } + + @Override + public void trace(String format, Object... arguments) { + log(heliumRecordLog, logHandler, Level.TRACE, format, arguments); + } + + @Override + public void trace(String msg, Throwable e) { + log(heliumRecordLog, logHandler, Level.TRACE, msg, e); + } + + @Override + public void debug(String format, Object... arguments) { + log(heliumRecordLog, logHandler, Level.DEBUG, format, arguments); + } + + @Override + public void debug(String msg, Throwable e) { + log(heliumRecordLog, logHandler, Level.DEBUG, msg, e); + } + + @Override + public void error(String format, Object... arguments) { + log(heliumRecordLog, logHandler, Level.ERROR, format, arguments); + } + + @Override + public void error(String msg, Throwable e) { + log(heliumRecordLog, logHandler, Level.ERROR, msg, e); + } +} diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/FormattingTuple.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/FormattingTuple.java new file mode 100644 index 00000000..cfaa1e64 --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/FormattingTuple.java @@ -0,0 +1,59 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Copyright notice + * This code copy from SLF4J which licensed under the MIT License. + * + */ +package com.alibaba.csp.sentinel.log; + +/** + * Holds the results of formatting done by {@link MessageFormatter}. + * + * @author Joern Huxhorn + */ +public class FormattingTuple { + + static public FormattingTuple NULL = new FormattingTuple(null); + + 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; + this.argArray = argArray; + } + + public String getMessage() { + return message; + } + + public Object[] getArgArray() { + return argArray; + } + + public Throwable getThrowable() { + return throwable; + } + +} diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/Level.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/Level.java new file mode 100644 index 00000000..d64c4bb0 --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/Level.java @@ -0,0 +1,34 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://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 com.alibaba.csp.sentinel.log; + +/** + * Logging levels + * @author xue8 + */ +public class Level extends java.util.logging.Level { + private static final String defaultBundle = "sun.util.logging.resources.logging"; + + public static final Level ERROR = new Level("ERROR", 1000); + public static final Level WARNING = new Level("WARNING", 900); + public static final Level INFO = new Level("INFO", 800); + public static final Level DEBUG = new Level("DEBUG", 700); + public static final Level TRACE = new Level("TRACE", 600); + + protected Level(String name, int value) { + super(name, value, defaultBundle); + } +} diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LogBase.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LogBase.java index 2de0bddd..a237dde7 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LogBase.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LogBase.java @@ -152,11 +152,10 @@ public class LogBase { return; } LoggerUtils.disableOtherHandlers(logger, handler); - if (params.length == 0) { - logger.log(level, detail); - } else { - logger.log(level, detail, params); - } + + FormattingTuple formattingTuple = MessageFormatter.arrayFormat(detail, params); + String message = formattingTuple.getMessage(); + logger.log(level, message); } protected static void log(Logger logger, Handler handler, Level level, String detail, Throwable throwable) { diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LogTarget.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LogTarget.java new file mode 100644 index 00000000..0dc3ef97 --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LogTarget.java @@ -0,0 +1,32 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://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 com.alibaba.csp.sentinel.log; + +import java.lang.annotation.*; + +/** + * @author xue8 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Documented +public @interface LogTarget { + /** + * Returns the kinds of log type. + * @return Returns the kinds of log type + */ + LogType value() default LogType.RECORD_LOG; +} diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LogType.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LogType.java new file mode 100644 index 00000000..c9c8cb29 --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LogType.java @@ -0,0 +1,25 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://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 com.alibaba.csp.sentinel.log; + +/** + * An enum marks log type. + * @author xue8 + */ +public enum LogType { + COMMAND_CENTER_LOG, + RECORD_LOG, +} diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/Logger.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/Logger.java new file mode 100644 index 00000000..8b4d5809 --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/Logger.java @@ -0,0 +1,113 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://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 com.alibaba.csp.sentinel.log; + +/** + * Provide logger SPI interface. + * The default implementation is {@link java.util.logging}. + * + * Notice, the placeholder only supports the most popular placeholder convention (slf4j). + * So, if you're not using slf4j, you should create adapters compatible with placeholders "{}". + * + * @author xue8 + */ +public interface Logger { + /** + * Log a message at the INFO level according to the specified format + * and arguments. + * @param format the format string + * @param arguments a list of arguments + */ + void info(String format, Object... arguments); + + /** + * Log an exception (throwable) at the INFO level with an + * accompanying message. + * + * @param msg the message accompanying the exception + * @param e the exception (throwable) to log + */ + void info(String msg, Throwable e); + + /** + * Log a message at the WARN level according to the specified format + * and arguments. + * @param format the format string + * @param arguments a list of arguments + */ + void warn(String format, Object... arguments); + + /** + * Log an exception (throwable) at the WARN level with an + * accompanying message. + * + * @param msg the message accompanying the exception + * @param e the exception (throwable) to log + */ + void warn(String msg, Throwable e); + + /** + * Log a message at the TRACE level according to the specified format + * and arguments. + * @param format the format string + * @param arguments a list of arguments + */ + void trace(String format, Object... arguments); + + /** + * Log an exception (throwable) at the TRACE level with an + * accompanying message. + * + * @param msg the message accompanying the exception + * @param e the exception (throwable) to log + */ + void trace(String msg, Throwable e); + + /** + * Log a message at the DEBUG level according to the specified format + * and arguments. + * @param format the format string + * @param arguments a list of arguments + */ + void debug(String format, Object... arguments); + + /** + * Log an exception (throwable) at the DEBUG level with an + * accompanying message. + * + * @param msg the message accompanying the exception + * @param e the exception (throwable) to log + */ + void debug(String msg, Throwable e); + + /** + * Log a message at the ERROR level according to the specified format + * and arguments. + * @param format the format string + * @param arguments a list of arguments + */ + void error(String format, Object... arguments); + + /** + * Log an exception (throwable) at the ERROR level with an + * accompanying message. + * + * @param msg the message accompanying the exception + * @param e the exception (throwable) to log + */ + void error(String msg, Throwable e); + +} diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/MessageFormatter.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/MessageFormatter.java new file mode 100644 index 00000000..e2c30f2e --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/MessageFormatter.java @@ -0,0 +1,427 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Copyright notice + * This code copy from SLF4J which licensed under the MIT License. + * + */ + +/** + * Copyright notice + * This code copy from SLF4J which licensed under the MIT License. + * + */ +package com.alibaba.csp.sentinel.log; + + +// contributors: lizongbo: proposed special treatment of array parameter values +// Joern Huxhorn: pointed out double[] omission, suggested deep array copy + +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.Map; + +/** + * Formats messages according to very simple substitution rules. Substitutions + * can be made 1, 2 or more arguments. + * + *

+ * For example, + * + *

+ * MessageFormatter.format("Hi {}.", "there")
+ * 
+ * + * will return the string "Hi there.". + *

+ * The {} pair is called the formatting anchor. It serves to designate + * the location where arguments need to be substituted within the message + * pattern. + *

+ * In case your message contains the '{' or the '}' character, you do not have + * to do anything special unless the '}' character immediately follows '{'. For + * example, + * + *

+ * MessageFormatter.format("Set {1,2,3} is not equal to {}.", "1,2");
+ * 
+ * + * will return the string "Set {1,2,3} is not equal to 1,2.". + * + *

+ * If for whatever reason you need to place the string "{}" in the message + * without its formatting anchor meaning, then you need to escape the + * '{' character with '\', that is the backslash character. Only the '{' + * character should be escaped. There is no need to escape the '}' character. + * For example, + * + *

+ * MessageFormatter.format("Set \\{} is not equal to {}.", "1,2");
+ * 
+ * + * will return the string "Set {} is not equal to 1,2.". + * + *

+ * The escaping behavior just described can be overridden by escaping the escape + * character '\'. Calling + * + *

+ * MessageFormatter.format("File name is C:\\\\{}.", "file.zip");
+ * 
+ * + * will return the string "File name is C:\file.zip". + * + *

+ * The formatting conventions are different than those of {@link MessageFormat} + * which ships with the Java platform. This is justified by the fact that + * SLF4J's implementation is 10 times faster than that of {@link MessageFormat}. + * This local performance difference is both measurable and significant in the + * larger context of the complete logging processing chain. + * + *

+ * See also {@link #format(String, Object)}, + * {@link #format(String, Object, Object)} and + * {@link #arrayFormat(String, Object[])} methods for more details. + * + * @author Ceki Gülcü + * @author Joern Huxhorn + */ +final public class MessageFormatter { + static final char DELIM_START = '{'; + static final char DELIM_STOP = '}'; + static final String DELIM_STR = "{}"; + private static final char ESCAPE_CHAR = '\\'; + + /** + * Performs single argument substitution for the 'messagePattern' passed as + * parameter. + *

+ * For example, + * + *

+     * MessageFormatter.format("Hi {}.", "there");
+     * 
+ * + * will return the string "Hi there.". + *

+ * + * @param messagePattern + * The message pattern which will be parsed and formatted + * @param arg + * The argument to be substituted in place of the formatting anchor + * @return The formatted message + */ + final public static FormattingTuple format(String messagePattern, Object arg) { + return arrayFormat(messagePattern, new Object[] { arg }); + } + + /** + * + * Performs a two argument substitution for the 'messagePattern' passed as + * parameter. + *

+ * For example, + * + *

+     * MessageFormatter.format("Hi {}. My name is {}.", "Alice", "Bob");
+     * 
+ * + * will return the string "Hi Alice. My name is Bob.". + * + * @param messagePattern + * The message pattern which will be parsed and formatted + * @param arg1 + * The argument to be substituted in place of the first formatting + * anchor + * @param arg2 + * The argument to be substituted in place of the second formatting + * anchor + * @return The formatted message + */ + final public static FormattingTuple format(final String messagePattern, Object arg1, Object arg2) { + return arrayFormat(messagePattern, new Object[] { arg1, arg2 }); + } + + + static final Throwable getThrowableCandidate(Object[] argArray) { + if (argArray == null || argArray.length == 0) { + return null; + } + + final Object lastEntry = argArray[argArray.length - 1]; + if (lastEntry instanceof Throwable) { + return (Throwable) lastEntry; + } + return null; + } + + final public static FormattingTuple arrayFormat(final String messagePattern, final Object[] argArray) { + Throwable throwableCandidate = getThrowableCandidate(argArray); + Object[] args = argArray; + if (throwableCandidate != null) { + args = trimmedCopy(argArray); + } + return arrayFormat(messagePattern, args, throwableCandidate); + } + + private static Object[] trimmedCopy(Object[] argArray) { + if (argArray == null || argArray.length == 0) { + throw new IllegalStateException("non-sensical empty or null argument array"); + } + final int trimemdLen = argArray.length - 1; + Object[] trimmed = new Object[trimemdLen]; + System.arraycopy(argArray, 0, trimmed, 0, trimemdLen); + return trimmed; + } + + final public static FormattingTuple arrayFormat(final String messagePattern, final Object[] argArray, Throwable throwable) { + + if (messagePattern == null) { + return new FormattingTuple(null, argArray, throwable); + } + + if (argArray == null) { + return new FormattingTuple(messagePattern); + } + + int i = 0; + int j; + // use string builder for better multicore performance + StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50); + + int L; + for (L = 0; L < argArray.length; L++) { + + j = messagePattern.indexOf(DELIM_STR, i); + + if (j == -1) { + // no more variables + if (i == 0) { // this is a simple string + return new FormattingTuple(messagePattern, argArray, throwable); + } else { // add the tail string which contains no variables and return + // the result. + sbuf.append(messagePattern, i, messagePattern.length()); + return new FormattingTuple(sbuf.toString(), argArray, throwable); + } + } else { + if (isEscapedDelimeter(messagePattern, j)) { + if (!isDoubleEscaped(messagePattern, j)) { + L--; // DELIM_START was escaped, thus should not be incremented + sbuf.append(messagePattern, i, j - 1); + sbuf.append(DELIM_START); + i = j + 1; + } else { + // The escape character preceding the delimiter start is + // itself escaped: "abc x:\\{}" + // we have to consume one backward slash + sbuf.append(messagePattern, i, j - 1); + deeplyAppendParameter(sbuf, argArray[L], new HashMap()); + i = j + 2; + } + } else { + // normal case + sbuf.append(messagePattern, i, j); + deeplyAppendParameter(sbuf, argArray[L], new HashMap()); + i = j + 2; + } + } + } + // append the characters following the last {} pair. + sbuf.append(messagePattern, i, messagePattern.length()); + return new FormattingTuple(sbuf.toString(), argArray, throwable); + } + + final static boolean isEscapedDelimeter(String messagePattern, int delimeterStartIndex) { + + if (delimeterStartIndex == 0) { + return false; + } + char potentialEscape = messagePattern.charAt(delimeterStartIndex - 1); + if (potentialEscape == ESCAPE_CHAR) { + return true; + } else { + return false; + } + } + + final static boolean isDoubleEscaped(String messagePattern, int delimeterStartIndex) { + if (delimeterStartIndex >= 2 && messagePattern.charAt(delimeterStartIndex - 2) == ESCAPE_CHAR) { + return true; + } else { + return false; + } + } + + // special treatment of array values was suggested by 'lizongbo' + private static void deeplyAppendParameter(StringBuilder sbuf, Object o, Map seenMap) { + if (o == null) { + sbuf.append("null"); + return; + } + if (!o.getClass().isArray()) { + safeObjectAppend(sbuf, o); + } else { + // check for primitive array types because they + // unfortunately cannot be cast to Object[] + 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 oAsString = o.toString(); + sbuf.append(oAsString); + } catch (Throwable t) { + sbuf.append("[FAILED toString()]"); + } + + } + + private static void objectArrayAppend(StringBuilder sbuf, Object[] a, Map seenMap) { + sbuf.append('['); + if (!seenMap.containsKey(a)) { + seenMap.put(a, null); + final int len = a.length; + for (int i = 0; i < len; i++) { + deeplyAppendParameter(sbuf, a[i], seenMap); + if (i != len - 1) { + sbuf.append(", "); + } + } + // allow repeats in siblings + seenMap.remove(a); + } else { + sbuf.append("..."); + } + sbuf.append(']'); + } + + private static void booleanArrayAppend(StringBuilder sbuf, boolean[] a) { + sbuf.append('['); + final 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('['); + final 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('['); + final 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('['); + final 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('['); + final 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('['); + final 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('['); + final 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('['); + final 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/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/RecordLog.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/RecordLog.java index a73b56b2..da7bb2ed 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/RecordLog.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/RecordLog.java @@ -15,9 +15,8 @@ */ package com.alibaba.csp.sentinel.log; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.Logger; +import java.util.Iterator; +import java.util.ServiceLoader; /*** * The basic logger for vital events. @@ -25,27 +24,71 @@ import java.util.logging.Logger; * @author youji.zj */ public class RecordLog extends LogBase { - private static final Logger heliumRecordLog = Logger.getLogger("cspSentinelRecordLog"); - private static final String FILE_NAME = "sentinel-record.log"; - private static Handler logHandler = null; + private static com.alibaba.csp.sentinel.log.Logger log = null; static { - logHandler = makeLogger(FILE_NAME, heliumRecordLog); + ServiceLoader load = ServiceLoader.load(Logger.class); + Logger logger = null; + Iterator iterator = load.iterator(); + while (iterator.hasNext()) { + Logger next = iterator.next(); + LogTarget annotation = next.getClass().getAnnotation(LogTarget.class); + if (annotation == null) { + continue; + } + String value = annotation.value().name(); + if (value.equals(LogType.RECORD_LOG.name())) { + logger = next; + break; + } + } + // Use user implementations. + if (logger != null) { + log = logger; + } else { + // Use default implementations. + log = new RecordLogLogging(); + } } - public static void info(String detail, Object... params) { - log(heliumRecordLog, logHandler, Level.INFO, detail, params); + public static void info(String format, Object... arguments) { + log.info(format, arguments); } - public static void info(String detail, Throwable e) { - log(heliumRecordLog, logHandler, Level.INFO, detail, e); + public static void info(String msg, Throwable e) { + log.info(msg, e); } - public static void warn(String detail, Object... params) { - log(heliumRecordLog, logHandler, Level.WARNING, detail, params); + public static void warn(String format, Object... arguments) { + log.warn(format, arguments); } - public static void warn(String detail, Throwable e) { - log(heliumRecordLog, logHandler, Level.WARNING, detail, e); + public static void warn(String msg, Throwable e) { + log.warn(msg, e); } + + public static void trace(String format, Object... arguments) { + log.trace(format, arguments); + } + + public static void trace(String msg, Throwable e) { + log.trace(msg, e); + } + + public static void debug(String format, Object... arguments) { + log.debug(format, arguments); + } + + public static void debug(String msg, Throwable e) { + log.debug(msg, e); + } + + public static void error(String format, Object... arguments) { + log.error(format, arguments); + } + + public static void error(String msg, Throwable e) { + log.error(msg, e); + } + } diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/RecordLogLogging.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/RecordLogLogging.java new file mode 100644 index 00000000..2a6bac6c --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/RecordLogLogging.java @@ -0,0 +1,79 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://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 com.alibaba.csp.sentinel.log; + +import java.util.logging.Handler; +import java.util.logging.Logger; + +/** + * Default logger implementation. + * @author xue8 + */ +public class RecordLogLogging extends LogBase implements com.alibaba.csp.sentinel.log.Logger { + private final Logger heliumRecordLog = Logger.getLogger("cspSentinelRecordLog"); + private final String FILE_NAME = "sentinel-record.log"; + private final Handler logHandler = makeLogger(FILE_NAME, heliumRecordLog); + + @Override + public void info(String format, Object... arguments) { + log(heliumRecordLog, logHandler, Level.INFO, format, arguments); + } + + @Override + public void info(String msg, Throwable e) { + log(heliumRecordLog, logHandler, Level.INFO, msg, e); + } + + @Override + public void warn(String format, Object... arguments) { + log(heliumRecordLog, logHandler, Level.WARNING, format, arguments); + } + + @Override + public void warn(String msg, Throwable e) { + log(heliumRecordLog, logHandler, Level.WARNING, msg, e); + } + + @Override + public void trace(String format, Object... arguments) { + log(heliumRecordLog, logHandler, Level.TRACE, format, arguments); + } + + @Override + public void trace(String msg, Throwable e) { + log(heliumRecordLog, logHandler, Level.TRACE, msg, e); + } + + @Override + public void debug(String format, Object... arguments) { + log(heliumRecordLog, logHandler, Level.DEBUG, format, arguments); + } + + @Override + public void debug(String msg, Throwable e) { + log(heliumRecordLog, logHandler, Level.DEBUG, msg, e); + } + + @Override + public void error(String format, Object... arguments) { + log(heliumRecordLog, logHandler, Level.ERROR, format, arguments); + } + + @Override + public void error(String msg, Throwable e) { + log(heliumRecordLog, logHandler, Level.ERROR, msg, e); + } +} diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/RecordLogTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/RecordLogTest.java index 50f9b63f..e90d8c63 100755 --- a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/RecordLogTest.java +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/RecordLogTest.java @@ -107,4 +107,14 @@ public class RecordLogTest { -} \ No newline at end of file + // Because log only writes into the file, + // can't read the log(file conflict), so no assertion in this unit test. + @Test + public void testMessageFormatter() { + RecordLog.info("1 2 {} 4 {} 6", "3", "5"); + RecordLog.info("1 2 {} 4 {} 6", "3"); + RecordLog.info("1 2 {} 4 {} 6"); + + RecordLog.info("1 2 \\{} 4 {} 6", "5"); + } +} diff --git a/sentinel-demo/pom.xml b/sentinel-demo/pom.xml index 4a9ac4de..1f055d29 100755 --- a/sentinel-demo/pom.xml +++ b/sentinel-demo/pom.xml @@ -36,6 +36,7 @@ sentinel-demo-zuul-gateway sentinel-demo-etcd-datasource sentinel-demo-spring-webmvc + sentinel-demo-log-logback diff --git a/sentinel-demo/sentinel-demo-log-logback/pom.xml b/sentinel-demo/sentinel-demo-log-logback/pom.xml new file mode 100644 index 00000000..d5802e98 --- /dev/null +++ b/sentinel-demo/sentinel-demo-log-logback/pom.xml @@ -0,0 +1,41 @@ + + + + sentinel-demo + com.alibaba.csp + 1.7.2-SNAPSHOT + + 4.0.0 + + sentinel-demo-log-logback + + + + ch.qos.logback + logback-classic + 1.2.3 + + + + ch.qos.logback + logback-core + 1.2.3 + + + + junit + junit + test + + + + com.github.stefanbirkner + system-rules + RELEASE + test + + + + diff --git a/sentinel-demo/sentinel-demo-log-logback/src/main/java/com/alibaba/csp/sentinel/demo/log/logback/CommandCenterLogLoggerImpl.java b/sentinel-demo/sentinel-demo-log-logback/src/main/java/com/alibaba/csp/sentinel/demo/log/logback/CommandCenterLogLoggerImpl.java new file mode 100644 index 00000000..9a144485 --- /dev/null +++ b/sentinel-demo/sentinel-demo-log-logback/src/main/java/com/alibaba/csp/sentinel/demo/log/logback/CommandCenterLogLoggerImpl.java @@ -0,0 +1,90 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://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 com.alibaba.csp.sentinel.demo.log.logback; + +import com.alibaba.csp.sentinel.log.LogTarget; +import com.alibaba.csp.sentinel.log.LogType; +import com.alibaba.csp.sentinel.log.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class is a demo shows how to create a customized logger implementation. + * + *
    + *
  • 1. Create a class which implements the {@link Logger} SPI interface
  • + *
  • 2. Use a {@link LogTarget} to specify the log type
  • + *
  • 3. Implement your own method
  • + *
  • 4. Add your logger in {@code com.alibaba.csp.sentinel.log.Logger} file which is stored in + * {@code resources/META-INF/services/} directory
  • + *
+ * + * @author xue8 + */ +@LogTarget(value = LogType.COMMAND_CENTER_LOG) +public class CommandCenterLogLoggerImpl implements Logger { + private final org.slf4j.Logger logger = LoggerFactory.getLogger("commandCenterLogLogger"); + + @Override + public void info(String format, Object... arguments) { + logger.info(format, arguments); + } + + @Override + public void info(String msg, Throwable e) { + logger.info(msg, e); + } + + @Override + public void warn(String format, Object... arguments) { + logger.warn(format, arguments); + } + + @Override + public void warn(String msg, Throwable e) { + logger.warn(msg, e); + } + + @Override + public void trace(String format, Object... arguments) { + logger.trace(format, arguments); + } + + @Override + public void trace(String msg, Throwable e) { + logger.trace(msg, e); + } + + @Override + public void debug(String format, Object... arguments) { + logger.debug(format, arguments); + } + + @Override + public void debug(String msg, Throwable e) { + logger.debug(msg, e); + } + + @Override + public void error(String format, Object... arguments) { + logger.error(format, arguments); + } + + @Override + public void error(String msg, Throwable e) { + logger.error(msg, e); + } + +} diff --git a/sentinel-demo/sentinel-demo-log-logback/src/main/java/com/alibaba/csp/sentinel/demo/log/logback/RecordLogLoggerImpl.java b/sentinel-demo/sentinel-demo-log-logback/src/main/java/com/alibaba/csp/sentinel/demo/log/logback/RecordLogLoggerImpl.java new file mode 100644 index 00000000..b444fc37 --- /dev/null +++ b/sentinel-demo/sentinel-demo-log-logback/src/main/java/com/alibaba/csp/sentinel/demo/log/logback/RecordLogLoggerImpl.java @@ -0,0 +1,90 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://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 com.alibaba.csp.sentinel.demo.log.logback; + +import com.alibaba.csp.sentinel.log.LogTarget; +import com.alibaba.csp.sentinel.log.LogType; +import com.alibaba.csp.sentinel.log.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class is a demo shows how to create a customized logger implementation. + * + *
    + *
  • 1. Create a class which implements the {@link Logger} SPI interface
  • + *
  • 2. Use a {@link LogTarget} to specify the log type
  • + *
  • 3. Implement your own method
  • + *
  • 4. Add your logger in {@code com.alibaba.csp.sentinel.log.Logger} file which is stored in + * {@code resources/META-INF/services/} directory
  • + *
+ * + * @author xue8 + */ +@LogTarget(value = LogType.RECORD_LOG) +public class RecordLogLoggerImpl implements Logger { + private final org.slf4j.Logger logger = LoggerFactory.getLogger("recordLogLogger"); + + @Override + public void info(String format, Object... arguments) { + logger.info(format, arguments); + } + + @Override + public void info(String msg, Throwable e) { + logger.info(msg, e); + } + + @Override + public void warn(String format, Object... arguments) { + logger.warn(format, arguments); + } + + @Override + public void warn(String msg, Throwable e) { + logger.warn(msg, e); + } + + @Override + public void trace(String format, Object... arguments) { + logger.trace(format, arguments); + } + + @Override + public void trace(String msg, Throwable e) { + logger.trace(msg, e); + } + + @Override + public void debug(String format, Object... arguments) { + logger.debug(format, arguments); + } + + @Override + public void debug(String msg, Throwable e) { + logger.debug(msg, e); + } + + @Override + public void error(String format, Object... arguments) { + logger.error(format, arguments); + } + + @Override + public void error(String msg, Throwable e) { + logger.error(msg, e); + } + +} diff --git a/sentinel-demo/sentinel-demo-log-logback/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.log.Logger b/sentinel-demo/sentinel-demo-log-logback/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.log.Logger new file mode 100644 index 00000000..536e7806 --- /dev/null +++ b/sentinel-demo/sentinel-demo-log-logback/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.log.Logger @@ -0,0 +1,2 @@ +com.alibaba.csp.sentinel.demo.log.logback.RecordLogLoggerImpl +com.alibaba.csp.sentinel.demo.log.logback.CommandCenterLogLoggerImpl diff --git a/sentinel-demo/sentinel-demo-log-logback/src/main/resources/logback.xml b/sentinel-demo/sentinel-demo-log-logback/src/main/resources/logback.xml new file mode 100644 index 00000000..ffb35ce9 --- /dev/null +++ b/sentinel-demo/sentinel-demo-log-logback/src/main/resources/logback.xml @@ -0,0 +1,42 @@ + + + + + %-5level %logger - %msg%n + + + + + sentinel-record.log + true + + %-5level %logger - %msg%n + + + + + sentinel-command-center.log + true + + %-5level %logger - %msg%n + + + + + + + + + + + + + + + + + + + + + diff --git a/sentinel-demo/sentinel-demo-log-logback/src/test/java/com/alibaba/csp/sentinel/demo/log/logback/CommandCenterLogTest.java b/sentinel-demo/sentinel-demo-log-logback/src/test/java/com/alibaba/csp/sentinel/demo/log/logback/CommandCenterLogTest.java new file mode 100644 index 00000000..c52d5cb2 --- /dev/null +++ b/sentinel-demo/sentinel-demo-log-logback/src/test/java/com/alibaba/csp/sentinel/demo/log/logback/CommandCenterLogTest.java @@ -0,0 +1,116 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://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 com.alibaba.csp.sentinel.demo.log.logback; + +import com.alibaba.csp.sentinel.log.CommandCenterLog; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.SystemOutRule; + +/** + * @author xue8 + */ +public class CommandCenterLogTest { + @Rule + public SystemOutRule log = new SystemOutRule().enableLog(); + + @Test + public void testLog() { + CommandCenterLog.info("init"); + log.clearLog(); + int count = 0; + + // info test + while (count++ < 1000) { + log.clearLog(); + CommandCenterLog.info("Count {}", count); + String str = String.format("INFO commandCenterLogLogger - Count %d" + System.lineSeparator(), count); + Assert.assertEquals(str, log.getLog()); + } + + // warn test + while (count++ < 2000) { + log.clearLog(); + CommandCenterLog.warn("Count {}", count); + String str = String.format("WARN commandCenterLogLogger - Count %d" + System.lineSeparator(), count); + Assert.assertEquals(str, log.getLog()); + } + + // trace test + while (count++ < 3000) { + log.clearLog(); + CommandCenterLog.trace("Count {}", count); + String str = String.format("TRACE commandCenterLogLogger - Count %d" + System.lineSeparator(), count); + Assert.assertEquals(str, log.getLog()); + } + + // debug test + while (count++ < 4000) { + log.clearLog(); + CommandCenterLog.debug("Count {}", count); + String str = String.format("DEBUG commandCenterLogLogger - Count %d" + System.lineSeparator(), count); + Assert.assertEquals(str, log.getLog()); + } + + // test error + while (count++ < 5000) { + log.clearLog(); + CommandCenterLog.error("Count {}", count); + String str = String.format("ERROR commandCenterLogLogger - Count %d" + System.lineSeparator(), count); + Assert.assertEquals(str, log.getLog()); + } + } + + @Test + public void testLogException() { + CommandCenterLog.info("init"); + log.clearLog(); + Exception e = new Exception("ex"); + + // info test + CommandCenterLog.info("Error", e); + // split the log for test + String[] logSplit = log.getLog().split(System.lineSeparator()); + Assert.assertEquals("INFO commandCenterLogLogger - Error", logSplit[0]); + + // warn test + log.clearLog(); + CommandCenterLog.warn("Error", e); + logSplit = log.getLog().split(System.lineSeparator()); + Assert.assertEquals("WARN commandCenterLogLogger - Error", logSplit[0]); + + // trace test + log.clearLog(); + CommandCenterLog.trace("Error", e); + logSplit = log.getLog().split(System.lineSeparator()); + Assert.assertEquals("TRACE commandCenterLogLogger - Error", logSplit[0]); + + // debug test + log.clearLog(); + CommandCenterLog.debug("Error", e); + logSplit = log.getLog().split(System.lineSeparator()); + Assert.assertEquals("DEBUG commandCenterLogLogger - Error", logSplit[0]); + + // error test + log.clearLog(); + CommandCenterLog.error("Error", e); + logSplit = log.getLog().split(System.lineSeparator()); + Assert.assertEquals("ERROR commandCenterLogLogger - Error", logSplit[0]); + } + + +} diff --git a/sentinel-demo/sentinel-demo-log-logback/src/test/java/com/alibaba/csp/sentinel/demo/log/logback/RecordLogTest.java b/sentinel-demo/sentinel-demo-log-logback/src/test/java/com/alibaba/csp/sentinel/demo/log/logback/RecordLogTest.java new file mode 100644 index 00000000..981a689d --- /dev/null +++ b/sentinel-demo/sentinel-demo-log-logback/src/test/java/com/alibaba/csp/sentinel/demo/log/logback/RecordLogTest.java @@ -0,0 +1,115 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.csp.sentinel.demo.log.logback; + +import com.alibaba.csp.sentinel.log.RecordLog; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.SystemOutRule; + +/** + * @author xue8 + */ +public class RecordLogTest { + @Rule + public SystemOutRule log = new SystemOutRule().enableLog(); + + @Test + public void testLog() { + RecordLog.info("init"); + log.clearLog(); + int count = 0; + + // info test + while (count++ < 1000) { + log.clearLog(); + RecordLog.info("Count {}", count); + String str = String.format("INFO recordLogLogger - Count %d" + System.lineSeparator(), count); + Assert.assertEquals(str, log.getLog()); + } + + // warn test + while (count++ < 2000) { + log.clearLog(); + RecordLog.warn("Count {}", count); + String str = String.format("WARN recordLogLogger - Count %d" + System.lineSeparator(), count); + Assert.assertEquals(str, log.getLog()); + } + + // trace test + while (count++ < 3000) { + log.clearLog(); + RecordLog.trace("Count {}", count); + String str = String.format("TRACE recordLogLogger - Count %d" + System.lineSeparator(), count); + Assert.assertEquals(str, log.getLog()); + } + + // debug test + while (count++ < 4000) { + log.clearLog(); + RecordLog.debug("Count {}", count); + String str = String.format("DEBUG recordLogLogger - Count %d" + System.lineSeparator(), count); + Assert.assertEquals(str, log.getLog()); + } + + // test error + while (count++ < 5000) { + log.clearLog(); + RecordLog.error("Count {}", count); + String str = String.format("ERROR recordLogLogger - Count %d" + System.lineSeparator(), count); + Assert.assertEquals(str, log.getLog()); + } + } + + @Test + public void testLogException() { + RecordLog.info("init"); + log.clearLog(); + Exception e = new Exception("ex"); + + // info test + RecordLog.info("Error", e); + // split the log for test + String[] logSplit = log.getLog().split(System.lineSeparator()); + Assert.assertEquals("INFO recordLogLogger - Error", logSplit[0]); + + // warn test + log.clearLog(); + RecordLog.warn("Error", e); + logSplit = log.getLog().split(System.lineSeparator()); + Assert.assertEquals("WARN recordLogLogger - Error", logSplit[0]); + + // trace test + log.clearLog(); + RecordLog.trace("Error", e); + logSplit = log.getLog().split(System.lineSeparator()); + Assert.assertEquals("TRACE recordLogLogger - Error", logSplit[0]); + + // debug test + log.clearLog(); + RecordLog.debug("Error", e); + logSplit = log.getLog().split(System.lineSeparator()); + Assert.assertEquals("DEBUG recordLogLogger - Error", logSplit[0]); + + // error test + log.clearLog(); + RecordLog.error("Error", e); + logSplit = log.getLog().split(System.lineSeparator()); + Assert.assertEquals("ERROR recordLogLogger - Error", logSplit[0]); + } + +}