This is an automated email from the ASF dual-hosted git repository.

freeandnil pushed a commit to branch Feature/239-Android-Support
in repository https://gitbox.apache.org/repos/asf/logging-log4net.git

commit 418062c39ad9c9945af7eeac100ef2ef65a72fe7
Author: Jan Friedrich <freeand...@apache.org>
AuthorDate: Tue Apr 29 13:04:28 2025 +0200

    #239 detect Android and use environment variables instead of AppSettings
---
 src/log4net.Tests/Util/SystemInfoTest.cs           |  7 +++-
 src/log4net/Appender/AdoNetAppender.cs             |  5 +--
 src/log4net/Core/DefaultRepositorySelector.cs      |  6 +--
 src/log4net/Util/SystemInfo.cs                     | 47 ++++++++++++++++++----
 .../modules/ROOT/pages/manual/configuration.adoc   | 10 ++++-
 5 files changed, 57 insertions(+), 18 deletions(-)

diff --git a/src/log4net.Tests/Util/SystemInfoTest.cs 
b/src/log4net.Tests/Util/SystemInfoTest.cs
index 8cd3fd75..9b9067c9 100644
--- a/src/log4net.Tests/Util/SystemInfoTest.cs
+++ b/src/log4net.Tests/Util/SystemInfoTest.cs
@@ -166,4 +166,9 @@ public void 
EqualsIgnoringCase_SameStringsDifferentCase_true()
   [Test]
   public void EqualsIgnoringCase_DifferentStrings_false()
     => Assert.That(SystemInfo.EqualsIgnoringCase("foo", "foobar"), Is.False);
-}
+
+  [Test]
+  [Platform(Include = "Win,Linux,MacOsX")]
+  public void IsAndoid()
+    => Assert.That(typeof(SystemInfo).GetProperty("IsAndroid", 
BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null), Is.False);
+}
\ No newline at end of file
diff --git a/src/log4net/Appender/AdoNetAppender.cs 
b/src/log4net/Appender/AdoNetAppender.cs
index 4453e39e..f720dfdf 100644
--- a/src/log4net/Appender/AdoNetAppender.cs
+++ b/src/log4net/Appender/AdoNetAppender.cs
@@ -449,10 +449,7 @@ protected override void SendBuffer(LoggingEvent[] events)
   /// Adds a parameter to the ordered list of command parameters.
   /// </para>
   /// </remarks>
-  public void AddParameter(AdoNetAppenderParameter parameter)
-  {
-    m_parameters.Add(parameter);
-  }
+  public void AddParameter(AdoNetAppenderParameter parameter) => 
m_parameters.Add(parameter);
 
   /// <summary>
   /// Writes the events to the database using the transaction specified.
diff --git a/src/log4net/Core/DefaultRepositorySelector.cs 
b/src/log4net/Core/DefaultRepositorySelector.cs
index f960a6e4..4cc14c88 100644
--- a/src/log4net/Core/DefaultRepositorySelector.cs
+++ b/src/log4net/Core/DefaultRepositorySelector.cs
@@ -469,10 +469,8 @@ public void AliasRepository(string repositoryAlias, 
ILoggerRepository repository
   /// Raises the <see cref="LoggerRepositoryCreatedEvent"/> event.
   /// </para>
   /// </remarks>
-  protected virtual void OnLoggerRepositoryCreatedEvent(ILoggerRepository 
repository)
-  {
-    LoggerRepositoryCreatedEvent?.Invoke(this, new 
LoggerRepositoryCreationEventArgs(repository));
-  }
+  protected virtual void OnLoggerRepositoryCreatedEvent(ILoggerRepository 
repository) 
+    => LoggerRepositoryCreatedEvent?.Invoke(this, new(repository));
 
   /// <summary>
   /// Gets the repository name and repository type for the specified assembly.
diff --git a/src/log4net/Util/SystemInfo.cs b/src/log4net/Util/SystemInfo.cs
index 6c0f05b7..30b23fdc 100644
--- a/src/log4net/Util/SystemInfo.cs
+++ b/src/log4net/Util/SystemInfo.cs
@@ -36,6 +36,11 @@ public static class SystemInfo
   private const string DefaultNullText = "(null)";
   private const string DefaultNotAvailableText = "NOT AVAILABLE";
 
+  /// <summary>
+  /// Is OperatingSystem Android
+  /// </summary>
+  internal static bool IsAndroid { get; } = IsAndroidCore();
+
   /// <summary>
   /// Initialize default values for private static fields.
   /// </summary>
@@ -68,6 +73,34 @@ static SystemInfo()
     NullText = nullText;
   }
 
