Самые полезные конфигурации NLog [закрыто]


348

Каковы наилучшие или наиболее полезные конфигурации для входа с NLog? (Они могут быть простыми или сложными, если они полезны.)

Я имею в виду примеры, такие как автоматическое сворачивание файлов журнала определенного размера, изменение макета (сообщения журнала) независимо от того, есть ли исключение, повышение уровня журнала после возникновения ошибки и т. Д.

Вот несколько ссылок:


3
Вот несколько советов по настройке производительности, основанных на тестировании: deep-depth.blogspot.com/2014/01/…
Neil

Ответы:


391

Некоторые из них относятся к категории общих советов по NLog (или ведению журналов), а не только к рекомендациям по конфигурации.

Вот некоторые общие ссылки для регистрации отсюда в SO (вы, возможно, уже видели некоторые или все из них):

log4net против Nlog

Регистрация лучших практик

Какой смысл вырубки фасада?

Почему регистраторы рекомендуют использовать регистратор для каждого класса?

Используйте общий шаблон именования вашего регистратора в зависимости от класса Logger logger = LogManager.GetCurrentClassLogger() . Это дает вам высокую степень детализации в ваших регистраторах и дает вам большую гибкость в настройке регистраторов (управление глобально, пространством имен, определенным именем регистратора и т. Д.).

При необходимости используйте регистраторы, не основанные на именах классов. Возможно, у вас есть одна функция, для которой вы действительно хотите управлять регистрацией отдельно. Возможно, у вас есть общие проблемы с журналированием (регистрация производительности).

Если вы не используете журналирование на основе имени класса, рассмотрите возможность именования ваших регистраторов в какой-то иерархической структуре (возможно, по функциональной области), чтобы вы могли поддерживать большую гибкость в вашей конфигурации. Например, у вас может быть функциональная область «база данных», FA «анализа» и FA «ui». У каждого из них могут быть подобласти. Итак, вы можете запросить регистраторы, как это:

Logger logger = LogManager.GetLogger("Database.Connect");
Logger logger = LogManager.GetLogger("Database.Query");
Logger logger = LogManager.GetLogger("Database.SQL");
Logger logger = LogManager.GetLogger("Analysis.Financial");
Logger logger = LogManager.GetLogger("Analysis.Personnel");
Logger logger = LogManager.GetLogger("Analysis.Inventory");

И так далее. С помощью иерархических регистраторов вы можете настроить ведение журнала глобально («*» или корневой регистратор), по FA (база данных, анализ, пользовательский интерфейс) или по подрайонам (Database.Connect и т. Д.).

У логгеров есть много опций конфигурации:

<logger name="Name.Space.Class1" minlevel="Debug" writeTo="f1" /> 
<logger name="Name.Space.Class1" levels="Debug,Error" writeTo="f1" /> 
<logger name="Name.Space.*" writeTo="f3,f4" />
<logger name="Name.Space.*" minlevel="Debug" maxlevel="Error" final="true" /> 

Смотрите справку NLog для получения дополнительной информации о том, что конкретно означает каждый из параметров. Вероятно, наиболее заметными элементами здесь являются возможность подстановки правил ведения журнала, концепция, что несколько правил ведения журнала могут «выполняться» для одного оператора ведения журнала, и что правило ведения журнала может быть помечено как «окончательное», поэтому последующие правила не будут выполняться для данное заявление о регистрации.

Используйте GlobalDiagnosticContext, MappedDiagnosticContext и NestedDiagnosticContext, чтобы добавить дополнительный контекст к выходным данным.

Используйте «переменную» в вашем конфигурационном файле, чтобы упростить. Например, вы можете определить переменные для ваших макетов, а затем ссылаться на переменную в целевой конфигурации, а не указывать макет напрямую.

  <variable name="brief" value="${longdate} | ${level} | ${logger} | ${message}"/>
  <variable name="verbose" value="${longdate} | ${machinename} | ${processid} | ${processname} | ${level} | ${logger} | ${message}"/>
  <targets>
    <target name="file" xsi:type="File" layout="${verbose}" fileName="${basedir}/${shortdate}.log" />
    <target name="console" xsi:type="ColoredConsole" layout="${brief}" />
  </targets>

Или вы можете создать «пользовательский» набор свойств для добавления в макет.

  <variable name="mycontext" value="${gdc:item=appname} , ${mdc:item=threadprop}"/>
  <variable name="fmt1withcontext" value="${longdate} | ${level} | ${logger} | [${mycontext}] |${message}"/>
  <variable name="fmt2withcontext" value="${shortdate} | ${level} | ${logger} | [${mycontext}] |${message}"/>

Или, вы можете делать такие вещи, как создание "день" или "месяц" рендеринга макета строго через конфигурацию:

  <variable name="day" value="${date:format=dddd}"/>
  <variable name="month" value="${date:format=MMMM}"/>
  <variable name="fmt" value="${longdate} | ${level} | ${logger} | ${day} | ${month} | ${message}"/>
  <targets>
    <target name="console" xsi:type="ColoredConsole" layout="${fmt}" />
  </targets>

