2009-12-15 12 views
7

Deseo incluir la pila de llamadas (por ejemplo, los métodos que me llamaron) en un mensaje log4net. ¿Hay una forma estándar de hacer esto?Soporta log4net incluyendo la pila de llamadas en un mensaje de registro

(sé que esto va a ser lento, pero sólo tengo que hacerlo por algunos errores)

+0

No es el método log4net Error (cadena, Excepción) suficiente para ¿esta? – CodeMonkeyKing

+1

@CodeMonkeyKing - Creo que el OP dice que quiere registrar el seguimiento de la pila en algunos escenarios donde no tiene una excepción. –

Respuesta

11

Sí - usted puede conseguir esta información de la pila utilizando los siguientes patrones en un diseño de patrón:

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

Consulte la documentación this en PatternLayout para obtener más información.

Editar en respuesta al comentario de Ian continuación: yo no creo log4net se puede configurar para escribir toda la pila.

Siempre puede volver a escribirlo usted mismo, utilizando algo como new StackTrace().ToString(), pero supongo que la razón por la que pregunta es desea que esto sea configurable en la configuración de registro.

Tendré un aspecto más profundo, pero mi intuición es que no hay forma de configurar esto, y que terminaría teniendo que implementar su propia clase de diseño.

Editar ++ OK - aquí es una clase de diseño de patrón personalizado que se deriva de PatternLayout pero añade en una pila% diseño.

Este código es un poco áspero - solo ilustrativo - ¡no está listo para la producción! (Por ejemplo, puede que no tenga permiso de seguridad para acceder a la pila que está intentando imprimir)

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

entonces usted puede configurar esto con la siguiente configuración de patrones (nota% pila al final del trazado):

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

cuando hice esa página, tomé% location para solo meen el nombre completo del método CALLING, no la pila de llamadas completa –

+0

Deseo incluir el callstack en algunos mensajes pero no en otros de forma predeterminada. (Por ejemplo, un registro para Ilog.Error() para incluir la pila de llamadas sería perfecto). –

+0

No necesita usar un bucle para escribir la pila de llamadas. Eche un vistazo a la documentación en StackFrame.ToString: http://msdn.microsoft.com/en-us/library/system.diagnostics.stackframe.tostring.aspx – CodeMonkeyKing

2

Si está detectando excepciones, simplemente inicie sesión Exception.ToString(), ya que contendrá la traza completa de la pila y estará disponible como el mensaje de registro normal.

2

La respuesta de Robs fue la mejor que encontré, decidí extenderla un poco para imprimir el seguimiento completo de la pila solo para excepciones si ayuda a alguien más.

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

Esto funcionó bien para mí, ¡gracias! Tenga en cuenta que (tal vez obviamente) debe incluir la excepción en la llamada de registro, p. 'log.Fatal (mensaje, ex)' – Geoff

0

No es el enfoque más limpio, pero una manera rápida de conseguir esto es crear una excepción artificial que contiene el seguimiento de la pila deseada:

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)

entonces se podría úselo de la siguiente manera:

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