C# 如何以编程方式更改文件位置?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1535736/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 18:31:22  来源:igfitidea点击:

How can I change the file location programmatically?

c#log4net

提问by

I am totally new to Log4net. I have managed to get something going by adding a config file and simple logging. I have hardcoded the value to be "C:\temp\log.txt"but this is not good enough.

我对 Log4net 完全陌生。通过添加配置文件和简单的日志记录,我设法使事情顺利进行。我已经对值进行了硬编码,"C:\temp\log.txt"但这还不够好。

The logs must go to the special folders

日志必须转到特殊文件夹

path = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);

and this path changes depending whether you are using Windows Server 2008 or Windows XP or Vista etc...

并且此路径会根据您使用的是 Windows Server 2008 还是 Windows XP 或 Vista 等而改变...

How can I just change the location of the file in log4net programmatically?

如何以编程方式更改文件在 log4net 中的位置?

This is what I have done:

这就是我所做的:

<configSections>
<section name="log4net"
         type="log4net.Config.Log4NetConfigurationSectionHandler,Log4net"/>
</configSections>
<log4net>         
    <root>
        <level value="DEBUG" />
        <appender-ref ref="LogFileAppender" />
    </root>
    <appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
        <param name="File" value="C:\temp\log.txt" />
        <param name="AppendToFile" value="true" />
        <rollingStyle value="Size" />
        <maxSizeRollBackups value="10" />
        <maximumFileSize value="10MB" />
        <staticLogFileName value="true" />
        <layout type="log4net.Layout.PatternLayout">
            <param name="ConversionPattern" value="%-5p%d{yyyy-MM-dd hh:mm:ss} – %m%n" />
        </layout>
    </appender>
</log4net>


class Program
{
    protected static readonly ILog log = LogManager.GetLogger(typeof(Program));

    static void Main(string[] args)
    {
        log4net.Config.XmlConfigurator.Configure();
        log.Warn("Log something");

        path = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);


        // How can I change where I log stuff?
    }
}

Just need to figure out how I can change to log stuff to where I want to.

只需要弄清楚如何更改以将内容记录到我想要的位置。

Any suggestions? Thanks a lot

有什么建议?非常感谢

采纳答案by Peter Lillevold

log4net can handle this for you. Any appender property of type string can be formatted, in this case, using the log4net.Util.PatternStringoption handler. PatternString even supports the SpecialFolderenum which enables the following elegant config:

log4net 可以为您处理这个问题。在这种情况下,可以使用log4net.Util.PatternString选项处理程序格式化任何 string 类型的 appender 属性。PatternString 甚至支持SpecialFolder枚举,它启用以下优雅的配置:

<appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender" >
    <file type="log4net.Util.PatternString" 
        value="%envFolderPath{CommonApplicationData}\test.txt" />
    ...
</appender>

Here's a unit test that proofs the pudding:

这是一个证明布丁的单元测试:

[Test]
public void Load()
{
    XmlConfigurator.Configure();
    var fileAppender = LogManager.GetRepository()
        .GetAppenders().First(appender => appender is RollingFileAppender);

    var expectedFile = 
        Path.Combine(
            Environment.GetFolderPath(
                Environment.SpecialFolder.CommonApplicationData),
                "test.txt");

    Assert.That(fileAppender, 
        Is.Not.Null & Has.Property("File").EqualTo(expectedFile));
}