Вы также можете использовать макет рендера для определения вашего имени файла:

  <variable name="day" value="${date:format=dddd}"/>
  <targets>
    <target name="file" xsi:type="File" layout="${verbose}" fileName="${basedir}/${day}.log" />
  </targets>

Если вы прокручиваете свой файл ежедневно, каждый файл может называться «Monday.log», «Tuesday.log» и т. Д.

Не бойтесь написать свой собственный рендер. Это просто и позволяет вам добавить свою собственную контекстную информацию в файл журнала через конфигурацию. Например, вот средство визуализации макета (на основе NLog 1.x, а не 2.0), которое может добавить Trace.CorrelationManager.ActivityId в журнал:

  [LayoutRenderer("ActivityId")]
  class ActivityIdLayoutRenderer : LayoutRenderer
  {
    int estimatedSize = Guid.Empty.ToString().Length;

    protected override void Append(StringBuilder builder, LogEventInfo logEvent)
    {
      builder.Append(Trace.CorrelationManager.ActivityId);
    }

    protected override int GetEstimatedBufferSize(LogEventInfo logEvent)
    {
      return estimatedSize;
    }
  }

Скажите NLog, где ваши расширения NLog (какая сборка) похожи на это:

  <extensions>
    <add assembly="MyNLogExtensions"/>
  </extensions>

Используйте пользовательский макет рендеринга, как это:

  <variable name="fmt" value="${longdate} | ${ActivityId} | ${message}"/>

Используйте асинхронные цели:

<nlog>
  <targets async="true">
    <!-- all targets in this section will automatically be asynchronous -->
  </targets>
</nlog>

И целевые оболочки по умолчанию:

<nlog>  
  <targets>  
    <default-wrapper xsi:type="BufferingWrapper" bufferSize="100"/>  
    <target name="f1" xsi:type="File" fileName="f1.txt"/>  
    <target name="f2" xsi:type="File" fileName="f2.txt"/>  
  </targets>  
  <targets>  
    <default-wrapper xsi:type="AsyncWrapper">  
      <wrapper xsi:type="RetryingWrapper"/>  
    </default-wrapper>  
    <target name="n1" xsi:type="Network" address="tcp://localhost:4001"/>  
    <target name="n2" xsi:type="Network" address="tcp://localhost:4002"/>  
    <target name="n3" xsi:type="Network" address="tcp://localhost:4003"/>  
  </targets>  
</nlog>

где уместно. См. Документы NLog для получения дополнительной информации о них.

Скажите NLog, чтобы он посмотрел и автоматически перезагрузил конфигурацию, если она изменилась:

<nlog autoReload="true" /> 

Есть несколько вариантов конфигурации, которые помогут с устранением неполадок NLog

<nlog throwExceptions="true" />
<nlog internalLogFile="file.txt" />
<nlog internalLogLevel="Trace|Debug|Info|Warn|Error|Fatal" />
<nlog internalLogToConsole="false|true" />
<nlog internalLogToConsoleError="false|true" />

См. Справку NLog для получения дополнительной информации.

В NLog 2.0 добавлены обертки LayoutRenderer, которые позволяют выполнять дополнительную обработку на выходе средства визуализации макета (например, обрезать пробел, верхний регистр, нижний регистр и т. Д.).

Не бойтесь заворачивать регистратор, если вы хотите изолировать свой код от жесткой зависимости от NLog, но переносите его правильно. В github-репозитории NLog есть примеры того, как оборачиваться. Другая причина для переноса может заключаться в том, что вы хотите автоматически добавлять определенную контекстную информацию к каждому зарегистрированному сообщению (помещая ее в LogEventInfo.Context).

Есть плюсы и минусы в обертывании (или абстрагировании) NLog (или любой другой фреймворк логирования в этом отношении). Приложив немного усилий, вы можете найти много информации здесь на SO, представляющей обе стороны.

Если вы рассматриваете упаковку, подумайте об использовании Common.Logging . Он работает довольно хорошо и позволяет вам легко переключаться на другую структуру ведения журналов, если вы хотите это сделать. Также, если вы рассматриваете упаковку, подумайте, как вы будете обрабатывать объекты контекста (GDC, MDC, NDC). Common.Logging в настоящее время не поддерживает абстракцию для них, но он предположительно находится в очереди возможностей для добавления.


3
Отличный ответ. Следует добавить, что $ {machine} должен быть $ {machinename}. Смотрите github.com/nlog/NLog/wiki/Layout-Renderers .
Лян

2
Я разветвил Common.Logging и добавил недостающую абстракцию, см. Проект GitHub или NuGet .
Дэнни Варод

Мне не удалось найти ничего более информативного о nlog в их собственной документации, возможно, я неправильно просматривал примеры github? Кто знает.
JARRRRG

Как использовать этот пользовательский рендерер с API (без файла конфигурации)? Вот что я пытаюсь сделать.
InteXX

