diff --git a/.github/workflows/android-reusable-test.yaml b/.github/workflows/android-reusable-test.yaml index 6bd06ab3..70812c96 100644 --- a/.github/workflows/android-reusable-test.yaml +++ b/.github/workflows/android-reusable-test.yaml @@ -41,33 +41,50 @@ jobs: - name: Setup Java uses: actions/setup-java@b36c23c0d998641eff861008f374ee103c25ac73 # 4.4.0 with: + distribution: 'temurin' java-version: 17 - - name: Setup Android SDK + - name: Setup Gradle + uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # 4.1.0 + + - name: Enable KVM + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + + - name: Create AVD Device + id: avd shell: bash env: ANDROID_SDK_VERSION: 10406996 ANDROID_SDK_CHECKSUM: 8919e8752979db73d8321e9babe2caedcc393750817c1a5f56c128ec442fb540 run: | - # Create home directory - export ANDROID_HOME=$HOME/.android/sdk - mkdir -p $ANDROID_HOME - - # Download and verify the `cmdline-tools` - curl -O $ANDROID_HOME/cmdline-tools.zip https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_SDK_VERSION}_latest.zip - echo "$ANDROID_SDK_CHECKSUM $ANDROID_HOME/cmdline-tools.zip" | sha256sum -c - unzip -d $ANDROID_HOME/cmdline-tools{,.zip} - mv $ANDROID_HOME/cmdline-tools/{cmdline-tools,latest} - - # Accept licenses - yes | $ANDROID_HOME/cmdline-tools/bin/sdkmanager --licenses - - # Installing the remaining tools - $ANDROID_HOME/cmdline-tools/bin/sdkmanager tools platform-tools - - # Exporting ANDROID_HOME and adding sdkmanager to the PATH - echo "ANDROID_HOME=$ANDROID_HOME" >> $GITHUB_ENV - echo $ANDROID_HOME/cmdline-tools/bin >> $GITHUB_PATH + # List installed and available packages + $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --list + # Download images + echo y | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager \ + --install "system-images;android-31;default;x86_64" \ + emulator platform-tools + # Create device + $ANDROID_HOME/cmdline-tools/latest/bin/avdmanager create avd \ + --name generic-api-31-device \ + --device "5.4in FWVGA" \ + --package "system-images;android-31;default;x86_64" + # Run emulator + $ANDROID_HOME/emulator/emulator \ + -no-audio -no-window \ + -avd generic-api-31-device & + # Enabled the cleanup job + echo EMULATOR_STARTED=true >> $GITHUB_ENV + # Wait for device to go online + # It might take up to 5 minutes + for i in {1..300}; do + # Don't stop the script if `adb` fails + boot_completed=$($ANDROID_HOME/platform-tools/adb shell getprop sys.boot_completed 2> /dev/null || echo "0") + if [ "${boot_completed}" = "1" ]; then break; fi + sleep 1 + done - name: Build id: build @@ -75,7 +92,17 @@ jobs: env: LOG4J_VERSION: ${{ inputs.log4j-version }} run: | - log4j-samples-android/gradlew -p log4j-samples-gradlew \ + log4j-samples-android/gradlew -p log4j-samples-android \ --console plain \ - -Plog4jVersion=$LOG4J_VERSION \ + -Plog4j.version=$LOG4J_VERSION \ build connectedCheck + + - name: Remove AVD Device + if: ${{ always() && env.EMULATOR_STARTED == 'true' }} + shell: bash + run: | + # Kill the emulator + $ANDROID_HOME/platform-tools/adb -s emulator-5554 emu kill + # Delete the device + $ANDROID_HOME/cmdline-tools/latest/bin/avdmanager delete avd \ + --name generic-api-31-device diff --git a/.github/workflows/android-test.yaml b/.github/workflows/android-test.yaml index 0f5d0d08..65aadaa0 100644 --- a/.github/workflows/android-test.yaml +++ b/.github/workflows/android-test.yaml @@ -19,18 +19,16 @@ name: android-test on: workflow_dispatch: - schedule: - - cron: "29 17 * * *" + inputs: + log4j-version: + description: The version of Log4j Core to use + default: "2.25.0-SNAPSHOT" permissions: read-all jobs: android-test: - strategy: - fail-fast: false - matrix: - log4j-version: [ "2.25.0-SNAPSHOT" ] - uses: apache/logging-log4j-samples/.github/workflows/android-reusable-test.yaml@main + uses: apache/logging-log4j-samples/.github/workflows/android-reusable-test.yaml@feature/android-tests with: - log4j-version: ${{ matrix.log4j-version }} + log4j-version: ${{ inputs.log4j-version }} diff --git a/README.adoc b/README.adoc index 44f47ae4..a2e5b428 100644 --- a/README.adoc +++ b/README.adoc @@ -18,20 +18,26 @@ limitations under the License. This repository contains several examples to help the user with some of the more advanced Log4j features. +xref:log4j-samples-android/README.adoc[`log4j-samples-android`]:: +Explains how to use Log4j Core on Android. + xref:log4j-samples-configuration/README.adoc[`log4j-samples-configuration`]:: -Explains how to use a custom programmatic configuration, +Explains how to use a custom programmatic configuration. xref:log4j-samples-flume-embedded/README.adoc[`log4j-samples-flume-embedded`]:: -Explains how to use an embedded Flume agent to send logs to https://flume.apache.org[Apache Flume], +Explains how to use an embedded Flume agent to send logs to https://flume.apache.org[Apache Flume.] xref:log4j-samples-flume-remote/README.adoc[`log4j-samples-flume-remote`]:: Explains how to send logs to https://flume.apache.org[Apache Flume], +xref:log4j-samples-graalvm/README.adoc[`log4j-samples-graalvm`]:: +Explains how to use Log4j API and its implementation to create native GraalVM images. + xref:log4j-samples-jlink/README.adoc[`log4j-samples-jlink`]:: An example of JLink custom JRE. xref:log4j-samples-loggerProperties/README.adoc[`log4j-samples-loggerProperties`]:: -Explains how to write a custom property lookup, +Explains how to write a custom property lookup. xref:log4j-spring-cloud-config-sample-application/README.md[`log4j-spring-cloud-config-sample-application`]:: An example of Spring Boot application that reads its logging configuration from a Spring Cloud Configuration Server. diff --git a/log4j-samples-android/README.adoc b/log4j-samples-android/README.adoc new file mode 100644 index 00000000..cfb318ca --- /dev/null +++ b/log4j-samples-android/README.adoc @@ -0,0 +1,40 @@ +//// +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You 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 + + https://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. +//// += Apache Log4j Samples: Android application + +This project is an example of Android application that uses +https://logging.apache.org/log4j/2.x/manual/api.html[the Log4j API] +for logging together with its reference implementation +https://logging.apache.org/log4j/2.x/manual/implementation.html[Log4j Core]. + +To build the project, run: + +[source,shell] +---- +./gradlew build +---- + +inside this directory. + +Since version `2.25.0` this application is part of the integration tests for a Log4j release. + +== Known Android limitations + +The Android platform does not implement all the features of a standard JRE, which will prevent you from using all the features offered by Log4j Core. +For a list of known limitations, see +https://logging.apache.org/log4j/2.x/faq.html#android[our Android F.A.Q. entry]. + diff --git a/log4j-samples-android/app/build.gradle b/log4j-samples-android/app/build.gradle index 05f2310a..c08592c2 100644 --- a/log4j-samples-android/app/build.gradle +++ b/log4j-samples-android/app/build.gradle @@ -20,6 +20,7 @@ plugins { android { compileSdk 34 + namespace "org.apache.logging.log4j.samples.android" defaultConfig { applicationId "org.apache.logging.log4j.samples.android" @@ -48,17 +49,29 @@ android { } } +def log4jVersion = providers.gradleProperty("log4j.version").get() + dependencies { - implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.11.0' + implementation 'androidx.appcompat:appcompat:1.7.0' + implementation 'com.google.android.material:material:1.12.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.2.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' + androidTestImplementation 'org.assertj:assertj-core:3.26.3' + + // Log4j + implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: log4jVersion + implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: log4jVersion + androidTestImplementation(group: 'org.apache.logging.log4j', name: 'log4j-core-test', version: log4jVersion) { + exclude group: 'com.google.code.java-allocation-instrumenter' + exclude group: 'org.apache.logging.log4j', module: 'log4j-api-test' + exclude group: 'org.hamcrest' + exclude group: 'org.junit.jupiter' + exclude group: 'org.junit.platform' + exclude group: 'org.springframework' + } - //Log4j - implementation 'org.apache.logging.log4j:log4j-api:2.24.1' - implementation 'org.apache.logging.log4j:log4j-core:2.24.1' -} \ No newline at end of file +} diff --git a/log4j-samples-android/app/src/androidTest/java/org/apache/logging/log4j/samples/android/ExampleInstrumentedTest.java b/log4j-samples-android/app/src/androidTest/java/org/apache/logging/log4j/samples/android/ExampleInstrumentedTest.java deleted file mode 100644 index 8c1487c8..00000000 --- a/log4j-samples-android/app/src/androidTest/java/org/apache/logging/log4j/samples/android/ExampleInstrumentedTest.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you 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 org.apache.logging.log4j.samples.android; - -import android.content.Context; - -import androidx.test.platform.app.InstrumentationRegistry; -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.*; - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); - assertEquals("com.example.log4japi", appContext.getPackageName()); - } -} \ No newline at end of file diff --git a/log4j-samples-android/app/src/androidTest/java/org/apache/logging/log4j/samples/android/MainActivityTest.java b/log4j-samples-android/app/src/androidTest/java/org/apache/logging/log4j/samples/android/MainActivityTest.java new file mode 100644 index 00000000..0ab867a9 --- /dev/null +++ b/log4j-samples-android/app/src/androidTest/java/org/apache/logging/log4j/samples/android/MainActivityTest.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you 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 org.apache.logging.log4j.samples.android; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; +import static org.assertj.core.api.Assertions.assertThat; + +import androidx.test.ext.junit.rules.ActivityScenarioRule; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import java.util.AbstractMap; +import java.util.Arrays; +import java.util.Map; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.appender.ConsoleAppender; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.LoggerConfig; +import org.apache.logging.log4j.core.test.appender.ListAppender; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Instrumented test, which will execute on an Android device. + */ +@RunWith(AndroidJUnit4.class) +public class MainActivityTest { + + @Rule + public ActivityScenarioRule activityScenarioRule = new ActivityScenarioRule<>(MainActivity.class); + + private static Configuration configuration; + private static ListAppender appender; + private static Logger logger; + + @BeforeClass + public static void setup() { + LoggerContext context = LoggerContext.getContext(false); + configuration = context.getConfiguration(); + appender = configuration.getAppender("LIST"); + logger = context.getLogger(MainActivity.class); + } + + @Test + public void hasExpectedConfiguration() { + assertThat(configuration.getConfigurationSource().getLocation()) + .startsWith("jar:file:") + .endsWith("!/log4j2-test.xml"); + // Check appenders + Appender console = configuration.getAppender("CONSOLE"); + assertThat(console).as("Console Appender").isInstanceOf(ConsoleAppender.class); + Appender list = configuration.getAppender("LIST"); + assertThat(list).as("List Appender").isInstanceOf(ListAppender.class); + // Check logger configurations + assertThat(configuration.getLoggers()).hasSize(2); + assertThat(configuration.getRootLogger().getExplicitLevel()).isEqualTo(Level.INFO); + assertThat(configuration.getRootLogger().getAppenders()) + .hasSize(2) + .containsExactly(entry("CONSOLE", console), entry("LIST", list)); + assertThat(configuration.getLoggerConfig(MainActivity.class.getName())) + .isNotNull() + .extracting(LoggerConfig::getExplicitLevel) + .isEqualTo(Level.DEBUG); + } + + @Test + public void logMessagesAreDelivered() { + assertThat(logger.getLevel()).isEqualTo(Level.DEBUG); + appender.clear(); + for (int buttonId : MainActivity.buttonIds) { + onView(withId(buttonId)).perform(click()); + } + Level[] expectedLevels = {Level.FATAL, Level.ERROR, Level.WARN, Level.INFO, Level.DEBUG}; + String[] expectedMessages = + Arrays.stream(expectedLevels).map(l -> l + "-Hello " + l + "!").toArray(String[]::new); + assertThat(appender.getMessages()).hasSize(expectedLevels.length).containsExactly(expectedMessages); + } + + @Test + public void logLevelChanges() { + assertThat(logger.getLevel()).isEqualTo(Level.DEBUG); + onView(withId(R.id.setLogLevelBtn)).perform(click()); + onView(withText("Set log level")).check(matches(isDisplayed())); + onView(withText("ERROR")).perform(click()); + onView(withText("Select")).perform(click()); + onView(withText("Set log level")).check(doesNotExist()); + assertThat(logger.getLevel()).isEqualTo(Level.ERROR); + } + + private static Map.Entry entry(String name, Appender appender) { + return new AbstractMap.SimpleImmutableEntry<>(name, appender); + } +} \ No newline at end of file diff --git a/log4j-samples-android/app/src/androidTest/resources/log4j2-test.xml b/log4j-samples-android/app/src/androidTest/resources/log4j2-test.xml new file mode 100644 index 00000000..c6b153c2 --- /dev/null +++ b/log4j-samples-android/app/src/androidTest/resources/log4j2-test.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/log4j-samples-android/app/src/main/AndroidManifest.xml b/log4j-samples-android/app/src/main/AndroidManifest.xml index 90140f5c..2b5ba9eb 100644 --- a/log4j-samples-android/app/src/main/AndroidManifest.xml +++ b/log4j-samples-android/app/src/main/AndroidManifest.xml @@ -16,8 +16,7 @@ ~ limitations under the License. --> + xmlns:tools="http://schemas.android.com/tools"> logLevels = Arrays.asList( + Level.OFF, Level.FATAL, Level.ERROR, Level.WARN, Level.INFO, Level.DEBUG, Level.TRACE, Level.ALL); - //Logger log = LogManager.getLogger(MainActivity.class); - Logger log = LogManager.getRootLogger(); + private int logLevelIdx; + private final Map buttonToLevel = new HashMap<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + logger.info("Creating Main Activity..."); setContentView(R.layout.activity_main); - /*// Manually read in log4j2.properties - LoggerContext context = (LoggerContext) LogManager.getContext(false); - try { - InputStream inputStream = getAssets().open("log4j2.properties"); - ConfigurationSource source = new ConfigurationSource(inputStream); - context.start(ConfigurationFactory.getInstance().getConfiguration(context, source)); - } catch (IOException e) { - e.printStackTrace(); - }*/ - - /*// Explicitly set the logging level for this logger to INFO - //Configurator.setLevel(log, Level.INFO); - LoggerConfig loggerConfig = context.getConfiguration().getLoggerConfig(log.getName()); - loggerConfig.setLevel(Level.INFO);*/ - - // - // UI bindings - // - //TextView displaying Log Level + // TextView displaying Log Level TextView logLevelTxt = findViewById(R.id.logLevelTxt); - logLevelTxt.setText(log.getLevel().name()); + logLevelTxt.setText(logger.getLevel().name()); - //Change log level + // Change log level Button setLogLevelBtn = findViewById(R.id.setLogLevelBtn); setLogLevelBtn.setOnClickListener(v -> { AlertDialog.Builder alertDialog = new AlertDialog.Builder(this); alertDialog.setTitle("Set log level"); - String[] logLevels = new String[]{"OFF", "FATAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE", "ALL"}; - logLevelIdx = Arrays.asList(logLevels).indexOf(log.getLevel().name()); + logLevelIdx = logLevels.indexOf(logger.getLevel()); - alertDialog.setSingleChoiceItems(logLevels, logLevelIdx, (dialog, which) -> logLevelIdx = which); + alertDialog.setSingleChoiceItems( + logLevels.stream().map(Level::name).toArray(String[]::new), + logLevelIdx, + (dialog, which) -> logLevelIdx = which); alertDialog.setPositiveButton("Select", (dialog, which) -> { - Configurator.setLevel(log, Level.valueOf(Arrays.asList(logLevels).get(logLevelIdx))); - logLevelTxt.setText(log.getLevel().name()); + Configurator.setLevel(logger, logLevels.get(logLevelIdx)); + logLevelTxt.setText(logger.getLevel().name()); dialog.dismiss(); }); alertDialog.setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss()); @@ -84,23 +80,20 @@ protected void onCreate(Bundle savedInstanceState) { alertDialog.show(); }); - //Log messages - Button fatalLogBtn = findViewById(R.id.fatalLogBtn); - fatalLogBtn.setOnClickListener(v -> log.fatal("Current log level is: " + log.getLevel().name())); - - Button errorLogBtn = findViewById(R.id.errorLogBtn); - errorLogBtn.setOnClickListener(v -> log.error("Current log level is: " + log.getLevel().name())); - - Button warnLogBtn = findViewById(R.id.warnLogBtn); - warnLogBtn.setOnClickListener(v -> log.warn("Current log level is: " + log.getLevel().name())); - - Button infoLogBtn = findViewById(R.id.infoLogBtn); - infoLogBtn.setOnClickListener(v -> log.info("Current log level is: " + log.getLevel().name())); - - Button debugLogBtn = findViewById(R.id.debugLogBtn); - debugLogBtn.setOnClickListener(v -> log.debug("Current log level is: " + log.getLevel().name())); + // Log messages + for (int i = 0; i < buttonIds.length; i++) { + Button button = findViewById(buttonIds[i]); + // The `OFF` level should not have a button + buttonToLevel.put(button, logLevels.get(i + 1)); + button.setOnClickListener(this::onLevelButtonClick); + } + logger.info("Main Activity created."); + } - Button traceLogBtn = findViewById(R.id.traceLogBtn); - traceLogBtn.setOnClickListener(v -> log.trace("Current log level is: " + log.getLevel().name())); + void onLevelButtonClick(View view) { + Level level = buttonToLevel.get(view); + if (level != null) { + logger.log(level, "Hello {}!", level); + } } } \ No newline at end of file diff --git a/log4j-samples-android/app/src/main/res/values/strings.xml b/log4j-samples-android/app/src/main/res/values/strings.xml index 354a9791..e9ed6e24 100644 --- a/log4j-samples-android/app/src/main/res/values/strings.xml +++ b/log4j-samples-android/app/src/main/res/values/strings.xml @@ -16,5 +16,5 @@ ~ limitations under the License. --> - Log4jAPI + Log4j Core test \ No newline at end of file diff --git a/log4j-samples-android/app/src/main/assets/log4j2.properties b/log4j-samples-android/app/src/main/resources/log4j2.StatusLogger.properties similarity index 50% rename from log4j-samples-android/app/src/main/assets/log4j2.properties rename to log4j-samples-android/app/src/main/resources/log4j2.StatusLogger.properties index c05bb880..92ff5ca0 100644 --- a/log4j-samples-android/app/src/main/assets/log4j2.properties +++ b/log4j-samples-android/app/src/main/resources/log4j2.StatusLogger.properties @@ -15,24 +15,5 @@ # limitations under the License. # ## -# Root logger configuration -status = error -name = PropertiesConfig - -# Appenders -appender.console.type = Console -appender.console.name = Console -appender.console.target = SYSTEM_OUT -appender.console.layout.type = PatternLayout -appender.console.layout.pattern = [%d{yyyy-MM-dd HH:mm:ss.SSS}] %-5p- %m%n - -# Root logger level and appenders -rootLogger.level = INFO -rootLogger.appenderRefs = stdout -rootLogger.appenderRef.stdout.ref = Console - -logger.com.example.log4japi.MainActivity.name = com.example.log4japi.MainActivity -logger.com.example.log4japi.MainActivity.level = DEBUG -logger.com.example.log4japi.MainActivity.additivity = false -logger.com.example.log4japi.MainActivity.appenderRefs = stdout -logger.com.example.log4japi.MainActivity.appenderRef.stdout.ref = Console \ No newline at end of file +# Set the level to `WARN` +log4j2.statusLoggerLevel = WARN \ No newline at end of file diff --git a/log4j-samples-android/app/src/main/resources/log4j2.component.properties b/log4j-samples-android/app/src/main/resources/log4j2.component.properties new file mode 100644 index 00000000..d5010672 --- /dev/null +++ b/log4j-samples-android/app/src/main/resources/log4j2.component.properties @@ -0,0 +1,17 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to you 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. +# +## diff --git a/log4j-samples-android/app/src/main/resources/log4j2.xml b/log4j-samples-android/app/src/main/resources/log4j2.xml new file mode 100644 index 00000000..73eb743c --- /dev/null +++ b/log4j-samples-android/app/src/main/resources/log4j2.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/log4j-samples-android/app/src/test/java/org/apache/logging/log4j/samples/android/ExampleUnitTest.java b/log4j-samples-android/app/src/test/java/org/apache/logging/log4j/samples/android/ExampleUnitTest.java deleted file mode 100644 index 67d09bd7..00000000 --- a/log4j-samples-android/app/src/test/java/org/apache/logging/log4j/samples/android/ExampleUnitTest.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you 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 org.apache.logging.log4j.samples.android; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Example local unit test, which will execute on the development machine (host). - * - * @see Testing documentation - */ -public class ExampleUnitTest { - @Test - public void addition_isCorrect() { - assertEquals(4, 2 + 2); - } -} \ No newline at end of file diff --git a/log4j-samples-android/build.gradle b/log4j-samples-android/build.gradle index b50e0db7..10ca0539 100644 --- a/log4j-samples-android/build.gradle +++ b/log4j-samples-android/build.gradle @@ -15,10 +15,6 @@ * limitations under the License. */ plugins { - id 'com.android.application' version '7.2.1' apply false - id 'com.android.library' version '7.2.1' apply false + id 'com.android.application' version '8.2.0' apply false + id 'com.android.library' version '8.2.0' apply false } - -tasks.register('clean', Delete) { - delete rootProject.buildDir -} \ No newline at end of file diff --git a/log4j-samples-android/gradle.properties b/log4j-samples-android/gradle.properties index 1ddc0d28..63e8e741 100644 --- a/log4j-samples-android/gradle.properties +++ b/log4j-samples-android/gradle.properties @@ -16,4 +16,16 @@ # ## # Required by AndroidX dependencies -android.useAndroidX = true \ No newline at end of file +android.useAndroidX = true + +## +# Version of Log4j to use +# +# Defaults to the last stable 2.x release +log4j.version = 2.24.1 + +## +# Additional Maven Repository to use +# +# Defaults to Apache Snapshots Repository +log4j.repository.url = https://repository.apache.org/snapshots \ No newline at end of file diff --git a/log4j-samples-android/gradle/wrapper/gradle-wrapper.jar b/log4j-samples-android/gradle/wrapper/gradle-wrapper.jar index e708b1c0..7454180f 100644 Binary files a/log4j-samples-android/gradle/wrapper/gradle-wrapper.jar and b/log4j-samples-android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/log4j-samples-android/gradle/wrapper/gradle-wrapper.properties b/log4j-samples-android/gradle/wrapper/gradle-wrapper.properties index 8609210f..880b651a 100644 --- a/log4j-samples-android/gradle/wrapper/gradle-wrapper.properties +++ b/log4j-samples-android/gradle/wrapper/gradle-wrapper.properties @@ -16,7 +16,7 @@ # ## distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip distributionPath=wrapper/dists -zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/log4j-samples-android/gradlew b/log4j-samples-android/gradlew index 4f906e0c..1b6c7873 100755 --- a/log4j-samples-android/gradlew +++ b/log4j-samples-android/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,67 +17,101 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -106,80 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/log4j-samples-android/settings.gradle b/log4j-samples-android/settings.gradle index 5c076177..018f37e6 100644 --- a/log4j-samples-android/settings.gradle +++ b/log4j-samples-android/settings.gradle @@ -26,7 +26,14 @@ dependencyResolutionManagement { repositories { google() mavenCentral() + // Points to the correct Apache snapshot/staging repository + // + // See gradle.properties + maven { + name = 'Apache Repository' + url = providers.gradleProperty("log4j.repository.url") + } } } -rootProject.name = "Log4jAPI" +rootProject.name = "Log4j Core Android" include ':app'