Ответ 1
Я не знаю, как сделать это очень легко. Сказав это, все примеры, которые вы даете, доступны (или довольно легко доступны с помощью специального кода) для добавления в каждое сообщение журнала. То есть каждое зарегистрированное сообщение может быть помечено исполняемым именем, версией файла, датой выпуска, идентификатором пользователя Windows и т.д. Через Layout и LayoutRenderers.
Это, очевидно, не то же самое, что просто создать заголовок в верхней части файла журнала, поэтому он может быть вам не полезен.
С другой стороны, вы можете использовать технику, упомянутую в разделе "Ответ" в этом сообщении, чтобы связать несколько средств визуализации макета с одной и той же целью. Вы можете определить макет, который содержит поля, которые вы хотите в своем заголовке, и установить фильтр в FilteringWrapper только для применения этого макета для первого сообщения сеанса (или вы можете использовать какой-либо другой метод, который он добавляет в выходной файл только один раз).
Используя свой файл NLog.config, вы можете достичь того, чего хотите. Обратите внимание, что я не пробовал это, поэтому я не знаю, является ли этот файл конфигурации действительным или, если это так, если он будет генерировать нужные результаты.
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.mono2.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogLevel="Warn"
internalLogFile="nlog log.log"
>
<variable name="HeaderLayout" value="${processname} ${gdc:item=version} ${gdc:item=releasedate} ${windows-identity}" />
<variable name="NormalLayout" value="${longdate} ${logger} ${level} ${message} />
<targets async="true">
<target name="file" xsi:type="File" fileName="log.log"
layout="${NormalLayout}">
</target>
<target name="fileHeader" xsi:type="File" fileName="log.log"
layout="${HeaderLayout}">
</target>
</targets>
<rules>
<logger name="HeaderLogger" minlevel="Trace" writeTo="fileHeader" final="true" />
<logger name="*" minlevel="Trace" writeTo="file" />
</rules>
</nlog>
В вашем коде логика запуска может выглядеть так:
public void Main()
{
AddHeaderToLogFile();
}
public void AddHeaderToLogFile()
{
Logger headerlogger = LogManager.GetLogger("HeaderLogger");
//Use GlobalDiagnosticContext in 2.0, GDC in pre-2.0
GlobalDiagnosticContext["releasedate"] = GetReleaseDate();
GlobalDiagnosticContext["version"] = GetFileVersion();
GlobalDiagnosticContext["someotherproperty"] = GetSomeOtherProperty();
headerlogger.Info("message doesn't matter since it is not specified in the layout");
//Log file should now have the header as defined by the HeaderLayout
//You could remove the global properties now if you are not going to log them in any
//more messages.
}
Идея здесь в том, что вы ставите версию файла, дату выпуска и т.д. в GDC при запуске программы. Запишите сообщение с регистратором "HeaderLogger" . Это сообщение будет записано в файл журнала, используя "HeaderLayout", поскольку "HeaderLogger" связан с целью "fileHeader", которая связана с "HeaderLayout". Поля, определенные в макете заголовка, записываются в файл журнала. Сообщения журнала подпоследовательности, так как они не будут использовать "HeaderLogger" , будут использовать макет "root" (*). Они перейдут к одному файлу, так как цели "file" и "fileHeader" в конечном итоге указывают на одно и то же имя файла.
Прежде чем я начал набирать этот ответ, я не был уверен, насколько легко вы можете добавить заголовок в свой файл журнала. Набрав это, я думаю, что это может быть довольно легко!
Удачи!
[EDIT] Что-то вроде этого может работать, чтобы изменить макет на основе уровня. В первом разделе я определил несколько переменных, каждый из которых определяет макет. В следующем разделе я определил несколько целей, каждый из которых использует один и тот же файл, но фильтруется, чтобы разрешать только сообщения определенного уровня. В заключительном разделе я определяю одно правило, которое будет отправлять все сообщения (отсюда имя журнала "*" ) ко всем целям. Поскольку каждая цель фильтруется по уровню, цель "трассировка" будет писать только сообщения "трассировки" и т.д. Таким образом, сообщения "трассировка" будут записаны с использованием макета "трассировка" , сообщения "отладки" будут записаны с помощью "отладки", макет и т.д. Поскольку все цели в конечном счете записываются в один и тот же файл, все сообщения попадают в один и тот же файл. Я не пробовал это, но думаю, что он, вероятно, сработает.
<variable name="TraceLayout" value="THIS IS A TRACE: ${longdate} ${level:upperCase=true} ${message}" />
<variable name="DebugLayout" value="THIS IS A DEBUG: ${longdate} ${level:upperCase=true} ${message}" />
<variable name="InfoLayout" value="THIS IS AN INFO: ${longdate} ${level:upperCase=true} ${message}" />
<targets async="true">
<target name="fileAsTrace" xsi:type="FilteringWrapper" condition="level==LogLevel.Trace">
<target xsi:type="File" fileName="log.log" layout="${TraceLayout}" />
</target>
<target name="fileAsDebug" xsi:type="FilteringWrapper" condition="level==LogLevel.Debug">
<target xsi:type="File" fileName="log.log" layout="${DebugLayout}" />
</target>
<target name="fileAsInfo" xsi:type="FilteringWrapper" condition="level==LogLevel.Info">
<target xsi:type="File" fileName="log.log" layout="${InfoLayout}" />
</target>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="fileAsTrace, fileAsDebug, fileAsInfo" />
</rules>
(Обратите внимание, что здесь я включил только 3 уровня).
Показывая, как (если это работает, так или иначе) применять другой макет, основанный на уровне, это похоже на необычный вариант использования. Я не говорю, что это хорошая идея или плохая идея, но я не могу сказать, что я действительно видел это очень. В зависимости от того, как вы хотите, чтобы ваш конечный результат выглядел, я показал, что вы можете или не можете быть лучшим способом его достижения. Возможно, вы можете опубликовать несколько примеров того, как вы хотите, чтобы ваш результат выглядел.
Вы также можете подумать о принятии моего первоначального ответа, а затем задать новый вопрос об изменении формата вывода на уровень, чтобы мы могли сосредоточить обсуждение в этом вопросе на проблеме уровня/макета. Это зависит от вас, если это кажется полезным или нет.
Это работает:
<variable name="TraceLayout" value="This is a TRACE - ${longdate} | ${logger} | ${level} | ${message}"/>
<variable name="DebugLayout" value="This is a DEBUG - ${longdate} | ${logger} | ${level} | ${message}"/>
<variable name="InfoLayout" value="This is an INFO - ${longdate} | ${logger} | ${level} | ${message}"/>
<variable name="WarnLayout" value="This is a WARN - ${longdate} | ${logger} | ${level} | ${message}"/>
<variable name="ErrorLayout" value="This is an ERROR - ${longdate} | ${logger} | ${level} | ${message}"/>
<variable name="FatalLayout" value="This is a FATAL - ${longdate} | ${logger} | ${level} | ${message}"/>
<targets>
<target name="fileAsTrace" xsi:type="FilteringWrapper" condition="level==LogLevel.Trace">
<target xsi:type="File" fileName="xxx.log" layout="${TraceLayout}" />
</target>
<target name="fileAsDebug" xsi:type="FilteringWrapper" condition="level==LogLevel.Debug">
<target xsi:type="File" fileName="xxx.log" layout="${DebugLayout}" />
</target>
<target name="fileAsInfo" xsi:type="FilteringWrapper" condition="level==LogLevel.Info">
<target xsi:type="File" fileName="xxx.log" layout="${InfoLayout}" />
</target>
<target name="fileAsWarn" xsi:type="FilteringWrapper" condition="level==LogLevel.Warn">
<target xsi:type="File" fileName="xxx.log" layout="${WarnLayout}" />
</target>
<target name="fileAsError" xsi:type="FilteringWrapper" condition="level==LogLevel.Error">
<target xsi:type="File" fileName="xxx.log" layout="${ErrorLayout}" />
</target>
<target name="fileAsFatal" xsi:type="FilteringWrapper" condition="level==LogLevel.Fatal">
<target xsi:type="File" fileName="xxx.log" layout="${FatalLayout}" />
</target>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="fileAsTrace,fileAsDebug,fileAsInfo,fileAsWarn,fileAsError,fileAsFatal" />
<logger name="*" minlevel="Info" writeTo="dbg" />
</rules>
Я установил один макет для каждого уровня ведения журнала, добавив в начале литеральную строку, которая описывает уровень сообщения (это означает, что для каждого уровня используется другой формат). Каждый макет связан с фильтром FilteringWrapper, который фильтрует на основе уровня сообщения и направляет любые сообщения, которые передают фильтр для регистрации в выходном файле. Каждый фильтр FilteringWrapper обертывает один и тот же выходной файл, поэтому все сообщения журнала будут регистрироваться в одном файле.
Вот раздел кода, который я использовал для тестирования:
logger.Trace("Trace msg");
logger.Debug("Debug msg");
logger.Info("Info msg");
logger.Warn("Warn msg");
logger.Error("Error msg");
logger.Fatal("Fatal msg");
И вот как выглядит результат:
This is a TRACE - 2010-11-22 13:20:00.4131 | NLogTest.Form1 | Trace | Trace msg
This is a DEBUG - 2010-11-22 13:20:00.4131 | NLogTest.Form1 | Debug | Debug msg
This is an INFO - 2010-11-22 13:20:00.4131 | NLogTest.Form1 | Info | Info msg
This is a WARN - 2010-11-22 13:20:00.4131 | NLogTest.Form1 | Warn | Warn msg
This is an ERROR - 2010-11-22 13:20:00.4131 | NLogTest.Form1 | Error | Error msg
This is a FATAL - 2010-11-22 13:20:00.4131 | NLogTest.Form1 | Fatal | Fatal msg
По-видимому, проблема в моей предыдущей информации о конфигурации была пространством между значениями "writeTo"
. Я думаю, что NLog чувствителен к этому. У меня было что-то вроде "writeTo=blah1, blah2, blah3".
Когда я изменил это на "writeTo=blah1,blah2,blah3"
, ошибка исчезла.
Удачи!