Хорошо понял. NewLineРаскладка выполняет задачу. Вот что я придумал. Это намного проще, чем я ожидал.
InteXX

65

Трактовать исключения по-разному

Мы часто хотим получить больше информации, когда есть исключение. Следующая конфигурация имеет две цели, файл и консоль, которые фильтруют, есть ли какая-либо информация об исключении. (РЕДАКТИРОВАТЬ: Jarek опубликовал о новом методе сделать это в vNext .)

Ключ должен иметь цель оболочки с xsi:type="FilteringWrapper" condition="length('${exception}')>0"

<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="VerboseLayout" 
              value="${longdate} ${level:upperCase=true} ${message}  
                    (${callsite:includSourcePath=true})"            />
    <variable name="ExceptionVerboseLayout"  
              value="${VerboseLayout} (${stacktrace:topFrames=10})  
                     ${exception:format=ToString}"                  />

    <targets async="true">
        <target name="file" xsi:type="File" fileName="log.log"
                layout="${VerboseLayout}">
        </target>

        <target name="fileAsException"  
                xsi:type="FilteringWrapper" 
                condition="length('${exception}')>0">
            <target xsi:type="File"  
                    fileName="log.log"  
                    layout="${ExceptionVerboseLayout}" />
        </target>

        <target xsi:type="ColoredConsole"
                name="console"
                layout="${NormalLayout}"/>

        <target xsi:type="FilteringWrapper"  
                condition="length('${exception}')>0"  
                name="consoleException">
            <target xsi:type="ColoredConsole" 
                    layout="${ExceptionVerboseLayout}" />
        </target>
    </targets>

    <rules>
        <logger name="*" minlevel="Trace" writeTo="console,consoleException" />
        <logger name="*" minlevel="Warn" writeTo="file,fileAsException" />
    </rules>

</nlog>

1
Это довольно круто с отдельной целью и FilteringWrapper для форматирования исключения. Я только что ответил на вопрос недавно от парня, который хотел включить рендеринг макета {исключение} в свой вывод, но он не хотел получать (), который, по-видимому, регистрируется, если нет НЕ исключения. Эта техника, вероятно, будет хорошо работать для него.
Wageoghe

+1 Очень мило. У меня была эта закладка в течение долгого времени, и меня сослались на «комментарий Пэта» из другого SO вопроса относительно условного размещения.
eduncan911

1
Если исключение зарегистрировано, оно будет зарегистрировано дважды (часть VerboseLayout).
Тиен До

2
Я только что попробовал это завтра в моем проекте, так как вы установили правило minlevel = "Warn" в "file, fileAsException", все журналы будут записываться сначала с целевым файлом (без фильтра), и если это исключение (как отфильтровано условие) это также будет зарегистрировано с fileAsException.
Тиен До

3
@ Tiendq О, я вижу. Это имеет смысл, хотя само исключение (во всех деталях) будет зарегистрировано только один раз (но его сообщение будет зарегистрировано дважды). Вы можете исправить это, добавив condition="length('${exception}')=0(или, может быть, это ==) к target name="file".
Пэт

60

По-видимому, теперь вы можете использовать NLog с Growl для Windows .

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <extensions>
        <add assembly="NLog.Targets.GrowlNotify" />
    </extensions>

    <targets>
        <target name="growl" type="GrowlNotify" password="" host="" port="" />
    </targets>

    <rules>
        <logger name="*" minLevel="Trace" appendTo="growl"/>
    </rules>

</nlog>

NLog с Growl для Windows Сообщение трассировки NLog с Growl для Windows Отладочное сообщение NLog с Growl для Windows NLog информационное сообщение с Growl для Windows NLog предупреждает сообщение с Growl для Windows Сообщение об ошибке NLog с Growl для Windows NLog фатальное сообщение с Growl для Windows


Можете ли вы сказать мне, что делать для соединения с remort? эта вещь работает для меня на localhost, но когда я дал IP-адрес в хосте, он не работает !!
Neel

@Neel, вы должны проверить настройки «Безопасность» в Growl на целевом компьютере. Вы должны явно включить уведомления «LAN» и, возможно, захотите установить пароль (который затем нужно будет добавить к цели NLog). Но мне не понравилось, что удаленные уведомления появлялись в Growl с «Происхождением» «Локальная машина»; Я должен был бы добавить хост к записям журнала, чтобы знать, откуда появились уведомления.
Кенни Эвитт

Я могу получать уведомления для работы на моей локальной машине, но не удаленно. У моих настроек безопасности нет пароля на рычание, поэтому я добавил только IP-адрес и порт. Но ничего не отправлено.
Джек Рейли

1
Этот проект мертв: 100%
Разработчик

28

Настроить NLog через XML, но программно

