Cấu hình NLog hữu ích nhất [đã đóng]


348

Các cấu hình tốt nhất hoặc hữu ích nhất để đăng nhập với NLog là gì? (Chúng có thể đơn giản hoặc phức tạp, miễn là chúng hữu ích.)

Tôi đang nghĩ về các ví dụ như tự động cuộn các tệp nhật ký ở một kích thước nhất định, thay đổi bố cục (thông điệp tường trình) cho dù có ngoại lệ hay không, leo thang cấp độ nhật ký một khi xảy ra lỗi, v.v.

Dưới đây là một số liên kết:


3
Dưới đây là một số mẹo điều chỉnh hiệu suất dựa trên thử nghiệm: deep-depth.blogspot.com/2014/01/ Khăn
Neil

Câu trả lời:


391

Một số trong số này thuộc danh mục các mẹo NLog (hoặc ghi nhật ký) chung thay vì các đề xuất cấu hình nghiêm ngặt.

Dưới đây là một số liên kết đăng nhập chung từ đây tại SO (bạn có thể đã thấy một số hoặc tất cả các liên kết này rồi):

log4net so với Nlog

Ghi nhật ký thực hành tốt nhất

Điểm của một mặt tiền đăng nhập là gì?

Tại sao logger khuyên bạn nên sử dụng logger mỗi lớp?

Sử dụng mẫu phổ biến của việc đặt tên logger của bạn dựa trên lớp Logger logger = LogManager.GetCurrentClassLogger(). Điều này cung cấp cho bạn mức độ chi tiết cao trong logger của bạn và mang lại cho bạn sự linh hoạt cao trong cấu hình của logger (kiểm soát toàn cầu, theo không gian tên, theo tên logger cụ thể, v.v.).

Sử dụng logger không dựa trên tên lớp khi thích hợp. Có thể bạn có một chức năng mà bạn thực sự muốn kiểm soát việc đăng nhập riêng. Có thể bạn có một số mối quan tâm đăng nhập chéo (ghi nhật ký hiệu suất).

Nếu bạn không sử dụng ghi nhật ký dựa trên tên lớp, hãy xem xét việc đặt tên các logger của bạn theo một loại cấu trúc phân cấp nào đó (có thể theo khu vực chức năng) để bạn có thể duy trì tính linh hoạt cao hơn trong cấu hình của mình. Ví dụ: bạn có thể có một khu vực chức năng "cơ sở dữ liệu", FA "phân tích" và FA "ui". Mỗi trong số này có thể có các khu vực phụ. Vì vậy, bạn có thể yêu cầu logger như thế này:

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

Và như thế. Với các trình ghi nhật ký phân cấp, bạn có thể định cấu hình ghi nhật ký toàn cầu ("*" hoặc trình ghi nhật ký gốc), bằng FA (Cơ sở dữ liệu, Phân tích, Giao diện người dùng) hoặc bằng phương thức ngầm (Database.Connect, v.v.).

Loggers có nhiều tùy chọn cấu hình:

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

Xem trợ giúp NLog để biết thêm thông tin về chính xác ý nghĩa của từng tùy chọn. Có lẽ các mục đáng chú ý nhất ở đây là khả năng quy tắc logger ký tự đại diện, khái niệm rằng nhiều quy tắc logger có thể "thực thi" cho một câu lệnh ghi nhật ký duy nhất và quy tắc logger có thể được đánh dấu là "cuối cùng" để các quy tắc tiếp theo sẽ không thực thi cho đưa ra tuyên bố đăng nhập.

Sử dụng GlobalDiagnellectContext, MappedDiagnellectContext và NestedDiagnellectContext để thêm bối cảnh bổ sung vào đầu ra của bạn.

Sử dụng "biến" trong tệp cấu hình của bạn để đơn giản hóa. Ví dụ: bạn có thể xác định các biến cho bố cục của mình và sau đó tham chiếu biến trong cấu hình đích thay vì chỉ định bố cục trực tiếp.

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

Hoặc, bạn có thể tạo một tập các thuộc tính "tùy chỉnh" để thêm vào bố cục.

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

Hoặc, bạn có thể thực hiện các công cụ như tạo trình kết xuất bố cục "ngày" hoặc "tháng" thông qua cấu hình:

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

