From 125b7495be9f869ce6d728d8dbd3e82c86dc4b57 Mon Sep 17 00:00:00 2001 From: Oleg Kuznetsov Date: Thu, 17 Oct 2019 15:21:41 +0300 Subject: [PATCH 1/8] added versions.fragment --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index 6dfc56a9..f92c82f9 100644 --- a/build.gradle +++ b/build.gradle @@ -31,6 +31,7 @@ ext { compileSdk : 28, appcompat : '1.0.2', androidx : '1.0.0', + fragment : '1.1.0', material : '1.0.0', lifecycle : '2.0.0', dagger : '2.17', From 00d1c97ad0a1783ada9791719085ea70665dc42c Mon Sep 17 00:00:00 2001 From: Oleg Kuznetsov Date: Thu, 17 Oct 2019 15:22:55 +0300 Subject: [PATCH 2/8] added Kotlin plugin --- logging/build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/logging/build.gradle b/logging/build.gradle index 21ed2450..aa5b69e8 100644 --- a/logging/build.gradle +++ b/logging/build.gradle @@ -1,4 +1,6 @@ apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-kapt' android { compileSdkVersion versions.compileSdk @@ -14,5 +16,6 @@ android { } dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "androidx.annotation:annotation:$versions.androidx" } From ba16c918931ff26399958690404ae802cb2a9382 Mon Sep 17 00:00:00 2001 From: Oleg Kuznetsov Date: Thu, 17 Oct 2019 15:24:26 +0300 Subject: [PATCH 3/8] removed Java classes --- .../core/log/ConsoleLogProcessor.java | 63 ---- .../java/ru/touchin/roboswag/core/log/Lc.java | 290 ------------------ .../ru/touchin/roboswag/core/log/LcGroup.java | 237 -------------- .../roboswag/core/log/LogProcessor.java | 61 ---- 4 files changed, 651 deletions(-) delete mode 100644 logging/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.java delete mode 100644 logging/src/main/java/ru/touchin/roboswag/core/log/Lc.java delete mode 100644 logging/src/main/java/ru/touchin/roboswag/core/log/LcGroup.java delete mode 100644 logging/src/main/java/ru/touchin/roboswag/core/log/LogProcessor.java diff --git a/logging/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.java b/logging/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.java deleted file mode 100644 index 8e00787e..00000000 --- a/logging/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov) - * - * This file is part of RoboSwag library. - * - * 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 ru.touchin.roboswag.core.log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.util.Log; - -/** - * Created by Gavriil Sitnikov on 13/11/2015. - * Simple {@link LogProcessor} implementation which is logging messages to console (logcat). - */ -public class ConsoleLogProcessor extends LogProcessor { - - private static final int MAX_LOG_LENGTH = 4000; - - public ConsoleLogProcessor(@NonNull final LcLevel lclevel) { - super(lclevel); - } - - @NonNull - private String normalize(@NonNull final String message) { - return message.replace("\r\n", "\n").replace("\0", ""); - } - - @Override - @SuppressWarnings({"WrongConstant", "LogConditional"}) - //WrongConstant, LogConditional: level.getPriority() is not wrong constant! - public void processLogMessage(@NonNull final LcGroup group, @NonNull final LcLevel level, - @NonNull final String tag, @NonNull final String message, @Nullable final Throwable throwable) { - final String messageToLog = normalize(message + (throwable != null ? '\n' + Log.getStackTraceString(throwable) : "")); - final int length = messageToLog.length(); - for (int i = 0; i < length; i++) { - int newline = messageToLog.indexOf('\n', i); - newline = newline != -1 ? newline : length; - do { - final int end = Math.min(newline, i + MAX_LOG_LENGTH); - Log.println(level.getPriority(), tag, messageToLog.substring(i, end)); - i = end; - } - while (i < newline); - } - - } - -} diff --git a/logging/src/main/java/ru/touchin/roboswag/core/log/Lc.java b/logging/src/main/java/ru/touchin/roboswag/core/log/Lc.java deleted file mode 100644 index 435852fc..00000000 --- a/logging/src/main/java/ru/touchin/roboswag/core/log/Lc.java +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov) - * - * This file is part of RoboSwag library. - * - * 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 ru.touchin.roboswag.core.log; - -import android.annotation.SuppressLint; -import android.os.Handler; -import android.os.Looper; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.text.TextUtils; -import android.util.Log; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import ru.touchin.roboswag.core.utils.ShouldNotHappenException; - -/** - * Created by Gavriil Sitnikov on 13/11/2015. - * General logging utility of RoboSwag library. - * You can initialize {@link LogProcessor} to intercept log messages and make decision how to show them. - * Also you can specify assertions behavior to manually make application more stable in production but intercept illegal states in some - * third-party tool to fix them later but not crash in production. - */ -@SuppressWarnings({"checkstyle:methodname", "PMD.ShortMethodName", "PMD.ShortClassName"}) -//MethodNameCheck,ShortMethodName: log methods better be 1-symbol -public final class Lc { - - public static final LcGroup GENERAL_LC_GROUP = new LcGroup("GENERAL"); - - public static final int STACK_TRACE_CODE_DEPTH; - - private static boolean crashOnAssertions = true; - @NonNull - private static LogProcessor logProcessor = new ConsoleLogProcessor(LcLevel.ERROR); - - static { - final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); - int stackDepth; - for (stackDepth = 0; stackDepth < stackTrace.length; stackDepth++) { - if (stackTrace[stackDepth].getClassName().equals(Lc.class.getName())) { - break; - } - } - STACK_TRACE_CODE_DEPTH = stackDepth + 1; - } - - /** - * Flag to crash application or pass it to {@link LogProcessor#processLogMessage(LcGroup, LcLevel, String, String, Throwable)} - * on specific {@link LcGroup#assertion(Throwable)} points of code. - * - * @return True if application should crash on assertion. - */ - public static boolean isCrashOnAssertions() { - return crashOnAssertions; - } - - /** - * Returns {@link LogProcessor} object to intercept incoming log messages (by default it returns {@link ConsoleLogProcessor}). - * - * @return Specific {@link LogProcessor}. - */ - @NonNull - public static LogProcessor getLogProcessor() { - return logProcessor; - } - - /** - * Initialize general logging behavior. - * - * @param logProcessor {@link LogProcessor} to intercept all log messages; - * @param crashOnAssertions Flag to crash application - * or pass it to {@link LogProcessor#processLogMessage(LcGroup, LcLevel, String, String, Throwable)} - * on specific {@link LcGroup#assertion(Throwable)} points of code. - */ - public static void initialize(@NonNull final LogProcessor logProcessor, final boolean crashOnAssertions) { - Lc.crashOnAssertions = crashOnAssertions; - Lc.logProcessor = logProcessor; - } - - /** - * Logs debug message via {@link #GENERAL_LC_GROUP}. - * - * @param message Message or format of message to log; - * @param args Arguments of formatted message. - */ - public static void d(@NonNull final String message, @NonNull final Object... args) { - GENERAL_LC_GROUP.d(message, args); - } - - /** - * Logs debug message via {@link #GENERAL_LC_GROUP}. - * - * @param throwable Exception to log; - * @param message Message or format of message to log; - * @param args Arguments of formatted message. - */ - public static void d(@NonNull final Throwable throwable, @NonNull final String message, @NonNull final Object... args) { - GENERAL_LC_GROUP.d(throwable, message, args); - } - - /** - * Logs info message via {@link #GENERAL_LC_GROUP}. - * - * @param message Message or format of message to log; - * @param args Arguments of formatted message. - */ - public static void i(@NonNull final String message, @NonNull final Object... args) { - GENERAL_LC_GROUP.i(message, args); - } - - /** - * Logs info message via {@link #GENERAL_LC_GROUP}. - * - * @param throwable Exception to log; - * @param message Message or format of message to log; - * @param args Arguments of formatted message. - */ - public static void i(@NonNull final Throwable throwable, @NonNull final String message, @NonNull final Object... args) { - GENERAL_LC_GROUP.i(throwable, message, args); - } - - /** - * Logs warning message via {@link #GENERAL_LC_GROUP}. - * - * @param message Message or format of message to log; - * @param args Arguments of formatted message. - */ - public static void w(@NonNull final String message, @NonNull final Object... args) { - GENERAL_LC_GROUP.w(message, args); - } - - /** - * Logs warning message via {@link #GENERAL_LC_GROUP}. - * - * @param throwable Exception to log; - * @param message Message or format of message to log; - * @param args Arguments of formatted message. - */ - public static void w(@NonNull final Throwable throwable, @NonNull final String message, @NonNull final Object... args) { - GENERAL_LC_GROUP.w(throwable, message, args); - } - - /** - * Logs error message via {@link #GENERAL_LC_GROUP}. - * - * @param message Message or format of message to log; - * @param args Arguments of formatted message. - */ - public static void e(@NonNull final String message, @NonNull final Object... args) { - GENERAL_LC_GROUP.e(message, args); - } - - /** - * Logs error message via {@link #GENERAL_LC_GROUP}. - * - * @param throwable Exception to log; - * @param message Message or format of message to log; - * @param args Arguments of formatted message. - */ - public static void e(@NonNull final Throwable throwable, @NonNull final String message, @NonNull final Object... args) { - GENERAL_LC_GROUP.e(throwable, message, args); - } - - /** - * Processes assertion. Normally it will throw {@link ShouldNotHappenException} and crash app. - * If it should crash or not is specified at {@link Lc#isCrashOnAssertions()}. - * In some cases crash on assertions should be switched off and assertion should be processed in {@link LogProcessor}. - * It is useful for example to not crash but log it as handled crash in Crashlitycs in production build. - * - * @param message Message that is describing assertion. - */ - public static void assertion(@NonNull final String message) { - GENERAL_LC_GROUP.assertion(message); - } - - /** - * Processes assertion. Normally it will throw {@link ShouldNotHappenException} and crash app. - * If it should crash or not is specified at {@link Lc#isCrashOnAssertions()}. - * In some cases crash on assertions should be switched off and assertion should be processed in {@link LogProcessor}. - * It is useful for example to not crash but log it as handled crash in Crashlitycs in production build. - * - * @param throwable Exception that is describing assertion. - */ - public static void assertion(@NonNull final Throwable throwable) { - GENERAL_LC_GROUP.assertion(throwable); - } - - /** - * Throws assertion on main thread (to avoid Rx exceptions e.g.) and cuts top causes by type of exception class. - * - * @param assertion Source throwable; - * @param exceptionsClassesToCut Classes which will be cut from top of causes stack of source throwable. - */ - @SafeVarargs - public static void cutAssertion(@NonNull final Throwable assertion, @NonNull final Class... exceptionsClassesToCut) { - new Handler(Looper.getMainLooper()).post(() -> { - final List processedExceptions = new ArrayList<>(); - Throwable result = assertion; - boolean exceptionAssignableFromIgnores; - do { - exceptionAssignableFromIgnores = false; - processedExceptions.add(result); - for (final Class exceptionClass : exceptionsClassesToCut) { - if (result.getClass().isAssignableFrom(exceptionClass)) { - exceptionAssignableFromIgnores = true; - result = result.getCause(); - break; - } - } - } - while (exceptionAssignableFromIgnores && result != null && !processedExceptions.contains(result)); - Lc.assertion(result != null ? result : assertion); - }); - } - - /** - * Returns line of code from where this method called. - * - * @param caller Object who is calling for code point; - * @return String represents code point. - */ - @NonNull - public static String getCodePoint(@Nullable final Object caller) { - return getCodePoint(caller, 1); - } - - /** - * Returns line of code from where this method called. - * - * @param caller Object who is calling for code point; - * @param stackShift caller Shift of stack (e.g. 2 means two elements deeper); - * @return String represents code point. - */ - @NonNull - public static String getCodePoint(@Nullable final Object caller, final int stackShift) { - final StackTraceElement traceElement = Thread.currentThread().getStackTrace()[STACK_TRACE_CODE_DEPTH + stackShift]; - return traceElement.getMethodName() + '(' + traceElement.getFileName() + ':' + traceElement.getLineNumber() + ')' - + (caller != null ? " of object " + caller.getClass().getSimpleName() + '(' + Integer.toHexString(caller.hashCode()) + ')' : ""); - } - - /** - * Returns line of code from where this method called. - * - * @param caller Object who is calling for code point; - * @param methodName String represents lifecycle method in which it was called - * @return String represents code point. - */ - @NonNull - public static String getCodePoint(@Nullable final Object caller, final String methodName) { - return methodName - + (caller != null ? " of object " + caller.getClass().getSimpleName() : ""); - } - - /** - * Prints stacktrace in log with specified tag. - * - * @param tag Tag to be shown in logs. - */ - - @SuppressLint("LogConditional") - public static void printStackTrace(@NonNull final String tag) { - final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); - if (Log.isLoggable(tag, Log.DEBUG)) { - Log.d(tag, TextUtils.join("\n", Arrays.copyOfRange(stackTrace, STACK_TRACE_CODE_DEPTH, stackTrace.length))); - } - } - - private Lc() { - } - -} diff --git a/logging/src/main/java/ru/touchin/roboswag/core/log/LcGroup.java b/logging/src/main/java/ru/touchin/roboswag/core/log/LcGroup.java deleted file mode 100644 index f1ea1b02..00000000 --- a/logging/src/main/java/ru/touchin/roboswag/core/log/LcGroup.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov) - * - * This file is part of RoboSwag library. - * - * 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 ru.touchin.roboswag.core.log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.text.SimpleDateFormat; -import java.util.Locale; - -import ru.touchin.roboswag.core.utils.ShouldNotHappenException; -import ru.touchin.roboswag.core.utils.ThreadLocalValue; - -/** - * Created by Gavriil Sitnikov on 14/05/2016. - * Group of log messages with specific tag prefix (name of group). - * It could be used in specific {@link LogProcessor} to filter messages by group. - */ -@SuppressWarnings({"checkstyle:methodname", "PMD.ShortMethodName"}) -//MethodNameCheck,ShortMethodName: log methods better be 1-symbol -public class LcGroup { - - /** - * Logging group to log UI metrics (like inflation or layout time etc.). - */ - public static final LcGroup UI_METRICS = new LcGroup("UI_METRICS"); - /** - * Logging group to log UI lifecycle (onCreate, onStart, onResume etc.). - */ - public static final LcGroup UI_LIFECYCLE = new LcGroup("UI_LIFECYCLE"); - - private static final ThreadLocalValue DATE_TIME_FORMATTER - = new ThreadLocalValue<>(() -> new SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault())); - - @NonNull - private final String name; - private boolean disabled; - - public LcGroup(@NonNull final String name) { - this.name = name; - } - - /** - * Disables logging of this group. - */ - public void disable() { - disabled = true; - } - - /** - * Enables logging of this group. - */ - public void enable() { - disabled = false; - } - - @NonNull - private String createLogTag() { - final StackTraceElement trace = Thread.currentThread().getStackTrace()[Lc.STACK_TRACE_CODE_DEPTH + 3]; - return trace.getFileName() + ':' + trace.getLineNumber(); - } - - @SuppressWarnings("PMD.AvoidCatchingThrowable") - //AvoidCatchingThrowable: it is needed to safety format message - @Nullable - private String createFormattedMessage(@Nullable final String message, @NonNull final Object... args) { - try { - if (args.length > 0 && message == null) { - throw new ShouldNotHappenException("Args are not empty but format message is null"); - } - return message != null ? (args.length > 0 ? String.format(message, args) : message) : null; - } catch (final Throwable formattingException) { - Lc.assertion(formattingException); - return null; - } - } - - @NonNull - private String createLogMessage(@Nullable final String formattedMessage) { - return DATE_TIME_FORMATTER.get().format(System.currentTimeMillis()) - + ' ' + Thread.currentThread().getName() - + ' ' + name - + (formattedMessage != null ? (' ' + formattedMessage) : ""); - } - - private void logMessage(@NonNull final LcLevel logLevel, @Nullable final String message, - @Nullable final Throwable throwable, @NonNull final Object... args) { - if (disabled || logLevel.lessThan(Lc.getLogProcessor().getMinLogLevel())) { - return; - } - - if (throwable == null && args.length > 0 && args[0] instanceof Throwable) { - Lc.w("Maybe you've misplaced exception with first format arg? format: %s; arg: %s", message, args[0]); - } - - final String formattedMessage = createFormattedMessage(message, args); - if (logLevel == LcLevel.ASSERT && Lc.isCrashOnAssertions()) { - throw createAssertion(formattedMessage, throwable); - } - - Lc.getLogProcessor().processLogMessage(this, logLevel, createLogTag(), createLogMessage(formattedMessage), throwable); - } - - @NonNull - private ShouldNotHappenException createAssertion(@Nullable final String message, @Nullable final Throwable exception) { - return exception != null - ? (message != null ? new ShouldNotHappenException(message, exception) - : (exception instanceof ShouldNotHappenException ? (ShouldNotHappenException) exception : new ShouldNotHappenException(exception))) - : (message != null ? new ShouldNotHappenException(message) : new ShouldNotHappenException()); - } - - /** - * Logs debug message. - * - * @param message Message or format of message to log; - * @param args Arguments of formatted message. - */ - public void d(@NonNull final String message, @NonNull final Object... args) { - logMessage(LcLevel.DEBUG, message, null, args); - } - - /** - * Logs debug message. - * - * @param throwable Exception to log; - * @param message Message or format of message to log; - * @param args Arguments of formatted message. - */ - public void d(@NonNull final Throwable throwable, @NonNull final String message, @NonNull final Object... args) { - logMessage(LcLevel.DEBUG, message, throwable, args); - } - - /** - * Logs info message. - * - * @param message Message or format of message to log; - * @param args Arguments of formatted message. - */ - public void i(@NonNull final String message, @NonNull final Object... args) { - logMessage(LcLevel.INFO, message, null, args); - } - - /** - * Logs info message. - * - * @param throwable Exception to log; - * @param message Message or format of message to log; - * @param args Arguments of formatted message. - */ - public void i(@NonNull final Throwable throwable, @NonNull final String message, @NonNull final Object... args) { - logMessage(LcLevel.INFO, message, throwable, args); - } - - /** - * Logs warning message. - * - * @param message Message or format of message to log; - * @param args Arguments of formatted message. - */ - public void w(@NonNull final String message, @NonNull final Object... args) { - logMessage(LcLevel.WARN, message, null, args); - } - - /** - * Logs warning message. - * - * @param throwable Exception to log; - * @param message Message or format of message to log; - * @param args Arguments of formatted message. - */ - public void w(@NonNull final Throwable throwable, @NonNull final String message, @NonNull final Object... args) { - logMessage(LcLevel.WARN, message, throwable, args); - } - - /** - * Logs error message. - * - * @param message Message or format of message to log; - * @param args Arguments of formatted message. - */ - public void e(@NonNull final String message, @NonNull final Object... args) { - logMessage(LcLevel.ERROR, message, null, args); - } - - /** - * Logs error message. - * - * @param throwable Exception to log; - * @param message Message or format of message to log; - * @param args Arguments of formatted message. - */ - public void e(@NonNull final Throwable throwable, @NonNull final String message, @NonNull final Object... args) { - logMessage(LcLevel.ERROR, message, throwable, args); - } - - /** - * Processes assertion. Normally it will throw {@link ShouldNotHappenException} and crash app. - * If it should crash or not is specified at {@link Lc#isCrashOnAssertions()}. - * In some cases crash on assertions should be switched off and assertion should be processed in {@link LogProcessor}. - * It is useful for example to not crash but log it as handled crash in Crashlitycs in production build. - * - * @param message Message that is describing assertion. - */ - public void assertion(@NonNull final String message) { - logMessage(LcLevel.ASSERT, "Assertion appears at %s with message: %s", null, Lc.getCodePoint(null, 2), message); - } - - /** - * Processes assertion. Normally it will throw {@link ShouldNotHappenException} and crash app. - * If it should crash or not is specified at {@link Lc#isCrashOnAssertions()}. - * In some cases crash on assertions should be switched off and assertion should be processed in {@link LogProcessor}. - * It is useful for example to not crash but log it as handled crash in Crashlitycs in production build. - * - * @param throwable Exception that is describing assertion. - */ - public void assertion(@NonNull final Throwable throwable) { - logMessage(LcLevel.ASSERT, "Assertion appears at %s", throwable, Lc.getCodePoint(null, 2)); - } - -} diff --git a/logging/src/main/java/ru/touchin/roboswag/core/log/LogProcessor.java b/logging/src/main/java/ru/touchin/roboswag/core/log/LogProcessor.java deleted file mode 100644 index 0c4c4a84..00000000 --- a/logging/src/main/java/ru/touchin/roboswag/core/log/LogProcessor.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov) - * - * This file is part of RoboSwag library. - * - * 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 ru.touchin.roboswag.core.log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -/** - * Created by Gavriil Sitnikov on 13/11/2015. - * Abstract object to intercept log messages coming from {@link LcGroup} and {@link Lc} log methods. - */ -public abstract class LogProcessor { - - @NonNull - private final LcLevel minLogLevel; - - public LogProcessor(@NonNull final LcLevel minLogLevel) { - this.minLogLevel = minLogLevel; - } - - /** - * Minimum logging level. - * Any messages with lower priority won't be passed into {@link #processLogMessage(LcGroup, LcLevel, String, String, Throwable)}. - * - * @return Minimum log level represented by {@link LcLevel} object. - */ - @NonNull - public LcLevel getMinLogLevel() { - return minLogLevel; - } - - /** - * Core method to process any incoming log messages from {@link LcGroup} and {@link Lc} with level higher or equals {@link #getMinLogLevel()}. - * - * @param group {@link LcGroup} where log message came from; - * @param level {@link LcLevel} level (priority) of message; - * @param tag String mark of message; - * @param message Message to log; - * @param throwable Exception to log. - */ - public abstract void processLogMessage(@NonNull final LcGroup group, @NonNull final LcLevel level, - @NonNull final String tag, @NonNull final String message, @Nullable final Throwable throwable); - -} From 65cd50805ab91dded81b0e43dc64b9dea9321dd7 Mon Sep 17 00:00:00 2001 From: Oleg Kuznetsov Date: Thu, 17 Oct 2019 15:25:31 +0300 Subject: [PATCH 4/8] added Kotlin classes --- .../roboswag/core/log/ConsoleLogProcessor.kt | 63 +++++ .../java/ru/touchin/roboswag/core/log/Lc.kt | 257 ++++++++++++++++++ .../ru/touchin/roboswag/core/log/LcGroup.kt | 230 ++++++++++++++++ .../core/log/{LcLevel.java => LcLevel.kt} | 38 +-- .../touchin/roboswag/core/log/LogProcessor.kt | 46 ++++ ...ption.java => ShouldNotHappenException.kt} | 29 +- ...eadLocalValue.java => ThreadLocalValue.kt} | 30 +- 7 files changed, 626 insertions(+), 67 deletions(-) create mode 100644 logging/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.kt create mode 100644 logging/src/main/java/ru/touchin/roboswag/core/log/Lc.kt create mode 100644 logging/src/main/java/ru/touchin/roboswag/core/log/LcGroup.kt rename logging/src/main/java/ru/touchin/roboswag/core/log/{LcLevel.java => LcLevel.kt} (57%) create mode 100644 logging/src/main/java/ru/touchin/roboswag/core/log/LogProcessor.kt rename logging/src/main/java/ru/touchin/roboswag/core/utils/{ShouldNotHappenException.java => ShouldNotHappenException.kt} (53%) rename logging/src/main/java/ru/touchin/roboswag/core/utils/{ThreadLocalValue.java => ThreadLocalValue.kt} (60%) diff --git a/logging/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.kt b/logging/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.kt new file mode 100644 index 00000000..a8d76238 --- /dev/null +++ b/logging/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.kt @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov) + * + * This file is part of RoboSwag library. + * + * 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 ru.touchin.roboswag.core.log + +import android.util.Log +import kotlin.math.min + +/** + * Simple [LogProcessor] implementation which is logging messages to console (logcat). + */ +class ConsoleLogProcessor(lclevel: LcLevel) : LogProcessor(lclevel) { + + companion object { + private const val MAX_LOG_LENGTH = 4000 + } + + private fun normalize(message: String): String = message + .replace("\r\n", "\n") + .replace("\u0000", "") + + @SuppressWarnings("WrongConstant", "LogConditional") + //WrongConstant, LogConditional: level.getPriority() is not wrong constant! + override fun processLogMessage( + group: LcGroup, + level: LcLevel, + tag: String, + message: String, + throwable: Throwable? + ) { + val messageToLog = normalize(message + if (throwable != null) '\n' + Log.getStackTraceString(throwable) else "") + val length = messageToLog.length + var i = 0 + while (i < length) { + var newline = messageToLog.indexOf('\n', i) + newline = if (newline != -1) newline else length + do { + val end = min(newline, i + MAX_LOG_LENGTH) + Log.println(level.priority, tag, messageToLog.substring(i, end)) + i = end + } while (i < newline) + i++ + } + + } + +} diff --git a/logging/src/main/java/ru/touchin/roboswag/core/log/Lc.kt b/logging/src/main/java/ru/touchin/roboswag/core/log/Lc.kt new file mode 100644 index 00000000..30dafb99 --- /dev/null +++ b/logging/src/main/java/ru/touchin/roboswag/core/log/Lc.kt @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2019 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov) + * + * This file is part of RoboSwag library. + * + * 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 ru.touchin.roboswag.core.log + +import android.annotation.SuppressLint +import android.os.Handler +import android.os.Looper +import android.util.Log +import ru.touchin.roboswag.core.utils.ShouldNotHappenException +import java.util.Arrays + +/** + * General logging utility of RoboSwag library. + * You can initialize [LogProcessor] to intercept log messages and make decision how to show them. + * Also you can specify assertions behavior to manually make application more stable in production but intercept illegal states in some + * third-party tool to fix them later but not crash in production. + */ +@SuppressWarnings("checkstyle:methodname", "PMD.ShortMethodName", "PMD.ShortClassName") +//MethodNameCheck,ShortMethodName: log methods better be 1-symbol +object Lc { + + private val GENERAL_LC_GROUP = LcGroup("GENERAL") + + val STACK_TRACE_CODE_DEPTH: Int + + /** + * Flag to crash application or pass it to [LogProcessor.processLogMessage] + * on specific [LcGroup.assertion] points of code. + * + * @return True if application should crash on assertion. + */ + var isCrashOnAssertions = true + private set + + /** + * Returns [LogProcessor] object to intercept incoming log messages (by default it returns [ConsoleLogProcessor]). + * + * @return Specific [LogProcessor]. + */ + var logProcessor: LogProcessor = ConsoleLogProcessor(LcLevel.ERROR) + private set + + init { + val stackTrace = Thread.currentThread().stackTrace + var stackDepth = 0 + while (stackDepth < stackTrace.size) { + if (stackTrace[stackDepth].className == Lc::class.java.name) { + break + } + stackDepth++ + } + STACK_TRACE_CODE_DEPTH = stackDepth + 1 + } + + /** + * Initialize general logging behavior. + * + * @param logProcessor [LogProcessor] to intercept all log messages; + * @param crashOnAssertions Flag to crash application + * or pass it to [LogProcessor.processLogMessage] + * on specific [LcGroup.assertion] points of code. + */ + fun initialize(logProcessor: LogProcessor, crashOnAssertions: Boolean) { + isCrashOnAssertions = crashOnAssertions + Lc.logProcessor = logProcessor + } + + /** + * Logs debug message via [.GENERAL_LC_GROUP]. + * + * @param message Message or format of message to log; + * @param args Arguments of formatted message. + */ + fun d(message: String, vararg args: Any) { + GENERAL_LC_GROUP.d(message, *args) + } + + /** + * Logs debug message via [.GENERAL_LC_GROUP]. + * + * @param throwable Exception to log; + * @param message Message or format of message to log; + * @param args Arguments of formatted message. + */ + fun d(throwable: Throwable, message: String, vararg args: Any) { + GENERAL_LC_GROUP.d(throwable, message, *args) + } + + /** + * Logs info message via [.GENERAL_LC_GROUP]. + * + * @param message Message or format of message to log; + * @param args Arguments of formatted message. + */ + fun i(message: String, vararg args: Any) { + GENERAL_LC_GROUP.i(message, *args) + } + + /** + * Logs info message via [.GENERAL_LC_GROUP]. + * + * @param throwable Exception to log; + * @param message Message or format of message to log; + * @param args Arguments of formatted message. + */ + fun i(throwable: Throwable, message: String, vararg args: Any) { + GENERAL_LC_GROUP.i(throwable, message, *args) + } + + /** + * Logs warning message via [.GENERAL_LC_GROUP]. + * + * @param message Message or format of message to log; + * @param args Arguments of formatted message. + */ + fun w(message: String, vararg args: Any) { + GENERAL_LC_GROUP.w(message, *args) + } + + /** + * Logs warning message via [.GENERAL_LC_GROUP]. + * + * @param throwable Exception to log; + * @param message Message or format of message to log; + * @param args Arguments of formatted message. + */ + fun w(throwable: Throwable, message: String, vararg args: Any) { + GENERAL_LC_GROUP.w(throwable, message, *args) + } + + /** + * Logs error message via [.GENERAL_LC_GROUP]. + * + * @param message Message or format of message to log; + * @param args Arguments of formatted message. + */ + fun e(message: String, vararg args: Any) { + GENERAL_LC_GROUP.e(message, *args) + } + + /** + * Logs error message via [.GENERAL_LC_GROUP]. + * + * @param throwable Exception to log; + * @param message Message or format of message to log; + * @param args Arguments of formatted message. + */ + fun e(throwable: Throwable, message: String, vararg args: Any) { + GENERAL_LC_GROUP.e(throwable, message, *args) + } + + /** + * Processes assertion. Normally it will throw [ShouldNotHappenException] and crash app. + * If it should crash or not is specified at [Lc.isCrashOnAssertions]. + * In some cases crash on assertions should be switched off and assertion should be processed in [LogProcessor]. + * It is useful for example to not crash but log it as handled crash in Crashlitycs in production build. + * + * @param message Message that is describing assertion. + */ + fun assertion(message: String) { + GENERAL_LC_GROUP.assertion(message) + } + + /** + * Processes assertion. Normally it will throw [ShouldNotHappenException] and crash app. + * If it should crash or not is specified at [Lc.isCrashOnAssertions]. + * In some cases crash on assertions should be switched off and assertion should be processed in [LogProcessor]. + * It is useful for example to not crash but log it as handled crash in Crashlitycs in production build. + * + * @param throwable Exception that is describing assertion. + */ + fun assertion(throwable: Throwable) { + GENERAL_LC_GROUP.assertion(throwable) + } + + /** + * Throws assertion on main thread (to avoid Rx exceptions e.g.) and cuts top causes by type of exception class. + * + * @param assertion Source throwable; + * @param exceptionsClassesToCut Classes which will be cut from top of causes stack of source throwable. + */ + @SafeVarargs + fun cutAssertion(assertion: Throwable, vararg exceptionsClassesToCut: Class) { + Handler(Looper.getMainLooper()).post { + val processedExceptions = arrayListOf() + var result: Throwable? = assertion + var exceptionAssignableFromIgnores: Boolean + do { + exceptionAssignableFromIgnores = false + processedExceptions.add(result!!) + for (exceptionClass in exceptionsClassesToCut) { + if (result!!.javaClass.isAssignableFrom(exceptionClass)) { + exceptionAssignableFromIgnores = true + result = result.cause + break + } + } + } while (exceptionAssignableFromIgnores && result != null && !processedExceptions.contains(result)) + Lc.assertion(result ?: assertion) + } + } + + /** + * Returns line of code from where this method called. + * + * @param caller Object who is calling for code point; + * @param stackShift caller Shift of stack (e.g. 2 means two elements deeper); + * @return String represents code point. + */ + fun getCodePoint(caller: Any?, stackShift: Int = 1): String { + val traceElement = Thread.currentThread().stackTrace[STACK_TRACE_CODE_DEPTH + stackShift] + return "${traceElement.methodName}(${traceElement.fileName}:${traceElement.lineNumber})" + .plus(caller?.let { " of object ${caller.javaClass.simpleName}(${Integer.toHexString(caller.hashCode())})" }.orEmpty()) + } + + /** + * Returns line of code from where this method called. + * + * @param caller Object who is calling for code point; + * @param methodName String represents lifecycle method in which it was called + * @return String represents code point. + */ + fun getCodePoint(caller: Any?, methodName: String): String = methodName + .plus(caller?.let { " of object ${caller.javaClass.simpleName}" }.orEmpty()) + + /** + * Prints stacktrace in log with specified tag. + * + * @param tag Tag to be shown in logs. + */ + + @SuppressLint("LogConditional") + fun printStackTrace(tag: String) { + val stackTrace = Thread.currentThread().stackTrace + if (Log.isLoggable(tag, Log.DEBUG)) { + Log.d(tag, Arrays.copyOfRange(stackTrace, STACK_TRACE_CODE_DEPTH, stackTrace.size).joinToString(separator = "\n")) + } + } + +} diff --git a/logging/src/main/java/ru/touchin/roboswag/core/log/LcGroup.kt b/logging/src/main/java/ru/touchin/roboswag/core/log/LcGroup.kt new file mode 100644 index 00000000..d02adf0e --- /dev/null +++ b/logging/src/main/java/ru/touchin/roboswag/core/log/LcGroup.kt @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2019 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov) + * + * This file is part of RoboSwag library. + * + * 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 ru.touchin.roboswag.core.log + +import java.text.SimpleDateFormat +import java.util.Locale +import ru.touchin.roboswag.core.utils.ShouldNotHappenException +import ru.touchin.roboswag.core.utils.ThreadLocalValue + +/** + * Group of log messages with specific tag prefix (name of group). + * It could be used in specific [LogProcessor] to filter messages by group. + */ +@SuppressWarnings("checkstyle:methodname", "PMD.ShortMethodName") +//MethodNameCheck,ShortMethodName: log methods better be 1-symbol +class LcGroup(private val name: String) { + + companion object { + + /** + * Logging group to log UI metrics (like inflation or layout time etc.). + */ + val UI_METRICS = LcGroup("UI_METRICS") + + /** + * Logging group to log UI lifecycle (onCreate, onStart, onResume etc.). + */ + val UI_LIFECYCLE = LcGroup("UI_LIFECYCLE") + + private val DATE_TIME_FORMATTER = ThreadLocalValue( + fabric = object : ThreadLocalValue.Fabric { + + override fun create(): SimpleDateFormat = SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault()) + } + ) + } + + private var disabled: Boolean = false + + /** + * Disables logging of this group. + */ + fun disable() { + disabled = true + } + + /** + * Enables logging of this group. + */ + fun enable() { + disabled = false + } + + private fun createLogTag(): String { + val trace = Thread.currentThread().stackTrace[Lc.STACK_TRACE_CODE_DEPTH + 3] + return "${trace.fileName}:${trace.lineNumber}" + } + + @SuppressWarnings("PMD.AvoidCatchingThrowable") + //AvoidCatchingThrowable: it is needed to safety format message + private fun createFormattedMessage(message: String?, vararg args: Any): String? = + try { + if (args.isNotEmpty() && message == null) { + throw ShouldNotHappenException("Args are not empty but format message is null") + } + message?.let { if (args.isNotEmpty()) String.format(message, *args) else message } + } catch (formattingException: Throwable) { + Lc.assertion(formattingException) + null + } + + private fun createLogMessage(formattedMessage: String?): String = DATE_TIME_FORMATTER.get()!!.format(System.currentTimeMillis()) + .plus(" ${Thread.currentThread().name}") + .plus(" $name") + .plus(formattedMessage?.let { " $formattedMessage" }.orEmpty()) + + private fun logMessage( + logLevel: LcLevel, + message: String, + throwable: Throwable?, + vararg args: Any + ) { + if (disabled || logLevel.lessThan(Lc.logProcessor.minLogLevel)) return + + if (throwable == null && args.isNotEmpty() && args[0] is Throwable) { + Lc.w("Maybe you've misplaced exception with first format arg? format: %s; arg: %s", message, args[0]) + } + + val formattedMessage = createFormattedMessage(message, *args) + if (logLevel == LcLevel.ASSERT && Lc.isCrashOnAssertions) { + throw createAssertion(formattedMessage, throwable) + } + + Lc.logProcessor.processLogMessage(this, logLevel, createLogTag(), createLogMessage(formattedMessage), throwable) + } + + private fun createAssertion(message: String?, exception: Throwable?): ShouldNotHappenException = when { + exception != null && message != null -> ShouldNotHappenException(message, exception) + exception != null && exception is ShouldNotHappenException -> exception + exception != null -> ShouldNotHappenException(exception) + message != null -> ShouldNotHappenException(message) + else -> ShouldNotHappenException() + } + + /** + * Logs debug message. + * + * @param message Message or format of message to log; + * @param args Arguments of formatted message. + */ + fun d(message: String, vararg args: Any) { + logMessage(LcLevel.DEBUG, message, null, *args) + } + + /** + * Logs debug message. + * + * @param throwable Exception to log; + * @param message Message or format of message to log; + * @param args Arguments of formatted message. + */ + fun d(throwable: Throwable, message: String, vararg args: Any) { + logMessage(LcLevel.DEBUG, message, throwable, *args) + } + + /** + * Logs info message. + * + * @param message Message or format of message to log; + * @param args Arguments of formatted message. + */ + fun i(message: String, vararg args: Any) { + logMessage(LcLevel.INFO, message, null, *args) + } + + /** + * Logs info message. + * + * @param throwable Exception to log; + * @param message Message or format of message to log; + * @param args Arguments of formatted message. + */ + fun i(throwable: Throwable, message: String, vararg args: Any) { + logMessage(LcLevel.INFO, message, throwable, *args) + } + + /** + * Logs warning message. + * + * @param message Message or format of message to log; + * @param args Arguments of formatted message. + */ + fun w(message: String, vararg args: Any) { + logMessage(LcLevel.WARN, message, null, *args) + } + + /** + * Logs warning message. + * + * @param throwable Exception to log; + * @param message Message or format of message to log; + * @param args Arguments of formatted message. + */ + fun w(throwable: Throwable, message: String, vararg args: Any) { + logMessage(LcLevel.WARN, message, throwable, *args) + } + + /** + * Logs error message. + * + * @param message Message or format of message to log; + * @param args Arguments of formatted message. + */ + fun e(message: String, vararg args: Any) { + logMessage(LcLevel.ERROR, message, null, *args) + } + + /** + * Logs error message. + * + * @param throwable Exception to log; + * @param message Message or format of message to log; + * @param args Arguments of formatted message. + */ + fun e(throwable: Throwable, message: String, vararg args: Any) { + logMessage(LcLevel.ERROR, message, throwable, *args) + } + + /** + * Processes assertion. Normally it will throw [ShouldNotHappenException] and crash app. + * If it should crash or not is specified at [Lc.isCrashOnAssertions]. + * In some cases crash on assertions should be switched off and assertion should be processed in [LogProcessor]. + * It is useful for example to not crash but log it as handled crash in Crashlitycs in production build. + * + * @param message Message that is describing assertion. + */ + fun assertion(message: String) { + logMessage(LcLevel.ASSERT, "Assertion appears at %s with message: %s", null, Lc.getCodePoint(null, 2), message) + } + + /** + * Processes assertion. Normally it will throw [ShouldNotHappenException] and crash app. + * If it should crash or not is specified at [Lc.isCrashOnAssertions]. + * In some cases crash on assertions should be switched off and assertion should be processed in [LogProcessor]. + * It is useful for example to not crash but log it as handled crash in Crashlitycs in production build. + * + * @param throwable Exception that is describing assertion. + */ + fun assertion(throwable: Throwable) { + logMessage(LcLevel.ASSERT, "Assertion appears at %s", throwable, Lc.getCodePoint(null, 2)) + } + +} diff --git a/logging/src/main/java/ru/touchin/roboswag/core/log/LcLevel.java b/logging/src/main/java/ru/touchin/roboswag/core/log/LcLevel.kt similarity index 57% rename from logging/src/main/java/ru/touchin/roboswag/core/log/LcLevel.java rename to logging/src/main/java/ru/touchin/roboswag/core/log/LcLevel.kt index fb72f33e..8d861111 100644 --- a/logging/src/main/java/ru/touchin/roboswag/core/log/LcLevel.java +++ b/logging/src/main/java/ru/touchin/roboswag/core/log/LcLevel.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov) + * Copyright (c) 2019 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov) * * This file is part of RoboSwag library. * @@ -17,16 +17,21 @@ * */ -package ru.touchin.roboswag.core.log; +package ru.touchin.roboswag.core.log -import androidx.annotation.NonNull; -import android.util.Log; +import android.util.Log /** - * Created by Gavriil Sitnikov on 14/05/2016. * Level of log message. */ -public enum LcLevel { +enum class LcLevel( + /** + * Standard [Log] integer value of level represents priority of message. + * + * @return Integer level. + */ + private val priority: Int +) { VERBOSE(Log.VERBOSE), DEBUG(Log.DEBUG), @@ -35,29 +40,12 @@ public enum LcLevel { ERROR(Log.ERROR), ASSERT(Log.ASSERT); - private final int priority; - - LcLevel(final int priority) { - this.priority = priority; - } - - /** - * Standard {@link Log} integer value of level represents priority of message. - * - * @return Integer level. - */ - public int getPriority() { - return priority; - } - /** * Compares priorities of LcLevels and returns if current is less than another. * - * @param logLevel {@link LcLevel} to compare priority with; + * @param logLevel [LcLevel] to compare priority with; * @return True if current level priority less than level passed as parameter. */ - public boolean lessThan(@NonNull final LcLevel logLevel) { - return this.priority < logLevel.priority; - } + fun lessThan(logLevel: LcLevel): Boolean = this.priority < logLevel.priority } diff --git a/logging/src/main/java/ru/touchin/roboswag/core/log/LogProcessor.kt b/logging/src/main/java/ru/touchin/roboswag/core/log/LogProcessor.kt new file mode 100644 index 00000000..0c64ba6a --- /dev/null +++ b/logging/src/main/java/ru/touchin/roboswag/core/log/LogProcessor.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov) + * + * This file is part of RoboSwag library. + * + * 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 ru.touchin.roboswag.core.log + +/** + * Abstract object to intercept log messages coming from [LcGroup] and [Lc] log methods. + */ +abstract class LogProcessor( + /** + * Minimum logging level. + * Any messages with lower priority won't be passed into [.processLogMessage]. + * + * @return Minimum log level represented by [LcLevel] object. + */ + val minLogLevel: LcLevel +) { + + /** + * Core method to process any incoming log messages from [LcGroup] and [Lc] with level higher or equals [.getMinLogLevel]. + * + * @param group [LcGroup] where log message came from; + * @param level [LcLevel] level (priority) of message; + * @param tag String mark of message; + * @param message Message to log; + * @param throwable Exception to log. + */ + abstract fun processLogMessage(group: LcGroup, level: LcLevel, tag: String, message: String, throwable: Throwable?) + +} diff --git a/logging/src/main/java/ru/touchin/roboswag/core/utils/ShouldNotHappenException.java b/logging/src/main/java/ru/touchin/roboswag/core/utils/ShouldNotHappenException.kt similarity index 53% rename from logging/src/main/java/ru/touchin/roboswag/core/utils/ShouldNotHappenException.java rename to logging/src/main/java/ru/touchin/roboswag/core/utils/ShouldNotHappenException.kt index 8bbed443..25ac62fd 100644 --- a/logging/src/main/java/ru/touchin/roboswag/core/utils/ShouldNotHappenException.java +++ b/logging/src/main/java/ru/touchin/roboswag/core/utils/ShouldNotHappenException.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov) + * Copyright (c) 2019 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov) * * This file is part of RoboSwag library. * @@ -17,33 +17,24 @@ * */ -package ru.touchin.roboswag.core.utils; - -import androidx.annotation.NonNull; +package ru.touchin.roboswag.core.utils /** - * Created by Gavriil Sitnikov on 13/11/2015. * Exception that should be threw when some unexpected code reached. * E.g. if some value null but it is not legal or in default case in switch if all specific cases should be processed. */ -public class ShouldNotHappenException extends RuntimeException { - - private static final long serialVersionUID = 0; +class ShouldNotHappenException : RuntimeException { - public ShouldNotHappenException() { - super(); + companion object { + private const val serialVersionUID: Long = 0 } - public ShouldNotHappenException(@NonNull final String detailMessage) { - super(detailMessage); - } + constructor() : super() - public ShouldNotHappenException(@NonNull final String detailMessage, @NonNull final Throwable throwable) { - super(detailMessage, throwable); - } + constructor(detailMessage: String) : super(detailMessage) - public ShouldNotHappenException(@NonNull final Throwable throwable) { - super(throwable); - } + constructor(detailMessage: String, throwable: Throwable) : super(detailMessage, throwable) + + constructor(throwable: Throwable) : super(throwable) } diff --git a/logging/src/main/java/ru/touchin/roboswag/core/utils/ThreadLocalValue.java b/logging/src/main/java/ru/touchin/roboswag/core/utils/ThreadLocalValue.kt similarity index 60% rename from logging/src/main/java/ru/touchin/roboswag/core/utils/ThreadLocalValue.java rename to logging/src/main/java/ru/touchin/roboswag/core/utils/ThreadLocalValue.kt index 8d3f1b1c..e32949a4 100644 --- a/logging/src/main/java/ru/touchin/roboswag/core/utils/ThreadLocalValue.java +++ b/logging/src/main/java/ru/touchin/roboswag/core/utils/ThreadLocalValue.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov) + * Copyright (c) 2019 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov) * * This file is part of RoboSwag library. * @@ -17,44 +17,28 @@ * */ -package ru.touchin.roboswag.core.utils; - -import androidx.annotation.NonNull; +package ru.touchin.roboswag.core.utils /** - * Created by Gavriil Sitnikov on 13/11/2015. * Thread local value with specified creator of value per thread. */ -public class ThreadLocalValue extends ThreadLocal { - - @NonNull - private final Fabric fabric; +class ThreadLocalValue(private val fabric: Fabric) : ThreadLocal() { - public ThreadLocalValue(@NonNull final Fabric fabric) { - super(); - this.fabric = fabric; - } - - @NonNull - @Override - protected T initialValue() { - return fabric.create(); - } + override fun initialValue(): T = fabric.create() /** * Fabric of thread-local objects. * * @param Type of objects. - */ - public interface Fabric { + */ + interface Fabric { /** * Creates object. * * @return new instance of object. */ - @NonNull - T create(); + fun create(): T } From 40ab5876cfede8ac3983ba1802278c098eac03ac Mon Sep 17 00:00:00 2001 From: Oleg Kuznetsov Date: Thu, 17 Oct 2019 15:34:08 +0300 Subject: [PATCH 5/8] small fixes --- logging/src/main/java/ru/touchin/roboswag/core/log/Lc.kt | 1 - logging/src/main/java/ru/touchin/roboswag/core/log/LcLevel.kt | 2 +- .../java/ru/touchin/roboswag/core/utils/ThreadLocalValue.kt | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/logging/src/main/java/ru/touchin/roboswag/core/log/Lc.kt b/logging/src/main/java/ru/touchin/roboswag/core/log/Lc.kt index 30dafb99..31a9e34b 100644 --- a/logging/src/main/java/ru/touchin/roboswag/core/log/Lc.kt +++ b/logging/src/main/java/ru/touchin/roboswag/core/log/Lc.kt @@ -245,7 +245,6 @@ object Lc { * * @param tag Tag to be shown in logs. */ - @SuppressLint("LogConditional") fun printStackTrace(tag: String) { val stackTrace = Thread.currentThread().stackTrace diff --git a/logging/src/main/java/ru/touchin/roboswag/core/log/LcLevel.kt b/logging/src/main/java/ru/touchin/roboswag/core/log/LcLevel.kt index 8d861111..7e36b4f6 100644 --- a/logging/src/main/java/ru/touchin/roboswag/core/log/LcLevel.kt +++ b/logging/src/main/java/ru/touchin/roboswag/core/log/LcLevel.kt @@ -30,7 +30,7 @@ enum class LcLevel( * * @return Integer level. */ - private val priority: Int + val priority: Int ) { VERBOSE(Log.VERBOSE), diff --git a/logging/src/main/java/ru/touchin/roboswag/core/utils/ThreadLocalValue.kt b/logging/src/main/java/ru/touchin/roboswag/core/utils/ThreadLocalValue.kt index e32949a4..e2aa6ef1 100644 --- a/logging/src/main/java/ru/touchin/roboswag/core/utils/ThreadLocalValue.kt +++ b/logging/src/main/java/ru/touchin/roboswag/core/utils/ThreadLocalValue.kt @@ -30,7 +30,7 @@ class ThreadLocalValue(private val fabric: Fabric) : ThreadLocal() { * Fabric of thread-local objects. * * @param Type of objects. - */ + */ interface Fabric { /** From a340f420c0064ba2e259ac894ccbe720c9c2e7f4 Mon Sep 17 00:00:00 2001 From: Oleg Kuznetsov Date: Thu, 17 Oct 2019 19:05:22 +0300 Subject: [PATCH 6/8] code review --- .../roboswag/core/log/ConsoleLogProcessor.kt | 31 +++++++------------ .../ru/touchin/roboswag/core/log/LcGroup.kt | 18 ++++++----- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/logging/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.kt b/logging/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.kt index a8d76238..4e6acd08 100644 --- a/logging/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.kt +++ b/logging/src/main/java/ru/touchin/roboswag/core/log/ConsoleLogProcessor.kt @@ -20,7 +20,6 @@ package ru.touchin.roboswag.core.log import android.util.Log -import kotlin.math.min /** * Simple [LogProcessor] implementation which is logging messages to console (logcat). @@ -31,10 +30,6 @@ class ConsoleLogProcessor(lclevel: LcLevel) : LogProcessor(lclevel) { private const val MAX_LOG_LENGTH = 4000 } - private fun normalize(message: String): String = message - .replace("\r\n", "\n") - .replace("\u0000", "") - @SuppressWarnings("WrongConstant", "LogConditional") //WrongConstant, LogConditional: level.getPriority() is not wrong constant! override fun processLogMessage( @@ -44,20 +39,18 @@ class ConsoleLogProcessor(lclevel: LcLevel) : LogProcessor(lclevel) { message: String, throwable: Throwable? ) { - val messageToLog = normalize(message + if (throwable != null) '\n' + Log.getStackTraceString(throwable) else "") - val length = messageToLog.length - var i = 0 - while (i < length) { - var newline = messageToLog.indexOf('\n', i) - newline = if (newline != -1) newline else length - do { - val end = min(newline, i + MAX_LOG_LENGTH) - Log.println(level.priority, tag, messageToLog.substring(i, end)) - i = end - } while (i < newline) - i++ - } - + message.plus(throwable?.let { "\n${Log.getStackTraceString(throwable)}" }.orEmpty()) + .normalize() + .split('\n') + .map { msg -> msg.chunked(MAX_LOG_LENGTH) } + .flatten() + .forEach { msg -> + Log.println(level.priority, tag, msg) + } } + private fun String.normalize(): String = this + .replace("\r\n", "\n") + .replace("\u0000", "") + } diff --git a/logging/src/main/java/ru/touchin/roboswag/core/log/LcGroup.kt b/logging/src/main/java/ru/touchin/roboswag/core/log/LcGroup.kt index d02adf0e..bba4573d 100644 --- a/logging/src/main/java/ru/touchin/roboswag/core/log/LcGroup.kt +++ b/logging/src/main/java/ru/touchin/roboswag/core/log/LcGroup.kt @@ -19,10 +19,10 @@ package ru.touchin.roboswag.core.log -import java.text.SimpleDateFormat -import java.util.Locale import ru.touchin.roboswag.core.utils.ShouldNotHappenException import ru.touchin.roboswag.core.utils.ThreadLocalValue +import java.text.SimpleDateFormat +import java.util.Locale /** * Group of log messages with specific tag prefix (name of group). @@ -44,12 +44,11 @@ class LcGroup(private val name: String) { */ val UI_LIFECYCLE = LcGroup("UI_LIFECYCLE") - private val DATE_TIME_FORMATTER = ThreadLocalValue( - fabric = object : ThreadLocalValue.Fabric { + private val DATE_TIME_FORMATTER = ThreadLocalValue(object : ThreadLocalValue.Fabric { - override fun create(): SimpleDateFormat = SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault()) - } - ) + override fun create(): SimpleDateFormat = SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault()) + + }) } private var disabled: Boolean = false @@ -86,7 +85,10 @@ class LcGroup(private val name: String) { null } - private fun createLogMessage(formattedMessage: String?): String = DATE_TIME_FORMATTER.get()!!.format(System.currentTimeMillis()) + private fun createLogMessage(formattedMessage: String?): String = DATE_TIME_FORMATTER.get() + .let { formatter -> + formatter?.format(System.currentTimeMillis()) ?: throw ShouldNotHappenException("Formatter is null") + } .plus(" ${Thread.currentThread().name}") .plus(" $name") .plus(formattedMessage?.let { " $formattedMessage" }.orEmpty()) From 341df57571ff23db29309f4063b936dd6eb36772 Mon Sep 17 00:00:00 2001 From: Oleg Kuznetsov Date: Thu, 17 Oct 2019 19:06:34 +0300 Subject: [PATCH 7/8] code review --- logging/src/main/java/ru/touchin/roboswag/core/log/LcGroup.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/logging/src/main/java/ru/touchin/roboswag/core/log/LcGroup.kt b/logging/src/main/java/ru/touchin/roboswag/core/log/LcGroup.kt index bba4573d..699e1a65 100644 --- a/logging/src/main/java/ru/touchin/roboswag/core/log/LcGroup.kt +++ b/logging/src/main/java/ru/touchin/roboswag/core/log/LcGroup.kt @@ -28,7 +28,6 @@ import java.util.Locale * Group of log messages with specific tag prefix (name of group). * It could be used in specific [LogProcessor] to filter messages by group. */ -@SuppressWarnings("checkstyle:methodname", "PMD.ShortMethodName") //MethodNameCheck,ShortMethodName: log methods better be 1-symbol class LcGroup(private val name: String) { From 22b073c0904ecdb7c915cc4adefe1722d1b62fb9 Mon Sep 17 00:00:00 2001 From: Oleg Kuznetsov Date: Thu, 17 Oct 2019 19:07:54 +0300 Subject: [PATCH 8/8] removed SuppressWarnings --- logging/src/main/java/ru/touchin/roboswag/core/log/Lc.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/logging/src/main/java/ru/touchin/roboswag/core/log/Lc.kt b/logging/src/main/java/ru/touchin/roboswag/core/log/Lc.kt index 31a9e34b..e2da9193 100644 --- a/logging/src/main/java/ru/touchin/roboswag/core/log/Lc.kt +++ b/logging/src/main/java/ru/touchin/roboswag/core/log/Lc.kt @@ -32,7 +32,6 @@ import java.util.Arrays * Also you can specify assertions behavior to manually make application more stable in production but intercept illegal states in some * third-party tool to fix them later but not crash in production. */ -@SuppressWarnings("checkstyle:methodname", "PMD.ShortMethodName", "PMD.ShortClassName") //MethodNameCheck,ShortMethodName: log methods better be 1-symbol object Lc {