Какой? Знаете ли вы, что вы можете указать NLog XML непосредственно для NLog из вашего приложения, в отличие от того, чтобы NLog считывал его из файла конфигурации? Ну, ты можешь. Допустим, у вас есть распределенное приложение, и вы хотите везде использовать одну и ту же конфигурацию. Вы можете хранить конфигурационный файл в каждом месте и поддерживать его отдельно, вы можете хранить его в центральном месте и передавать его в спутниковые местоположения, или вы могли бы сделать много других вещей. Или вы можете сохранить свой XML в базе данных, получить его при запуске приложения и настроить NLog напрямую с этим XML (возможно, периодически проверять, изменился ли он).

  string xml = @"<nlog>
                   <targets>
                     <target name='console' type='Console' layout='${message}' />
                   </targets>

                   <rules>
                     <logger name='*' minlevel='Error' writeTo='console' />
                   </rules>
                 </nlog>";

  StringReader sr = new StringReader(xml);
  XmlReader xr = XmlReader.Create(sr);
  XmlLoggingConfiguration config = new XmlLoggingConfiguration(xr, null);
  LogManager.Configuration = config;
  //NLog is now configured just as if the XML above had been in NLog.config or app.config

  logger.Trace("Hello - Trace"); //Won't log
  logger.Debug("Hello - Debug"); //Won't log
  logger.Info("Hello - Info");   //Won't log
  logger.Warn("Hello - Warn");   //Won't log
  logger.Error("Hello - Error"); //Will log
  logger.Fatal("Hello - Fatal"); //Will log

  //Now let's change the config (the root logging level) ...
  string xml2 = @"<nlog>
                  <targets>
                     <target name='console' type='Console' layout='${message}' />
                   </targets>

                   <rules>
                     <logger name='*' minlevel='Trace' writeTo='console' />
                   </rules>
                 </nlog>";

  StringReader sr2 = new StringReader(xml2);
  XmlReader xr2 = XmlReader.Create(sr2);
  XmlLoggingConfiguration config2 = new XmlLoggingConfiguration(xr2, null);
  LogManager.Configuration = config2;

  logger.Trace("Hello - Trace"); //Will log
  logger.Debug("Hello - Debug"); //Will log
  logger.Info("Hello - Info");   //Will log
  logger.Warn("Hello - Warn");   //Will log
  logger.Error("Hello - Error"); //Will log
  logger.Fatal("Hello - Fatal"); //Will log

Я не уверен, насколько это надежно, но этот пример предоставляет полезную отправную точку для людей, которые могут захотеть попробовать подобную настройку.


это работает очень хорошо ... за исключением того, что с помощью этого больше невозможно динамически переконфигурировать систему регистрации. Это особенно
верно,

2
Это сработало, хотя мне пришлось написать «хороший» XML, включив в него:<?xml version='1.0' encoding='utf-8' ?><nlog xmlns='http://nlog-project.org/schemas/NLog.xsd' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>
Gady

1
Это хороший переход в централизованную конфигурацию. Будущие читатели, жестко запрограммированный xml в этом примере предназначен только для демонстрации (IMHO), реальная реализация может быть чтение из базы данных или централизованного файла.
granadaCoder

@wageoghe; Почему я получаю сообщение об ошибке (регистратор не существует)? Я просто
копирую и вставляю

22

Регистрация различных уровней в зависимости от того, есть ли ошибка

Этот пример позволяет вам получить больше информации, когда в вашем коде есть ошибка. По сути, он буферизует сообщения и выводит их только на определенном уровне журнала (например, Warn), если не выполнено определенное условие (например, произошла ошибка, поэтому уровень журнала> = Error), затем он выведет больше информации (например, все сообщения от уровней журнала> = Trace). Поскольку сообщения буферизуются, это позволяет вам собирать информацию о том, что произошло до того, как было зарегистрировано Error или ErrorException - очень полезно!

Я адаптировал этот пример из исходного кода . Сначала меня бросили, потому что я пропустил AspNetBufferingWrapper(поскольку мое не является приложением ASP) - оказывается, что PostFilteringWrapper требует некоторой буферизованной цели. Обратите внимание, что target-refэлемент, использованный в приведенном выше примере, нельзя использовать в NLog 1.0 (я использую 1.0 Refresh для приложения .NET 4.0); необходимо поместить вашу цель в блок обертки. Также обратите внимание, что логический синтаксис (т. Е. Символы больше или меньше символов, <и>) должен использовать символы, а не XML для этих символов (т. Е. &gt;И&lt; ), иначе NLog приведет к ошибке.

app.config:

