mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
feat: add option to dump AndroidIDE logs to file
This commit is contained in:
@@ -28,6 +28,7 @@ import com.itsaky.androidide.databinding.ActivityPreferencesBinding;
|
||||
import com.itsaky.androidide.fragments.IDEPreferencesFragment;
|
||||
import com.itsaky.androidide.preferences.AboutPreferences;
|
||||
import com.itsaky.androidide.preferences.ConfigurationPreferences;
|
||||
import com.itsaky.androidide.preferences.DeveloperOptionsPreferences;
|
||||
import com.itsaky.androidide.preferences.IDEPreferences;
|
||||
import com.itsaky.androidide.preferences.PrivacyPreferences;
|
||||
import java.util.ArrayList;
|
||||
@@ -59,6 +60,7 @@ public class PreferencesActivity extends LimitlessIDEActivity {
|
||||
prefs.getChildren().clear();
|
||||
prefs.addPreference(new ConfigurationPreferences());
|
||||
prefs.addPreference(new PrivacyPreferences());
|
||||
prefs.addPreference(new DeveloperOptionsPreferences());
|
||||
prefs.addPreference(new AboutPreferences());
|
||||
|
||||
final var args = new Bundle();
|
||||
|
||||
@@ -215,12 +215,6 @@ abstract class BaseEditorActivity :
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
StrictMode.setVmPolicy(
|
||||
StrictMode.VmPolicy.Builder(StrictMode.getVmPolicy()).detectLeakedClosableObjects().build()
|
||||
)
|
||||
}
|
||||
|
||||
registerLanguageServers()
|
||||
|
||||
if (savedInstanceState != null && savedInstanceState.containsKey(KEY_PROJECT_PATH)) {
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.itsaky.androidide.activities.editor
|
||||
|
||||
import android.os.Process
|
||||
import com.itsaky.androidide.utils.Environment
|
||||
import com.itsaky.androidide.utils.ILogger
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
|
||||
/**
|
||||
* Reads the logs from AndroidIDE and saves it to a file in the projects directory.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class IDELogcatReader : Thread("IDELogcatReader") {
|
||||
|
||||
private val log = ILogger.newInstance("IDELogcatReader")
|
||||
|
||||
override fun run() {
|
||||
val date = Date()
|
||||
val dateFormat = SimpleDateFormat("yyyy-MM-dd_HH:mm:ss.SSS")
|
||||
val outputFile = File(Environment.ANDROIDIDE_HOME,
|
||||
"logs/AndroidIDE-LOG-${dateFormat.format(date)}.txt")
|
||||
log.debug("Creating output file: $outputFile")
|
||||
outputFile.parentFile!!.mkdirs()
|
||||
outputFile.createNewFile()
|
||||
outputFile.outputStream().buffered().use {
|
||||
try {
|
||||
val process = ProcessBuilder("logcat", "--pid=${Process.myPid()}", "-v",
|
||||
"threadtime").let { builder ->
|
||||
builder.redirectErrorStream(true)
|
||||
builder.start()
|
||||
}
|
||||
|
||||
process.inputStream.bufferedReader().forEachLine { line ->
|
||||
it.write("$line\n".toByteArray())
|
||||
it.flush()
|
||||
}
|
||||
|
||||
log.info("Process ended with exit code :", process.waitFor())
|
||||
} catch (err: Throwable) {
|
||||
log.error("Failed to read logs", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ package com.itsaky.androidide.app
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.StrictMode
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.work.Constraints
|
||||
@@ -32,6 +33,7 @@ import com.blankj.utilcode.util.ThrowableUtils.getFullStackTrace
|
||||
import com.google.android.material.color.DynamicColors
|
||||
import com.itsaky.androidide.BuildConfig
|
||||
import com.itsaky.androidide.activities.CrashHandlerActivity
|
||||
import com.itsaky.androidide.activities.editor.IDELogcatReader
|
||||
import com.itsaky.androidide.buildinfo.BuildInfo
|
||||
import com.itsaky.androidide.editor.schemes.IDEColorSchemeProvider
|
||||
import com.itsaky.androidide.eventbus.events.preferences.PreferenceChangeEvent
|
||||
@@ -40,6 +42,8 @@ import com.itsaky.androidide.events.EditorEventsIndex
|
||||
import com.itsaky.androidide.events.LspApiEventsIndex
|
||||
import com.itsaky.androidide.events.LspJavaEventsIndex
|
||||
import com.itsaky.androidide.events.ProjectsApiEventsIndex
|
||||
import com.itsaky.androidide.preferences.KEY_DEVOPTS_DEBUGGING_DUMPLOGS
|
||||
import com.itsaky.androidide.preferences.dumpLogs
|
||||
import com.itsaky.androidide.preferences.internal.STAT_OPT_IN
|
||||
import com.itsaky.androidide.preferences.internal.enableMaterialYou
|
||||
import com.itsaky.androidide.preferences.internal.statOptIn
|
||||
@@ -63,6 +67,7 @@ import kotlin.system.exitProcess
|
||||
class IDEApplication : BaseApplication() {
|
||||
|
||||
private var uncaughtExceptionHandler: UncaughtExceptionHandler? = null
|
||||
private var ideLogcatReader: IDELogcatReader? = null
|
||||
private val log = ILogger.newInstance("IDEApplication")
|
||||
|
||||
init {
|
||||
@@ -78,6 +83,16 @@ class IDEApplication : BaseApplication() {
|
||||
Thread.setDefaultUncaughtExceptionHandler { thread, th -> handleCrash(thread, th) }
|
||||
super.onCreate()
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
StrictMode.setVmPolicy(
|
||||
StrictMode.VmPolicy.Builder(StrictMode.getVmPolicy()).penaltyLog().detectAll().build()
|
||||
)
|
||||
|
||||
if (dumpLogs) {
|
||||
startLogcatReader()
|
||||
}
|
||||
}
|
||||
|
||||
EventBus.builder()
|
||||
.addIndex(AppEventsIndex())
|
||||
.addIndex(EditorEventsIndex())
|
||||
@@ -164,15 +179,37 @@ class IDEApplication : BaseApplication() {
|
||||
})
|
||||
}
|
||||
|
||||
private fun startLogcatReader() {
|
||||
if (ideLogcatReader != null) {
|
||||
// already started
|
||||
return
|
||||
}
|
||||
|
||||
log.info("Starting logcat reader...")
|
||||
ideLogcatReader = IDELogcatReader().also { it.start() }
|
||||
}
|
||||
|
||||
private fun stopLogcatReader() {
|
||||
log.info("Stopping logcat reader...")
|
||||
ideLogcatReader?.interrupt()
|
||||
ideLogcatReader = null
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onPrefChanged(event: PreferenceChangeEvent) {
|
||||
val enabled = event.value as? Boolean? ?: return
|
||||
if (event.key == STAT_OPT_IN) {
|
||||
val enabled = event.value as Boolean
|
||||
if (enabled) {
|
||||
reportStatsIfNecessary()
|
||||
} else {
|
||||
cancelStatUploadWorker()
|
||||
}
|
||||
} else if (event.key == KEY_DEVOPTS_DEBUGGING_DUMPLOGS) {
|
||||
if (enabled) {
|
||||
startLogcatReader()
|
||||
} else {
|
||||
stopLogcatReader()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.preferences
|
||||
|
||||
import com.itsaky.androidide.R
|
||||
import com.itsaky.androidide.preferences.internal.prefManager
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
internal const val KEY_DEVOPTS = "ide.prefs.developerOptions"
|
||||
internal const val KEY_DEVOPTS_DEBUGGING = "${KEY_DEVOPTS}.debugging"
|
||||
internal const val KEY_DEVOPTS_DEBUGGING_DUMPLOGS = "${KEY_DEVOPTS_DEBUGGING}.dumpLogs"
|
||||
|
||||
@Parcelize
|
||||
internal class DeveloperOptionsScreen(override val key: String = KEY_DEVOPTS,
|
||||
override val title: Int = R.string.title_developer_options,
|
||||
override val summary: Int? = R.string.idepref_devOptions_summary,
|
||||
override val children: List<IPreference> = mutableListOf()) : IPreferenceScreen() {
|
||||
|
||||
init {
|
||||
addPreference(DebuggingPreferences())
|
||||
}
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
internal class DebuggingPreferences(
|
||||
override val key: String = KEY_DEVOPTS_DEBUGGING,
|
||||
override val title: Int = R.string.idepref_group_debugging,
|
||||
override val children: List<IPreference> = mutableListOf()) : IPreferenceGroup() {
|
||||
|
||||
init {
|
||||
addPreference(DumpLogsPreference())
|
||||
}
|
||||
}
|
||||
|
||||
internal var dumpLogs: Boolean
|
||||
get() = prefManager.getBoolean(KEY_DEVOPTS_DEBUGGING_DUMPLOGS, false)
|
||||
set(value) {
|
||||
prefManager.putBoolean(KEY_DEVOPTS_DEBUGGING_DUMPLOGS, value)
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
internal class DumpLogsPreference(
|
||||
override val key: String = KEY_DEVOPTS_DEBUGGING_DUMPLOGS,
|
||||
override val title: Int = R.string.idepref_devOptions_dumpLogs_title,
|
||||
override val summary: Int? = R.string.idepref_devOptions_dumpLogs_summary) :
|
||||
SwitchPreference(setValue = ::dumpLogs::set, getValue = ::dumpLogs::get)
|
||||
@@ -19,9 +19,9 @@ package com.itsaky.androidide.preferences
|
||||
|
||||
import android.content.Intent
|
||||
import com.itsaky.androidide.activities.AboutActivity
|
||||
import com.itsaky.androidide.resources.R.string
|
||||
import com.itsaky.androidide.app.BaseApplication
|
||||
import com.itsaky.androidide.app.IDEApplication
|
||||
import com.itsaky.androidide.resources.R.string
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
private const val KEY_GITHUB = "idepref_gh"
|
||||
@@ -82,6 +82,7 @@ class ConfigurationPreferences(
|
||||
override val title: Int = string.configure,
|
||||
override val children: List<IPreference> = mutableListOf()
|
||||
) : IPreferenceGroup() {
|
||||
|
||||
init {
|
||||
addPreference(GeneralPreferences())
|
||||
addPreference(EditorPreferences())
|
||||
@@ -95,17 +96,31 @@ class PrivacyPreferences(
|
||||
override val title: Int = string.title_privacy,
|
||||
override val children: List<IPreference> = mutableListOf()
|
||||
) : IPreferenceGroup() {
|
||||
|
||||
init {
|
||||
addPreference(StatPreferences())
|
||||
}
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
class DeveloperOptionsPreferences(
|
||||
override val key: String = "idepref_devOpts",
|
||||
override val title: Int = string.title_developer_options,
|
||||
override val children: List<IPreference> = mutableListOf()
|
||||
) : IPreferenceGroup() {
|
||||
|
||||
init {
|
||||
addPreference(DeveloperOptionsScreen())
|
||||
}
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
class AboutPreferences(
|
||||
override val key: String = "idepref_category_about",
|
||||
override val title: Int = string.about,
|
||||
override val children: List<IPreference> = mutableListOf()
|
||||
) : IPreferenceGroup() {
|
||||
|
||||
init {
|
||||
addPreference(github)
|
||||
addPreference(channel)
|
||||
|
||||
@@ -581,4 +581,9 @@
|
||||
<string name="title_android_veresion">Android veresion</string>
|
||||
<string name="btn_opt_in">Opt-in</string>
|
||||
<string name="btn_no_thanks">No, thanks</string>
|
||||
<string name="title_developer_options">Developer options</string>
|
||||
<string name="idepref_devOptions_summary">Experimental/debugging options for AndroidIDE</string>
|
||||
<string name="idepref_group_debugging">Debugging</string>
|
||||
<string name="idepref_devOptions_dumpLogs_title">Dump logs</string>
|
||||
<string name="idepref_devOptions_dumpLogs_summary">Dump AndroidIDE logs to $HOME/.androidide/logs</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user