Як я можу програмно змінити розташування файлу?


75

Я абсолютно новачок у 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?
    }
}

Потрібно лише зрозуміти, як я можу змінити журнали матеріалів туди, куди хочу.

Будь-які пропозиції? Дуже дякую


Вам потрібно перекопати IAppenders вашого реєстратора та встановити FileAppender.File на потрібний вихідний шлях. Ось справді хороший приклад .
Cam Soper

Відповіді:


89

log4net може впоратися з цим. Будь-яку властивість appender типу string можна відформатувати, в цьому випадку, використовуючи обробник опцій 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 =" робить вашу конфігурацію набагато зручнішою для читання.


1
Привіт Дякую. Я спробував це, але досі не входив до програми. Дані. Я подивився Інтернет. Я знайшов щось, що називається PatternString, але я не знаю, як ним користуватися. Хтось мав приклад?

@brix - Моя початкова відповідь не спрацювала, тому мені довелося довести собі, що log4net може це зробити. PatternString - це рішення :)
Пітер Ліллевольд,

2
% envFolderPath {}, здається, не є частиною поточного (1.2.10) випуску log4net. Мені довелося витягнути r606477 із їхнього сховища диверсій, щоб увімкнути цю дуже корисну функцію. Я би очікував, що це буде включено до наступного (1.2.11) випуску.
Родні Шулер,

1
@rschuler: Я додав відповідь, яка буде працювати з 1.2.10, див. нижче
codeulike

3
З відповіді @ JackAce нижче, після скидання шляху до файлу, обов’язково зателефонуйте ".ActivateOptions ()", щоб активувати його. Виклик .file встановить його, але він не використовуватиметься до виклику ActivateOptions
Джеффрі Найт,

46

Я знайшов мутацію цього коду в інтернетах:

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 (), якщо це веб-програма) відповідно до ваших потреб.


4
Я відчуваю, що це дає набагато більше контролю над розташуванням файлу.
fregas

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

Потім налаштуйте параметр File вашого FileAppender наступним чином:

<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 (або інше).


Дякую за це. До речі, додаток у MyAppName, на мою думку, стосується проекту, а не програми.
Jamie Kitson

Це єдиний варіант, який працює, якщо ви переглядаєте файл log4net.config для внесення змін ( logging.apache.org/log4net/release/sdk/html/… )
cbp

7

Як щодо простого:

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

Чому так складно робити справді просту справу?


12
Тому що шлях змінюється залежно від того, перебуваєте ви у Windows Server 2008, winxp чи vista тощо ... У XP немає папки c: \ ProgramData, лише в Win7 / Vista. Отже, якщо ми збираємось розгортати цей додаток, він повинен бути сумісним для всіх ОС. :-) Жорстке кодування - це не найкраща практика.
Іршад,

7
Я використовую log4net v2.0.3, і ця властивість недоступна в XmlConfigurator. Повинні бути видалені з моменту розміщення відповіді.
Appetere

Дякую Джейсоне за виправлення моєї англійської мови, це не моя основна мова;) Дякую!
Ерік

@Irshad: так, але ми можемо прочитати конфігурацію з бази даних (наприклад). Якщо ми можемо змінити цей шлях програмно, іноді це краще
Хоанг Лонг

4

Це спрацювало для мене:

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

Якщо потрібно написати в спеціальні папки, я знайшов тут допомогу (2-й та 3-й приклад).

Редагувати:

Щоб відповісти на ОП. Це працює для області "всіх користувачів":

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

Що зазвичай є "C: \ ProgramData" у новіших версіях Windows.

Дивіться також це:
Як вказати загальну папку даних програми для log4net? == https://stackoverflow.com/a/1889591/503621 та коментарі
&
https://superuser.com/q/405097/47628
https://stackoverflow.com/a/5550502/503621


Environment.SpecialFolder.CommonApplicationDataі $(APPDATA)не однакові папки
Себастьян Неграс

Ви також можете спробувати "$ {ALLUSERSPROFILE}". .
bshea

3

Щоб також змінити шлях журналу помилок (на основі відповіді 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();
            }
        }
    }
}

3

Відповідь 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()

2

Чудовий варіант використання OfType<T>фільтра LINQ :

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

Коли я це зробив, він просто заново створив порожній файл з новою назвою, але потім Logger.GetLoggerпродовжував писати на стару назву.
Джей Салліван

1

Як альтернативу програмній роботі ви можете використовувати змінні середовища та настроювані шаблони у файлі конфігурації. Дивіться цю відповідь на подібне запитання .

Подивіться на "PatternString для конфігурації на основі шаблону" у примітках до випуску Log4Net V1.2.10 .

Крім того, якщо ви думаєте писати в каталог, такий як Enviroment.SpecialFolder.CommonApplicationData, вам потрібно врахувати:

  • Чи всі екземпляри вашої програми для всіх користувачів матимуть доступ до запису до журналу? Наприклад, я не вірю, що адміністратори не зможуть писати до Enviroment.SpecialFolder.CommonApplicationData.

  • Суперечка, якщо кілька екземплярів вашої програми (для одного або різних користувачів) намагаються здійснити один і той же файл. Ви можете використовувати "мінімальну модель блокування" (дивhttp://logging.apache.org/log4net/release/config-examples.html, щоб дозволити декільком процесам писати в один і той же файл журналу, але, ймовірно, це вплине на продуктивність. Або ви можете надати кожному процесу інший файл журналу, наприклад, включивши ідентифікатор процесу до імені файлу, використовуючи настроюваний шаблон.


1

У поточній версії Log4Net (2.0.8.0) ви можете просто використовувати <file value="${ProgramData}\myFolder\LogFiles\" />для C:\ProgramData\..і ${LocalAppData}дляC:\Users\user\AppData\Local\


0

Якщо вам доводиться розгортатись у невідомих системах і ви хочете скористатися простим рішенням Philipp M, навіть з різними спеціальними папками, ви можете отримати потрібний шлях до спеціальної папки та встановити користувацьку змінну env перед завантаженням конфігурації log4net. string localData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); Environment.SetEnvironmentVariable("MY_FOLDER_DATA", localData); XmlConfigurator.Configure( ...

... лише для того, щоб переконатися, що змінна env існує та має значення, яке ви бажаєте.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.