Bạn cũng có thể sử dụng kết xuất bố cục để xác định tên tệp của mình:

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

Nếu bạn cuộn tệp của mình hàng ngày, mỗi tệp có thể được đặt tên là "Thứ Hai.log", "Thứ Ba.log", v.v.

Đừng ngại viết trình kết xuất bố cục của riêng bạn. Thật dễ dàng và cho phép bạn thêm thông tin ngữ cảnh của riêng bạn vào tệp nhật ký thông qua cấu hình. Ví dụ: đây là trình kết xuất bố cục (dựa trên NLog 1.x, không phải 2.0) có thể thêm Trace.CorrelationManager.ActivityId vào nhật ký:

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

Nói với NLog nơi tiện ích mở rộng NLog của bạn (lắp ráp gì) như thế này:

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

Sử dụng trình kết xuất bố cục tùy chỉnh như thế này:

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

Sử dụng các mục tiêu không đồng bộ:

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

Và các trình bao bọc mục tiêu mặc định:

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

nơi thích hợp. Xem tài liệu NLog để biết thêm thông tin về những tài liệu đó.

Yêu cầu NLog xem và tự động tải lại cấu hình nếu thay đổi:

<nlog autoReload="true" /> 

Có một số tùy chọn cấu hình để giúp khắc phục sự cố NLog

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

Xem Trợ giúp NLog để biết thêm.

NLog 2.0 bổ sung các trình bao bọc LayoutRenderer cho phép xử lý bổ sung được thực hiện trên đầu ra của trình kết xuất bố cục (chẳng hạn như cắt xén khoảng trắng, đường trên, đường dưới, v.v.).

Đừng ngại bọc logger nếu bạn muốn cách ly mã của mình khỏi sự phụ thuộc cứng vào NLog, nhưng bọc chính xác. Có những ví dụ về cách bọc trong kho github của NLog. Một lý do khác để gói có thể là bạn muốn tự động thêm thông tin ngữ cảnh cụ thể vào từng thông điệp đã ghi (bằng cách đưa nó vào LogEventInfo.Context).

Có những ưu và nhược điểm để gói (hoặc trừu tượng hóa) NLog (hoặc bất kỳ khung ghi nhật ký nào khác cho vấn đề đó). Với một chút nỗ lực, bạn có thể tìm thấy nhiều thông tin ở đây trên SO trình bày cả hai mặt.

Nếu bạn đang xem xét gói, hãy xem xét sử dụng Common.Logging . Nó hoạt động khá tốt và cho phép bạn dễ dàng chuyển sang một khung đăng nhập khác nếu bạn muốn làm như vậy. Ngoài ra nếu bạn đang xem xét việc gói, hãy nghĩ về cách bạn sẽ xử lý các đối tượng bối cảnh (GDC, MDC, NDC). Common.Logging hiện không hỗ trợ sự trừu tượng hóa cho chúng, nhưng nó được cho là trong hàng đợi các khả năng cần thêm.


3
Câu trả lời chính xác. Chỉ cần thêm một điều, $ {machine} phải là $ {machinename}. Xem github.com/nlog/NLog/wiki/Layout-Renderers .
liang

2
Tôi đã rẽ nhánh Common.Logging và thêm phần trừu tượng bị thiếu, xem dự án GitHub hoặc NuGet .
Daniel Varod

Tôi không thể tìm thấy bất cứ điều gì là thông tin về nlog trong tài liệu của riêng họ, có lẽ tôi đang xem qua các ví dụ github sai cách? Ai biết.
JARRRG

Làm cách nào để sử dụng trình kết xuất tùy chỉnh đó với API (không có tệp cấu hình)? Đây là những gì tôi đang cố gắng thực hiện.
InteXX

OK đã nhận nó. Các NewLinebố trí hoàn thành nhiệm vụ. Đây là những gì tôi nghĩ ra. Nó chắc chắn đơn giản hơn nhiều so với tôi mong đợi.
InteXX

65

Đối xử với các ngoại lệ khác nhau

Chúng ta thường muốn có thêm thông tin khi có ngoại lệ. Cấu hình sau đây có hai mục tiêu, một tệp và bàn điều khiển, sẽ lọc xem có thông tin ngoại lệ nào hay không. (EDIT: Jarek đã đăng về một phương pháp mới để làm điều này trong vNext .)

