Aspects in Generating XML Error Logs

1

I have an application developed in C # that uses a SQL Server database, this application is made available through a virtualized application server, I do not have direct access to the database or the application server.

I want to create a way to log all errors in the application so that later I can check them and correct them, if possible.

I was able to do this in the following way:

// Fontes
// http://www.macoratti.net/13/07/c_excep.htm
// https://tiesontrowbridge.com/code/using-linq-to-easily-serialize-an-exception-to-xml

// Classe que executa a aplicação (Program.cs)
static void Main()
{
    // define o modo de tratamento dos erros não manipulados
    Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new frmMenu());
}

// -----------------------------------------------------------------------------

// Classe que serializa Exception (ExceptionXElement.cs)
using System;
using System.Collections;
using System.Linq;
using System.Xml.Linq;

public class ExceptionXElement : XElement
{
    public ExceptionXElement(Exception exception)
        : this(exception, false)
    { ; }

    public ExceptionXElement(Exception exception, bool omitStackTrace)
        : base(new Func<XElement>(() =>
        {
            XElement root = new XElement(exception.GetType().ToString());

            if (exception.Message != null)
            {
                root.Add(new XElement("Message", exception.Message));
            }

            if (!omitStackTrace && exception.StackTrace != null)
            {
                root.Add(
                    new XElement("StackTrace",
                        from frame in exception.StackTrace.Split('\n')
                        let prettierFrame = frame.Substring(6).Trim()
                        select new XElement("Frame", prettierFrame))
                    );
            }

            if (exception.Data.Count > 0)
            {
                root.Add(
                        new XElement("Data",
                        from entry in exception.Data.Cast<DictionaryEntry>()
                        let key = entry.Key.ToString()
                        let value = (entry.Value == null) ? "null" : entry.Value.ToString()
                        select new XElement(key, value))
                );
            }

            if (exception.InnerException != null)
            {
                root.Add(new ExceptionXElement(exception.InnerException, omitStackTrace));
            }

            return root;
        })())
    { ; }
}

// -----------------------------------------------------------------------------

// Classe que representa o formulário principal de aplicação (frmMenu.cs)
using System;
using System.Threading;
using System.Windows.Forms;

public partial class frmMenu : Form
{
    public frmMenu()
    {
        InitializeComponent();

        // assina o evento para tratar exceções não manipuladas
        Application.ThreadException += 
            new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
    }

    private void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
    {
        ExceptionXElement logException = new ExceptionXElement(e.Exception);
        logException.Save(@"D:\" + DateTime.Now.ToString("dd-MM-yy_HH-mm-ss") + ".xml");
        Close();
    }

    private void frmMenu_Shown(object sender, EventArgs e)
    {
        int dividendo = 15, divisor = 0, resultado;
        resultado = dividendo / divisor; // linha 25
        Console.WriteLine("{0} / {1} = {2}", dividendo, divisor, resultado);
    }
}

The output looks like this:

<?xml version="1.0" encoding="utf-8"?>
<System.DivideByZeroException>
  <Message>Tentativa de divisão por zero.</Message>
  <StackTrace>
    <Frame>frmMenu.frmMenu_Shown(Object sender, EventArgs e) na frmMenu.cs:linha 25</Frame>
    <Frame>System.Windows.Forms.Form.OnShown(EventArgs e)</Frame>
    <Frame>System.Windows.Forms.Form.CallShownEvent()</Frame>
    <Frame>System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry tme)</Frame>
    <Frame>System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object obj)</Frame>
    <Frame>System.Threading.ExecutionContext.runTryCode(Object userData)</Frame>
    <Frame>System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)</Frame>
    <Frame>System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)</Frame>
    <Frame>System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)</Frame>
    <Frame>System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry tme)</Frame>
    <Frame>System.Windows.Forms.Control.InvokeMarshaledCallbacks()</Frame>
  </StackTrace>
</System.DivideByZeroException>

My doubts are as follows:

  • Is this the correct way to log in all application errors?
  • Is there any contraindication to using this method?
  • How should I display the error message for users in the Application_ThreadException method? Something very generic or more specific?
asked by anonymous 13.12.2014 / 02:10

2 answers

2

Is this the correct way to log all application errors?

There is no absolute and correct way to get the stack status of your application. This is one of the forms, and I found it very interesting how you did it.

You do not necessarily need to generate an XML to get the errors. You could have written the stack straight into a plain text file, since it's just you checking it out and no further processing will be done on the mass of data.

Is there any contraindication to using this method?

Here you generate an XML file for each exception you get in your application:

private void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
    ExceptionXElement logException = new ExceptionXElement(e.Exception);
    logException.Save(@"D:\" + DateTime.Now.ToString("dd-MM-yy_HH-mm-ss") + ".xml");
    Close();
}

The only problem with it is if some kind of exception you get very recurring, the amount of files will increase greatly, and it is not a very efficient way to filter the errors for the content of it.

Consider sending the exceptions to the Windows Event Log, where you can filter errors by a number of criteria:

private void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
    System.Diagnostics.EventLog.WriteEntry("MeuSistema", e.Exception.StackTrace,                  
                                   System.Diagnostics.EventLogEntryType.Error);
}

How do I display the error message for users in the Application_ThreadException method? Something very generic or more specific?

Depends on the general profile of your user. If it is a more technical application, aimed at a more demanding user profile, the correct message is very specific. Otherwise, there is no need.

    
13.12.2014 / 02:54
1

Repeating a well-known phrase: there is no silver bullet.

Error handling, as well as a log, is a particular case of your software. For example, my advice to your scenario is to evaluate the types of errors that can occur. Many software factories have "knowledge libraries" with known bugs and their causes.

Then, they add to the software their own codes for this error and use those codes in the logs to be able to predict which solution they will use, just by the type of error. This reduces the error analysis work.

The way to log itself is the same, perhaps with a somewhat more complex system, which attempts repositories in layers:

  • Try to automatically send the log to me
  • If it does not work, try to save it to a log file that also stores the stack of variables from the context where the error occurred (so I can preface a partial debug of null values)
  • If it does not work, use the Windows / Linux log.

So you have some advantages:

  • Anyone can propose a solution to the client without having to stop a programmer, as long as the error is known. And you do not have to stop a programmer.

  • You do not need a client to report an error, it is sent to you and you can act proactively.

Disadvantage:

  • Preparing the log component to deal with these error codes gives work.

  • Keeping knowledge about bugs and solutions organized in a way that you can use, work, and require discipline.

13.12.2014 / 14:04