Skip to content

Commit 1cb8cb2

Browse files
committed
feat: Show logger details in patcher screen
1 parent 6dd7805 commit 1cb8cb2

8 files changed

Lines changed: 203 additions & 114 deletions

File tree

app/src/main/java/app/revanced/manager/patcher/PatcherProgress.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package app.revanced.manager.patcher
22

33
import android.os.Parcelable
4+
import app.revanced.manager.patcher.logger.LogLevel
45
import kotlinx.parcelize.Parcelize
56

67

@@ -17,6 +18,12 @@ sealed class ProgressEvent : Parcelable {
1718
val message: String? = null,
1819
) : ProgressEvent()
1920

21+
data class Log(
22+
override val stepId: StepId,
23+
val level: LogLevel,
24+
val message: String,
25+
) : ProgressEvent()
26+
2027
data class Completed(
2128
override val stepId: StepId,
2229
) : ProgressEvent()

app/src/main/java/app/revanced/manager/patcher/Session.kt

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import app.revanced.library.ApkUtils.applyTo
44
import app.revanced.manager.patcher.Session.Companion.component1
55
import app.revanced.manager.patcher.Session.Companion.component2
66
import app.revanced.manager.patcher.logger.Logger
7+
import app.revanced.manager.patcher.logger.forStep
8+
import app.revanced.manager.patcher.logger.withJavaLogging
79
import app.revanced.patcher.PatchesResult
810
import app.revanced.patcher.patch.Patch
911
import app.revanced.patcher.patch.PatchResult
@@ -31,7 +33,11 @@ class Session(
3133
private val tempDir = File(cacheDir).resolve("patcher").also { it.mkdirs() }
3234

3335

34-
private suspend fun applyPatchesVerbose(patcher: Patcher, indices: Map<Patch, Int>) =
36+
private suspend fun applyPatchesVerbose(
37+
patcher: Patcher,
38+
indices: Map<Patch, Int>,
39+
phaseLogger: Logger = logger,
40+
) =
3541
withContext(
3642
Dispatchers.Default
3743
) {
@@ -49,8 +55,8 @@ class Session(
4955
exception.toRemoteError(),
5056
)
5157
)
52-
logger.error("${patch.name} failed:")
53-
logger.error(exception.stackTraceToString())
58+
phaseLogger.error("${patch.name} failed:")
59+
phaseLogger.error(exception.stackTraceToString())
5460
throw exception
5561
}
5662

@@ -59,51 +65,52 @@ class Session(
5965
StepId.ExecutePatch(index),
6066
)
6167
)
62-
63-
logger.info("${patch.name} succeeded")
6468
}
6569
}
6670

