Programing

프로그래밍 방식으로 파일 위치를 어떻게 변경할 수 있습니까?

lottogame 2020. 10. 29. 07:46
반응형

프로그래밍 방식으로 파일 위치를 어떻게 변경할 수 있습니까?


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

내가 원하는 곳에 기록하도록 변경하는 방법을 알아 내면됩니다.

어떤 제안? 고마워


log4net이이를 처리 할 수 ​​있습니다. string 유형의 모든 appender 속성은이 경우 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"));
}

NB : 위의 샘플에서 보여준 압축 속성 구문을 사용하는 것이 좋습니다. "<property name ="을 모두 제거하면 구성을 훨씬 더 읽기 쉽게 만들 수 있습니다.


인터 웹에서이 코드의 변형을 발견했습니다.

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을 프로그래밍 방식으로 설정할 수 있어야합니다 (예 : 웹 애플리케이션 인 경우 Server.MapPath ()를 사용할 수 있음).


과 같은 베드로의 대답은 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>

기본적으로는 SpecialFolderPatternConverter 클래스를 가리키는 %folder변환기를 보도록 지시합니다 folder. 그런 다음 Convert해당 클래스 를 호출 하여 CommonApplicationData (또는 기타) 열거 형 값을 전달합니다.


간단한 방법 :

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

정말 간단한 일을하는 것이 왜 그렇게 복잡한가요?


이것은 나를 위해 일했습니다.

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

특수 폴더에 쓸 필요가있는 경우 여기 에서 도움말을 찾았 습니다 (두 번째 및 세 번째 예).

편집하다:

OP에 대답하려면 .. 이것은 '모든 사용자'영역에서 작동합니다.

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

새 버전의 Windows에서는 일반적으로 "C : \ ProgramData"입니다.

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


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

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

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

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\


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.


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.

참고URL : https://stackoverflow.com/questions/1535736/how-can-i-change-the-file-location-programmatically

반응형