2009-12-15 11 views
7

Desidero includere lo stack di chiamate (ad esempio i metodi che mi hanno chiamato) in un messaggio log4net. C'è un modo standard per farlo?Supporto log4net incluso lo stack di chiamate in un messaggio di registro

(so che questo sarà lento, ma ho solo bisogno di farlo su alcuni errori)

+0

Non è il metodo log4net Errore (stringa, Eccezione) sufficiente per Questo? – CodeMonkeyKing

+1

@CodeMonkeyKing - Penso che l'OP stia dicendo che vuole registrare la traccia dello stack in alcuni scenari in cui non ha un'eccezione. –

risposta

11

Sì - è possibile ottenere queste informazioni dello stack utilizzando i seguenti modelli in un modello di layout:

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

Vedere la documentazione this sul PatternLayout per ulteriori informazioni.

Modifica in risposta al commento di Ian di seguito: Io non credo log4net può essere configurato per scrivere l'intero stack.

È sempre possibile tornare a scrivere da solo, utilizzando qualcosa come new StackTrace().ToString(), ma sto indovinando il motivo che si chiede è che si desidera che questo sia configurabile nella configurazione di registrazione.

Avrò un aspetto più profondo, ma il mio istinto è che non c'è modo di configurarlo e che si finirebbe per dover implementare la propria classe di layout.

Modifica ++ OK - qui è una classe modello di layout personalizzato che deriva dalla PatternLayout ma aggiunge in una pila% layout.

Questo codice è un po 'approssimativo - solo illustrativo - non è pronto per la produzione! (Per esempio, non si può avere l'autorizzazione di sicurezza per accedere alla pila si sta tentando di stampa)

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()); 
      } 
     } 
    } 
} 

È quindi possibile configurare questo con la seguente configurazione modello (% pila nota alla fine del layout):

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

quando ho eseguito il raid di quella pagina, ho scelto% location per visualizzare solo il nome completo del metodo CALLING, non lo stack completo delle chiamate –

+0

Desidero includere il callstack in alcuni messaggi ma non in altri per impostazione predefinita. (Ad esempio un registro per Ilog.Errore() per includere il callstack in modo che sia azzeccato.) –

+0

Non è necessario utilizzare un ciclo per scrivere lo stack di chiamate. Dai un'occhiata alla documentazione su StackFrame.ToString: http://msdn.microsoft.com/en-us/library/system.diagnostics.stackframe.tostring.aspx – CodeMonkeyKing

2

Se si stanno rilevando eccezioni, è sufficiente registrare Exception.ToString(), poiché conterrà l'intera traccia dello stack e sarà disponibile come normale messaggio di registro.

2

La risposta di Rob è stata la migliore che ho trovato, ho deciso di estenderlo un po 'per stampare la traccia dello stack completo solo per le eccezioni se aiuta qualcun altro.

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

Questo ha funzionato bene per me - grazie! Si noti che (forse ovviamente) è necessario includere l'eccezione nella chiamata di registro, ad es. 'log.Fatal (message, ex)' – Geoff

0

Non è l'approccio più pulito, ma un modo rapido per raggiungere questo obiettivo è quello di creare un'eccezione artificiale che contiene la traccia dello stack desiderato:

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)

allora si potrebbe utilizzarlo in questo modo:

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