Điều quan trọng là có một mục tiêu bao bọc với 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
Điều đó thật tuyệt vời với mục tiêu riêng biệt và FilteringWrapper để định dạng ngoại lệ. Tôi vừa trả lời một câu hỏi gần đây từ một anh chàng muốn đưa trình kết xuất bố cục {ngoại lệ} vào đầu ra của anh ta nhưng anh ta không muốn nhận () rõ ràng đã được ghi nếu không có ngoại lệ. Kỹ thuật này có thể sẽ làm việc tốt cho anh ta.
mức lương

+1 Rất đẹp. Tôi đã đánh dấu trang này trong một thời gian dài và được chuyển đến "Nhận xét của Pat" từ một câu hỏi SO khác liên quan đến bố cục có điều kiện.
eduncan911

1
Nếu một ngoại lệ được ghi lại, nó sẽ được ghi lại hai lần (phần VerboseLayout).
Tiến Đô

2
Tôi vừa thử nó vào ngày mai trong dự án của mình, vì bạn đặt quy tắc minlevel = "Warn" thành "file, fileAsException", tất cả các nhật ký sẽ được ghi lại đầu tiên với mục tiêu tệp (không có bộ lọc) và nếu đó là ngoại lệ (như được lọc bởi điều kiện) nó cũng sẽ được ghi lại với fileAsException.
Tiến Đô

3
@Tiendq ơi, tôi hiểu rồi. Điều đó có ý nghĩa, mặc dù bản thân ngoại lệ (chi tiết đầy đủ) sẽ chỉ được ghi lại một lần (nhưng thông điệp của nó sẽ được ghi lại hai lần). Bạn có thể sửa nó bằng cách thêm condition="length('${exception}')=0(hoặc có thể nó ==) vào target name="file".
Pat

60

Rõ ràng, bây giờ bạn có thể sử dụng NLog với Growl cho 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 với Growl cho Windows Tin nhắn theo dõi NLog với Growl cho Windows Thông báo gỡ lỗi NLog với Growl cho Windows Thông báo thông tin NLog với Growl cho Windows Thông báo cảnh báo NLog với Growl cho Windows Thông báo lỗi NLog với Growl cho Windows Thông báo gây tử vong cho NLog với Growl cho Windows


bạn có thể cho tôi biết phải làm gì để kết nối lại không? điều này hoạt động với tôi cho localhost nhưng khi tôi đã cung cấp một số địa chỉ IP trong máy chủ thì nó không hoạt động !!
Neel

@Neel, bạn nên kiểm tra cài đặt "Bảo mật" trong Growl trên máy tính mục tiêu. Bạn phải bật rõ ràng thông báo "LAN" và bạn có thể muốn thiết lập mật khẩu (sau đó bạn sẽ cần thêm vào mục tiêu NLog của mình). Nhưng tôi không thích rằng các thông báo từ xa xuất hiện trong Growl với "Nguồn gốc" của "Máy cục bộ"; Tôi phải thêm máy chủ vào các mục nhật ký để biết thông báo bắt nguồn từ đâu.
Kenny Evitt

Tôi có thể nhận được các thông báo để làm việc trên máy cục bộ của mình, nhưng không phải từ xa. Cài đặt bảo mật của tôi không có mật khẩu khi phát ra, vì vậy tất cả những gì tôi đã thêm là IP và cổng. Nhưng không có gì được gửi.
Jack Reilly

1
Dự án này đã chết 100%
Nhà phát triển

28

Định cấu hình NLog qua XML, nhưng theo lập trình

Gì? Bạn có biết rằng bạn có thể chỉ định trực tiếp NLog XML cho NLog từ ứng dụng của mình, trái ngược với việc NLog đọc nó từ tệp cấu hình không? Bạn có thể. Giả sử bạn có một ứng dụng phân tán và bạn muốn sử dụng cùng một cấu hình ở mọi nơi. Bạn có thể giữ một tệp cấu hình ở mỗi vị trí và duy trì nó một cách riêng biệt, bạn có thể duy trì một tệp ở một vị trí trung tâm và đẩy nó ra các vị trí vệ tinh hoặc bạn có thể làm nhiều việc khác. Hoặc, bạn có thể lưu trữ XML của mình trong cơ sở dữ liệu, lấy nó khi khởi động ứng dụng và định cấu hình NLog trực tiếp với XML đó (có thể kiểm tra lại định kỳ để xem nó có thay đổi không).

  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