6771
suspend fun run(output: File, selectedPatches: PatchList) {
6872
val indices = HashMap<Patch, Int>(selectedPatches.size)
6973
selectedPatches.forEachIndexed { idx, patch -> indices[patch] = idx }
7074

71-
val result = runStep(StepId.ExecutePatches, onEvent) {
72-
java.util.logging.Logger.getLogger("").apply {
73-
handlers.forEach {
74-
it.close()
75-
removeHandler(it)
75+
val prepareLogger = logger.forStep(StepId.ReadAPK, onEvent)
76+
val patchingLogger = logger.forStep(StepId.ExecutePatches, onEvent)
77+
val writingLogger = logger.forStep(StepId.WriteAPK, onEvent)
78+
79+
val patcher = runStep(StepId.ReadAPK, onEvent) {
80+
prepareLogger.withJavaLogging {
81+
patcher(
82+
apkFile = input,
83+
temporaryFilesPath = tempDir,
84+
frameworkFileDirectory = frameworkDir,
85+
aaptBinaryPath = File(aaptPath)
86+
) { _packageName, _version ->
87+
selectedPatches.toSet()
7688
}
77-
78-
addHandler(logger.handler)
7989
}
90+
}
8091

81-
val patcher = patcher(
82-
apkFile = input,
83-
temporaryFilesPath = tempDir,
84-
frameworkFileDirectory = frameworkDir,
85-
aaptBinaryPath = File(aaptPath)
86-
) { _packageName, _version ->
87-
selectedPatches.toSet()
92+
val result = runStep(StepId.ExecutePatches, onEvent) {
93+
patchingLogger.withJavaLogging {
94+
patchingLogger.info("Applying patches...")
95+
applyPatchesVerbose(patcher, indices, patchingLogger)
8896
}
89-
90-
logger.info("Applying patches...")
91-
applyPatchesVerbose(patcher, indices)
9297
}
9398

9499
runStep(StepId.WriteAPK, onEvent) {
95-
logger.info("Writing patched files...")
100+
writingLogger.withJavaLogging {
101+
writingLogger.info("Writing patched files...")
96102

97-
val patched = tempDir.resolve("result.apk")
98-
withContext(Dispatchers.IO) {
99-
Files.copy(input.toPath(), patched.toPath(), StandardCopyOption.REPLACE_EXISTING)
100-
}
101-
result.applyTo(patched)
103+
val patched = tempDir.resolve("result.apk")
104+
withContext(Dispatchers.IO) {
105+
Files.copy(input.toPath(), patched.toPath(), StandardCopyOption.REPLACE_EXISTING)
106+
}
107+
result.applyTo(patched)
102108

103-
logger.info("Patched apk saved to $patched")
109+
writingLogger.info("Patched apk saved to $patched")
104110

105-
withContext(Dispatchers.IO) {
106-
Files.move(patched.toPath(), output.toPath(), StandardCopyOption.REPLACE_EXISTING)
111+
withContext(Dispatchers.IO) {
112+
Files.move(patched.toPath(), output.toPath(), StandardCopyOption.REPLACE_EXISTING)
113+
}
107114
}
108115
}
109116
}

app/src/main/java/app/revanced/manager/patcher/logger/Logger.kt

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
package app.revanced.manager.patcher.logger
22

3+
import app.revanced.manager.patcher.ProgressEvent
4+
import app.revanced.manager.patcher.StepId
35
import java.util.logging.Handler
46
import java.util.logging.Level
57
import java.util.logging.LogRecord
68

79
abstract class Logger {
810
abstract fun log(level: LogLevel, message: String)
911

12+
// Logger-name metadata is only used for step-view filtering; default behavior keeps full logs.
13+
open fun log(level: LogLevel, message: String, loggerName: String?) = log(level, message)
14+
1015
fun trace(msg: String) = log(LogLevel.TRACE, msg)
1116
fun info(msg: String) = log(LogLevel.INFO, msg)
1217
fun warn(msg: String) = log(LogLevel.WARN, msg)
@@ -15,12 +20,13 @@ abstract class Logger {
1520
val handler = object : Handler() {
1621
override fun publish(record: LogRecord) {
1722
val msg = record.message
23+
val loggerName = record.loggerName
1824

1925
when (record.level) {
20-
Level.INFO -> info(msg)
21-
Level.SEVERE -> error(msg)
22-
Level.WARNING -> warn(msg)
23-
else -> trace(msg)
26+
Level.INFO -> log(LogLevel.INFO, msg, loggerName)
27+
Level.SEVERE -> log(LogLevel.ERROR, msg, loggerName)
28+
Level.WARNING -> log(LogLevel.WARN, msg, loggerName)
29+
else -> log(LogLevel.TRACE, msg, loggerName)
2430
}
2531
}
2632

@@ -29,6 +35,40 @@ abstract class Logger {
2935
}
3036
}
3137

38+
fun Logger.forStep(stepId: StepId, onEvent: (ProgressEvent) -> Unit) = object : Logger() {
39+
override fun log(level: LogLevel, message: String) {
40+
this@forStep.log(level, message)
41+
onEvent(ProgressEvent.Log(stepId, level, message))
42+
}
43+
44+
override fun log(level: LogLevel, message: String, loggerName: String?) {
45+
this@forStep.log(level, message, loggerName)
46+
47+
// App loggers should use empty or "app.revanced" prefix;
48+
// filter out logs from libraries to avoid cluttering the step view.
49+
if (loggerName.isNullOrEmpty() || loggerName.startsWith("app.revanced")) {
50+
onEvent(ProgressEvent.Log(stepId, level, message))
51+
}
52+
}
53+
}
54+
55+
inline fun <T> Logger.withJavaLogging(block: () -> T): T {
56+
val rootLogger = java.util.logging.Logger.getLogger("")
57+
58+
rootLogger.handlers.forEach {
59+
it.close()
60+
rootLogger.removeHandler(it)
61+
}
62+
63+
rootLogger.addHandler(handler)
64+
65+
return try {
66+
block()
67+
} finally {
68+
rootLogger.removeHandler(handler)
69+
}
70+
}
71+
3272
enum class LogLevel {
3373
TRACE,
3474
INFO,

app/src/main/java/app/revanced/manager/patcher/runtime/CoroutineRuntime.kt

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import app.revanced.manager.patcher.ProgressEvent
55
import app.revanced.manager.patcher.Session
66
import app.revanced.manager.patcher.StepId
77
import app.revanced.manager.patcher.logger.Logger
8+
import app.revanced.manager.patcher.logger.forStep
9+
import app.revanced.manager.patcher.logger.withJavaLogging
810
import app.revanced.manager.patcher.patch.PatchBundle
911
import app.revanced.manager.patcher.runStep
1012
import app.revanced.manager.util.Options
@@ -24,45 +26,47 @@ class CoroutineRuntime(context: Context) : Runtime(context) {
2426
logger: Logger,
2527
onEvent: (ProgressEvent) -> Unit,
2628
) {
29+
val loadLogger = logger.forStep(StepId.LoadPatches, onEvent)
30+
2731
val patchList = runStep(StepId.LoadPatches, onEvent) {
28-
val selectedBundles = selectedPatches.keys
29-
val bundles = bundles()
30-
val uids = bundles.entries.associate { (key, value) -> value to key }
32+
loadLogger.withJavaLogging {
33+
val selectedBundles = selectedPatches.keys
34+
val bundles = bundles()
35+
val uids = bundles.entries.associate { (key, value) -> value to key }
3136

32-
val allPatches =
33-
PatchBundle.Loader.patches(bundles.values, packageName)
34-
.mapKeys { (b, _) -> uids[b]!! }
35-
.filterKeys { it in selectedBundles }
37+
val allPatches =
38+
PatchBundle.Loader.patches(bundles.values, packageName)
39+
.mapKeys { (b, _) -> uids[b]!! }
40+
.filterKeys { it in selectedBundles }
3641

37-
val patchList = selectedPatches.flatMap { (bundle, selected) ->
38-
allPatches[bundle]?.filter { it.name in selected }
39-
?: throw IllegalArgumentException("Patch bundle $bundle does not exist")
40-
}
42+
val patchList = selectedPatches.flatMap { (bundle, selected) ->
43+
allPatches[bundle]?.filter { it.name in selected }
44+
?: throw IllegalArgumentException("Patch bundle $bundle does not exist")
45+
}
4146

42-
// Set all patch options.
43-
options.forEach { (bundle, bundlePatchOptions) ->
44-
val patches = allPatches[bundle] ?: return@forEach
45-
bundlePatchOptions.forEach { (patchName, configuredPatchOptions) ->
46-
val patchOptions = patches.single { it.name == patchName }.options
47-
configuredPatchOptions.forEach { (key, value) ->
48-
patchOptions[key] = value
47+
// Set all patch options.
48+
options.forEach { (bundle, bundlePatchOptions) ->
49+
val patches = allPatches[bundle] ?: return@forEach
50+
bundlePatchOptions.forEach { (patchName, configuredPatchOptions) ->
51+
val patchOptions = patches.single { it.name == patchName }.options
52+
configuredPatchOptions.forEach { (key, value) ->
53+
patchOptions[key] = value
54+
}
4955
}
5056
}
51-
}
5257

53-
patchList
58+
patchList
59+
}
5460
}
5561

56-
val session = runStep(StepId.ReadAPK, onEvent) {
57-
Session(
58-
cacheDir,
59-
frameworkPath,
60-
aaptPath,
61-
logger,
62-
File(inputFile),
63-
onEvent,
64-
)
65-
}
62+
val session = Session(
63+
cacheDir,
64+
frameworkPath,
65+
aaptPath,
66+
logger,
67+
File(inputFile),
68+
onEvent,
69+
)
6670

6771
session.use { s ->
6872
s.run(

app/src/main/java/app/revanced/manager/patcher/runtime/process/PatcherProcess.kt

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import app.revanced.manager.patcher.Session
1313
import app.revanced.manager.patcher.StepId
1414
import app.revanced.manager.patcher.logger.LogLevel
1515
import app.revanced.manager.patcher.logger.Logger
16+
import app.revanced.manager.patcher.logger.forStep
17+
import app.revanced.manager.patcher.logger.withJavaLogging
1618
import app.revanced.manager.patcher.patch.PatchBundle
1719
import app.revanced.manager.patcher.runStep
1820
import app.revanced.manager.patcher.runtime.ProcessRuntime
@@ -61,40 +63,41 @@ class PatcherProcess() : IPatcherProcess.Stub() {
6163

6264
logger.info("Memory limit: ${Runtime.getRuntime().maxMemory() / (1024 * 1024)}MB")
6365

66+
val loadLogger = logger.forStep(StepId.LoadPatches, ::onEvent)
6467
val patchList = runStep(StepId.LoadPatches, ::onEvent) {
65-
val allPatches = PatchBundle.Loader.patches(
66-
parameters.configurations.map { it.bundle },
67-
parameters.packageName
68-
)
69-
70-
parameters.configurations.flatMap { config ->
71-
val patches = (allPatches[config.bundle] ?: return@flatMap emptyList())
72-
.filter { it.name in config.patches }
73-
.associateBy { it.name }
74-
75-
config.options.forEach { (patchName, opts) ->
76-
val patchOptions = patches[patchName]?.options
77-
?: throw Exception("Patch with name $patchName does not exist.")
78-
79-
opts.forEach { (key, value) ->
80-
patchOptions[key] = value
68+
loadLogger.withJavaLogging {
69+
val allPatches = PatchBundle.Loader.patches(
70+
parameters.configurations.map { it.bundle },
71+
parameters.packageName
72+
)
73+
74+
parameters.configurations.flatMap { config ->
75+
val patches = (allPatches[config.bundle] ?: return@flatMap emptyList())
76+
.filter { it.name in config.patches }
77+
.associateBy { it.name }
78+
79+
config.options.forEach { (patchName, opts) ->
80+
val patchOptions = patches[patchName]?.options
81+
?: throw Exception("Patch with name $patchName does not exist.")
82+
83+
opts.forEach { (key, value) ->
84+
patchOptions[key] = value
85+
}
8186
}
82-
}
8387

84-
patches.values
88+
patches.values
89+
}
8590
}
8691
}
8792

88-
val session = runStep(StepId.ReadAPK, ::onEvent) {
89-
Session(
90-
cacheDir = parameters.cacheDir,
91-
aaptPath = parameters.aaptPath,
92-
frameworkDir = parameters.frameworkDir,
93-
logger = logger,
94-
input = File(parameters.inputFile),
95-
onEvent = ::onEvent,
96-
)
97-
}
93+
val session = Session(
94+
cacheDir = parameters.cacheDir,
95+
aaptPath = parameters.aaptPath,
96+
frameworkDir = parameters.frameworkDir,
97+
logger = logger,
98+
input = File(parameters.inputFile),
99+
onEvent = ::onEvent,
100+
)
98101

99102
session.use {
100103
it.run(File(parameters.outputFile), patchList)

0 commit comments

Comments
 (0)