Error Log Generation Aspects by XML

Asked

Viewed 191 times

1

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

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

I was able to do it this 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 way out was 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:

  • This is the right way to log in all errors of the application?
  • Is there any contraindication to use this method?
  • How should I display the error message to users in the method Application_ThreadException? Something very generic or more specific?

2 answers

2


This is the correct way to log all application errors?

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

Not necessarily you need to generate an XML to get the errors. Could have written the stack directly into a text file without formatting, since it is only you who will check and no further processing will be done in the data mass.

Is there any contraindication to use this method?

Here you generate an XML file for every exception obtained 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 that if some kind of exception you have becomes too recurrent, the amount of files will increase a lot, beyond what is not a very efficient way to filter the errors by the contents of it.

Consider sending exceptions to Windows Event Log, where you can filter errors through a series of criteria:

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

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

It depends on the general profile of your user. If it is a more technical application, aimed at a more demanding user profile, the correct thing is to be very specific. Otherwise, there is no need.

  • 1

    considering your answer, I believe I will not have problems with this method, I tried to do some tests with the EventLog, but from what I’ve noticed he needs administrator privileges to work, which unfortunately I won’t be able to, but it would be a legal option to use as well

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 for your scenario is to evaluate the types of errors that may occur. Many sofware factories have "knowledge libraries" with the known errors and their causes.

Then, they add to the software, proper codes for this error and use these codes in the logs to be able to predict which solution they will use, only by the type of error. Thus reduce the work of error analysis.

The way to log in itself is the same, perhaps with a slightly more complex system, which tries layered repositories:

  • Try to automatically upload the log to me
  • If it doesn’t work, try to store it in a log file that also stores the stack of variables from the context where the error occurred (so I can advance a partial debug of null values)
  • If it doesn’t work, use the Windows/Linux log.

So you have some advantages:

  • Any attendant can propose a solution to the customer without having to stop a programmer as long as the error is known. And you don’t need to stop a programmer.

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

Downside:

  • Preparing the log component to handle these error codes takes work.

  • Keeping knowledge about mistakes and solutions organized in a way that can be used, gives work and requires discipline.

  • this approach would be interesting if it were a large system and with a team of developers, but in my case it is a small application, with a small number of users and with a single developer... eu, rsrs

  • 1

    So try to predict initially some simple things like, the type of error you want to log in, the errors that can occur in the places you are logging in and whether you want to take action before or after being warned of the error. Dai is easier to predict how you will write your logs and what type of log to use.

  • this way I found picks up errors from any part of the application from the moment the Form main is displayed, the amount of errors will probably be small and will mostly be logical errors, as one I found through a log, tried to use the TextBox.Substring(0, 2) without first checking whether the TextBox was filled

Browser other questions tagged

You are not signed in. Login or sign up in order to post.