En yararlı NLog yapılandırmaları [kapalı]


348

NLog ile oturum açmak için en iyi veya en kullanışlı yapılandırmalar nelerdir? (Bunlar, faydalı oldukları sürece basit veya karmaşık olabilir.)

Günlük dosyalarının belirli bir boyutta otomatik olarak yuvarlanması, bir istisna olup olmadığının düzenini (günlük iletisi) değiştirme, bir hata oluştuğunda günlük düzeyini yükseltme gibi örnekleri düşünüyorum.

İşte bazı bağlantılar:


3
Teste dayanan bazı performans ayarlama ipuçları: deep-depth.blogspot.com/2014/01/…
Neil

Yanıtlar:


391

Bunlardan bazıları, kesinlikle yapılandırma önerileri yerine genel NLog (veya günlük kaydı) ipuçları kategorisine girer.

Burada SO'daki bazı genel günlük bağlantıları aşağıdadır (bunların bir kısmını veya tümünü zaten görmüş olabilirsiniz):

log4net ve Nlog karşılaştırması

Günlüğe kaydetme en iyi uygulamaları

Ahşap bir cephenin anlamı nedir?

Kaydediciler neden sınıf başına bir günlükçünün kullanılmasını önerir?

Logger'ınızı sınıfa göre adlandırmanın ortak şeklini kullanın Logger logger = LogManager.GetCurrentClassLogger(). Bu, günlükçülerinizde yüksek derecede ayrıntı düzeyi sağlar ve günlükçülerin yapılandırılmasında büyük esneklik sağlar (küresel olarak, ad alanına göre, belirli günlükçü adına göre kontrol edin).

Uygun olduğunda, sınıf adı tabanlı olmayan günlükçüler kullanın. Belki de günlüğe kaydetmeyi ayrı ayrı kontrol etmek istediğiniz bir işleve sahipsiniz. Belki bazı kesişen kayıt kaygılarınız var (performans günlüğü).

Classname tabanlı günlük kaydı kullanmıyorsanız, yapılandırmanızda daha fazla esneklik sağlayabilmeniz için günlüklerinizi bir tür hiyerarşik yapıda (belki de işlevsel alana göre) adlandırmayı düşünün. Örneğin, bir "veritabanı" işlev alanı, bir "analiz" FA ve "ui" FA olabilir. Bunların her birinin alt alanları olabilir. Yani, bunun gibi günlükçüler talep edebilirsiniz:

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

Ve bunun gibi. Hiyerarşik kaydedicilerle günlük kaydını global olarak ("*" veya kök kaydedici), FA (Veritabanı, Analiz, UI) veya alt bölge (Database.Connect vb.) İle yapılandırabilirsiniz.

Kaydedicilerin birçok yapılandırma seçeneği vardır:

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

Seçeneklerin her birinin tam olarak ne anlama geldiğiyle ilgili daha fazla bilgi için NLog yardımına bakın . Muhtemelen buradaki en dikkate değer öğeler, günlükçü kurallarını joker karakterlere çevirme yeteneğidir, birden çok günlükçü kuralının tek bir günlük ifadesi için "yürütebildiği" ve bir günlükçü kuralının "son" olarak işaretlenebileceği, böylece sonraki kuralların günlük kaydı ifadesi.

Çıktınıza ek bağlam eklemek için GlobalDiagnosticContext, MappedDiagnosticContext ve NestedDiagnosticContext öğelerini kullanın.

Basitleştirmek için yapılandırma dosyanızda "değişken" kullanın. Örneğin, mizanpajlarınız için değişkenler tanımlayabilir ve ardından düzeni doğrudan belirtmek yerine, hedef yapılandırmadaki değişkene başvurabilirsiniz.

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

Veya bir düzene eklemek üzere "özel" özellikler kümesi oluşturabilirsiniz.

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

Ya da, yapılandırma yoluyla kesinlikle "gün" veya "ay" düzen oluşturucuları gibi şeyler yapabilirsiniz:

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