Tôi không chắc nó mạnh đến mức nào, nhưng ví dụ này cung cấp một điểm khởi đầu hữu ích cho những người có thể muốn thử cấu hình như thế này.


nó hoạt động rất tốt ... ngoại trừ bằng cách sử dụng nó, nó không còn có thể tự động cấu hình lại hệ thống ghi nhật ký. Điều này đặc biệt như vậy nếu bạn liên kết đến một tệp bên ngoài (bao gồm)
Newtopian

2
Điều này đã làm việc, mặc dù tôi đã phải viết XML "tốt" bằng cách bao gồm:<?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
Đây là một segway tốt đẹp vào cấu hình tập trung. Các độc giả tương lai, xml được mã hóa cứng trong mẫu này chỉ dành cho bản demo (IMHO), đọc nó từ cơ sở dữ liệu hoặc tệp tập trung có thể là triển khai thực sự.
granadaCoder

@wageoghe; Tại sao tôi gặp lỗi (logger không tồn tại)? Tôi chỉ sao chép và dán mã
Bsflasher

22

Ghi nhật ký các cấp khác nhau tùy thuộc vào việc có lỗi hay không

Ví dụ này cho phép bạn có thêm thông tin khi có lỗi trong mã của bạn. Về cơ bản, nó đệm các thông báo và chỉ xuất ra các thông báo ở một mức nhật ký nhất định (ví dụ: Warn) trừ khi một điều kiện nhất định được đáp ứng (ví dụ: đã xảy ra lỗi, vì vậy mức nhật ký là> = Error), sau đó nó sẽ xuất thêm thông tin (ví dụ: tất cả các thông báo từ cấp độ nhật ký> = Dấu vết). Vì các tin nhắn được đệm, điều này cho phép bạn thu thập thông tin theo dõi về những gì đã xảy ra trước khi Lỗi hoặc Lỗi ngoại lệ được ghi lại - rất hữu ích!

Tôi đã điều chỉnh cái này từ một ví dụ trong mã nguồn . Lúc đầu tôi bị ném vì tôi đã bỏ qua AspNetBufferingWrapper(vì ứng dụng của tôi không phải là ứng dụng ASP) - hóa ra PostFilteringWrapper yêu cầu một số mục tiêu được đệm. Lưu ý rằng target-refphần tử được sử dụng trong ví dụ được liên kết ở trên không thể được sử dụng trong NLog 1.0 (Tôi đang sử dụng 1.0 Làm mới cho ứng dụng .NET 4.0); nó là cần thiết để đặt mục tiêu của bạn trong khối bao bọc. Cũng lưu ý rằng cú pháp logic (nghĩa là lớn hơn hoặc nhỏ hơn ký hiệu, <và>) phải sử dụng các ký hiệu, chứ không phải các lối thoát XML cho các ký hiệu đó (tức là &gt;&lt;) nếu không NLog sẽ bị lỗi.

ứng dụng.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>

Trong một số phiên bản của NLog (đối với mono và tôi nghĩ 2.0), điều này gây ra StackOverflowException, nhưng không phải ở phiên bản khác (làm mới NLog 1).
Pat

Về lỗi tràn - Có vẻ như chỉ do bố cục thuộc loại CSV - nếu tôi thực hiện bố cục thông thường thì không có vấn đề gì.
Pat

FileAsCsv target-ref dùng để làm gì? Tôi đang cố gắng lấy ví dụ này để làm việc với NLog v2.0.0.2000 nhưng đến nay vẫn thất bại.
Peter Mounce

@PeterMounce fileAsCsvMục tiêu-ref chỉ là một tạo tác từ thử nghiệm của tôi. Tôi tin rằng NLog 2 có / có vấn đề với CsvLayouts mà NLog 1 / Làm mới không có.
Pat

22

Tôi đã cung cấp một vài câu trả lời hợp lý thú vị cho câu hỏi này:

Nlog - Tạo phần tiêu đề cho tệp nhật ký

Thêm một tiêu đề:

Câu hỏi muốn biết làm thế nào để thêm tiêu đề vào tệp nhật ký. Sử dụng các mục cấu hình như thế này cho phép bạn xác định định dạng tiêu đề riêng biệt với định dạng của phần còn lại của các mục nhật ký. Sử dụng một trình ghi nhật ký duy nhất, có lẽ được gọi là "tiêu đề" để ghi nhật ký một tin nhắn khi bắt đầu ứng dụng và bạn nhận được tiêu đề của mình:

Xác định bố cục tiêu đề và tệp:

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

Xác định mục tiêu bằng cách sử dụng bố trí:

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

Xác định logger:

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

Viết tiêu đề, có thể là sớm trong chương trình:

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

Đây phần lớn chỉ là một phiên bản khác của ý tưởng "Điều trị ngoại lệ khác nhau".

Ghi nhật ký từng cấp độ với một bố cục khác nhau

Tương tự, người đăng muốn biết cách thay đổi định dạng cho mỗi cấp ghi nhật ký. Tôi không rõ mục tiêu cuối cùng là gì (và liệu nó có thể đạt được theo cách "tốt hơn" hay không), nhưng tôi có thể cung cấp một cấu hình thực hiện những gì anh ta yêu cầu:

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

Một lần nữa, rất giống với Điều trị ngoại lệ khác nhau .


1
Mát mẻ! Tôi đã không nhìn thấy GlobalDiagnosticsContexttrước đó.
Pat

10

Đăng nhập vào Twitter

Dựa trên bài đăng này về một ứng dụng Twitter log4net, Tôi nghĩ rằng tôi sẽ thử viết một mục tiêu Twitter của NLog (sử dụng NLog 1.0 refresh chứ không phải 2.0). Than ôi, cho đến nay tôi đã không thể có được một Tweet để thực sự đăng thành công. Tôi không biết liệu có lỗi gì trong mã của tôi, Twitter, kết nối / tường lửa internet của công ty chúng tôi hay không. Tôi đang đăng mã ở đây trong trường hợp ai đó quan tâm đến việc dùng thử. Lưu ý rằng có ba phương pháp "Đăng" khác nhau. Cái đầu tiên tôi thử là PostMessageToTwitter. PostMessageToTwitter về cơ bản giống như PostLoggingEvent trong bài viết gốc. Nếu tôi sử dụng, tôi nhận được một ngoại lệ 401. PostMessageBasic cũng có ngoại lệ tương tự. PostMessage chạy không có lỗi, nhưng thông báo vẫn không được gửi lên Twitter. PostMessage và PostMessageBasic dựa trên các ví dụ mà tôi tìm thấy ở đây trên SO.

FYI - Tôi vừa tìm thấy một bình luận của @Jason Diller cho một câu trả lời trong bài đăng này nói rằng twitter sẽ tắt xác thực cơ bản "vào tháng tới". Điều này đã trở lại vào tháng 5 năm 2010 và bây giờ là tháng 12 năm 2010, vì vậy tôi đoán đó có thể là lý do tại sao điều này không hoạt động.

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

Cấu hình nó như thế này:

Nói với NLog lắp ráp có chứa mục tiêu:

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

Cấu hình mục tiêu:

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

Nếu ai đó thử điều này và có thành công, hãy đăng lại với những phát hiện của bạn.


Twitter sử dụng OAuth - .NET có nhà cung cấp trong dotnetopenauth.net
Pat

7

Báo cáo cho một trang web / cơ sở dữ liệu bên ngoài

Tôi muốn một cách đơn giản và tự động báo cáo lỗi (vì người dùng thường không) từ các ứng dụng của chúng tôi. Giải pháp đơn giản nhất tôi có thể đưa ra là một URL công khai - một trang web có thể lấy dữ liệu đầu vào và lưu trữ nó vào cơ sở dữ liệu - được gửi dữ liệu khi có lỗi ứng dụng. (Cơ sở dữ liệu sau đó có thể được kiểm tra bởi nhà phát triển hoặc tập lệnh để biết nếu có lỗi mới.)

