Konfigurasi NLog paling berguna [ditutup]


348

Apa konfigurasi terbaik atau paling berguna untuk masuk dengan NLog? (Ini bisa sederhana atau kompleks, asalkan berguna.)

Saya sedang memikirkan contoh-contoh seperti secara otomatis menggulir file log pada ukuran tertentu, mengubah tata letak (pesan log) apakah ada pengecualian, meningkatkan level log setelah kesalahan terjadi, dll.

Berikut ini beberapa tautan:


3
Berikut adalah beberapa tips penyesuaian kinerja berdasarkan pengujian: deep-depth.blogspot.com/2014/01/…
Neil

Jawaban:


391

Beberapa di antaranya masuk dalam kategori tips NLog (atau logging) umum daripada saran konfigurasi yang ketat.

Berikut adalah beberapa tautan logging umum dari sini di SO (Anda mungkin telah melihat beberapa atau semua ini sudah):

log4net vs. Nlog

Mencatat praktik terbaik

Apa gunanya fasad logging?

Mengapa logger merekomendasikan menggunakan logger per kelas?

Gunakan pola umum penamaan logger Anda berdasarkan kelas Logger logger = LogManager.GetCurrentClassLogger(). Ini memberi Anda tingkat granularitas tinggi dalam logger Anda dan memberi Anda fleksibilitas besar dalam konfigurasi logger (kontrol secara global, dengan namespace, dengan nama logger tertentu, dll).

Gunakan penebang berbasis non-classname yang sesuai. Mungkin Anda memiliki satu fungsi yang ingin Anda kendalikan secara terpisah. Mungkin Anda memiliki masalah penebangan lintas sektoral (pencatatan kinerja).

Jika Anda tidak menggunakan logging berbasis nama-kelas, pertimbangkan untuk memberi nama penebang Anda dalam beberapa jenis struktur hierarkis (mungkin berdasarkan area fungsional) sehingga Anda dapat mempertahankan fleksibilitas yang lebih besar dalam konfigurasi Anda. Misalnya, Anda mungkin memiliki area fungsional "basis data", FA "analisis", dan FA "ui". Masing-masing mungkin memiliki sub-area. Jadi, Anda dapat meminta penebang seperti ini:

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

Dan seterusnya. Dengan hierarki logger, Anda dapat mengonfigurasi logging secara global ("*" atau root logger), oleh FA (Database, Analisis, UI), atau dengan subarea (Database.Connect, dll).

Penebang memiliki banyak opsi konfigurasi:

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

Lihat bantuan NLog untuk informasi lebih lanjut tentang apa arti masing-masing opsi. Mungkin item yang paling penting di sini adalah kemampuan untuk wildcard logger rules, konsep bahwa beberapa aturan logger dapat "mengeksekusi" untuk pernyataan logging tunggal, dan bahwa aturan logger dapat ditandai sebagai "final" sehingga aturan selanjutnya tidak akan dijalankan untuk diberikan pernyataan logging.

Gunakan GlobalDiagnosticContext, MappedDiagnosticContext, dan NestedDiagnosticContext untuk menambahkan konteks tambahan pada output Anda.

Gunakan "variabel" dalam file konfigurasi Anda untuk menyederhanakan. Misalnya, Anda dapat menentukan variabel untuk tata letak Anda dan kemudian merujuk variabel dalam konfigurasi target daripada menentukan tata letak secara langsung.

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

Atau, Anda bisa membuat set properti "khusus" untuk ditambahkan ke tata letak.

  <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}"/>

Atau, Anda dapat melakukan hal-hal seperti membuat penyaji tata letak "hari" atau "bulan" secara ketat melalui konfigurasi:

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

Anda juga dapat menggunakan renders tata letak untuk mendefinisikan nama file Anda:

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

Jika Anda menggulung file setiap hari, setiap file dapat dinamai "Monday.log", "Tuesday.log", dll.

Jangan takut untuk menulis penyaji tata letak Anda sendiri. Mudah dan memungkinkan Anda untuk menambahkan informasi konteks Anda sendiri ke file log melalui konfigurasi. Misalnya, berikut adalah penyaji tata letak (berdasarkan NLog 1.x, bukan 2.0) yang dapat menambahkan Trace.CorrelationManager.ActivityId ke log:

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

Beri tahu NLog di mana ekstensi NLog Anda (seperti apa rakitan) seperti ini:

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

Gunakan renderer tata letak khusus seperti ini:

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

Gunakan target async:

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

Dan pembungkus target default:

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

jika perlu. Lihat NLog docs untuk info lebih lanjut tentang mereka.

