Bonjour, c'est ma première publication, merci d'être indulgent :)
Ce code permet d'utiliser la gestion d'erreur C# (System.Diagnostics.Trace.TraceError(); TraceWarning() ... ou System.Diagnostics.Debug.WriteLine();)
et de rattacher le tout à un logger qui ecrit les erreurs dans un fichier de log.
L'intéret est d'avoir des codes dans mes librairies qui ne depend pas systématiquement d'un code de gestion d'erreur et ecriture de log.
2eme point : je voulais un system de log facile à intégrer genre un objet static à l'appli, quelques instructions dans un constructeur et ca roule tout seul.
public static MyLogListener monLogListener;
static void Main() {
string logPath = "C:\\log.txt";
monLogListener = new MyLogListener(logPath);
System.Diagnostics.Trace.Listeners.Add(monLogListener);
System.Diagnostics.Trace.AutoFlush = true;
Je voulais aussi avoir comme fonctionnalités :
- un system d'écriture ROBUSTE : supporte l'ecriture multiple depuis plusieurs threads ou application sur un même fichier en même temps
- limitation de la taille du fichier (derniers logs ecrasé)
- ecriture des logs non blocante pour l'appli
- ecriture des logs inversées (le log le plus récent en début de fichier et pas à la fin)
- Ecriture de la date
- Ecriture du message des exceptions passés en argument à TraceError()
- un system de notification qui indique qu'une erreur a été detecté (pour notifier l'utilisateur par exemple).
Voila je vous laisse tester et reprendre ce qui vous intéresse. En esperant que ce code vous sera utile.
Source / Exemple :
/**
- Auteur : Tyrann
-
- Projet : ---
-
- Module:
- MyLogListener.cs
-
- Sujet:
- Creation d'une class Listener permettant d'écrire très simplement des logs dans un fichier text
- L'ecriture est inversé (+facile à lire), dernier log ecrit en debut du fichier
- La taille du fichier de log est limité à 1 Mo par defaut ou modifiable la propriété MaxLogSize
-
- Autre implemetation utile :
- - un system d'écriture ROBUSTE : supporte l'ecriture multiple depuis plusieurs threads ou application sur un même fichier en même temps
- - limitation de la taille du fichier (derniers logs ecrasé)
- - ecriture des logs non blocante pour l'appli
- - ecriture des logs inversées (le log le plus récent en début de fichier et pas à la fin)
- - Ecriture de la date & heure du log
- - Ecriture du message des exceptions passés en argument à TraceError()
- - un system de notification qui indique qu'une erreur a été detecté (pour notifier l'utilisateur par exemple).
-
- Des qu'une exception est recu,
- => la valeur ExceptionDetected = true
- => LastException contient la derniere exception recu
- => LastErrorMsg contient le derniere message recu
- => un evenement ExceptionDetectedEvent peut être envoyé (exemple affiche une bulle de message sur les erreurs)
*
- Il est aussi possible de l'utiliser + simplement ainsi :
- MyLogListener log = new MyLogListener("C:\\log.txt");
- log.WriteLine(message);
*
-
-
- Pour l'utiliser le logger dans votre code
- Lors de l'initalisation dans votre application (class main ou constructeur fenetre appli)
- ==========================================
- // object globale
- public static MyLogListener monLogListener;
- //constructeur de l'appli
- public Form1() {
- MyLogListener myLister = new MyLogListener("Log.txt");
- myLister.MaxLogSize = 0;// 1000000;
- myLister.WriteDateInfo = true;
- myLister.ErrorDetectedEvent += myLister_ErrorDetectedEvent;
- Trace.Listeners.Add(myLister);
- Trace.AutoFlush = true;
*
-
-
- // la fonction de traitement de l'évenement
- static void monLogListener_ErrorDetectedEvent(object sender, EventArgs e)
- {
- //recuperation du message de l'erreur
- string msgErr = monLogListener.LastErrorMsg;
- //recuperation du message de l'exception
- Exception ex = monLogListener.LastException;
- string msgEx = "";
- if (ex != null) msgEx = ex.Message;
- MessageBox.Show("Hoo une erreur est survenue, son message : " + msgErr + ".\r\n Exeption msg = " + msgEx);
- }
*
*
- Là ou vous voulez mettre un log
- =================================
- try{System.IO.Directory.CreateDirectory("");}
- catch (Exception err)
- {
- System.Diagnostics.Trace.WriteLine("message avec Trace.WriteLine()");
- System.Diagnostics.Trace.TraceError("message avec Trace.TraceError");
- System.Diagnostics.Trace.TraceWarning("message avec Trace.TraceWarning");
- System.Diagnostics.Trace.TraceInformation("message avec Trace.TraceInformation");
-
- System.Diagnostics.Trace.WriteLine(err);
- System.Diagnostics.Trace.TraceError("exception avec Trace.WriteLine()", err);
- System.Diagnostics.Trace.TraceWarning("exception avec Trace.WriteLine()", err);
- System.Diagnostics.Trace.TraceInformation("exception avec Trace.TraceInformation", err);
-
- System.Diagnostics.Debug.WriteLine(err);
- }
-
-
-
-
-
-
Autre exemple complet :
namespace TestLog
{
static class Program
{
public static MyLogListener monLogListener;
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
//initialisation du logger
string logPath = "C:\\log.txt";
monLogListener = new MyLogListener(logPath);
monLogListener.MaxLogSize = 0;// 1000000;
monLogListener.WriteDateInfo = true;
System.Diagnostics.Trace.Listeners.Add(monLogListener);
System.Diagnostics.Trace.AutoFlush = true;
Application.Run(new MaFenetre());
}
}
public partial class MaFenetre : Form
{
public MaFenetre()
{
InitializeComponent();
//ecoute evenement qd erreur
Program.monLogListener.ErrorDetectedEvent += monLogListener_ErrorDetectedEvent;
}
static void monLogListener_ErrorDetectedEvent(object sender, EventArgs e)
{
//recuperation du message de l'erreur
string msgErr = Program.monLogListener.LastErrorMsg;
//recuperation du message de l'exception
Exception ex = Program.monLogListener.LastException;
string msgEx = "";
if (ex != null) msgEx = ex.Message;
MessageBox.Show("Hoo une erreur est survenue, son message : " + msgErr + ".\r\n Exeption msg = " + msgEx);
}
private void button1_Click(object sender, EventArgs e)
{
// Exemple d'implementation d'erreur
try
{
System.IO.Directory.CreateDirectory("");
}
catch (Exception err)
{
System.Diagnostics.Trace.TraceError("Ho l'erreur",err);
}
}
private void button2_Click(object sender, EventArgs e)
{
Application.Run(new Form1());
}
}
}
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Collections;
namespace TestLog
{
public class MyLogListener : TraceListener
{
#region objets, variable privé
//surveillance rapprocher du fichier de log
private FileSystemWatcher watcher = null;
//chemin complet du fichier de log
private string _logPath;
// object pour le lock concernant l'ecriture dans le fichier
private Object fileLock = new Object();
// flag si messagebox deja affiché encas d'erreur (histoire de pas en avoir 15000
private bool alreadyMsgBox = false;
//Pile FIFO qui contient les message de log a écrire dans un fichier
private static Stack StackDeLogAEcrire;
#endregion
#region Propriétés
/// <summary>
/// Chemin vers le fichier de log
/// </summary>
public string LogPath
{
set
{
_logPath = value;
string fileName = Path.GetFileName(_logPath);
if (Path.GetExtension(_logPath) == string.Empty)
throw new Exception("Bad log filename, extension require!");
string directoryLogPath = Path.GetDirectoryName(_logPath);
// on vérifie que le répertoire existe
if (directoryLogPath != string.Empty)
if (!Directory.Exists(directoryLogPath))
Directory.CreateDirectory(directoryLogPath);
}
get { return _logPath; }
}
private int _maxLogInWait;
/// <summary>
/// Limitateur de la pile de message en attente (element de securité), à laisser à 50
/// car plus la taille de message en attente augmente plus le logger prend des ressources systems et nuie à l'ensemble
/// Mettre 0 pour desactiver cette protection (pour debuggage par exemple).
/// </summary>
public int MaxLogInWait
{
set { _maxLogInWait = value; }
get { return _maxLogInWait; }
}
private long _maxLogSize;
/// <summary>
/// Taille max du fichier de log (en oct). Mettre 0 desactive la limitation
/// </summary>
public long MaxLogSize
{
set { _maxLogSize = value; }
get { return _maxLogSize; }
}
private bool _showFatalErrorInMessageBox;
/// <summary>
/// Active l'affichage d'erreur fatal dans une boite de message
/// il est conseillé de désactiver cette propriete pour des processus serveurs
/// </summary>
public bool ShowFatalErrorInMessageBox
{
set { _showFatalErrorInMessageBox = value; }
get { return _showFatalErrorInMessageBox; }
}
private bool _WriteDateInfo;
/// <summary>
/// Indique si la date et l'heure est ecrite lors de chaque ecriture de log
/// </summary>
public bool WriteDateInfo
{
set { _WriteDateInfo = value; }
get { return _WriteDateInfo; }
}
private bool _indicateDate;
/// <summary>
/// Indique si la date et l'heure est ecrite lors de chaque ecriture de log
/// </summary>
public bool IndicateDate
{
set { _indicateDate = value; }
get { return _indicateDate; }
}
private bool _IsErrorDetected = false;
/// <summary>
/// Indique si une exception a été recu
/// </summary>
public bool IsErrorDetected
{
private set { _IsErrorDetected = value; }
get { return _IsErrorDetected; }
}
private Exception _LastException = null;
/// <summary>
/// Pointe sur la derniere exception recu
/// </summary>
public Exception LastException
{
private set { _LastException = value; }
get { return _LastException; }
}
private string _LastErrMsg = null;
/// <summary>
/// Indique le dernier message d'erreur recu
/// </summary>
public string LastErrorMsg
{
private set { _LastErrMsg = value; }
get { return _LastErrMsg; }
}
#endregion
#region Methodes publiques
/// <summary>
/// Constructeur
/// </summary>
/// /// <param name="logPath">
/// [in] Spécifie le chemin du fichier de log
/// </param>
public MyLogListener(string logPath)
{
if ((logPath == null) || (logPath == string.Empty))
throw new Exception("The path of the log file is required.");
LogPath = logPath;
MaxLogSize = 1000000; //1 Mo;
IndicateDate = true;
WriteDateInfo = true;
StackDeLogAEcrire = new Stack();
ShowFatalErrorInMessageBox = true;
MaxLogInWait = 50;
//lance le detecteur de changement sur le fichier de logs - cf.WriteInFicThreadStart()
watcher = new FileSystemWatcher();
watcher.Path = System.IO.Path.GetDirectoryName(this.LogPath);
// surveille que notre fichier.
watcher.Filter = System.IO.Path.GetFileName(this.LogPath); // "*.txt";
// surveille les changements dernier acces, dernieère ecriture, renommage et repertoire
watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName; //| NotifyFilters.DirectoryName;
// Add event handlers.
watcher.Changed += new FileSystemEventHandler(watcher_Changed);
watcher.Created += new FileSystemEventHandler(watcher_Changed);
watcher.Deleted += new FileSystemEventHandler(watcher_Changed);
watcher.Renamed += new RenamedEventHandler(watcher_Changed);
}
public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message)
{
//si de type information et ecriture log info non demandé : on sort
if (eventType == TraceEventType.Information)
if (this.WriteDateInfo == false)
return;
if (message == string.Empty)
message = "";
if (eventType == TraceEventType.Error)
{
//envoi l'evenement
RaiseExceptionDetectedEvent(message, null);
}
message = " Type : " + eventType.ToString() + " - message : " + message + "\r\n";
WriteLine(message);
}
public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message, params object[] args)
{
//si de type information et ecriture log info non demandé : on sort
if (eventType == TraceEventType.Information)
if (this.WriteDateInfo == false)
return;
if (args.Length > 0)
{
if (args[0] is Exception)
{
Exception ex = (Exception)args[0];
if (message == string.Empty)
message = "";
if (eventType == TraceEventType.Error)
{
//envoi de l'evenement
RaiseExceptionDetectedEvent(message, ex);
}
//formatage du message
string messageErr = " Type : " + eventType.ToString() + " - message : " + message + "\r\n";
messageErr += String.Format("EXCEPTION type : {0} \r\n Message d'erreur: {1} \r\n Origine : {2} \r\n", ex.GetType().ToString(), ex.Message, ex.StackTrace);
WriteLine(messageErr);
}
}
}
/// <summary>
/// Evénement indiquant la reception d'une erreur provenant de Trace.TraceError
/// </summary>
public event EventHandler ErrorDetectedEvent;
// Methode pour envoyer un evenement SearchContactEvent
protected virtual void RaiseExceptionDetectedEvent(string messageCourt, Exception ex)
{
this.LastException = ex;
this.LastErrorMsg = messageCourt;
this.IsErrorDetected = true;
EventHandler eventHandler = ErrorDetectedEvent;
if (eventHandler != null)
eventHandler(this, new EventArgs());
}
public override void WriteLine(string message, string cate)
{
WriteInFic(message + "\r\n");
}
public override void WriteLine(string message)
{
Write(message + "\r\n");
}
public override void Write(string message)
{
if (this.IndicateDate == true)
message = DateTime.Now.ToString()
+ ":" + DateTime.Now.Millisecond.ToString()
+ " -> " + message;
WriteInFic(message);
}
public override void WriteLine(object o)
{
base.WriteLine(o); //renvoi au pere, object non traité
}
#endregion
#region Methodes privées
private void WriteInFic(string message)
{
bool runThread = false;
lock (StackDeLogAEcrire)
{
//si la pile de message est vide on lancera le thread d'ecriture
if (StackDeLogAEcrire.Count == 0)
runThread = true;
StackDeLogAEcrire.Push(message);
}
if (runThread)
{
//lancement du thread
Thread WriteThread = new Thread(WriteInFicThreadStart);
WriteThread.Start();
}
}
private void WriteInFicThreadStart()
{
//Fonction coeur
//Cette fonction est lancé dans un thread car elle permet l'ecriture inversé des log dans un fichier
//l'opération consiste à
// 1- Prise de controle du fichier de log
// 2- recopie de son contenu dans un fichier temporaire
// 3- ecriture du message
// 4- ajoute les ancien log present dans le fichier temporaire
try
{
//verrouillage en cas de multi-thread/multi acces interne appli (à priori inutile mais bon y a le mauvais developpeur et le BON developpeur, le mauvais fait plein de bug et le bon ... aussi :))
lock (fileLock)
{
long tailleFic = 0; // compteur de taille du fichier pour appliquer le limitateur de taille des logs
//si un fichier de log existe, on faudra rajouter son contenu apres notre message de log
string oldFilePath = this.LogPath + ".old";
bool bRecopieNedd = false;
if (File.Exists(this.LogPath))
bRecopieNedd = true;
//1- tentative de prise de controle du fichier de log
//Permet de prend en compte le cas de plusieurs appli/process accèdant au même fichier de log
FileStream fsw = null;
try
{
fsw = new FileStream(this.LogPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read);
}
catch
{
//if (watcher == null)
//{
//}
// commence la surveillance.
if (watcher.EnableRaisingEvents == false)
watcher.EnableRaisingEvents = true;
return;
}
//2- recopie de son contenu dans un fichier temporaire
if (File.Exists(oldFilePath))
File.Delete(oldFilePath);
if(bRecopieNedd)
File.Copy(this.LogPath, oldFilePath);
//3- ecriture du message
//ouverture du flux en ecriture
System.IO.StreamWriter sw = new StreamWriter(fsw);
//ecriture du message
lock (StackDeLogAEcrire)
{
while (StackDeLogAEcrire.Count > 0)
{
string message = (string)StackDeLogAEcrire.Pop();
sw.Write(message);
tailleFic += message.Length; //1car = 1oct
}
}
//4- ajoute les ancien log present dans le fichier temporaire
if (bRecopieNedd)
{
using (StreamReader sr = new StreamReader(oldFilePath))
{
String line;
while ((line = sr.ReadLine()) != null)
{
tailleFic += line.Length; //1car = 1oct
//si taille max defini des log est atteint on arrete la recopie
if (tailleFic < this.MaxLogSize || this.MaxLogSize == 0)
sw.WriteLine(line);
else
break;
}
}
//suppression du fichier temporaire
File.Delete(oldFilePath);
}
//relachement du flux et fichier
sw.Close();
fsw.Close();
//si entre temps il y a des nouveaux element à ecrire on relance le thread
lock (StackDeLogAEcrire)
{
if (StackDeLogAEcrire.Count > 0)
{
//Protection : Limitation de la pile et capacité de traitement du logger
//si autant de message en attente = ralentissement machine, process ... pas forcement judicieux
if (StackDeLogAEcrire.Count > MaxLogInWait && MaxLogInWait != 0)
{
StackDeLogAEcrire.Clear();
StackDeLogAEcrire.Push("==== DEPASSEMENT DU NOMBRE DE MESSAGE QUE LE GESTIONNAIRE DE LOG PEUT TRAITER !!! ===");
StackDeLogAEcrire.Push("==== CERTAINS LOGS N'ONT PAS ETE ECRIT. ===");
}
Console.WriteLine("Le thread d'eriture a ete relance");
Thread WriteThread = new Thread(WriteInFicThreadStart);
WriteThread.Start();
}
}
}
}
catch
{
//si une erreur -- affichage de l'erreur dans une box (si propriété autorisée)
if (this.ShowFatalErrorInMessageBox)
{
if (!this.alreadyMsgBox)
{
System.Windows.Forms.MessageBox.Show("Erreur Ecriture de log impossible!", System.Reflection.Assembly.GetEntryAssembly().FullName, System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
alreadyMsgBox = true;
}
}
}
}
private void watcher_Changed(object source, FileSystemEventArgs e)
{
// l'etat du fichier de log a changer, on relance l'ecriture
// arrete la surveillance.
if (watcher.EnableRaisingEvents == true)
watcher.EnableRaisingEvents = false;
WriteInFicThreadStart();
}
#endregion
}
}
Vous n'êtes pas encore membre ?
inscrivez-vous, c'est gratuit et ça prend moins d'une minute !
Les membres obtiennent plus de réponses que les utilisateurs anonymes.
Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.
Le fait d'être membre vous permet d'avoir des options supplémentaires.