2009-12-15 8 views
7

Tôi muốn bao gồm ngăn xếp cuộc gọi (ví dụ: các phương thức đã gọi cho tôi) trong thông báo log4net. Có cách nào tiêu chuẩn để làm điều này?Hỗ trợ log4net có bao gồm ngăn xếp cuộc gọi trong thông báo tường trình

(Tôi biết điều này sẽ bị chậm, nhưng tôi chỉ cần làm điều đó trên một số lỗi)

+0

Không phải là phương pháp Log4net Lỗi (chuỗi, ngoại lệ) đủ cho điều này? – CodeMonkeyKing

+1

@CodeMonkeyKing - Tôi nghĩ rằng OP đang nói rằng anh ta muốn ghi lại dấu vết ngăn xếp trong một số tình huống mà anh ta không có ngoại lệ. –

Trả lời

11

Có - bạn có thể nhận thông tin chồng này sử dụng các mẫu sau trong một bố cục mẫu:

%type %file %line %method %location %class 

Xem tài liệu this trên PatternLayout để biết thêm thông tin.

Chỉnh sửa để đáp ứng với bình luận của Ian dưới đây: tôi không nghĩ log4net có thể được cấu hình để viết ra toàn bộ stack.

Bạn luôn có thể tự viết lại cho chính mình, sử dụng một cái gì đó như new StackTrace().ToString(), nhưng tôi đoán lý do bạn hỏi là bạn muốn cấu hình này trong cấu hình ghi nhật ký.

Tôi sẽ có cái nhìn sâu sắc hơn, nhưng cảm giác ruột của tôi không có cách nào để định cấu hình điều này và bạn sẽ phải triển khai lớp Bố cục của riêng mình.

Chỉnh sửa ++ OK - đây là lớp bố cục mẫu tùy chỉnh xuất phát từ PatternLayout nhưng thêm vào bố cục% ngăn xếp.

Mã này hơi thô - chỉ minh họa - không phải sản xuất đã sẵn sàng! (Ví dụ, bạn có thể không có sự cho phép an ninh để truy cập stack bạn đang cố gắng in)

public class CustomPatternLayout : PatternLayout 
{ 
    public CustomPatternLayout() 
    { 
     this.AddConverter("stack", typeof(StackTraceConverter)); 
    } 
} 

public class StackTraceConverter : PatternLayoutConverter 
{ 
    protected override void Convert(TextWriter writer, LoggingEvent loggingEvent) 
    { 
     var stack = new StackTrace(); 

     var frames = stack.GetFrames(); 
     for (var i = 0; i < frames.Length; i++) 
     { 
      var frame = frames[i]; 

      // if the stack frame corresponds to still being inside the log4net assembly, skip it. 
      if (frame.GetMethod().DeclaringType.Assembly != typeof(LogManager).Assembly) 
      { 
       writer.WriteLine("{0}.{1} line {2}", 
        frame.GetMethod().DeclaringType.FullName, 
        frame.GetMethod().Name, 
        frame.GetFileLineNumber()); 
      } 
     } 
    } 
} 

Sau đó bạn có thể cấu hình này với cấu hình mẫu sau (lưu ý% chồng ở phần cuối của layout):

<layout type="ScratchPad.CustomPatternLayout,ScratchPad"> 
    <conversionPattern value="%date %-5level %message%newline %type %file %line %method %location %class %stack" /> 
    </layout> 
+0

khi tôi truy cập trang đó, tôi đã lấy% vị trí để chỉ xem tên đầy đủ của phương thức GỌI, chứ không phải ngăn xếp đầy đủ –

+0

Tôi muốn bao gồm callstack trong một số thư chứ không phải là các thư khác theo mặc định. (Ví dụ: nhật ký cho Ilog.Lỗi() để bao gồm các callstack sẽ được tại chỗ trên.) –

+0

Bạn không cần phải sử dụng một vòng lặp để viết ra các ngăn xếp cuộc gọi. Hãy xem tài liệu trên StackFrame.ToString: http://msdn.microsoft.com/en-us/library/system.diagnostics.stackframe.tostring.aspx – CodeMonkeyKing

2

Nếu bạn đang bắt ngoại lệ, thì chỉ cần đăng nhập Exception.ToString(), vì sẽ chứa dấu vết ngăn xếp đầy đủ và sẽ có sẵn dưới dạng thông điệp tường trình bình thường.

2

Câu trả lời là tốt nhất tôi tìm thấy, tôi quyết định mở rộng nó một chút để in toàn bộ dấu vết ngăn xếp chỉ cho trường hợp ngoại lệ nếu nó giúp bất cứ ai khác.