Beri tahu NLog untuk menonton dan memuat ulang konfigurasi secara otomatis jika berubah:

<nlog autoReload="true" /> 

Ada beberapa opsi konfigurasi untuk membantu mengatasi masalah NLog

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

Lihat Bantuan NLog untuk info lebih lanjut.

NLog 2.0 menambahkan pembungkus LayoutRenderer yang memungkinkan pemrosesan tambahan dilakukan pada output dari penyaji tata letak (seperti memangkas spasi putih, huruf besar, huruf kecil, dll.).

Jangan takut untuk membungkus logger jika Anda ingin melindungi kode Anda dari ketergantungan keras pada NLog, tetapi bungkus dengan benar. Ada beberapa contoh bagaimana membungkus repositori github NLog. Alasan lain untuk membungkus mungkin karena Anda ingin secara otomatis menambahkan informasi konteks spesifik untuk setiap pesan yang dicatat (dengan memasukkannya ke LogEventInfo.Context).

Ada pro dan kontra untuk membungkus (atau abstrak) NLog (atau kerangka kerja logging lainnya dalam hal ini). Dengan sedikit usaha, Anda dapat menemukan banyak info di sini di SO menyajikan kedua belah pihak.

Jika Anda mempertimbangkan untuk membungkus, pertimbangkan untuk menggunakan Common.Logging . Ini berfungsi dengan cukup baik dan memungkinkan Anda untuk dengan mudah beralih ke kerangka kerja pencatatan yang lain jika Anda ingin melakukannya. Juga jika Anda mempertimbangkan untuk membungkus, pikirkan tentang bagaimana Anda akan menangani objek konteks (GDC, MDC, NDC). Common.Logging saat ini tidak mendukung abstraksi untuk mereka, tetapi seharusnya dalam antrian kemampuan untuk menambahkan.


3
Jawaban yang bagus Hanya satu hal untuk ditambahkan, $ {machine} harus menjadi $ {machinename}. Lihat github.com/nlog/NLog/wiki/Layout-Renderers .
liang

2
Saya bercabang dengan Common.Logging dan menambahkan abstraksi yang hilang, lihat proyek GitHub atau NuGet .
Danny Varod

Saya gagal menemukan sesuatu yang informatif tentang nlog dalam dokumentasi mereka sendiri, mungkin saya melihat contoh-contoh github dengan cara yang salah? Siapa tahu.
JARRRRG

Bagaimana cara menggunakan pembuat render khusus dengan API (tanpa file konfigurasi)? Inilah yang saya coba capai.
InteXX

OK mengerti. The NewLinetata letak menyelesaikan tugas. Inilah yang saya pikirkan. Ini pasti jauh lebih sederhana daripada yang saya harapkan.
InteXX

65

Memperlakukan pengecualian secara berbeda

Kami sering ingin mendapatkan informasi lebih lanjut ketika ada pengecualian. Konfigurasi berikut memiliki dua target, file dan konsol, yang memfilter apakah ada info pengecualian atau tidak. (EDIT: Jarek telah memposting tentang metode baru untuk melakukan ini di vNext .)

Kuncinya adalah memiliki target pembungkus 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
Itu cukup keren dengan target terpisah dan FilteringWrapper untuk memformat pengecualian. Saya baru saja menjawab pertanyaan baru-baru ini dari seorang pria yang ingin memasukkan penata letak {exception} dalam outputnya tetapi dia tidak ingin mendapatkan () yang tampaknya dicatat jika BUKAN pengecualian. Teknik ini mungkin akan bekerja dengan baik untuknya.
wageoghe

+1 Sangat bagus. Saya sudah memiliki bookmark ini untuk waktu yang lama, dan dirujuk ke "komentar Pat" dari pertanyaan SO lainnya sehubungan dengan tata letak bersyarat.
eduncan911

1
Jika pengecualian dicatat, itu akan dicatat dua kali (bagian VerboseLayout).
Tien Do

2
Saya baru saja mencobanya besok di proyek saya, karena Anda menetapkan aturan minlevel = "Peringatkan" ke "file, fileAsException", semua log akan dicatat terlebih dahulu dengan target file (tanpa filter), dan jika itu merupakan pengecualian (seperti difilter oleh kondisi) itu juga akan dicatat dengan fileAsException.
Tien Do

3
@Tiendq Oh, begitu. Itu masuk akal, meskipun pengecualian itu sendiri (dalam detail lengkap) hanya akan dicatat satu kali (tetapi pesannya akan dicatat dua kali). Anda mungkin dapat memperbaikinya dengan menambahkan condition="length('${exception}')=0(atau mungkin itu ==) ke target name="file".
Pat

