log4net - 设置 XML 配置以使用运行时指定的 FileAppender 路径

log4net - set up XML config to use runtime-specified path for FileAppender

我们正在构建一个 Unity 游戏客户端,它将在(至少)Windows、OS X 和 iOS 上 运行。我正在使用 log4net 来处理日志记录,我想使用 FileAppender 将错误记录到一个文件中以协助错误报告。我想将此文件放在 UnityEngine.Application.persistentDataPath 指向的任何目录中,这样无论 OS 如何,文件都会出现在合理的地方,我正在努力寻找一种将其插入 [=29] 的方法=] 配置我传递给 log4net 的 XMLConfigurator.

我可以设置一个虚拟路径并在 运行 时更改它,但是(除了笨拙之外)这有一个缺点,即到我可以更改 FileAppender 的设置时,它已经在我放入 XML 配置的任何位置创建了一个空日志文件,这意味着我之后需要清理空文件。

那么,有没有办法在 XML 配置文件中将 FileAppender 指向 UnityEngine.Application.persistentDataPath

您可以创建自定义 PatternConverter 以允许您执行此操作

XMLConfigurator 在运行时解析 %someString 变量,所以你可以设置自定义 PatternConverter 到 return UnityEngine.Application.persistentDataPath 的值,并设置向上 XML 以便配置器将使用它来解析您的变量之一。

所以,您需要这样的 class:

public class PersistentDataStoreConverter : PatternConverter {
    protected override void Convert(TextWriter writer, object state) {
        writer.Write(UnityEngine.Application.persistentDataPath);
    }
}

然后您可以在 XML 配置中使用该转换器,如下所示:

<appender name="Logfile" type="log4net.Appender.FileAppender">
    <file type="log4net.Util.PatternString">
        <converter>
            <!-- Tell the configurator that this is the converter for "%persistentDataStore" below -->
            <name value="persistentDataStore" />
            <!-- Tell the converter what to call to do the conversion -->
            <type value="Namespace.PersistentDataStoreConverter, AssemblyName" />
        </converter>
        <conversionPattern value="%persistentDataStore/logfile-%date{yyyy-MM-dd-HH-mm-ss}.log" />
    </file>
    <!-- Also specify layout etc... -->
</appender>

一个常见的方法是设置一个 属性 ,它在配置期间被评估:注意 file 属性必须设置为类型 PatternString 才能评估变量:

[TestMethod]
public void Set_Log_Path_At_Runtime()
{
    log4net.GlobalContext.Properties["logpath"] =
    Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Test.log");

     // Use stream as is convenient for a unit test, in normal usage
     // would probably want to use the overload that takes a fileinfo.
     using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(config)))
            XmlConfigurator.Configure(ms);

    var appender = log4net.LogManager.GetRepository()
                .GetAppenders()
                .OfType<RollingFileAppender>()
                .First();

    Console.WriteLine(appender.File);
}

private const string config = @"<?xml version=""1.0"" encoding=""utf-8"" ?>

        <log4net debug=""false"">

          <appender name=""RollingFileAppender"" type=""log4net.Appender.RollingFileAppender"">
        <file type=""log4net.Util.PatternString"" value=""%property{logpath}"" />
        <lockingModel type=""log4net.Appender.FileAppender+MinimalLock"" />
        <layout type=""log4net.Layout.PatternLayout"">
          <conversionPattern value=""%date [%thread] %-5level %logger [%property{NDC}] - %message%newline"" />
        </layout>
          </appender>
          <root>
        <level value=""ALL"" />
        <appender-ref ref=""RollingFileAppender"" />
          </root>
        </log4net>";

因为这是使用 the overload of Configure that takes a Stream,将 XML 加载到变量中,将文件位置从占位符更改为实际位置,并将其作为流传递到已配置。