Pokud se sháníte po jednoduchém mechanizmu, jak ve Vašich aplikacích logovat výjimky, pak zřejmě v .NET pro consolové/WinForm aplikace marně hledáte něco, jako je healthMonitoring pro ASP.NET, kde je připraven jednoduchý, ale mocný nástroj a lze pomocí pouhých několika řádků ve web.config souboru získat přehled o výjimkách ve Vaší aplikaci, např.:
<configuration>
<system.web>
<healthMonitoring enabled="true">
<rules>
<add name="Mail Notifications on All Errors" eventName="All Errors" provider="SimpleMailWebEventProvider" profile="Default"/>
</rules>
<providers>
<add name="SimpleMailWebEventProvider" type="System.Web.Management.SimpleMailWebEventProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" from="errors@havit.cz" to="errors@example.cz" subjectPrefix="MyApplication: " buffer="true" bufferMode="Notification" maxEventLength="4096" maxMessagesPerNotification="2"/>
</providers>
</healthMonitoring>
</system.web>
</configuration>
…výše uvedená konfigurace Vám zajistí zasílání mailových notifikacích o neobsloužených výjimkách a chyách Vaší aplikace, jednoduché, prosté.
Jak na to u consolových a WinForm aplikací?
Pro consolové či WinForm aplikace nic jako healthMonitoring připraveno není, nicméně s pár řádky kódu lze dosáhnout obdobného efektu a dokonce u toho využít široké možnosti pro tracing v .NET 2.0. Základní myšlenka je prostá:
- .NET 2.0+ v sobě obsahuje široké možnosti pro tracing, kdy můžeme definovat různé zdroje zpráv (TraceSource), zprávy různé rozdělovat (Switch), filtrovat (TraceFilter) a směrovat na různé listenery (výstup do konzole, XML, textového souboru, event-logu, SQL, SMTP, atd. další si můžeme napsat) – to vše bez nutnosti jakýchkoliv doplňků – ať už Microsoftích Enterprise Library / Application Blocků, či produktů třetích stran typu Log4Net. Základní přehled o možnostech si můžeme udělat například z článku A Tracing Primer – Part I [Mike Rousos].
- .NET sám nemá mechanizmus, jak výjimky zapisovat do trace
- napsat pár řádků kódu, které zajistí zápis výjimek do trace je snadné!!!
- do trace můžeme buď výjimky posílat explicitně (níže volání ExceptionTracer.TraceException(myException)),
- nebo se můžeme přihlásit k odběru událostí, které neošetřené výjimky způsobují,
- obecně jde o událost AppDomain.CurrentDomain.UnhandledException
- ve WinForm aplikacích pak událost Application.ThreadException
Interface pro nasazení? …prostý
V nejjednodušší variantě si můžeme udělat například třídu ExceptionTracer, kterou přihlásíme k odběru příslušných událostí a v obsluze událostí už budeme jen výjimku posílat do trace.
Pro konzolové aplikace:
class Program
{
static void Main(string[] args)
{
ExceptionTracer.SubscribeToUnhandledExceptions();
throw new InvalidOperationException("Chybka!");
// ExceptionTracer.Default.TraceException(new ArgumentNullException("param", "Sakrapísek!"));
}
}
Pro WinFormAplikace:
static class Program
{
[STAThread]
static void Main()
{
ExceptionTracer.SubscribeToWindowsFormsThreadExceptions();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
App.config pak může vypadat nějak takto:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics>
<sources>
<source name="Exceptions" switchValue="Error">
<listeners>
<add name="LogFileListener"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="Exceptions.log"
/>
<add name="XmlListener"
initializeData="Exceptions.xml"
type="System.Diagnostics.XmlWriterTraceListener"
/>
</listeners>
</source>
</sources>
</system.diagnostics>
</configuration>
ExceptionTracer [Simplified Version]
Jednoduchá, ale plně funkční verze ExceptionTracekru může vypadat třeba takto:
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Windows.Forms;
namespace Havit.Diagnostics
{
public class ExceptionTracer
{
/// <summary>
/// Přihlásí ExceptionTracer k odběru všech neobsloužených výjimek (event AppDomain.CurrentDomain.UnhandledException).
/// </summary>
public static void SubscribeToUnhandledExceptions()
{
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
if (e.ExceptionObject is Exception)
{
TraceException((Exception)e.ExceptionObject);
}
}
/// <summary>
/// Přihlásí ExceptionTracer k odběru všech neobsloužených výjimek WinForm (event Application.ThreadException).
/// </summary>
public static void SubscribeToWindowsFormsThreadExceptions()
{
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
}
private static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
TraceException(e.Exception);
// původní implementace obsluhy výjimky, WinForm dialog s chybou
using (ThreadExceptionDialog excptDlg = new ThreadExceptionDialog(e.Exception))
{
DialogResult result = excptDlg.ShowDialog();
if (result == DialogResult.Abort)
{
Application.Exit();
}
}
}
/// <summary>
/// Pošle do trace zadanou výjimku.
/// </summary>
/// <param name="exception">výjimka k zaznamenání</param>
public static void TraceException(Exception exception)
{
TraceSource ts = new TraceSource("Exceptions");
ts.TraceEvent(TraceEventType.Critical, 0, exception.ToString());
ts.Flush();
ts.Close();
}
}
}
Příklad výstupu [XML]
<E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent">
<System xmlns="http://schemas.microsoft.com/2004/06/windows/eventlog/system">
<EventID>0</EventID>
<Type>3</Type>
<SubType Name="Critical">0</SubType>
<Level>1</Level>
<TimeCreated SystemTime="2008-01-31T21:08:35.0625000Z" />
<Source Name="Exceptions" />
<Correlation ActivityID="{00000000-0000-0000-0000-000000000000}" />
<Execution ProcessName="WindowsFormsApplication1" ProcessID="1232" ThreadID="1" />
<Channel />
<Computer>OSKAR</Computer>
</System>
<ApplicationData>System.ApplicationException: Test !!! at WindowsFormsApplication1.Form1.button1_Click(Object sender, EventArgs e) in D:\Development\ExceptionLogging\WindowsFormsApplication1\Form1.cs:line 20 at System.Windows.Forms.Control.OnClick(EventArgs e) at System.Windows.Forms.Button.OnClick(EventArgs e) at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent) at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks) at System.Windows.Forms.Control.WndProc(Message& m) at System.Windows.Forms.ButtonBase.WndProc(Message& m) at System.Windows.Forms.Button.WndProc(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m) at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)</ApplicationData>
</E2ETraceEvent>
Líbí se mi to:
Líbí Načítání...