60

Rupanya, Anda sekarang dapat menggunakan NLog dengan Growl untuk 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 dengan Growl untuk Windows NLog melacak pesan dengan Growl untuk Windows Pesan debug NLog dengan Growl untuk Windows Pesan info NLog dengan Growl untuk Windows NLog memperingatkan pesan dengan Growl untuk Windows Pesan kesalahan NLog dengan Growl untuk Windows Pesan fatal NLog dengan Growl untuk Windows


dapatkah Anda memberi tahu saya apa yang harus dilakukan untuk koneksi remort? hal ini berfungsi untuk saya untuk localhost tetapi ketika saya telah memberikan beberapa alamat ip di host tidak berfungsi !!
Neel

@Neel, Anda harus memeriksa pengaturan "Keamanan" di Growl di komputer target. Anda harus secara eksplisit mengaktifkan pemberitahuan "LAN" dan Anda mungkin ingin mengatur kata sandi (yang kemudian perlu Anda tambahkan ke target NLog Anda). Tetapi saya tidak suka bahwa notifikasi jarak jauh muncul di Growl dengan "Asal" dari "Mesin Lokal"; Saya harus menambahkan host ke entri log untuk mengetahui dari mana notifikasi berasal.
Kenny Evitt

Saya bisa mendapatkan notifikasi untuk bekerja pada mesin lokal saya, tetapi tidak dari jarak jauh. Pengaturan keamanan saya tidak memiliki kata sandi pada growl, jadi yang saya tambahkan adalah IP dan port. Tapi tidak ada yang dikirim.
Jack Reilly

1
Proyek ini sudah mati 100%
Pengembang

28

Konfigurasikan NLog via XML, tetapi secara terprogram

Apa? Tahukah Anda bahwa Anda dapat menentukan NLog XML langsung ke NLog dari aplikasi Anda, dan bukannya NLog membacanya dari file konfigurasi? Anda bisa. Katakanlah Anda memiliki aplikasi terdistribusi dan Anda ingin menggunakan konfigurasi yang sama di mana-mana. Anda dapat menyimpan file konfigurasi di setiap lokasi dan memeliharanya secara terpisah, Anda bisa menyimpannya di lokasi pusat dan mendorongnya ke lokasi satelit, atau Anda mungkin bisa melakukan banyak hal lainnya. Atau, Anda bisa menyimpan XML dalam database, mendapatkannya di startup aplikasi, dan mengonfigurasi NLog langsung dengan XML itu (mungkin memeriksa kembali secara berkala untuk melihat apakah sudah berubah).

  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

Saya tidak yakin seberapa kuat ini, tetapi contoh ini memberikan titik awal yang berguna bagi orang-orang yang mungkin ingin mencoba mengonfigurasi seperti ini.


ini bekerja dengan sangat baik ... kecuali dengan menggunakan ini tidak lagi memungkinkan untuk mengkonfigurasi ulang sistem logging secara dinamis. Ini khususnya terjadi jika Anda menautkan ke file luar (termasuk)
Newtopian

2
Ini berhasil, meskipun saya harus menulis XML "baik" dengan memasukkan:<?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
Ini adalah segway yang bagus untuk konfigurasi terpusat. Pembaca masa depan, xml kode keras dalam sampel ini hanya untuk demo (IMHO), membacanya dari database atau file terpusat mungkin merupakan implementasi nyata.
granadaCoder

@wageoghe; Mengapa saya mendapatkan kesalahan (logger tidak ada)? Saya hanya menyalin dan menempelkan kode
Bsflasher

22

Mencatat level yang berbeda tergantung pada ada tidaknya kesalahan

Contoh ini memungkinkan Anda untuk mendapatkan informasi lebih lanjut ketika ada kesalahan dalam kode Anda. Pada dasarnya, buffer pesan dan hanya output yang pada tingkat log tertentu (misalnya Peringatkan) kecuali kondisi tertentu terpenuhi (misalnya ada kesalahan, sehingga tingkat log adalah> = Kesalahan), maka itu akan menampilkan lebih banyak info (misalnya semua pesan dari tingkat log> = Lacak). Karena pesan di buffer, ini memungkinkan Anda mengumpulkan informasi tentang apa yang terjadi sebelum Kesalahan atau ErrorException dicatat - sangat berguna!