Tôi đã viết trang web bằng PHP và tạo một cơ sở dữ liệu, người dùng và bảng mysql để lưu trữ dữ liệu. Tôi đã quyết định bốn biến số người dùng, id và dấu thời gian. Các biến có thể (bao gồm trong URL hoặc dưới dạng dữ liệu POST) là:

  • app (tên ứng dụng)
  • msg (tin nhắn - ví dụ: Ngoại lệ xảy ra ...)
  • dev (nhà phát triển - ví dụ: Pat)
  • src(nguồn - điều này sẽ đến từ một biến liên quan đến máy mà ứng dụng đang chạy, ví dụ Environment.MachineNamehoặc một số như vậy)
  • log (một tệp nhật ký hoặc thông điệp dài dòng)

(Tất cả các biến là tùy chọn, nhưng không có gì được báo cáo nếu không có biến nào được đặt - vì vậy nếu bạn chỉ truy cập URL trang web, không có gì được gửi đến db.)

Để gửi dữ liệu tới URL, tôi đã sử dụng WebServicemục tiêu của NLog . (Lưu ý, lúc đầu tôi có một vài vấn đề với mục tiêu này. Mãi đến khi tôi nhìn vào nguồn mà tôi nhận ra rằng tôi urlkhông thể kết thúc bằng một /.)

Nói chung, đây không phải là một hệ thống tồi để giữ các tab trên các ứng dụng bên ngoài. (Tất nhiên, điều lịch sự cần làm là thông báo cho người dùng của bạn rằng bạn sẽ báo cáo dữ liệu có thể nhạy cảm và cung cấp cho họ cách để chọn vào / ra.)

Công cụ MySQL

(Người dùng db chỉ có các INSERTđặc quyền trên một bảng này trong cơ sở dữ liệu riêng của mình.)

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'

Mã trang web

(PHP 5.3 hoặc 5.2 khi bật PDO , tệp nằm trong thư mục)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;
}
?>

Mã ứng dụng (tệp cấu hình 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>

Lưu ý: có thể có một số vấn đề với kích thước của tệp nhật ký, nhưng tôi chưa tìm ra một cách đơn giản để cắt bớt nó (ví dụ: taillệnh của la * nix ).


Điều này đã làm việc cho một dự án, nhưng trong các dự án khác, tôi đã gặp vấn đề với url: InternalException: System.InvalidCastException Message = Không hợp lệ từ 'System.String' đến 'System.Uri'. Source = mscorlib StackTrace: tại System.Convert.DefaultToType (giá trị IConvertible, Type targetType, nhà cung cấp IFormatProvider) tại System.String.System.IConvertible.ToType (Loại loại, nhà cung cấp IFormatProvider) tại System.Convert.Change , Nhà cung cấp IFormatProvider)
Pat

Một tùy chọn khác nếu bạn muốn có thể theo dõi nhật ký và được thông báo trong trường hợp có lỗi sẽ là Mục tiêu Twitter. Xem liên kết này cho Twitter Appender được viết cho log4net: twitterappender.codeplex.com Bài đăng blog ban đầu thảo luận về vấn đề này ở đây: caseywatson.com/2009/07/07/log4net-twitter-awemme Rất dễ để viết một cái gì đó tương tự cho NLog.
mức lương

Tôi đã bị lừa khi viết một NLog TwitterTarget nhưng thực sự không thành công khi nhận được một Tweet được đăng. Tôi đã đăng mã như một câu trả lời. Hãy thử nếu bạn muốn.
tiền lương

6

Cách dễ dàng hơn để ghi nhật ký từng cấp nhật ký với bố cục khác nhau bằng cách sử dụng Bố cục có điều kiện

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

Xem https://github.com/NLog/NLog/wiki/When-Filter để biết cú pháp


4

Đăng nhập từ Silverlight

Khi sử dụng NLog với Silverlight, bạn có thể gửi theo dõi đến phía máy chủ thông qua dịch vụ web được cung cấp . Bạn cũng có thể ghi vào một tệp cục bộ trong Bộ lưu trữ biệt lập, rất hữu ích nếu máy chủ web không khả dụng. Xem ở đây để biết chi tiết, tức là sử dụng một cái gì đó như thế này để biến mình thành mục tiêu:

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);
                    }
                }
            }
        }
    } 
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.