Dosya adınızı tanımlamak için mizanpaj render'larını da kullanabilirsiniz:

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

Dosyanızı günlük olarak alırsanız, her dosya "Monday.log", "Tuesday.log" vb. Olarak adlandırılabilir.

Kendi düzen oluşturucunuzu yazmaktan korkmayın. Kolaydır ve konfigürasyon yoluyla günlük dosyasına kendi bağlam bilgilerinizi eklemenizi sağlar. Örneğin, Trace.CorrelationManager.ActivityId öğesini günlüğe ekleyebilen bir düzen oluşturucu (2.0 değil NLog 1.x tabanlı):

  [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'a NLog uzantılarınızın (hangi montaj) nerede olduğunu söyleyin:

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

Özel düzen oluşturucuyu şu şekilde kullanın:

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

Zaman uyumsuz hedefler kullanın:

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

Ve varsayılan hedef sarmalayıcılar:

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

uygun olduğunda. Bunlar hakkında daha fazla bilgi için NLog belgelerine bakın.

NLog'a yapılandırmayı izlemesi ve değişmesi durumunda otomatik olarak yeniden yüklemesini söyleyin:

<nlog autoReload="true" /> 

NLog sorunlarını gidermeye yardımcı olacak birkaç yapılandırma seçeneği vardır

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

Daha fazla bilgi için NLog Yardım'a bakın.

NLog 2.0, bir düzen oluşturucunun çıktısında ek işlem yapılmasına izin veren LayoutRenderer sarmalayıcıları ekler (boşlukları kırpma, büyük harf, küçük harf, vb. Gibi).

Kodunuzu NLog'a büyük bir bağımlılıktan yalıtmak istiyorsanız, kaydediciyi sarmaktan korkmayın, ancak doğru bir şekilde sarın. NLog'un github deposunu nasıl kapatacağınıza dair örnekler var. Kaydırmanın başka bir nedeni de, kaydedilen her iletiye (LogEventInfo.Context içine koyarak) otomatik olarak belirli bağlam bilgileri eklemek istemeniz olabilir.

NLog (veya bu konu için herhangi bir günlük kaydı çerçevesi) kaydırmanın (veya soyutlanmasının) artıları ve eksileri vardır. Biraz çaba ile, burada SO her iki tarafta sunan bol miktarda bilgi bulabilirsiniz.

Kaydırma yapmayı düşünüyorsanız, Common.Logging'i kullanmayı düşünün . Oldukça iyi çalışıyor ve isterseniz başka bir günlük çerçevesine kolayca geçmenizi sağlar. Ayrıca kaydırmayı düşünüyorsanız, bağlam nesnelerini (GDC, MDC, NDC) nasıl ele alacağınızı düşünün. Günlük: Şu anda onlar için bir soyutlamayı desteklememektedir, ancak sözde ekleme yetenekleri kuyruğundadır.


3
Mükemmel cevap. Eklenecek tek bir şey var, $ {machine} $ {machinename} olmalıdır. Bkz. Github.com/nlog/NLog/wiki/Layout-Renderers .
liang

2
Ben Common.Logging çatallı ve eksik soyutlama eklendi, bkz GitHub proje veya Nuget .
Danny Varod

Kendi belgelerinde nlog hakkında bilgilendirici bir şey bulamadım, belki de github örneklerine yanlış bir şekilde bakıyorum? Kim bilir.
JARRRRG

Bu özel oluşturucu API ile nasıl kullanılır (yapılandırma dosyası yok)? İşte yapmaya çalıştığım şey.
InteXX

Tamam anladım. NewLineDüzen görevi gerçekleştirir. İşte ortaya çıkardığım şey. Kesinlikle olmasını beklediğimden çok daha basit.
InteXX

65

İstisnaları farklı şekilde ele almak

Bir istisna olduğunda genellikle daha fazla bilgi almak isteriz. Aşağıdaki yapılandırmada, bir istisna bilgisi olup olmadığını filtreleyen bir dosya ve konsol olmak üzere iki hedef bulunur. (EDIT: Jarek bunu vNext'te yapmak için yeni bir yöntem yayınladı .)

Anahtar, bir sarıcı hedefin 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
İstisnayı biçimlendirmek için ayrı hedef ve FilteringWrapper ile bu çok güzel. Son zamanlarda {exception} düzen oluşturucusunu çıktısına dahil etmek isteyen bir adamdan son zamanlarda bir soruya cevap verdim, ancak bir istisna yoksa görünüşte günlüğe kaydedilen () almak istemedi. Bu teknik muhtemelen onun için iyi çalışır.
wageoghe

+1 Çok hoş. Ben uzun bir süre bu yer imi ve koşullu bir düzen ile ilgili başka bir SO sorudan "Pat'ın yorum" atıfta bulundum.
eduncan911

1
Bir istisna günlüğe kaydedilirse, iki kez günlüğe kaydedilir (VerboseLayout bölümü).
Tien Do

2
Ben yarın projemde denedim, "dosya, fileAsException" için bir minlevel = "Uyar" kuralı ayarladığınızdan, tüm günlükler ilk olarak dosya hedefi (filtre yok) ile günlüğe kaydedilir ve (bir istisna ise) koşul) ayrıca fileAsException ile günlüğe kaydedilir.
Tien Do

3
@Tiendq Oh, anlıyorum. Bu, istisnanın kendisi (tam ayrıntılı olarak) yalnızca bir kez kaydedilecek olsa da (mesajı iki kez kaydedilecektir) mantıklıdır. Muhtemelen bunu ekleyerek condition="length('${exception}')=0(veya belki de ==) düzeltebilirsiniz target name="file".
Pat

60

Görünüşe göre, NLog'u Growl with Windows için kullanabilirsiniz .

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

Windows için Growl ile NLog NLWindows için Growl ile izleme izleme mesajı Windows için Growl ile NLog hata ayıklama iletisi NLWindows için Growl ile bilgi bilgisi mesajı NLog, Windows için Growl ile mesajı uyar Windows için Growl ile NLog hata mesajı NLWindows için Growl ile önemli mesaj


Hatırlatma bağlantısı için ne yapacağımı söyleyebilir misin? şey localhost için benim için çalışıyor ama ana bilgisayardaki bazı ip adresi verdiğinizde çalışmıyor!
Neel

@Neel, hedef bilgisayarda Growl'de "Güvenlik" ayarlarını kontrol etmelisiniz. "LAN" bildirimlerini açıkça etkinleştirmeniz gerekir ve bir parola (daha sonra NLog hedefinize eklemeniz gerekir) ayarlamak isteyebilirsiniz. Ama uzaktan bildirimlerin "Yerel Makine" nin "Kökeni" ile Growl'da gösterilmesini beğenmedim; Bildirimlerin nereden geldiğini öğrenmek için ana bilgisayarı günlük girişlerine eklemem gerekir.
Kenny Evitt

Yerel makinemde çalışmak için bildirimler alabilirim, ancak uzaktan alamıyorum. Güvenlik ayarlarımın homurtu ile ilgili şifresi yok, bu yüzden eklediğim tek şey IP ve port oldu. Ama hiçbir şey gönderilmez.
Jack Reilly

1
Bu proje% 100 ölü
Geliştirici

28

NLog'u XML yoluyla, ancak Programlı olarak yapılandırma

Ne? NLog XML'yi yapılandırma dosyasından okumak yerine NLog XML'yi doğrudan uygulamanızdan NLog olarak belirtebileceğinizi biliyor muydunuz? Evet, yapabilirsin. Diyelim ki dağıtılmış bir uygulamanız var ve her yerde aynı yapılandırmayı kullanmak istiyorsunuz. Her konumda bir yapılandırma dosyası tutabilir ve ayrı olarak muhafaza edebilirsiniz, bir dosyayı merkezi bir konumda tutabilir ve uydu konumlarına gönderebilirsiniz veya muhtemelen başka birçok şey yapabilirsiniz. Veya XML'inizi bir veritabanında depolayabilir, uygulama başlangıcında alabilir ve NLog'u doğrudan bu XML ile yapılandırabilirsiniz (belki de değişip değişmediğini görmek için düzenli olarak kontrol edebilirsiniz).

  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

Bunun ne kadar sağlam olduğundan emin değilim, ancak bu örnek, böyle yapılandırmayı denemek isteyebilecek kişiler için yararlı bir başlangıç ​​noktası sağlar.


çok iyi çalışıyor ... ancak bunu kullanarak günlük sistemini dinamik olarak yeniden yapılandırmak artık mümkün değil. Bu, özellikle dış bir dosyaya (dahil) bağlantı
verirseniz geçerlidir

2
Ben de dahil "iyi" XML yazmak zorunda olmasına rağmen, bu çalıştı:<?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
Bu, merkezileştirilmiş konfigürasyona hoş bir geçiş. Gelecekte okuyucular, bu örnekte sabit kodlu xml sadece demo (IMHO) içindir, bir veritabanından veya merkezi dosyadan okumak gerçek uygulama olabilir.
granadaCoder

@wageoghe; Neden hata alıyorum (kaydedici mevcut değil)? Sadece kodu kopyalayıp yapıştırıyorum
Bsflasher

22

Bir hatanın olup olmamasına bağlı olarak farklı düzeylerin günlüğe kaydedilmesi

Bu örnek, kodunuzda bir hata olduğunda daha fazla bilgi edinmenizi sağlar. (Örneğin uyar) Temel olarak, bu mesaj tamponlar ve sadece belirli bir günlük düzeyinde bu çıktılar sürece , belirli bir durum (günlük düzeyi> = Hata yani örneğin bir hata olduğunu) karşılandığında, daha sonra bu çıktısı daha fazla bilgi (örn günlük düzeylerindeki tüm iletiler> = İzleme). İletiler arabelleğe alındığından, bu bir Hata veya ErrorException günlüğe kaydedilmeden önce neler olduğu hakkında izleme bilgileri toplamanızı sağlar - çok yararlı!

Bunu kaynak koddaki bir örnekten uyarladım . İlk başta atıldım çünkü AspNetBufferingWrapper(benim bir ASP uygulaması olmadığından) - PostFilteringWrapper'ın arabelleğe alınmış bir hedef gerektirdiği ortaya çıktı . Not, target-ref(bir .NET 4.0 uygulama için 1.0 Yenile kullanıyorum) yukarıda bağlı örnekte kullanılan elemanı nlog 1.0 kullanılamaz; hedefinizi sargı bloğunun içine koymak gerekir. Ayrıca mantık sözdiziminin (yani, sembollerden daha büyük veya daha küçük, <ve>) sembolleri kullanması gerektiğini unutmayın, bu semboller için XML kaçmaz (yani &gt;ve &lt;) veya NLog hata verir.

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'un bazı sürümlerinde (mono ve bence 2.0 için) bu, StackOverflowException özelliğine neden olur, ancak diğerlerinde değil (NLog 1 yenileme).
Pat

Taşma ile ilgili olarak - Bu sadece CSV tipindeki yerleşime bağlı gibi görünüyor - düzenli bir düzen yaparsam sorun yoktur.
Pat

FileAsCsv target-ref ne için var? NLog v2.0.0.2000 karşı çalışmak için bu örnek almaya çalışıyorum ama şimdiye kadar başarısız.
Peter Mounce

@PeterMounce Target fileAsCsv-ref, testlerimden sadece bir eser. NLog 2, NLog 1 / Refresh'in sahip olmadığı CsvLayouts ile ilgili / sorunlar olduğuna inanıyorum.
Patenti

22

Bu soruya birkaç ilginç cevap verdim:

Nlog - Günlük dosyası için Başlık Bölümü oluşturma

Üstbilgi Ekleme:

Soru, günlük dosyasına nasıl başlık ekleneceğini bilmek istiyordu. Bunun gibi yapılandırma girişlerini kullanmak, üstbilgi biçimini, günlük girişlerinin geri kalanından ayrı olarak tanımlamanıza olanak tanır. Uygulamanın başlangıcında tek bir iletiyi günlüğe kaydetmek için "headerlogger" olarak adlandırılan tek bir günlükçü kullanın ve üstbilginizi alın:

Üstbilgi ve dosya düzenlerini tanımlayın:

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

Yerleşimleri kullanarak hedefleri tanımlayın:

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

Kaydedicileri tanımlayın:

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

Başlığı, muhtemelen programın başlarında yazın:

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

Bu büyük ölçüde "İstisnaları farklı şekilde tedavi etme" fikrinin sadece bir başka versiyonudur.

Her günlük düzeyini farklı bir düzenle günlüğe kaydetme

Benzer şekilde poster, günlük düzeyi başına biçimin nasıl değiştirileceğini bilmek istiyordu. Son hedefin ne olduğu (ve "daha iyi" bir şekilde gerçekleştirilip gerçekleştirilemeyeceği) bana açık değildi, ancak sorduğunu yapan bir yapılandırma sağlayabildim:

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

Yine, İstisnaları farklı şekilde tedavi etmeye çok benzer .


1
Güzel! Daha GlobalDiagnosticsContextönce görmemiştim .
Pat

10

Twitter'da oturum aç

Bu yazıya dayanarak bir log4net Twitter Appender hakkında, Bir NLog Twitter Hedefi yazmada elimi deneyeceğimi düşündüm (2.0 değil NLog 1.0 yenileme kullanarak). Ne yazık ki, şimdiye kadar gerçekten başarılı bir şekilde göndermek için bir Tweet alamadım. Kodumda, Twitter'da, şirketimizin internet bağlantısında / güvenlik duvarında veya nede bir sorun olup olmadığını bilmiyorum. Birisi denemek isteyen ilgilenen durumunda burada kodu gönderiyorum. Üç farklı "Gönder" yöntemi olduğunu unutmayın. İlk denediğim PostMessageToTwitter. PostMessageToTwitter esas olarak orignal gönderideki PostLoggingEvent ile aynıdır. Bunu kullanırsam 401 istisnası olur. PostMessageBasic aynı istisnayı alır. PostMessage hatasız çalışır, ancak mesaj yine de Twitter'a ulaşmaz. PostMessage ve PostMessageBasic burada SO bulduğum örneklere dayanmaktadır.

FYI - Şimdi bu yazıdaki @Jason Diller tarafından Twitter'ın temel kimlik doğrulamasını "gelecek ay" kapatacağını söyleyen bir cevaba bir yorum buldum . Bu geri Mayıs 2010'da oldu ve şimdi Aralık 2010, bu yüzden bu neden çalışmıyor olabilir sanırım.

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

Bu şekilde yapılandırın:

NLog'a hedefi içeren montajı söyleyin:

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

Hedefi yapılandırın:

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

Birisi bunu denerse ve başarılı olursa, bulgularınızla geri gönderin.


Twitter OAuth kullanıyor - .NET'in dotnetopenauth.net'te
Pat

8

Koşullu Düzenleri kullanarak her bir günlük düzeyini farklı bir düzenle Günlüğe Kaydetmenin Daha Kolay Yolu

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

Sözdizimi için bkz. Https://github.com/NLog/NLog/wiki/When-Filter .


7

Harici bir web sitesine / veritabanına raporlama

Uygulamalarımızdan gelen hataları (kullanıcılar genellikle bilmediği için) basit ve otomatik olarak bildirmenin bir yolunu istedim. Gelebileceğim en kolay çözüm, bir uygulama hatası üzerine veri gönderilen genel bir URL - giriş alabilen ve bir veritabanına depolayabilen bir web sayfasıydı. (Veri tabanı daha sonra yeni hatalar olup olmadığını öğrenmek için bir geliştirici veya komut dosyası tarafından kontrol edilebilir.)

Web sayfasını PHP'de yazdım ve verileri depolamak için bir mysql veritabanı, kullanıcı ve tablo oluşturdum. Dört kullanıcı değişkeni, bir id ve bir zaman damgası karar verdim. Olası değişkenler (URL'ye veya POST verileri olarak dahil edilmiştir):

  • app (Uygulama Adı)
  • msg (mesaj - örneğin İstisna oluştu ...)
  • dev (geliştirici - ör. Pat)
  • src(kaynak - bu, uygulamanın çalıştığı makineyle ilgili bir değişkenden gelir, örneğin Environment.MachineNameveya bunun gibi)
  • log (bir günlük dosyası veya ayrıntılı mesaj)