The following test verifies that log4net actually writes to disk (which basically makes this an "integration" test, not a unit test, but we'll leave it at that for now):

以下测试验证 log4net 是否实际写入磁盘(这基本上使其成为“集成”测试,而不是单元测试,但我们暂时将其保留):

[Test]
public void Log4net_WritesToDisk()
{
    var expectedFile = 
        Path.Combine(
            Environment.GetFolderPath(
                Environment.SpecialFolder.CommonApplicationData),
                "test.txt");

    if (File.Exists(expectedFile))
        File.Delete(expectedFile);

    XmlConfigurator.Configure();

    var log = LogManager.GetLogger(typeof (ConfigTest));
    log.Info("Message from test");

    LogManager.Shutdown();

    Assert.That(File.ReadAllText(expectedFile), 
        Text.Contains("Message from test"));
}

NB: I strongly suggest using the compact property syntax demonstrated in the above sample. Removing all those "<property name=" makes your config that much more readable.

注意:我强烈建议使用上面示例中演示的紧凑属性语法。删除所有这些 "<property name=" 使您的配置更具可读性。

回答by Joe

As an alternative to doing this programatically, you can use environment variables and customizable patterns in the configuration file. See this response to a similar question.

作为以编程方式执行此操作的替代方法,您可以在配置文件中使用环境变量和可自定义模式。 请参阅对类似问题的回复

Look at "PatternString for pattern based configuration" in the Log4Net V1.2.10 release notes.

查看Log4Net V1.2.10 发行说明“PatternString for pattern based configuration” 。

Also if you are thinking of writing to a directory such as Enviroment.SpecialFolder.CommonApplicationData you need to consider:

此外,如果您正在考虑写入诸如 Enviroment.SpecialFolder.CommonApplicationData 之类的目录,则需要考虑:

  • Will all instances of your application for all users have write access to the log file? E.g. I don't believe non-administrators will be able to write to Enviroment.SpecialFolder.CommonApplicationData.

  • Contention if multiple instances of your application (for the same or different users) are attempting to the same file. You can use the "minimal locking model" (see http://logging.apache.org/log4net/release/config-examples.htmlto allow multiple processes to write to the same log file, but there probably will be a performance impact. Or you could give each process a different log file, e.g. by including the process id in the filename using a customizable pattern.

  • 是否所有用户的应用程序的所有实例都具有对日志文件的写访问权限?例如,我不相信非管理员将能够写入 Enviroment.SpecialFolder.CommonApplicationData。

  • 如果您的应用程序的多个实例(针对相同或不同的用户)尝试访问同一个文件,则会发生争用。您可以使用“最小锁定模型”(请参阅http://logging.apache.org/log4net/release/config-examples.html允许多个进程写入同一个日志文件,但可能会对性能产生影响。或者您可以为每个进程提供不同的日志文件,例如通过使用可自定义的模式在文件名中包含进程 ID。

回答by codeulike

Looks like Peter's answerdoesn't work for Log4net v1.2.10.0. An alternative method is described here.

看起来彼得的回答不适用于 Log4net v1.2.10.0。此处描述另一种方法。

Basically the method is to implement a custom pattern converter for the log4net config file.

基本上,该方法是为 log4net 配置文件实现自定义模式转换器。

First add this class to your project:

首先将此类添加到您的项目中:

public class SpecialFolderPatternConverter : log4net.Util.PatternConverter
{
    override protected void Convert(System.IO.TextWriter writer, object state)
    {
        Environment.SpecialFolder specialFolder = (Environment.SpecialFolder)Enum.Parse(typeof(Environment.SpecialFolder), base.Option, true);
        writer.Write(Environment.GetFolderPath(specialFolder));
    }
}

Then set up the File parameter of your FileAppender as follows:

然后设置 FileAppender 的 File 参数如下:

<file type="log4net.Util.PatternString">
    <converter>
      <name value="folder" />
      <type value="MyAppName.SpecialFolderPatternConverter,MyAppName" />
    </converter>
    <conversionPattern value="%folder{CommonApplicationData}\SomeOtherFolder\log.txt" />
  </file>

Basically the %foldertells it to look at the converter called folderwhich points it to the SpecialFolderPatternConverter class. It then calls Converton that class, passing in the CommonApplicationData (or whatever) enum value.

基本上%folder告诉它查看被调用的转换器,该转换器将folder它指向 SpecialFolderPatternConverter 类。然后调用Convert该类,传入 CommonApplicationData(或其他)枚举值。

回答by HymanAce

I found a mutation of this code in the interwebs:

我在互联网上发现了这段代码的一个突变:

XmlConfigurator.Configure();
log4net.Repository.Hierarchy.Hierarchy h =
(log4net.Repository.Hierarchy.Hierarchy) LogManager.GetRepository();
foreach (IAppender a in h.Root.Appenders)
{
    if (a is FileAppender)
    {
        FileAppender fa = (FileAppender)a;
        // Programmatically set this to the desired location here
        string logFileLocation = @"C:\MySpecialFolder\MyFile.log";

        // Uncomment the lines below if you want to retain the base file name
        // and change the folder name...
        //FileInfo fileInfo = new FileInfo(fa.File);
        //logFileLocation = string.Format(@"C:\MySpecialFolder\{0}", fileInfo.Name);

        fa.File = logFileLocation;
        fa.ActivateOptions();
        break;
    }
}

This works for me. Our application needs to put the log file in a folder that contains the version number of the app based on the AssemblyInfo.cs file.

这对我有用。我们的应用程序需要将日志文件放在包含基于 AssemblyInfo.cs 文件的应用程序版本号的文件夹中。

You should be able to set the logFileLocation programmatically (e.g. you can use Server.MapPath() if this is a web application) to suit your needs.

您应该能够以编程方式设置 logFileLocation(例如,如果这是一个 Web 应用程序,您可以使用 Server.MapPath())以满足您的需要。

回答by bshea

This worked for me:

这对我有用:

  <log4net>
    <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
..
      <file value="${APPDATA}\MyApp\MyApp Client\logs\Log.txt"/>
..
  </log4net>

If needing to write to special folders I found help here(2nd and 3rd example).

如果需要写入特殊文件夹,我在这里找到了帮助(第二个和第三个示例)。

Edit:

编辑:

To answer OP.. This works for 'all users' area:

回答 OP .. 这适用于“所有用户”区域:

      ...
      <file value="${ALLUSERSPROFILE}\MyApp\MyApp Client\logs\Log.txt"/>
      ...

Which is normally "C:\ProgramData" in newer versions of Windows.

在较新版本的 Windows 中,它通常是“C:\ProgramData”。

See these too:
How to specify common application data folder for log4net?== https://stackoverflow.com/a/1889591/503621and comments
&
https://superuser.com/q/405097/47628
https://stackoverflow.com/a/5550502/503621

请参阅这些:
如何为 log4net 指定通用应用程序数据文件夹?== https://stackoverflow.com/a/1889591/503621和评论
&
https://superuser.com/q/405097/47628
https://stackoverflow.com/a/5550502/503621

回答by Eric

How about a simple:

一个简单的怎么样:

XmlConfigurator.LogFullFilename = @"c:\ProgramData\MyApp\Myapp.log";

Why is it so complex to do a really simple thing?

为什么做一件非常简单的事情会如此复杂?

回答by user3529289

If you have to deploy to unknown systems and you want to use the simple solution by Philipp M even with different special folders you can retrieve the special folder path you want and set a custom env variable before loading the log4net configuration. string localData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); Environment.SetEnvironmentVariable("MY_FOLDER_DATA", localData); XmlConfigurator.Configure( ...

如果您必须部署到未知系统,并且即使使用不同的特殊文件夹,您也想使用 Philipp M 的简单解决方案,您可以检索所需的特殊文件夹路径并在加载 log4net 配置之前设置自定义 env 变量。 string localData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); Environment.SetEnvironmentVariable("MY_FOLDER_DATA", localData); XmlConfigurator.Configure( ...

... just to be sure the env variable exists and has the value you want.

...只是为了确保 env 变量存在并具有您想要的值。

回答by Jim109

To also change the error log's path (based on HymanAce's answer):

还要更改错误日志的路径(基于 HymanAce 的回答):

private static void SetLogPath(string path, string errorPath)
{
    XmlConfigurator.Configure();
    log4net.Repository.Hierarchy.Hierarchy h =
    (log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository();
    foreach (var a in h.Root.Appenders)
    {
        if (a is log4net.Appender.FileAppender)
        {
            if (a.Name.Equals("LogFileAppender"))
            { 
                log4net.Appender.FileAppender fa = (log4net.Appender.FileAppender)a;                    
                string logFileLocation = path; 
                fa.File = logFileLocation;                   
                fa.ActivateOptions();
            }
            else if (a.Name.Equals("ErrorFileAppender"))
            {
                log4net.Appender.FileAppender fa = (log4net.Appender.FileAppender)a;
                string logFileLocation = errorPath;
                fa.File = logFileLocation;
                fa.ActivateOptions();
            }
        }
    }
}

回答by Gigo

Great use case for LINQs OfType<T>filter:

LINQOfType<T>过滤器的绝佳用例:

/// <summary>
/// Applies a transformation to the filenames of all FileAppenders.
/// </summary>
public static void ChangeLogFile(Func<string,string> transformPath)
{
    // iterate over all FileAppenders
    foreach (var fileAppender in LogManager.GetRepository().GetAppenders().OfType<FileAppender>())
    {
        // apply transformation to the filename
        fileAppender.File = transformPath(fileAppender.File);
        // notify the logging subsystem of the configuration change
        fileAppender.ActivateOptions();
    }
}

If the filename in the app.config is log.txtthis will change the log output to logs/some_name_log.txt:

如果 app.config 中的文件名是log.txt这会将日志输出更改为logs/some_name_log.txt

ChangeLogFile(path => Path.Combine("logs", $"some_name_{Path.GetFileName(path)}"));

To Answer the OPs original problem that would be:

回答 OP 的原始问题,即:

ChangeLogFile(path => Path.Combine(
    Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), path));

回答by Apfelkuacha

In the current version of Log4Net (2.0.8.0) you could simply use <file value="${ProgramData}\myFolder\LogFiles\" />for C:\ProgramData\..and ${LocalAppData}for C:\Users\user\AppData\Local\

在当前版本的 Log4Net (2.0.8.0) 中,您可以简单地使用<file value="${ProgramData}\myFolder\LogFiles\" />forC:\ProgramData\..${LocalAppData}forC:\Users\user\AppData\Local\