<?xml version="1.0"?>
<configuration>
    <configSections>
        <section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/>
    </configSections>

    <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          throwExceptions="true" internalLogToConsole="true" internalLogLevel="Warn" internalLogFile="nlog.log">
        <variable name="appTitle" value="My app"/>
        <variable name="csvPath" value="${specialfolder:folder=Desktop:file=${appTitle} log.csv}"/>

        <targets async="true">
            <!--The following will keep the default number of log messages in a buffer and write out certain levels if there is an error and other levels if there is not. Messages that appeared before the error (in code) will be included, since they are buffered.-->
            <wrapper-target xsi:type="BufferingWrapper" name="smartLog">
                <wrapper-target xsi:type="PostFilteringWrapper">
                    <!--<target-ref name="fileAsCsv"/>-->
                    <target xsi:type="File" fileName="${csvPath}"
                    archiveAboveSize="4194304" concurrentWrites="false" maxArchiveFiles="1" archiveNumbering="Sequence"
                    >
                        <layout xsi:type="CsvLayout" delimiter="Tab" withHeader="false">
                            <column name="time" layout="${longdate}" />
                            <column name="level" layout="${level:upperCase=true}"/>
                            <column name="message" layout="${message}" />
                            <column name="callsite" layout="${callsite:includeSourcePath=true}" />
                            <column name="stacktrace" layout="${stacktrace:topFrames=10}" />
                            <column name="exception" layout="${exception:format=ToString}"/>
                            <!--<column name="logger" layout="${logger}"/>-->
                        </layout>
                    </target>

                     <!--during normal execution only log certain messages--> 
                    <defaultFilter>level >= LogLevel.Warn</defaultFilter>

                     <!--if there is at least one error, log everything from trace level--> 
                    <when exists="level >= LogLevel.Error" filter="level >= LogLevel.Trace" />
                </wrapper-target>
            </wrapper-target>

        </targets>

        <rules>
            <logger name="*" minlevel="Trace" writeTo="smartLog"/>
        </rules>
    </nlog>
</configuration>

В некоторых версиях NLog (для моно и я думаю 2.0) это вызывает исключение StackOverflowException, но не в других (обновление NLog 1).
Пэт

Что касается переполнения - кажется, это связано только с макетом, имеющим тип CSV - если я делаю обычный макет, проблем не возникает.
Пэт

Для чего нужен файл AsCsv target-ref? Я пытаюсь заставить этот пример работать против NLog v2.0.0.2000, но пока не удается.
Питер Маунс

@PeterMounce fileAsCsvtarget-ref - это просто артефакт моего тестирования. Я считаю, что у NLog 2 были / были проблемы с CsvLayouts, которых у NLog 1 / Refresh не было.
Pat

22

Я дал пару довольно интересных ответов на этот вопрос:

Nlog - Создание раздела заголовка для файла журнала

Добавление заголовка:

Вопрос хотел узнать, как добавить заголовок в файл журнала. Использование таких записей конфигурации позволяет вам определять формат заголовка отдельно от формата остальных записей журнала. Используйте один регистратор, возможно, называемый headerlogger, чтобы зарегистрировать одно сообщение в начале приложения, и вы получите свой заголовок:

Определите заголовок и расположение файлов:

  <variable name="HeaderLayout" value="This is the header.  Start time = ${longdate} Machine = ${machinename} Product version = ${gdc:item=version}"/>
  <variable name="FileLayout" value="${longdate} | ${logger} | ${level} | ${message}" />

Определите цели, используя макеты:

<target name="fileHeader" xsi:type="File" fileName="xxx.log" layout="${HeaderLayout}" />
<target name="file" xsi:type="File" fileName="xxx.log" layout="${InfoLayout}" />

Определите регистраторы:

<rules>
  <logger name="headerlogger" minlevel="Trace" writeTo="fileHeader" final="true" />
  <logger name="*" minlevel="Trace" writeTo="file" />
</rules>

Напишите шапку, вероятно, в начале программы:

  GlobalDiagnosticsContext.Set("version", "01.00.00.25");

  LogManager.GetLogger("headerlogger").Info("It doesn't matter what this is because the header format does not include the message, although it could");

Это в значительной степени просто еще одна версия идеи «Другое отношение к исключениям».

Регистрировать каждый уровень журнала с разным макетом

Кроме того, автор хотел знать, как изменить формат для каждого уровня ведения журнала. Мне не было ясно, какова конечная цель (и может ли она быть достигнута «лучшим» способом), но я смог предоставить конфигурацию, которая выполняла то, что он просил:

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

Опять же, очень похоже на трактовку исключений по-разному .


1
Прохладно! Я не видел GlobalDiagnosticsContextраньше.
Пэт

10

Войти в твиттер

Исходя из этого этом посте о log4net Twitter AppenderЯ подумал, что попробую свои силы в написании NLog Twitter Target (используя обновление NLog 1.0, а не 2.0). Увы, до сих пор я не смог получить твит для успешной публикации. Я не знаю, что-то не так в моем коде, Twitter, интернет-соединении / брандмауэре нашей компании или что-то в этом роде. Я публикую здесь код на случай, если кто-то захочет его опробовать. Обратите внимание, что есть три разных метода «Post». Первый, который я попробовал - это PostMessageToTwitter. PostMessageToTwitter по сути такой же, как PostLoggingEvent в оригинальной публикации. Если я использую это, я получаю исключение 401. PostMessageBasic получает то же исключение. PostMessage работает без ошибок, но сообщение все равно не попадает в Twitter. PostMessage и PostMessageBasic основаны на примерах, которые я нашел здесь на SO.