(Değişkenlerin tümü isteğe bağlıdır, ancak hiçbiri ayarlanmadıysa hiçbir şey rapor edilmez - bu nedenle yalnızca web sitesi URL'sini ziyaret ederseniz db'ye hiçbir şey gönderilmez.)

Verileri URL'ye göndermek için NLog'un WebServicehedefini kullandım . (Not, ilk başta bu hedefle ilgili birkaç problemim vardı. Kaynağa bakana kadar a ile bitemediğimi anlayana urlkadar değildi /.)

Sonuç olarak, harici uygulamalarda sekmeleri tutmak için kötü bir sistem değildir. (Elbette yapılacak kibar şey , kullanıcılarınıza büyük olasılıkla hassas verileri rapor edeceğinizi bildirmek ve onlara katılma / çıkış yapma yolu sunmaktır.)

MySQL işleri

(Db kullanıcısının INSERTkendi veritabanındaki bu tabloda yalnızca ayrıcalıkları vardır .)

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'

Web sitesi kodu

(PHP 5.3 veya 5.2 PDO etkin , dosya index.phpiçinde /reportklasöründe)

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

Uygulama kodu (NLog yapılandırma dosyası)

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

Not: Günlük dosyasının boyutu ile ilgili bazı sorunlar olabilir, ancak kısaltmak için basit bir yol bulamadım (örn. La * nix'in tailkomutu ).


Bu bir proje için çalıştı, ancak diğerlerinde ben ile ilgili sorunlar vardı url: InnerException: System.InvalidCastException Message = 'System.String' den 'System.Uri' geçersiz geçersiz. Kaynak = mscorlib StackTrace: at System.Convert.ChangeType (Nesne değeri, Tür conversionType) adresindeki System.Convert.DefaultToType (IConvertible değeri, Tür targetType, IFormatProvider sağlayıcı) , IFormatProvider sağlayıcı)
Pat

Günlüğü izleyebilmek ve bir hata durumunda bilgilendirilmek istiyorsanız başka bir seçenek de Twitter Hedefi olacaktır. Log4net için yazılmış bir Twitter Appender için bu bağlantıya bakın: twitterappender.codeplex.com Bunu tartışan orijinal blog yazısı burada: caseywatson.com/2009/07/07/log4net-twitter-awesome Benzer bir şey yazmak oldukça kolay olmalı NLog.
wageoghe

Ben bir NLog TwitterTarget yazma ile aptal ama aslında bir Tweet gönderilir başarı elde etmedim. Kodu bir cevap olarak gönderdim. İsterseniz denemek için çekinmeyin.
wageoghe

4

Giriş yap Silverlight

Silverlight ile NLog kullanırken, sağlanan web hizmeti aracılığıyla izlemeyi sunucu tarafına gönderebilirsiniz . Web sunucusu kullanılamıyorsa kullanışlı olan Yalıtılmış Depolama Alanındaki yerel bir dosyaya da yazabilirsiniz. Ayrıntılar için buraya bakın, yani kendinizi hedef yapmak için böyle bir şey kullanın:

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);
                    }
                }
            }
        }
    } 
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.