Найбільш корисні конфігурації NLog [закрито]


348

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

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

Ось декілька посилань:


3
Ось декілька порад щодо налаштування ефективності на основі тестування: deep-depth.blogspot.com/2014/01/…
Neil

Відповіді:


391

Деякі з них відносяться до категорії загальних порад NLog (або ведення журналів), а не суто пропозицій щодо конфігурації.

Ось декілька загальних посилань на реєстрацію звідси на SO (ви, можливо, вже бачили деякі чи всі):

log4net проти Nlog

Реєстрація найкращих практик

У чому сенс фасаду лісозаготівлі?

Чому лісоруби рекомендують використовувати реєстратор для кожного класу?

Використовуйте загальну схему іменування вашого реєстратора на основі класу Logger logger = LogManager.GetCurrentClassLogger(). Це дає вам високу ступінь деталізації у ваших реєстраторах та дає велику гнучкість у конфігурації реєстраторів (керуйте глобально, за простором імен, за конкретним іменем реєстратора тощо).

Використовуйте некласові реєстратори, де це доречно. Можливо, у вас є одна функція, за якою ви дійсно хочете окремо контролювати ведення журналу. Можливо, у вас є проблеми щодо наскрізного ведення журналів (реєстрація продуктивності).

Якщо ви не використовуєте журнал на основі класів, розгляньте, як назвати ваші реєстратори в якійсь ієрархічній структурі (можливо, за функціональною областю), щоб ви могли підтримувати більшу гнучкість у своїй конфігурації. Наприклад, у вас може бути функціональна область "бази даних", ФА "аналіз" та ФА "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

Трактуйте винятки по-різному

Ми часто хочемо отримати більше інформації, коли є виняток. Наведена нижче конфігурація має дві цілі, файл і консоль, які фільтрують, чи є інформація про виключення чи ні. (EDIT: Джарек опублікував про новий метод цього в 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 Дуже приємно. Я давно це робив у закладках, і з іншого питання про те, що стосується умовного планування, перейшов до "коментаря Пата".
eduncan911

1
Якщо буде зареєстровано виняток, він буде реєструватися двічі (частина VerboseLayout).
Tien Do

2
Я щойно спробував це завтра в своєму проекті, оскільки ви встановили правило minlevel = "Попередження" на "файл, 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


Ви можете сказати мені, що робити для ремонта з’єднання? річ працює для мене для localhost, але коли я дав ip адресу в хості, він не працює !!
Ніл

@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

Я не впевнений, наскільки це надійно, але цей приклад дає корисну вихідну точку для людей, які могли б спробувати налаштувати подібну.


він працює дуже добре ... за винятком випадків, коли це неможливо динамічно переконфігурувати систему реєстрації. Це особливо так, якщо ви посилаєтесь на зовнішній файл (включити)
Newtopian

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; Чому я отримую помилку (реєстратор не існує)? Я просто копіюю і вставляю код
Bsflasher

22

Реєстрування різних рівнів залежно від того, є помилка чи ні

Цей приклад дозволяє отримати більше інформації, коли в коді є помилка. В основному, він буферизує повідомлення і виводить лише ті, що знаходяться на певному рівні журналу (наприклад, Попередження), якщо не виконано певну умову (наприклад, сталася помилка, тому рівень журналу є> = Помилка), тоді він виведе більше інформації (наприклад, всі повідомлення з рівнів журналу> = Трасування). Оскільки повідомлення буферизовані, це дозволяє збирати інформацію про те, що сталося до того, як помилка чи 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 - якщо я роблю звичайний макет, немає проблем.
Пт

Для чого існує файл targetAfCv-ref? Я намагаюся змусити цей приклад працювати проти NLog v2.0.0.2000, але поки що не вдається.
Пітер Мунс

@PeterMounce Цільовий перелік fileAsCsv- це лише артефакт мого тестування. Я вважаю, що у NLog 2 виникли проблеми з CsvLayouts, яких у NLog 1 / Refresh не було.
Пат

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

Увійдіть у Twitter

На основі цього повідомлення про log4net Twitter Appender, Я думав, що я спробую себе в написанні цілі NLog Twitter (використовуючи оновлення NLog 1.0, а не 2.0). На жаль, поки що мені не вдалося отримати твіт, щоб фактично успішно розмістити публікацію. Я не знаю, чи щось не так у моєму коді, Twitter, підключенні до Інтернету / брандмауері нашої компанії чи що. Я розміщую тут код на випадок, якщо хтось зацікавлений спробувати його. Зауважте, що існує три різні методи "Пост". Перше, що я спробував - це PostMessageToTwitter. PostMessageToTwitter по суті такий же, як PostLoggingEvent у початковому дописі. Якщо я використовую, я отримую виняток 401. PostMessageBasic отримує той самий виняток. PostMessage працює без помилок, але повідомлення все ще не доводить його до Twitter. PostMessage та PostMessageBasic ґрунтуються на прикладах, які я знайшов тут на SO.

FYI - Я щойно знайшов коментар @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 (розробник - наприклад, Pat)
  • src(джерело - це походить від змінної, що стосується машини, на якій працює додаток, наприклад, Environment.MachineNameабо якоїсь такої)
  • log (файл журналу або багатослівне повідомлення)

(Усі змінні необов’язкові, але нічого не повідомляється, якщо жодна з них не встановлена ​​- тому, якщо ви просто завітаєте до URL-адреси веб-сайту, нічого не надсилається в db.)

Щоб надіслати дані за URL-адресою, я використав WebServiceціль NLog . (Зауважте, спочатку у мене було кілька проблем з цією ціллю. Тільки тоді, коли я подивився на джерело, я зрозумів, що мій urlне може закінчитися /.)

Загалом, це не погана система для зберігання вкладок на зовнішніх додатках. (Звичайно, ввічлива річ - це повідомити своїх користувачів про те, що ви будете повідомляти про можливі конфіденційні дані та дати їм спосіб відмовитися / відмовитися.)

MySQL речі

(Користувач db має лише 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 = Недійсний привід з 'System.String' до 'System.Uri'. Source = mscorlib StackTrace: у System.Convert.DefaultToType (значення IConvertible, тип targetType, постачальник IFormatProvider) у System.String.System.IConvertible.ToType (тип типу, постачальник IFormatProvider) на System.Convert.ChangeTypeT (значення об'єкта перетворення, значення об'єкта , постачальник IFormatProvider)
патент

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

Я обдурив написання NLog TwitterTarget, але не мав успіху, фактично опублікувавши Twitter. Я опублікував код як відповідь. Ви можете спробувати це, якщо хочете.
wageoghe

4

Вхід від Silverlight

Під час використання NLog з Silverlight ви можете надіслати слід на сторону сервера через надану веб-службу. Ви також можете написати в локальний файл у Isolated Storage, який стане в нагоді, якщо веб-сервер недоступний. Деталі див. Тут , тобто використовуйте щось подібне, щоб зробити собі ціль:

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.