From 1e0dc747e054a05527495d27f14f9f3ac896931a Mon Sep 17 00:00:00 2001 From: Akash Yadav Date: Mon, 9 Oct 2023 18:24:00 +0530 Subject: [PATCH] fix: make LogLine recyclable --- .idea/androidTestResultsUserPreferences.xml | 13 ++ .../itsaky/androidide/app/IDEApplication.kt | 3 + .../fragments/output/LogViewFragment.kt | 4 +- .../com/itsaky/androidide/models/LogLine.java | 166 ------------------ .../itsaky/androidide/utils/JvmLogger.java | 15 +- .../itsaky/androidide/utils/LogTagUtils.java | 40 +++++ .../com/itsaky/androidide/models/LogLine.java | 142 +++++++++++++++ .../androidide/utils/RecyclableObjectPool.kt | 3 +- .../itsaky/androidide/tooling/impl/Main.java | 5 +- subprojects/tooling-api/build.gradle.kts | 1 + 10 files changed, 217 insertions(+), 175 deletions(-) delete mode 100644 logger/src/main/java/com/itsaky/androidide/models/LogLine.java create mode 100644 logger/src/main/java/com/itsaky/androidide/utils/LogTagUtils.java create mode 100644 shared/src/main/java/com/itsaky/androidide/models/LogLine.java diff --git a/.idea/androidTestResultsUserPreferences.xml b/.idea/androidTestResultsUserPreferences.xml index f77d50377..cf12fd35d 100644 --- a/.idea/androidTestResultsUserPreferences.xml +++ b/.idea/androidTestResultsUserPreferences.xml @@ -83,6 +83,19 @@ + + + + + + + diff --git a/app/src/main/java/com/itsaky/androidide/app/IDEApplication.kt b/app/src/main/java/com/itsaky/androidide/app/IDEApplication.kt index a46b8e426..d72628516 100755 --- a/app/src/main/java/com/itsaky/androidide/app/IDEApplication.kt +++ b/app/src/main/java/com/itsaky/androidide/app/IDEApplication.kt @@ -54,6 +54,7 @@ import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE import com.itsaky.androidide.tasks.executeAsync import com.itsaky.androidide.treesitter.TreeSitter import com.itsaky.androidide.utils.ILogger +import com.itsaky.androidide.utils.RecyclableObjectPool import com.itsaky.androidide.utils.VMUtils import com.itsaky.androidide.utils.flashError import io.github.rosemoe.sora.widget.schemes.EditorColorScheme @@ -74,6 +75,8 @@ class IDEApplication : BaseApplication() { if (!VMUtils.isJvm()) { TreeSitter.loadLibrary() } + + RecyclableObjectPool.DEBUG = BuildConfig.DEBUG } override fun onCreate() { diff --git a/app/src/main/java/com/itsaky/androidide/fragments/output/LogViewFragment.kt b/app/src/main/java/com/itsaky/androidide/fragments/output/LogViewFragment.kt index 52f9a06ea..9fc03c34d 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/output/LogViewFragment.kt +++ b/app/src/main/java/com/itsaky/androidide/fragments/output/LogViewFragment.kt @@ -130,6 +130,8 @@ abstract class LogViewFragment : line.toString() } + line.recycle() + if (!lineString.endsWith("\n")) { lineString += "\n" } @@ -194,7 +196,7 @@ abstract class LogViewFragment : abstract fun isSimpleFormattingEnabled(): Boolean protected open fun logLine(priority: Priority, tag: String, message: String) { - val line = LogLine(priority, tag, message) + val line = LogLine.obtain(priority, tag, message) appendLog(line) } diff --git a/logger/src/main/java/com/itsaky/androidide/models/LogLine.java b/logger/src/main/java/com/itsaky/androidide/models/LogLine.java deleted file mode 100644 index 077ed000d..000000000 --- a/logger/src/main/java/com/itsaky/androidide/models/LogLine.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - */ -package com.itsaky.androidide.models; - -import static com.itsaky.androidide.utils.LogUtils.preProcessLogTag; - -import com.itsaky.androidide.utils.ILogger; -import java.util.Objects; - -public class LogLine { - - public String unformatted; - public String date; - public String time; - public String pid; - public String tid; - public String tag; - public String message; - public ILogger.Priority priority; - public boolean formatted; - - // For JSONRpc - @SuppressWarnings("unused") - protected LogLine() { - } - - public LogLine(ILogger.Priority priority, String tag, String message) { - this(priority, "", "", "", "", tag, message); - } - - public LogLine( - ILogger.Priority priority, - String date, - String time, - String pid, - String tid, - String tag, - String message) { - this(priority, date, time, pid, tid, tag, message, true); - } - - public LogLine( - ILogger.Priority priority, - String date, - String time, - String pid, - String tid, - String tag, - String message, - boolean formatted) { - this.date = date; - this.time = time; - this.pid = pid; - this.tid = tid; - this.tag = preProcessLogTag(tag); - this.message = message; - this.priority = priority; - this.formatted = formatted; - } - - public LogLine(String unformatted) { - this.unformatted = unformatted; - this.formatted = false; - } - - public static LogLine forLogString(final String log) { - if (log == null) { - return null; - } - try { - final var split = log.split("\\s", 7); - return new LogLine( - parsePriority(split[4].charAt(0)), - split[0], // date - split[1], // time - split[2], // process id - split[3], // thread id - split[5], // tag - split[6] // message - ); - } catch (Throwable th) { - return new LogLine(log); - } - } - - private static ILogger.Priority parsePriority(char s) { - return ILogger.priority(Character.toUpperCase(s)); - } - - public String toSimpleString() { - return this.formatted - ? String.format( - "%-25s %-2s %s", trimIfNeeded(tag, 25), ILogger.priorityChar(priority), message) - : this.unformatted; - } - - private String trimIfNeeded(String tag, int maxLength) { - final var sb = new StringBuilder(tag); - final var length = tag.length(); - if (length > maxLength) { - final var start = length - maxLength; - sb.delete(0, start); - - // When the tag is long enough, prefix the tag with '..' - sb.setCharAt(0, '.'); - sb.setCharAt(1, '.'); - } - - return sb.toString(); - } - - public String formattedTagAndMessage() { - return this.formatted - ? String.format("%-25s %-2s", trimIfNeeded(tag, 25), message) - : this.unformatted; - } - - @Override - public int hashCode() { - return Objects.hash(unformatted, date, time, pid, tid, tag, message, priority, formatted); - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final LogLine logLine = (LogLine) o; - return formatted == logLine.formatted - && Objects.equals(unformatted, logLine.unformatted) - && Objects.equals(date, logLine.date) - && Objects.equals(time, logLine.time) - && Objects.equals(pid, logLine.pid) - && Objects.equals(tid, logLine.tid) - && Objects.equals(tag, logLine.tag) - && Objects.equals(message, logLine.message) - && priority == logLine.priority; - } - - @Override - public String toString() { - return this.formatted - ? String.format( - "%s %s %s %s %-2s %-25s %s", - date, time, pid, tid, ILogger.priorityChar(priority), trimIfNeeded(tag, 25), message) - : this.unformatted; - } -} diff --git a/logger/src/main/java/com/itsaky/androidide/utils/JvmLogger.java b/logger/src/main/java/com/itsaky/androidide/utils/JvmLogger.java index 5e6bb9408..c2abad101 100644 --- a/logger/src/main/java/com/itsaky/androidide/utils/JvmLogger.java +++ b/logger/src/main/java/com/itsaky/androidide/utils/JvmLogger.java @@ -17,7 +17,7 @@ package com.itsaky.androidide.utils; -import com.itsaky.androidide.models.LogLine; +import static com.itsaky.androidide.utils.LogTagUtils.trimTagIfNeeded; /** * {@link ILogger} implementation for the JVM. @@ -34,15 +34,20 @@ public class JvmLogger extends ILogger { @Override protected void doLog(Priority priority, String message) { - final var log = new LogLine(priority, TAG, message); if (interceptor != null) { - interceptor.onLog(log); + interceptor.onLog(priority, TAG, message); } else { - System.err.println(log.toSimpleString()); + System.err.printf( + "%-25s %-2s %s%n", + trimTagIfNeeded(TAG, 25), + ILogger.priorityChar(priority), + message + ); } } public interface LogInterceptor { - void onLog(LogLine line); + + void onLog(Priority priority, String tag, String message); } } diff --git a/logger/src/main/java/com/itsaky/androidide/utils/LogTagUtils.java b/logger/src/main/java/com/itsaky/androidide/utils/LogTagUtils.java new file mode 100644 index 000000000..e330ee89e --- /dev/null +++ b/logger/src/main/java/com/itsaky/androidide/utils/LogTagUtils.java @@ -0,0 +1,40 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.utils; + +/** + * @author Akash Yadav + */ +@SuppressWarnings("unused") +public class LogTagUtils { + + public static String trimTagIfNeeded(String tag, int maxLength) { + final var sb = new StringBuilder(tag); + final var length = tag.length(); + if (length > maxLength) { + final var start = length - maxLength; + sb.delete(0, start); + + // When the tag is long enough, prefix the tag with '..' + sb.setCharAt(0, '.'); + sb.setCharAt(1, '.'); + } + + return sb.toString(); + } +} diff --git a/shared/src/main/java/com/itsaky/androidide/models/LogLine.java b/shared/src/main/java/com/itsaky/androidide/models/LogLine.java new file mode 100644 index 000000000..640b51f1c --- /dev/null +++ b/shared/src/main/java/com/itsaky/androidide/models/LogLine.java @@ -0,0 +1,142 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ +package com.itsaky.androidide.models; + +import static com.itsaky.androidide.utils.LogTagUtils.trimTagIfNeeded; + +import com.itsaky.androidide.utils.ILogger; +import com.itsaky.androidide.utils.ILogger.Priority; +import com.itsaky.androidide.utils.RecyclableObjectPool; +import com.itsaky.androidide.utils.RecyclableObjectPool.Recyclable; +import java.util.Objects; + +public class LogLine implements Recyclable { + + private static final RecyclableObjectPool logLinePool = new RecyclableObjectPool<>( + RecyclableObjectPool.CAPACITY_DEFAULT, LogLine.class, LogLine::new); + + public String unformatted; + public String date; + public String time; + public String pid; + public String tid; + public String tag; + public String message; + public ILogger.Priority priority; + public boolean formatted; + + public static LogLine obtain(Priority priority, String tag, String message) { + return obtain(priority, tag, message, true); + } + + public static LogLine obtain(Priority priority, String tag, String message, boolean formatted) { + final var logLine = logLinePool.obtain(); + logLine.priority = priority; + logLine.tag = tag; + logLine.message = message; + logLine.formatted = formatted; + return logLine; + } + + public static LogLine forLogString(final String log) { + if (log == null) { + return null; + } + final var logLine = logLinePool.obtain(); + try { + final var split = log.split("\\s", 7); + logLine.priority = ILogger.priority(Character.toUpperCase(split[4].charAt(0))); + logLine.date = split[0]; + logLine.time = split[1]; // time + logLine.pid = split[2]; // process id + logLine.tid = split[3]; // thread id + logLine.tag = split[5]; // tag + logLine.message = split[6]; // message + logLine.formatted = true; + } catch (Throwable th) { // do not log the exception with ILogger + logLine.unformatted = log; + logLine.formatted = false; + } + + return logLine; + } + + // For JSONRpc and Recyclable + protected LogLine() { + resetToDefault(); + } + + public String toSimpleString() { + return this.formatted + ? String.format( + "%-25s %-2s %s", trimTagIfNeeded(tag, 25), ILogger.priorityChar(priority), message) + : this.unformatted; + } + + + public String formattedTagAndMessage() { + return this.formatted + ? String.format("%-25s %-2s", trimTagIfNeeded(tag, 25), message) + : this.unformatted; + } + + protected void resetToDefault() { + unformatted = date = time = pid = tid = tag = message = null; + priority = Priority.DEBUG; + formatted = false; + } + + @Override + public void recycle() { + resetToDefault(); + logLinePool.recycle(this); + } + + @Override + public int hashCode() { + return Objects.hash(unformatted, date, time, pid, tid, tag, message, priority, formatted); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final LogLine logLine = (LogLine) o; + return formatted == logLine.formatted + && Objects.equals(unformatted, logLine.unformatted) + && Objects.equals(date, logLine.date) + && Objects.equals(time, logLine.time) + && Objects.equals(pid, logLine.pid) + && Objects.equals(tid, logLine.tid) + && Objects.equals(tag, logLine.tag) + && Objects.equals(message, logLine.message) + && priority == logLine.priority; + } + + @Override + public String toString() { + return this.formatted + ? String.format( + "%s %s %s %s %-2s %-25s %s", + date, time, pid, tid, ILogger.priorityChar(priority), trimTagIfNeeded(tag, 25), message) + : this.unformatted; + } +} diff --git a/shared/src/main/java/com/itsaky/androidide/utils/RecyclableObjectPool.kt b/shared/src/main/java/com/itsaky/androidide/utils/RecyclableObjectPool.kt index 34cda4f0f..78fdfb371 100644 --- a/shared/src/main/java/com/itsaky/androidide/utils/RecyclableObjectPool.kt +++ b/shared/src/main/java/com/itsaky/androidide/utils/RecyclableObjectPool.kt @@ -128,7 +128,8 @@ class RecyclableObjectPool @JvmOv val access = IntPair.getFirst(acc) val cacheHit = IntPair.getSecond(acc) - log.debug("${javaClass.simpleName}: $objName") + val simpleName = objName.let { if (it.contains('.')) it.substringAfterLast('.') else it } + log.debug("${javaClass.simpleName}: $simpleName($objName)") log.debug(" Recycle count : $rec") log.debug(" Access count : $access") log.debug(" Cache hit count : $cacheHit") diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/Main.java b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/Main.java index 04aa18c40..404cc351f 100644 --- a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/Main.java +++ b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/Main.java @@ -25,6 +25,7 @@ import com.itsaky.androidide.tooling.api.util.ToolingApiLauncher; import com.itsaky.androidide.tooling.impl.internal.ProjectImpl; import com.itsaky.androidide.tooling.impl.progress.ForwardingProgressListener; import com.itsaky.androidide.utils.ILogger; +import com.itsaky.androidide.utils.ILogger.Priority; import com.itsaky.androidide.utils.JvmLogger; import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; @@ -123,9 +124,9 @@ public class Main { } } - private static void onLog(LogLine line) { + private static void onLog(Priority priority, String tag, String message) { if (client != null) { - client.logMessage(line); + client.logMessage(LogLine.obtain(priority, tag, message)); } } diff --git a/subprojects/tooling-api/build.gradle.kts b/subprojects/tooling-api/build.gradle.kts index 9dd5268ca..dd250cdcc 100644 --- a/subprojects/tooling-api/build.gradle.kts +++ b/subprojects/tooling-api/build.gradle.kts @@ -24,6 +24,7 @@ plugins { dependencies { api(projects.buildInfo) api(projects.logger) + api(projects.shared) api(projects.subprojects.toolingApiModel) api(projects.subprojects.toolingApiEvents)