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

发布于 2024-08-06 20:45:16 字数 1791 浏览 4 评论 0原文

我对 Log4net 完全陌生。我已经设法通过添加配置文件和简单的日志记录来完成一些事情。我已将值硬编码为 "C:\temp\log.txt" 但这还不够好。

日志必须转到特殊文件夹

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

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

我怎样才能以编程方式更改 log4net 中文件的位置?

这就是我所做的:

<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?
    }
}

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

有什么建议吗? 多谢

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.

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...

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

待天淡蓝洁白时 2024-08-13 20:45:16

log4net 可以为您处理这个问题。任何字符串类型的附加器属性都可以格式化,在本例中,使用 log4net.Util.PatternString 选项处理程序。 PatternString 甚至支持 SpecialFolder 枚举,它支持以下优雅的配置:

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

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

[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));
}

以下测试验证 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"));
}

注意:我强烈建议使用上面示例中演示的紧凑属性语法。删除所有这些“

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

<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):

[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.

你不是我要的菜∠ 2024-08-13 20:45:16

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

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;
    }
}

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

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

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.

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.

山川志 2024-08-13 20:45:16

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

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

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

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));
    }
}

然后按如下方式设置 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>

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

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

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

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:

<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 %folder tells it to look at the converter called folder which points it to the SpecialFolderPatternConverter class. It then calls Convert on that class, passing in the CommonApplicationData (or whatever) enum value.

吃不饱 2024-08-13 20:45:16

简单的怎么样:

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

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

How about a simple:

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

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

勿挽旧人 2024-08-13 20:45:16

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

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();
            }
        }
    }
}

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

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();
            }
        }
    }
}
筱武穆 2024-08-13 20:45:16

这对我有用:

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

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

编辑:

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

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

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

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

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:

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

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

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

浪荡不羁 2024-08-13 20:45:16

JackAce的答案,使用Linq更简洁:

C#

XmlConfigurator.Configure();
var appender = (LogManager.GetRepository() as Hierarchy).Root.Appenders
    .OfType<FileAppender>()
    .First();

appender.File = logPath;
appender.ActivateOptions();

VB.NET

XmlConfigurator.Configure()
Dim appender = CType(LogManager.GetRepository(), Hierarchy).Root.Appenders _
    .OfType(FileAppender)() _
    .First()

appender.File = logPath
appender.ActivateOptions()

JackAce's answer, just more concise using Linq:

C#

XmlConfigurator.Configure();
var appender = (LogManager.GetRepository() as Hierarchy).Root.Appenders
    .OfType<FileAppender>()
    .First();

appender.File = logPath;
appender.ActivateOptions();

VB.NET

XmlConfigurator.Configure()
Dim appender = CType(LogManager.GetRepository(), Hierarchy).Root.Appenders _
    .OfType(FileAppender)() _
    .First()

appender.File = logPath
appender.ActivateOptions()
不打扰别人 2024-08-13 20:45:16

LINQ 的绝佳用例 OfType过滤器:

/// <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();
    }
}

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

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

:回答OP的原始问题是:

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

Great use case for LINQs OfType<T> filter:

/// <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.txt this will change the log output to logs/some_name_log.txt:

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

To Answer the OPs original problem that would be:

ChangeLogFile(path => Path.Combine(
    Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), path));
安静被遗忘 2024-08-13 20:45:16

在当前版本的 Log4Net (2.0.8.0) 中,您可以简单地使用 表示 C:\ProgramData\ ..${LocalAppData} 用于 C:\Users\user\AppData\Local\

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\

旧情别恋 2024-08-13 20:45:16

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

请参阅Log4Net V1.2.10 发行说明<中的“基于模式的配置的PatternString” /a>.

此外,如果您正在考虑写入 Enviroment.SpecialFolder.CommonApplicationData 等目录,您需要考虑:

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.

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

  • 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.html to 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.

毁梦 2024-08-13 20:45:16

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

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

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( ...

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

深空失忆 2024-08-13 20:45:16

那些想要将文件路径设置为项目根目录路径的人可以参考下面的代码。 .NET 5 API 项目更改中实现了此功能

我已在Log4net.config 文件的 。请记住 type="log4net.Util.PatternString" 这行非常重要

<appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
<file type="log4net.Util.PatternString" value="%property{LoggerFilePath}\Logs\server.log" />
<appendToFile value="true" />
<maximumFileSize value="100KB" />
<maxSizeRollBackups value="2" />
<layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%date %5level %logger.%method [%line] - MESSAGE: %message%newline %exception" />
</layout>

然后,在 Program.cs 文件中

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureLogging((context,loggerBuilder) => 
        {
            log4net.GlobalContext.Properties["LoggerFilePath"] = context.HostingEnvironment.ContentRootPath;
            loggerBuilder.ClearProviders();
            loggerBuilder.AddLog4Net();
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

谢谢!

Those who are looking to set the file path to Project root dicrectory Path can refer below code. I have implemented this in .NET 5 API Project

Changes for Log4net.config file. Remember type="log4net.Util.PatternString" this line is very important

<appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
<file type="log4net.Util.PatternString" value="%property{LoggerFilePath}\Logs\server.log" />
<appendToFile value="true" />
<maximumFileSize value="100KB" />
<maxSizeRollBackups value="2" />
<layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%date %5level %logger.%method [%line] - MESSAGE: %message%newline %exception" />
</layout>

Then, in Program.cs file

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureLogging((context,loggerBuilder) => 
        {
            log4net.GlobalContext.Properties["LoggerFilePath"] = context.HostingEnvironment.ContentRootPath;
            loggerBuilder.ClearProviders();
            loggerBuilder.AddLog4Net();
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

Thanks!

記憶穿過時間隧道 2024-08-13 20:45:16
//Make sure you include System.Linq
//Though is an answer to old question, 
//but more concise, just two lines of code. 
((FileAppender)LogManager.GetRepository().GetAppenders().Select(a => a).First()).File = @"C:\log.txt";
((FileAppender)LogManager.GetRepository().GetAppenders().Select(a => a).First()).ActivateOptions();
//Make sure you include System.Linq
//Though is an answer to old question, 
//but more concise, just two lines of code. 
((FileAppender)LogManager.GetRepository().GetAppenders().Select(a => a).First()).File = @"C:\log.txt";
((FileAppender)LogManager.GetRepository().GetAppenders().Select(a => a).First()).ActivateOptions();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文