К вашему сведению - я только что нашел комментарий @Jason Diller к ответу в этом посте, в котором говорится, что твиттер отключит базовую аутентификацию «в следующем месяце». Это было еще в мае 2010 года, а сейчас декабрь 2010 года, поэтому я думаю, что это может быть причиной того, что это не работает.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Web;
using System.IO;

using NLog;
using NLog.Targets;
using NLog.Config;

namespace NLogExtensions
{
  [Target("TwitterTarget")]
  public class TwitterTarget : TargetWithLayout
  {
    private const string REQUEST_CONTENT_TYPE = "application/x-www-form-urlencoded";  

    private const string REQUEST_METHOD = "POST";  

    // The source attribute has been removed from the Twitter API,  
    // unless you're using OAuth.  
    // Even if you are using OAuth, there's still an approval process.  
    // Not worth it; "API" will work for now!  
    // private const string TWITTER_SOURCE_NAME = "Log4Net";  
    private const string TWITTER_UPDATE_URL_FORMAT = "http://twitter.com/statuses/update.xml?status={0}";  

    [RequiredParameter]
    public string TwitterUserName { get; set; }

    [RequiredParameter]
    public string TwitterPassword { get; set; }

    protected override void Write(LogEventInfo logEvent)
    {
      if (string.IsNullOrWhiteSpace(TwitterUserName) || string.IsNullOrWhiteSpace(TwitterPassword)) return;

      string msg = this.CompiledLayout.GetFormattedMessage(logEvent);

      if (string.IsNullOrWhiteSpace(msg)) return;

      try
      {
        //PostMessageToTwitter(msg);
        PostMessageBasic(msg);
      }
      catch (Exception ex)
      {
        //Should probably do something here ...
      }
    }

    private void PostMessageBasic(string msg)
    {
      // Create a webclient with the twitter account credentials, which will be used to set the HTTP header for basic authentication 
      WebClient client = new WebClient { Credentials = new NetworkCredential { UserName = TwitterUserName, Password = TwitterPassword } };

      // Don't wait to receive a 100 Continue HTTP response from the server before sending out the message body 
      ServicePointManager.Expect100Continue = false;

      // Construct the message body 
      byte[] messageBody = Encoding.ASCII.GetBytes("status=" + msg);

      // Send the HTTP headers and message body (a.k.a. Post the data) 
      client.UploadData(@"http://twitter.com/statuses/update.xml", messageBody);
    }

    private void PostMessage(string msg)
    {
      string user = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(TwitterUserName + ":" + TwitterPassword));
      byte [] bytes = System.Text.Encoding.UTF8.GetBytes("status=" + msg.ToTweet());
      HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://twitter.com/statuses/update.xml");
      request.Method = "POST";
      request.ServicePoint.Expect100Continue = false;
      request.Headers.Add("Authorization", "Basic " + user);
      request.ContentType = "application/x-www-form-urlencoded";
      request.ContentLength = bytes.Length;
      Stream reqStream = request.GetRequestStream();
      reqStream.Write(bytes, 0, bytes.Length);
      reqStream.Close();
    }

    private void PostMessageToTwitter(string msg)
    {
      var updateRequest = HttpWebRequest.Create(string.Format(TWITTER_UPDATE_URL_FORMAT,
                                                HttpUtility.UrlEncode(msg.ToTweet()))) as HttpWebRequest;
      updateRequest.ContentLength = 0;
      updateRequest.ContentType = REQUEST_CONTENT_TYPE;
      updateRequest.Credentials = new NetworkCredential(TwitterUserName, TwitterPassword);
      updateRequest.Method = REQUEST_METHOD;

      updateRequest.ServicePoint.Expect100Continue = false;

      var updateResponse = updateRequest.GetResponse() as HttpWebResponse;

      if (updateResponse.StatusCode != HttpStatusCode.OK && updateResponse.StatusCode != HttpStatusCode.Continue)
      {
        throw new Exception(string.Format("An error occurred while invoking the Twitter REST API [Response Code: {0}]", updateResponse.StatusCode));
      }
    }
  }

  public static class Extensions
  {
    public static string ToTweet(this string s)
    {
      if (string.IsNullOrEmpty(s) || s.Length < 140)
      {
        return s;
      }

      return s.Substring(0, 137) + "...";
    }
  }
}

Настройте это так:

Скажите NLog сборку, содержащую цель:

<extensions>
  <add assembly="NLogExtensions"/>
</extensions>

Настройте цель:

<targets>
    <target name="twitter" type="TwitterTarget" TwitterUserName="yourtwittername" TwitterPassword="yourtwitterpassword" layout="${longdate} ${logger} ${level} ${message}" />
</targets>

Если кто-то попробует это и добьется успеха, опубликуйте свои выводы


Twitter использует OAuth - .NET имеет поставщика в dotnetopenauth.net
Pat

8

Более простой способ регистрировать каждый уровень журнала с помощью другого макета, используя условные макеты

