Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a217bb28 authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Add a way to configure log levels" into main am: 4b8c7658 am: fd5b56a2

parents adbfe51c fd5b56a2
Loading
Loading
Loading
Loading
+24 −7
Original line number Original line Diff line number Diff line
@@ -105,6 +105,9 @@ public class RavenwoodRuntimeEnvironmentController {
    private static final String LIBRAVENWOOD_INITIALIZER_NAME = "ravenwood_initializer";
    private static final String LIBRAVENWOOD_INITIALIZER_NAME = "ravenwood_initializer";
    private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime";
    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
     * When enabled, attempt to dump all thread stacks just before we hit the
     * overall Tradefed timeout, to aid in debugging deadlocks.
     * overall Tradefed timeout, to aid in debugging deadlocks.
@@ -232,21 +235,23 @@ public class RavenwoodRuntimeEnvironmentController {
        // Make sure libravenwood_runtime is loaded.
        // Make sure libravenwood_runtime is loaded.
        System.load(RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_RUNTIME_NAME));
        System.load(RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_RUNTIME_NAME));


        Log_ravenwood.setLogLevels(getLogTags());
        Log_ravenwood.onRavenwoodRuntimeNativeReady();
        Log_ravenwood.onRavenwoodRuntimeNativeReady();


        // Do the basic set up for the android sysprops.
        // Do the basic set up for the android sysprops.
        RavenwoodSystemProperties.initialize();
        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()),
        // Do this after loading RAVENWOOD_NATIVE_RUNTIME_NAME (which backs Os.setenv()),
        // before loadFrameworkNativeCode() (which uses $ANDROID_LOG_TAGS).
        // before loadFrameworkNativeCode() (which uses $ANDROID_LOG_TAGS).
        if (RAVENWOOD_VERBOSE_LOGGING) {
        // This would also prevent libbase from crashing the process (b/381112373) because
            RavenwoodCommonUtils.log(TAG, "Force enabling verbose logging");
        // the string format it accepts is very limited.
        try {
        try {
            Os.setenv("ANDROID_LOG_TAGS", "*:v", true);
            Os.setenv("ANDROID_LOG_TAGS", "*:v", true);
        } catch (ErrnoException e) {
        } catch (ErrnoException e) {
            throw new RuntimeException(e);
            throw new RuntimeException(e);
        }
        }
        }


        // Make sure libandroid_runtime is loaded.
        // Make sure libandroid_runtime is loaded.
        RavenwoodNativeLoader.loadFrameworkNativeCode();
        RavenwoodNativeLoader.loadFrameworkNativeCode();
@@ -333,6 +338,18 @@ public class RavenwoodRuntimeEnvironmentController {
        initializeCompatIds();
        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() {
    private static void loadRavenwoodProperties() {
        var props = RavenwoodSystemProperties.readProperties("ravenwood.properties");
        var props = RavenwoodSystemProperties.readProperties("ravenwood.properties");


+40 −0
Original line number Original line Diff line number Diff line
@@ -193,6 +193,12 @@ public final class RavenwoodRule implements TestRule {
        return IS_ON_RAVENWOOD;
        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
     * @deprecated Use
     * {@code androidx.test.platform.app.InstrumentationRegistry.getInstrumentation().getContext()}
     * {@code androidx.test.platform.app.InstrumentationRegistry.getInstrumentation().getContext()}
@@ -242,6 +248,40 @@ public final class RavenwoodRule implements TestRule {
        return System.currentTimeMillis();
        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...
    // Below are internal to ravenwood. Don't use them from normal tests...


    public static class RavenwoodPrivate {
    public static class RavenwoodPrivate {
+94 −5
Original line number Original line Diff line number Diff line
@@ -15,8 +15,10 @@
 */
 */
package android.util;
package android.util;


import android.annotation.Nullable;
import android.util.Log.Level;
import android.util.Log.Level;


import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.RuntimeInit;
import com.android.internal.os.RuntimeInit;
import com.android.ravenwood.RavenwoodRuntimeNative;
import com.android.ravenwood.RavenwoodRuntimeNative;
import com.android.ravenwood.common.RavenwoodCommonUtils;
import com.android.ravenwood.common.RavenwoodCommonUtils;
@@ -24,7 +26,9 @@ import com.android.ravenwood.common.RavenwoodCommonUtils;
import java.io.PrintStream;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Locale;
import java.util.Map;


/**
/**
 * Ravenwood "native substitution" class for {@link android.util.Log}.
 * Ravenwood "native substitution" class for {@link android.util.Log}.
@@ -35,16 +39,101 @@ import java.util.Locale;
 */
 */
public class Log_ravenwood {
public class Log_ravenwood {


    public static final SimpleDateFormat sTimestampFormat =
    private static final SimpleDateFormat sTimestampFormat =
            new SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US);
            new SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US);


    public static boolean isLoggable(String tag, @Level int level) {
    private static final Object sLock = new Object();
        return true;

    @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) {
    public static int println_native(int bufID, int priority, String tag, String msg) {
        if (priority < Log.INFO && !RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING) {
        if (!isLoggable(tag, priority)) {
            return msg.length(); // No verbose logging.
            return msg.length();
        }
        }


        final String prio;
        final String prio;
+109 −0
Original line number Original line Diff line number Diff line
/*
 * 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);
    }
}