Saya mengadaptasi yang ini dari contoh di kode sumber . Saya dilempar pada awalnya karena saya meninggalkan AspNetBufferingWrapper(karena saya bukan aplikasi ASP) - ternyata PostFilteringWrapper membutuhkan beberapa target buffered. Perhatikan bahwa target-refelemen yang digunakan dalam contoh yang ditautkan di atas tidak dapat digunakan di NLog 1.0 (Saya menggunakan 1.0 Refresh untuk aplikasi .NET 4.0); Anda harus meletakkan target di dalam blok pembungkus. Perhatikan juga bahwa sintaks logika (yaitu simbol lebih besar atau lebih kecil dari, <dan>) ​​harus menggunakan simbol, bukan XML yang lolos untuk simbol tersebut (yaitu &gt;dan &lt;) atau NLog akan kesalahan.

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>

Dalam beberapa versi NLog (untuk mono dan saya pikir 2.0), ini menyebabkan StackOverflowException, tetapi tidak pada yang lain (penyegaran NLog 1).
Pat

Mengenai overflow - Sepertinya hanya karena tata letak dari tipe CSV - jika saya melakukan tata letak biasa tidak ada masalah.
Pat

Untuk apa target-ref fileAsCsv ada? Saya mencoba untuk mendapatkan contoh ini untuk bekerja melawan NLog v2.0.0.2000 tetapi sejauh ini gagal.
Peter Mounce

@PeterMounce fileAsCsvTarget-ref hanyalah artefak dari pengujian saya. Saya percaya NLog 2 memiliki / memiliki masalah dengan CsvLayouts yang tidak dimiliki NLog 1 / Refresh.
Pat

22

Saya memberikan beberapa jawaban yang cukup menarik untuk pertanyaan ini:

Nlog - Membuat Bagian Header untuk file log

Menambahkan Header:

Pertanyaannya ingin tahu bagaimana cara menambahkan header ke file log. Menggunakan entri konfigurasi seperti ini memungkinkan Anda untuk menentukan format header secara terpisah dari format sisa entri log. Gunakan logger tunggal, mungkin disebut "headerlogger" untuk mencatat satu pesan di awal aplikasi dan Anda mendapatkan header Anda:

Tentukan tajuk dan tata letak file:

  <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}" />

Tentukan target menggunakan tata letak:

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

Tentukan penebang:

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

Tulis tajuknya, mungkin di awal program:

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

Ini sebagian besar hanyalah versi lain dari gagasan "Memperlakukan pengecualian secara berbeda".

Log setiap tingkat log dengan tata letak yang berbeda

Demikian pula, poster ingin tahu cara mengubah format per level logging. Tidak jelas bagi saya apa tujuan akhirnya (dan apakah tujuan itu dapat dicapai dengan cara yang "lebih baik"), tetapi saya dapat memberikan konfigurasi yang melakukan apa yang dia minta:

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

Sekali lagi, sangat mirip dengan Memperlakukan pengecualian secara berbeda .


1
Keren! Saya belum pernah melihat GlobalDiagnosticsContextsebelumnya.
Pat

10

Masuk ke Twitter

Berdasarkan posting ini tentang App4 Twitter Twitter, Saya pikir saya akan mencoba menulis NLog Twitter Target (menggunakan refresh NLog 1.0, bukan 2.0). Sayangnya, sejauh ini saya belum bisa mendapatkan Tweet untuk benar-benar memposting. Saya tidak tahu apakah ada yang salah dalam kode saya, Twitter, koneksi internet / firewall perusahaan kami, atau apa. Saya memposting kode di sini jika seseorang tertarik untuk mencobanya. Perhatikan bahwa ada tiga metode "Posting" yang berbeda. Yang pertama saya coba adalah PostMessageToTwitter. PostMessageToTwitter pada dasarnya sama dengan PostLoggingEvent di postingan orignal. Jika saya menggunakan itu saya mendapatkan pengecualian 401. PostMessageBasic mendapatkan pengecualian yang sama. PostMessage berjalan tanpa kesalahan, tetapi pesannya tetap tidak sampai ke Twitter. PostMessage dan PostMessageBasic didasarkan pada contoh yang saya temukan di SO.

FYI - Saya baru saja menemukan komentar oleh @Jason Diller untuk jawaban dalam posting ini yang mengatakan bahwa twitter akan mematikan otentikasi dasar "bulan depan". Ini kembali pada Mei 2010 dan sekarang Desember 2010, jadi saya kira itu bisa jadi mengapa ini tidak berhasil.

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) + "...";
    }
  }
}

Konfigurasikan seperti ini:

Beri tahu NLog majelis yang berisi target:

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

Konfigurasikan target:

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

Jika seseorang mencoba ini dan berhasil, kirim kembali dengan temuan Anda.


