Поддерживает ли log4net включение стека вызовов в сообщение журнала - PullRequest
7 голосов
/ 15 декабря 2009

Я хочу включить стек вызовов (например, методы, которые вызвали меня) в сообщение log4net. Есть ли стандартный способ сделать это?

(я знаю, что это будет медленно, но мне нужно сделать это только при некоторых ошибках)

Ответы [ 4 ]

12 голосов
/ 15 декабря 2009

Да - вы можете получить эту информацию стека, используя следующие шаблоны в макете шаблона:

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

См. эту документацию по PatternLayout для получения дополнительной информации.

Отредактируйте в ответ на комментарий Иана ниже: Я не думаю, log4net можно настроить для записи всего стека.

Вы всегда можете сами написать об этом, используя что-то вроде new StackTrace().ToString(), но я предполагаю, что причина, по которой вы спрашиваете, заключается в том, что вы хотите, чтобы это можно было настроить в конфигурации регистрации.

У меня будет более глубокий взгляд, но мое внутреннее чувство состоит в том, что нет способа настроить это, и что вам в конечном итоге придется реализовать свой собственный класс Layout.

Edit ++ ОК - вот пользовательский класс макета шаблона, который наследуется от PatternLayout, но добавляет в макет% стека.

Этот код немного грубоват - только для иллюстрации - не готов к производству! (например, у вас может не быть разрешения безопасности для доступа к стопке, которую вы пытаетесь распечатать)

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

Затем вы можете настроить это с помощью следующей конфигурации шаблона (отметьте% stack в конце макета):

  <layout type="ScratchPad.CustomPatternLayout,ScratchPad">
    <conversionPattern value="%date %-5level %message%newline %type %file %line %method %location %class %stack" />
  </layout>
2 голосов
/ 22 февраля 2014

Ответ Робса был лучшим, который я нашел, я решил немного его расширить, чтобы вывести полную трассировку стека только для исключений, если это поможет кому-то еще.

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();
                }
            }
        }
    }
}
2 голосов
/ 15 декабря 2009

Если вы перехватываете исключения, просто зарегистрируйте Exception.ToString () , поскольку он будет содержать полную трассировку стека и будет доступен как обычное сообщение журнала.

1 голос
/ 19 мая 2017

Это не самый чистый подход, но быстрый способ добиться этого - создать искусственное исключение, которое содержит желаемую трассировку стека:

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

( полный источник )

Тогда вы можете использовать это так:

Log.Error("It went kaboom", new ArtificialStackTraceException());
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...