<variable name="VerboseLayout" value="${level:uppercase=true}: ${longdate} | ${logger}    : 
${when:when=level == LogLevel.Trace:inner=MONITOR_TRACE ${message}} 
${when:when=level == LogLevel.Debug:inner=MONITOR_DEBUG ${message}} 
${when:when=level == LogLevel.Info:inner=MONITOR_INFO ${message}} 
${when:when=level == LogLevel.Warn:inner=MONITOR_WARN ${message}} 
${when:when=level == LogLevel.Error:inner=MONITOR_ERROR ${message}} 
${when:when=level == LogLevel.Fatal:inner=MONITOR_CRITICAL ${message}} |     
${exception:format=tostring} | ${newline} ${newline}" />

См. Https://github.com/NLog/NLog/wiki/When-Filter для синтаксиса


7

Отчетность на внешний сайт / базу данных

Мне нужен был способ просто и автоматически сообщать об ошибках (поскольку пользователи часто этого не делают) из наших приложений. Самым простым решением, которое я мог придумать, был общедоступный URL-адрес - веб-страница, которая может принимать данные и сохранять их в базе данных, - которые отправляют данные при ошибке приложения. (Затем база данных может быть проверена разработчиком или скриптом, чтобы узнать, есть ли новые ошибки.)

Я написал веб-страницу на PHP и создал базу данных, пользователя и таблицу mysql для хранения данных. Я выбрал четыре пользовательские переменные, идентификатор и временную метку. Возможные переменные (включенные в URL или в виде данных POST):

  • app (Имя приложения)
  • msg (сообщение - например, возникла исключительная ситуация ...)
  • dev (разработчик - например, Пэт)
  • src(источник - это может быть переменная, относящаяся к машине, на которой запущено приложение, например, Environment.MachineNameили что-то подобное)
  • log (файл журнала или подробное сообщение)

(Все переменные являются необязательными, но ничего не сообщается, если ни одна из них не установлена ​​- поэтому, если вы просто посещаете URL-адрес веб-сайта, ничего не отправляется в БД.)

Чтобы отправить данные на URL, я использовал WebServiceцель NLog . (Обратите внимание, у меня было несколько проблем с этой целью вначале. Только когда я посмотрел на источник, я понял, что мой urlне может закончиться с /.)

В целом, это неплохая система для отслеживания внешних приложений. (Конечно, вежливая вещь - это сообщить своим пользователям, что вы будете сообщать о потенциально конфиденциальных данных, и дать им возможность отказаться от участия.)

MySQL материал

(Пользователь БД имеет INSERTправа только для этой таблицы в своей базе данных.)

CREATE TABLE `reports` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `ts` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `applicationName` text,
  `message` text,
  `developer` text,
  `source` text,
  `logData` longtext,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='storage place for reports from external applications'

Код сайта

(PHP 5.3 или 5.2 с включенным PDO , файл находится в папке)index.php/report

<?php
$app = $_REQUEST['app'];
$msg = $_REQUEST['msg'];
$dev = $_REQUEST['dev'];
$src = $_REQUEST['src'];
$log = $_REQUEST['log'];

$dbData =
    array(  ':app' => $app,
            ':msg' => $msg,
            ':dev' => $dev,
            ':src' => $src,
            ':log' => $log
    );
//print_r($dbData); // For debugging only! This could allow XSS attacks.
if(isEmpty($dbData)) die("No data provided");

try {
$db = new PDO("mysql:host=$host;dbname=reporting", "reporter", $pass, array(
    PDO::ATTR_PERSISTENT => true
));
$s = $db->prepare("INSERT INTO reporting.reports 
    (
    applicationName, 
    message, 
    developer, 
    source, 
    logData
    )
    VALUES
    (
    :app, 
    :msg, 
    :dev, 
    :src, 
    :log
    );"
    );
$s->execute($dbData);
print "Added report to database";
} catch (PDOException $e) {
// Sensitive information can be displayed if this exception isn't handled
//print "Error!: " . $e->getMessage() . "<br/>";
die("PDO error");
}

function isEmpty($array = array()) {
    foreach ($array as $element) {
        if (!empty($element)) {
            return false;
        }
    }
    return true;
}
?>

Код приложения (файл конфигурации NLog)