Twitter menggunakan OAuth - .NET memiliki penyedia di dotnetopenauth.net
Pat

8

Cara yang Lebih Mudah Untuk Log setiap tingkat log dengan tata letak yang berbeda menggunakan Tata Letak Bersyarat

<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}" />

Lihat https://github.com/NLog/NLog/wiki/When-Filter untuk sintaksis


7

Pelaporan ke situs web / basis data eksternal

Saya ingin cara sederhana dan otomatis melaporkan kesalahan (karena pengguna sering tidak) dari aplikasi kami. Solusi termudah yang bisa saya buat adalah URL publik - halaman web yang dapat mengambil input dan menyimpannya ke database - yang mengirim data pada kesalahan aplikasi. (Basis data kemudian dapat diperiksa oleh dev atau skrip untuk mengetahui apakah ada kesalahan baru.)

Saya menulis halaman web dalam PHP dan membuat database mysql, pengguna, dan tabel untuk menyimpan data. Saya memutuskan empat variabel pengguna, id, dan cap waktu. Variabel yang mungkin (baik termasuk dalam URL atau sebagai data POST) adalah:

  • app (nama aplikasi)
  • msg (pesan - mis. Pengecualian terjadi ...)
  • dev (pengembang - mis. Pat)
  • src(sumber - ini akan datang dari variabel yang berkaitan dengan mesin tempat aplikasi berjalan, misalnya Environment.MachineNameatau semacamnya)
  • log (file log atau pesan verbal)

(Semua variabel bersifat opsional, tetapi tidak ada yang dilaporkan jika tidak ada yang disetel - jadi jika Anda hanya mengunjungi URL situs web, tidak ada yang dikirim ke db.)

Untuk mengirim data ke URL, saya menggunakan WebServicetarget NLog . (Catatan, saya punya beberapa masalah dengan target ini pada awalnya. Tidak sampai saya melihat sumber yang saya tahu bahwa saya urltidak bisa berakhir dengan a /.)

Secara keseluruhan, ini bukan sistem yang buruk untuk mengawasi aplikasi eksternal. (Tentu saja, hal yang sopan untuk dilakukan adalah memberi tahu pengguna Anda bahwa Anda akan melaporkan data yang mungkin sensitif dan memberi mereka cara untuk memilih masuk / keluar.)

Hal-hal MySQL

(Pengguna db hanya memiliki INSERThak istimewa pada tabel ini dalam database sendiri.)

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'

Kode situs web

(PHP 5.3 atau 5.2 dengan PDO diaktifkan , file ada index.phpdi /reportfolder)

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

Kode aplikasi (file konfigurasi 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>

Catatan: mungkin ada beberapa masalah dengan ukuran file log, tapi saya belum menemukan cara sederhana untuk memotongnya (mis. tailPerintah a la * nix's ).


Ini berfungsi untuk satu proyek, tetapi di yang lain saya memiliki masalah dengan url: InnerException: System.InvalidCastException Pesan = Pemain tidak valid dari 'System.String' ke 'System.Uri'. Sumber = mscorlib StackTrace: at System.Convert.DefaultToType (Nilai IConvertible, Ketik targetType, penyedia IFormatProvider) di System.String.System.IConvertible.ToType (tipe Type, penyedia IFormatProvider) di System.Convert.ChangeType (Nilai objek, jenis konversiType , Penyedia IFormatProvider)
Pat

Opsi lain jika Anda ingin dapat memonitor log dan diberitahu jika terjadi kesalahan adalah Target Twitter. Lihat tautan ini untuk Twitter Appender yang ditulis untuk log4net: twitterappender.codeplex.com Posting blog asli yang membahas ini ada di sini: caseywatson.com/2009/07/07/log4net-twitter-awesome Seharusnya cukup mudah untuk menulis sesuatu yang serupa untuk NLog.
wageoghe

Saya telah membodohi dengan menulis TargetLog NLog tetapi belum berhasil benar-benar diposting Tweet. Saya telah memposting kode sebagai jawaban. Jangan ragu untuk mencobanya jika Anda mau.
wageoghe

4

Log dari Silverlight

Saat menggunakan NLog dengan Silverlight Anda dapat mengirim jejak ke sisi server melalui layanan web yang disediakan . Anda juga dapat menulis ke file lokal di Penyimpanan Terpencil, yang berguna jika server web tidak tersedia. Lihat di sini untuk detailnya, yaitu gunakan sesuatu seperti ini untuk menjadikan diri Anda target:

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);
                    }
                }
            }
        }
    } 
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.