public class StackTraceConverter : PatternLayoutConverter 
{ 
    private static readonly Assembly _assembly = typeof (PatternLayoutConverter).Assembly; 

    public StackTraceConverter() 
    { 
     base.IgnoresException = false; 
    } 

    protected override void Convert(TextWriter writer, LoggingEvent loggingEvent) 
    { 
     var ex = loggingEvent.ExceptionObject; 
     if (ex == null) 
      return; 
     writer.WriteLine(ex.ToString()); 

     bool displayFilenames = true; // we'll try, but demand may fail 
     var stack = new StackTrace(displayFilenames); 
     int skip = 0; 
     for (var i = 0; i < stack.FrameCount; i++) 
     { 
      var sf = stack.GetFrame(i); 
      var mb = sf.GetMethod(); 
      if (mb != null) 
      { 
       var t = mb.DeclaringType; 
       if (t.Assembly != _assembly) 
       { 
        //this skips the current method and the method catching the exception 
        if (skip < 2) 
        { 
         skip++; 
         continue; 
        } 
        writer.Write(" at "); 

        // if there is a type (non global method) print it 
        if (t != null) 
        { 
         writer.Write(t.FullName.Replace('+', '.')); 
         writer.Write("."); 
        } 
        writer.Write(mb.Name); 

        // deal with the generic portion of the method 
        if (mb is MethodInfo && mb.IsGenericMethod) 
        { 
         Type[] typars = ((MethodInfo) mb).GetGenericArguments(); 
         writer.Write("["); 
         int k = 0; 
         bool fFirstTyParam = true; 
         while (k < typars.Length) 
         { 
          if (fFirstTyParam == false) 
           writer.Write(","); 
          else 
           fFirstTyParam = false; 

          writer.Write(typars[k].Name); 
          k++; 
         } 
         writer.Write("]"); 
        } 

        // arguments printing 
        writer.Write("("); 
        ParameterInfo[] pi = mb.GetParameters(); 
        bool fFirstParam = true; 
        for (int j = 0; j < pi.Length; j++) 
        { 
         if (fFirstParam == false) 
          writer.Write(", "); 
         else 
          fFirstParam = false; 

         String typeName = "<UnknownType>"; 
         if (pi[j].ParameterType != null) 
          typeName = pi[j].ParameterType.Name; 
         writer.Write(typeName + " " + pi[j].Name); 
        } 
        writer.Write(")"); 

        // source location printing 
        if (displayFilenames && (sf.GetILOffset() != -1)) 
        { 
         // If we don't have a PDB or PDB-reading is disabled for the module, 
         // then the file name will be null. 
         String fileName = null; 

         // Getting the filename from a StackFrame is a privileged operation - we won't want 
         // to disclose full path names to arbitrarily untrusted code. Rather than just omit 
         // this we could probably trim to just the filename so it's still mostly usefull. 
         try 
         { 
          fileName = sf.GetFileName(); 
         } 
         catch (SecurityException) 
         { 
          // If the demand for displaying filenames fails, then it won't 
          // succeed later in the loop. Avoid repeated exceptions by not trying again. 
          displayFilenames = false; 
         } 

         if (fileName != null) 
         { 
          // tack on " in c:\tmp\MyFile.cs:line 5" 
          writer.Write(" in {0}:line {1}", fileName, sf.GetFileLineNumber()); 
         } 
        } 
        writer.WriteLine(); 
       } 
      } 
     } 
    } 
} 
+0

Điều này làm việc tốt cho tôi - cảm ơn! Lưu ý rằng (có lẽ hiển nhiên) bạn phải bao gồm ngoại lệ trong cuộc gọi nhật ký, ví dụ: 'log.Fatal (message, ex)' – Geoff

0

Đó không phải là cách tiếp cận sạch, nhưng một cách nhanh chóng để đạt được điều này là tạo ra một ngoại lệ nhân tạo có chứa stack trace mong muốn:

public class ArtificialStackTraceException : Exception 
{ 
    public ArtificialStackTraceException(
     bool shouldIncludeFileInfo = true, 
     string message = "Artificial exception used to generate a stack trace." 
    ) : base(message) 
    { 
     var stackTrace = new System.Diagnostics.StackTrace(shouldIncludeFileInfo); 
     StackTrace = stackTrace.ToString(); 
    } 

    public override string StackTrace { get; } 

    public override string ToString() 
    { 
     return $"{nameof(ArtificialStackTraceException)}:{Environment.NewLine}{StackTrace}"; 
    } 
} 

(full source)

Sau đó, bạn có thể sử dụng nó như vậy:

Log.Error("It went kaboom", new ArtificialStackTraceException());