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'