diff options
author | 2024-11-26 12:36:26 -0800 | |
---|---|---|
committer | 2024-11-26 15:15:48 -0800 | |
commit | e11147f7ff3ab692421a16f0d90e34274ee4f362 (patch) | |
tree | 1cd50a7e3b8d1f6e0fffff90eabf0bc056cbfe96 | |
parent | c4a715709ee9116e17a2c81772e87061680ae2b7 (diff) |
Add a way to configure log levels
Bug: 381112373
Bug: 380949304
Bug: 380938496
Test: $ANDROID_BUILD_TOP/frameworks/base/ravenwood/scripts/run-ravenwood-tests.sh -s
Flag: EXEMPT host test change only
Change-Id: I3851ba55abf8cdbdf518dda83b4780f2128c013d
4 files changed, 267 insertions, 12 deletions
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java index 172cec3b8e13..3d2bb8e78a7e 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java @@ -105,6 +105,9 @@ public class RavenwoodRuntimeEnvironmentController { private static final String LIBRAVENWOOD_INITIALIZER_NAME = "ravenwood_initializer"; private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime"; + private static final String ANDROID_LOG_TAGS = "ANDROID_LOG_TAGS"; + private static final String RAVENWOOD_ANDROID_LOG_TAGS = "RAVENWOOD_" + ANDROID_LOG_TAGS; + /** * When enabled, attempt to dump all thread stacks just before we hit the * overall Tradefed timeout, to aid in debugging deadlocks. @@ -232,20 +235,22 @@ public class RavenwoodRuntimeEnvironmentController { // Make sure libravenwood_runtime is loaded. System.load(RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_RUNTIME_NAME)); + Log_ravenwood.setLogLevels(getLogTags()); Log_ravenwood.onRavenwoodRuntimeNativeReady(); // Do the basic set up for the android sysprops. RavenwoodSystemProperties.initialize(); + // Enable all log levels for native logging, until we'll have a way to change the native + // side log level at runtime. // Do this after loading RAVENWOOD_NATIVE_RUNTIME_NAME (which backs Os.setenv()), // before loadFrameworkNativeCode() (which uses $ANDROID_LOG_TAGS). - if (RAVENWOOD_VERBOSE_LOGGING) { - RavenwoodCommonUtils.log(TAG, "Force enabling verbose logging"); - try { - Os.setenv("ANDROID_LOG_TAGS", "*:v", true); - } catch (ErrnoException e) { - throw new RuntimeException(e); - } + // This would also prevent libbase from crashing the process (b/381112373) because + // the string format it accepts is very limited. + try { + Os.setenv("ANDROID_LOG_TAGS", "*:v", true); + } catch (ErrnoException e) { + throw new RuntimeException(e); } // Make sure libandroid_runtime is loaded. @@ -333,6 +338,18 @@ public class RavenwoodRuntimeEnvironmentController { initializeCompatIds(); } + /** + * Get log tags from environmental variable. + */ + @Nullable + private static String getLogTags() { + var logTags = System.getenv(RAVENWOOD_ANDROID_LOG_TAGS); + if (logTags == null) { + logTags = System.getenv(ANDROID_LOG_TAGS); + } + return logTags; + } + private static void loadRavenwoodProperties() { var props = RavenwoodSystemProperties.readProperties("ravenwood.properties"); diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java index e49d3d934e9f..d4b15c66c68b 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java @@ -193,6 +193,12 @@ public final class RavenwoodRule implements TestRule { return IS_ON_RAVENWOOD; } + private static void ensureOnRavenwood(String featureName) { + if (!IS_ON_RAVENWOOD) { + throw new RuntimeException(featureName + " is only supported on Ravenwood."); + } + } + /** * @deprecated Use * {@code androidx.test.platform.app.InstrumentationRegistry.getInstrumentation().getContext()} @@ -242,6 +248,40 @@ public final class RavenwoodRule implements TestRule { return System.currentTimeMillis(); } + /** + * Equivalent to setting the ANDROID_LOG_TAGS environmental variable. + * + * See https://developer.android.com/tools/logcat#filteringOutput for the string format. + * + * NOTE: this works only on Ravenwood. + */ + public static void setAndroidLogTags(@Nullable String androidLogTags) { + ensureOnRavenwood("RavenwoodRule.setAndroidLogTags()"); + try { + Class<?> logRavenwoodClazz = Class.forName("android.util.Log_ravenwood"); + var setter = logRavenwoodClazz.getMethod("setLogLevels", String.class); + setter.invoke(null, androidLogTags); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + /** + * Set a log level for a given tag. Pass NULL to {@code tag} to change the default level. + * + * NOTE: this works only on Ravenwood. + */ + public static void setLogLevel(@Nullable String tag, int level) { + ensureOnRavenwood("RavenwoodRule.setLogLevel()"); + try { + Class<?> logRavenwoodClazz = Class.forName("android.util.Log_ravenwood"); + var setter = logRavenwoodClazz.getMethod("setLogLevel", String.class, int.class); + setter.invoke(null, tag, level); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + // Below are internal to ravenwood. Don't use them from normal tests... public static class RavenwoodPrivate { diff --git a/ravenwood/runtime-helper-src/framework/android/util/Log_ravenwood.java b/ravenwood/runtime-helper-src/framework/android/util/Log_ravenwood.java index 7b26fe531e7e..96fa21221694 100644 --- a/ravenwood/runtime-helper-src/framework/android/util/Log_ravenwood.java +++ b/ravenwood/runtime-helper-src/framework/android/util/Log_ravenwood.java @@ -15,8 +15,10 @@ */ package android.util; +import android.annotation.Nullable; import android.util.Log.Level; +import com.android.internal.annotations.GuardedBy; import com.android.internal.os.RuntimeInit; import com.android.ravenwood.RavenwoodRuntimeNative; import com.android.ravenwood.common.RavenwoodCommonUtils; @@ -24,7 +26,9 @@ import com.android.ravenwood.common.RavenwoodCommonUtils; import java.io.PrintStream; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.HashMap; import java.util.Locale; +import java.util.Map; /** * Ravenwood "native substitution" class for {@link android.util.Log}. @@ -35,16 +39,101 @@ import java.util.Locale; */ public class Log_ravenwood { - public static final SimpleDateFormat sTimestampFormat = + private static final SimpleDateFormat sTimestampFormat = new SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US); - public static boolean isLoggable(String tag, @Level int level) { - return true; + private static final Object sLock = new Object(); + + @GuardedBy("sLock") + private static int sDefaultLogLevel; + + @GuardedBy("sLock") + private static final Map<String, Integer> sTagLogLevels = new HashMap<>(); + + /** + * Used by {@link android.platform.test.ravenwood.RavenwoodRule#setAndroidLogTags(String)} + * via reflections. + */ + public static void setLogLevels(String androidLogTags) { + var map = parseLogLevels(androidLogTags); + + synchronized (sLock) { + sTagLogLevels.clear(); + sTagLogLevels.putAll(map); + + var def = map.get("*"); + sDefaultLogLevel = def != null ? def + : RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING ? Log.VERBOSE : Log.INFO; + } + } + + private static Map<String, Integer> parseLogLevels(String androidLogTags) { + final Map<String, Integer> ret = new HashMap<>(); + + if (androidLogTags == null) { + return ret; + } + + String[] tagPairs = androidLogTags.trim().split("\\s+"); + for (String tagPair : tagPairs) { + String[] parts = tagPair.split(":"); + if (parts.length == 2) { + String tag = parts[0]; + try { + int priority = switch (parts[1]) { + case "V": yield Log.VERBOSE; + case "D": yield Log.DEBUG; + case "I": yield Log.INFO; + case "W": yield Log.WARN; + case "E": yield Log.ERROR; + case "F": yield Log.ERROR + 1; // Not used in the java side. + case "S": yield Integer.MAX_VALUE; // Silent + default: throw new IllegalArgumentException( + "Invalid priority level for tag: " + tag); + }; + + ret.put(tag, priority); + } catch (IllegalArgumentException e) { + System.err.println(e.getMessage()); + } + } else { + System.err.println("Invalid tag format: " + tagPair); + } + } + + return ret; + } + + /** + * Used by {@link android.platform.test.ravenwood.RavenwoodRule#setLogLevel(String, int)} + * via reflections. Pass NULL to {@code tag} to set the default level. + */ + public static void setLogLevel(@Nullable String tag, int level) { + synchronized (sLock) { + if (tag == null) { + sDefaultLogLevel = level; + } else { + sTagLogLevels.put(tag, level); + } + } + } + + /** + * Replaces {@link Log#isLoggable}. + */ + public static boolean isLoggable(String tag, @Level int priority) { + synchronized (sLock) { + var threshold = sTagLogLevels.get(tag); + if (threshold == null) { + threshold = sDefaultLogLevel; + } + return priority >= threshold; + } } public static int println_native(int bufID, int priority, String tag, String msg) { - if (priority < Log.INFO && !RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING) { - return msg.length(); // No verbose logging. + if (!isLoggable(tag, priority)) { + return msg.length(); } final String prio; diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodLogLevelTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodLogLevelTest.java new file mode 100644 index 000000000000..7be562c86434 --- /dev/null +++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodLogLevelTest.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.ravenwoodtest.coretest; + +import static org.junit.Assert.assertEquals; + +import android.platform.test.ravenwood.RavenwoodRule; +import android.util.Log; + +import com.android.ravenwood.common.RavenwoodCommonUtils; + +import org.junit.Test; + +public class RavenwoodLogLevelTest { + /** + * Assert that the `priority` is loggable, but one level below is not. + */ + private void assertBarelyLoggable(String tag, int priority) { + assertEquals(true, Log.isLoggable(tag, priority)); + assertEquals(false, Log.isLoggable(tag, priority - 1)); + } + + @Test + public void testDefaultLogTags() { + RavenwoodRule.setAndroidLogTags(null); + + // Info should always be loggable. + assertEquals(true, Log.isLoggable("TAG1", Log.INFO)); + assertEquals(true, Log.isLoggable("TAG2", Log.INFO)); + + var verboseEnabled = RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING; + assertEquals(verboseEnabled, Log.isLoggable("TAG1", Log.DEBUG)); + assertEquals(verboseEnabled, Log.isLoggable("TAG2", Log.VERBOSE)); + } + + @Test + public void testAllVerbose() { + RavenwoodRule.setAndroidLogTags("*:V"); + + assertEquals(true, Log.isLoggable("TAG1", Log.INFO)); + assertEquals(true, Log.isLoggable("TAG2", Log.INFO)); + + assertEquals(true, Log.isLoggable("TAG1", Log.DEBUG)); + assertEquals(true, Log.isLoggable("TAG2", Log.VERBOSE)); + } + + @Test + public void testAllSilent() { + RavenwoodRule.setAndroidLogTags("*:S"); + + assertEquals(false, Log.isLoggable("TAG1", Log.ASSERT)); + assertEquals(false, Log.isLoggable("TAG2", Log.ASSERT)); + } + + @Test + public void testComplex() { + RavenwoodRule.setAndroidLogTags("TAG1:W TAG2:D *:I"); + + assertBarelyLoggable("TAG1", Log.WARN); + assertBarelyLoggable("TAG2", Log.DEBUG); + assertBarelyLoggable("TAG3", Log.INFO); + } + + @Test + public void testAllVerbose_setLogLevel() { + RavenwoodRule.setAndroidLogTags(null); + RavenwoodRule.setLogLevel(null, Log.VERBOSE); + + assertEquals(true, Log.isLoggable("TAG1", Log.INFO)); + assertEquals(true, Log.isLoggable("TAG2", Log.INFO)); + + assertEquals(true, Log.isLoggable("TAG1", Log.DEBUG)); + assertEquals(true, Log.isLoggable("TAG2", Log.VERBOSE)); + } + + @Test + public void testAllSilent_setLogLevel() { + RavenwoodRule.setAndroidLogTags(null); + RavenwoodRule.setLogLevel(null, Log.ASSERT + 1); + + assertEquals(false, Log.isLoggable("TAG1", Log.ASSERT)); + assertEquals(false, Log.isLoggable("TAG2", Log.ASSERT)); + } + + @Test + public void testComplex_setLogLevel() { + RavenwoodRule.setAndroidLogTags(null); + RavenwoodRule.setLogLevel(null, Log.INFO); + RavenwoodRule.setLogLevel("TAG1", Log.WARN); + RavenwoodRule.setLogLevel("TAG2", Log.DEBUG); + + assertBarelyLoggable("TAG1", Log.WARN); + assertBarelyLoggable("TAG2", Log.DEBUG); + assertBarelyLoggable("TAG3", Log.INFO); + } +} |