+  private static bool IsAndroidCore() // 
https://stackoverflow.com/questions/47521008/how-can-i-distinguish-between-unix-and-android-on-netstandard-2-0
+  {
+    if (Environment.OSVersion.Platform != PlatformID.Unix)
+      return false;
+    using System.Diagnostics.Process process = new()
+    {
+      StartInfo = new()
+      {
+        FileName = "getprop",
+        Arguments = "ro.build.user",
+        RedirectStandardOutput = true,
+        UseShellExecute = false,
+        CreateNoWindow = true
+      }
+    };
+
+    try
+    {
+      process.Start();
+      string output = process.StandardOutput.ReadToEnd();
+      return !string.IsNullOrEmpty(output);
+    }
+    catch (Exception ex) when (!ex.IsFatal())
+    {
+      return false;
+    }
+  }
+
   /// <summary>
   /// Gets the system dependent line terminator.
   /// </summary>
@@ -414,10 +447,8 @@ public static string AssemblyFileName(Assembly myAssembly)
   /// then all the loaded assemblies will be searched for the type.
   /// </para>
   /// </remarks>
-  public static Type? GetTypeFromString(Type relativeType, string typeName, 
bool throwOnError, bool ignoreCase)
-  {
-    return GetTypeFromString(relativeType.EnsureNotNull().Assembly, typeName, 
throwOnError, ignoreCase);
-  }
+  public static Type? GetTypeFromString(Type relativeType, string typeName, 
bool throwOnError, bool ignoreCase) 
+    => GetTypeFromString(relativeType.EnsureNotNull().Assembly, typeName, 
throwOnError, ignoreCase);
 
   /// <summary>
   /// Loads the type specified in the type string.
@@ -438,10 +469,8 @@ public static string AssemblyFileName(Assembly myAssembly)
   /// in the assembly then all the loaded assemblies will be searched for the 
type.
   /// </para>
   /// </remarks>
-  public static Type? GetTypeFromString(string typeName, bool throwOnError, 
bool ignoreCase)
-  {
-    return GetTypeFromString(Assembly.GetCallingAssembly(), typeName, 
throwOnError, ignoreCase);
-  }
+  public static Type? GetTypeFromString(string typeName, bool throwOnError, 
bool ignoreCase) 
+    => GetTypeFromString(Assembly.GetCallingAssembly(), typeName, 
throwOnError, ignoreCase);
 
   /// <summary>
   /// Loads the type specified in the type string.
@@ -646,6 +675,8 @@ public static bool TryParse(string s, out short val)
   /// <returns>the value for the key, or <c>null</c></returns>
   public static string? GetAppSetting(string key)
   {
+    if (IsAndroid)
+      return Environment.GetEnvironmentVariable(key); // Android does not 
support config files
     try
     {
       return ConfigurationManager.AppSettings[key];
diff --git a/src/site/antora/modules/ROOT/pages/manual/configuration.adoc 
b/src/site/antora/modules/ROOT/pages/manual/configuration.adoc
index ccb1c9bc..fff7d2db 100644
--- a/src/site/antora/modules/ROOT/pages/manual/configuration.adoc
+++ b/src/site/antora/modules/ROOT/pages/manual/configuration.adoc
@@ -349,7 +349,6 @@ The section must specify the 
`log4net.Config.Log4NetConfigurationSectionHandler,
 
 The following is a simple example configuration file that specifies the 
correct section handler to use for the `log4net` section.
 
-
 [source,xml]
 ----
 <configuration>
@@ -427,6 +426,15 @@ The example shows how to embed configuration data in a 
.config file while allowi
 Since the .NET config parser throws errors for unregistered elements, the 
`log4net` section is registered using 
`System.Configuration.IgnoreSectionHandler`.
 This tells .NET to ignore the section, as it will be processed by log4net 
instead.
 
+[#android]
+== Android
+
+Android does not support `.config` files.
+
+Instead, you can set `AppSettings` values as environment variables for your 
process — this works only on Android.
+
+You must also load the `log4net` configuration manually, for example by 
reading it from an XML file at runtime.
+
 [#configuration-syntax]
 == Configuration Syntax
 

Reply via email to