<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      throwExceptions="true" internalLogToConsole="true" internalLogLevel="Warn" internalLogFile="nlog.log">
    <variable name="appTitle" value="My External App"/>
    <variable name="csvPath" value="${specialfolder:folder=Desktop:file=${appTitle} log.csv}"/>
    <variable name="developer" value="Pat"/>

    <targets async="true">
        <!--The following will keep the default number of log messages in a buffer and write out certain levels if there is an error and other levels if there is not. Messages that appeared before the error (in code) will be included, since they are buffered.-->
        <wrapper-target xsi:type="BufferingWrapper" name="smartLog">
            <wrapper-target xsi:type="PostFilteringWrapper">
                <target xsi:type="File" fileName="${csvPath}"
                archiveAboveSize="4194304" concurrentWrites="false" maxArchiveFiles="1" archiveNumbering="Sequence"
                >
                    <layout xsi:type="CsvLayout" delimiter="Comma" withHeader="false">
                        <column name="time" layout="${longdate}" />
                        <column name="level" layout="${level:upperCase=true}"/>
                        <column name="message" layout="${message}" />
                        <column name="callsite" layout="${callsite:includeSourcePath=true}" />
                        <column name="stacktrace" layout="${stacktrace:topFrames=10}" />
                        <column name="exception" layout="${exception:format=ToString}"/>
                        <!--<column name="logger" layout="${logger}"/>-->
                    </layout>
                </target>

                 <!--during normal execution only log certain messages--> 
                <defaultFilter>level >= LogLevel.Warn</defaultFilter>

                 <!--if there is at least one error, log everything from trace level--> 
                <when exists="level >= LogLevel.Error" filter="level >= LogLevel.Trace" />
            </wrapper-target>
        </wrapper-target>

        <target xsi:type="WebService" name="web"
                url="http://example.com/report" 
                methodName=""
                namespace=""
                protocol="HttpPost"
                >
            <parameter name="app" layout="${appTitle}"/>
            <parameter name="msg" layout="${message}"/>
            <parameter name="dev" layout="${developer}"/>
            <parameter name="src" layout="${environment:variable=UserName} (${windows-identity}) on ${machinename} running os ${environment:variable=OSVersion} with CLR v${environment:variable=Version}"/>
            <parameter name="log" layout="${file-contents:fileName=${csvPath}}"/>
        </target>

    </targets>

    <rules>
        <logger name="*" minlevel="Trace" writeTo="smartLog"/>
        <logger name="*" minlevel="Error" writeTo="web"/>
    </rules>
</nlog>

Примечание: могут быть некоторые проблемы с размером файла журнала, но я не нашел простого способа его усечь (например, tailкоманда la * nix ).


Это работало для одного проекта, но в других у меня были проблемы с url: InnerException: System.InvalidCastException Message = Invalid cast из 'System.String' в 'System.Uri'. Source = mscorlib StackTrace: at System.Convert.DefaultToType (значение IConvertible, тип targetType, поставщик IFormatProvider) в System.String.System.IConvertible.ToType (тип type, поставщик IFormatProvider) в System.Convert.ChangeType (значение объекта, тип преобразования) , Провайдер IFormatProvider)
Пэт

Другой вариант, если вы хотите иметь возможность отслеживать журнал и получать уведомления в случае ошибки, - это цель Twitter. Посмотрите эту ссылку для Twitter Appender, написанного для log4net: twitterappender.codeplex.com Оригинальное сообщение в блоге, обсуждающее это, находится здесь: caseywatson.com/2009/07/07/log4net-twitter-awesome. Должно быть довольно легко написать что-то подобное для NLog.
wageoghe

Я дурачился с написанием NLog TwitterTarget, но не добился успеха в публикации твита. Я отправил код в качестве ответа. Не стесняйтесь попробовать, если хотите.
wageoghe

4

Вход от Silverlight

При использовании NLog с Silverlight вы можете отправить трассировку на сторону сервера через предоставленный веб-сервис. Вы также можете записать в локальный файл в изолированном хранилище, что пригодится, если веб-сервер недоступен. Смотрите здесь для деталей, т.е. используйте что-то вроде этого, чтобы сделать себя целью:

namespace NLogTargets
{
    [Target("IsolatedStorageTarget")]
    public sealed class IsolatedStorageTarget : TargetWithLayout
    {
        IsolatedStorageFile _storageFile = null;
        string _fileName = "Nlog.log"; // Default. Configurable through the 'filename' attribute in nlog.config

        public IsolatedStorageTarget()
        {
        }

        ~IsolatedStorageTarget()
        {
            if (_storageFile != null)
            {
                _storageFile.Dispose();
                _storageFile = null;
            }
        }

        public string filename
        {
            set
            {
                _fileName = value; 
            }
            get
            {
                return _fileName;  
            }
         }

        protected override void Write(LogEventInfo logEvent)
        {
            try
            {
                writeToIsolatedStorage(this.Layout.Render(logEvent));
            }
            catch (Exception e)
            {
                // Not much to do about his....
            }
        }

        public void writeToIsolatedStorage(string msg)
        {
            if (_storageFile == null)
                _storageFile = IsolatedStorageFile.GetUserStoreForApplication();
            using (IsolatedStorageFile isolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
            {
                // The isolated storage is limited in size. So, when approaching the limit
                // simply purge the log file. (Yeah yeah, the file should be circular, I know...)
                if (_storageFile.AvailableFreeSpace < msg.Length * 100)
                {
                    using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(_fileName, FileMode.Truncate, FileAccess.Write, isolatedStorage))
                    { }
                }
                // Write to isolated storage
                using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(_fileName, FileMode.Append, FileAccess.Write, isolatedStorage))
                {
                    using (TextWriter writer = new StreamWriter(stream))
                    {
                        writer.WriteLine(msg);
                    }
                }
            }
        }
    } 
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.