Как я могу изменить расположение файла программным путем?
Я абсолютно не знаком с 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?
}
}
Просто нужно выяснить, как я могу изменить материал журнала туда, где хочу.
Любые предложения?
Большое спасибо
Ответы
Ответ 1
log4net может справиться с этим для вас. Любое свойство appender строки типа может быть отформатировано, в данном случае с помощью log4net.Util.PatternString. PatternString даже поддерживает SpecialFolder enum, который позволяет использовать следующую элегантную конфигурацию:
<appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender" >
<file type="log4net.Util.PatternString"
value="%envFolderPath{CommonApplicationData}\\test.txt" />
...
</appender>
Здесь a unit test, который доказывает пудинг:
[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 фактически записывает на диск (что в основном делает это "интеграционным" тестом, а не unit test, но мы оставим его на этом пока):
[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: Я настоятельно рекомендую использовать синтаксис свойств compact, продемонстрированный в приведенном выше примере. Удаление всех этих "< property name=" делает вашу конфигурацию более читаемой.
Ответ 2
Я нашел мутацию этого кода в 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;
}
}
Это работает для меня. Наше приложение должно поместить файл журнала в папку, содержащую номер версии приложения, на основе файла AssemblyInfo.cs.
Вы должны быть в состоянии установить logFileLocation программно (например, вы можете использовать Server.MapPath(), если это веб-приложение) в соответствии с вашими потребностями.
Ответ 3
Похоже, ответ Peter не работает для 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 (или любого другого).
Ответ 4
Как насчет простой:
XmlConfigurator.LogFullFilename = @"c:\ProgramData\MyApp\Myapp.log";
Почему так сложно сделать действительно простую вещь?
Ответ 5
Это сработало для меня:
<log4net>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
..
<file value="${APPDATA}\MyApp\MyApp Client\logs\Log.txt"/>
..
</log4net>
Если вам нужно писать в специальные папки, я нашел help здесь (2-й и 3-й примеры).
Edit:
Ответ на OP.. Это работает для области "всех пользователей":
...
<file value="${ALLUSERSPROFILE}\MyApp\MyApp Client\logs\Log.txt"/>
...
Обычно это "C:\ProgramData" в новых версиях Windows.
Смотрите также:
Как указать общую папку данных приложения для log4net? == fooobar.com/questions/84509/... и комментарии
& Амп;
https://superuser.com/q/405097/47628
fooobar.com/questions/84510/...
Ответ 6
Также изменить путь журнала ошибок (на основе ответа 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();
}
}
}
}
Ответ 7
В качестве альтернативы для этого программно, вы можете использовать переменные среды и настраиваемые шаблоны в файле конфигурации. См. ответ на аналогичный вопрос.
Посмотрите на "PatternString для конфигурации на основе шаблонов" в заметки о выпуске Log4Net V1.2.10.
Также, если вы подумываете писать в такой каталог, как Enviroment.SpecialFolder.CommonApplicationData, вам нужно подумать:
-
Будут ли все экземпляры вашего приложения для всех пользователей иметь доступ на запись в файл журнала? Например. Я не верю, что не-администраторы смогут писать в Enviroment.SpecialFolder.CommonApplicationData.
-
Конфликт, если несколько экземпляров вашего приложения (для тех же или разных пользователей) пытаются использовать один и тот же файл. Вы можете использовать "минимальную блокирующую модель" (см. http://logging.apache.org/log4net/release/config-examples.html, чтобы позволить нескольким процессам записывать в один и тот же файл журнала, но, вероятно, это повлияет на производительность. Или вы можете дать каждому процессу другой файл журнала, например, включив идентификатор процесса в имя файла с помощью настраиваемого шаблона.
Ответ 8
Если вам нужно развернуть неизвестные системы, и вы хотите использовать простое решение от Philipp M даже с различными специальными папками, вы можете получить специальный путь к папке, который вы хотите, и установить пользовательскую переменную env перед загрузкой конфигурации log4net.
string localData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
Environment.SetEnvironmentVariable("MY_FOLDER_DATA", localData);
XmlConfigurator.Configure( ...
... чтобы убедиться, что переменная env существует и имеет нужное значение.
Ответ 9
Большой вариант использования для LINQs OfType<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();
}
}
Если имя файла в app.config равно log.txt
, это изменит выход журнала на logs/some_name_log.txt
:
ChangeLogFile(path => Path.Combine("logs", $"some_name_{Path.GetFileName(path)}"));
Чтобы ответить на исходную проблему OPs, которая будет:
ChangeLogFile(path => Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), path));