Est-ce que ce qui suit serait la bonne façon d'implémenter une classe de journalisation thread-safe assez simple?
Je sais que je ne ferme jamais explicitement le TextWriter
, serait-ce un problème?
Lorsque j'ai utilisé initialement le TextWriter.Synchronized
méthode, cela ne semblait pas fonctionner jusqu'à ce que je l'initialise dans un constructeur statique et le rende en lecture seule comme ceci:
public static class Logger
{
static readonly TextWriter tw;
static Logger()
{
tw = TextWriter.Synchronized(File.AppendText(SPath() + "\\Log.txt"));
}
public static string SPath()
{
return ConfigManager.GetAppSetting("logPath");
}
public static void Write(string logMessage)
{
try
{
Log(logMessage, tw);
}
catch (IOException e)
{
tw.Close();
}
}
public static void Log(string logMessage, TextWriter w)
{
w.WriteLine("{0} {1}", DateTime.Now.ToLongTimeString(),
DateTime.Now.ToLongDateString());
w.WriteLine(" :");
w.WriteLine(" :{0}", logMessage);
w.WriteLine("-------------------------------");
// Update the underlying file.
w.Flush();
}
}
Je vais adopter une approche complètement différente ici des autres réponses et supposer que vous voulez réellement apprendre à écrire un meilleur code adapté aux threads et que vous ne cherchez pas de suggestions de tiers de notre part (même si vous finissez par utiliser une.)
Comme d'autres l'ont dit, vous créez un thread sécurisé TextWriter
ce qui signifie que les appels à WriteLine sont thread-safe, cela ne signifie pas qu'un groupe d'appels à WriteLine
va être effectué comme une opération atomique. J'entends par là qu'il n'y a aucune garantie que les quatre appels WriteLine vont se passer en séquence. Vous pouvez avoir un thread-safe TextWriter
, mais vous n'avez pas un thread-safe Logger.Log
méthode;) Pourquoi? Parce qu'à tout moment pendant ces quatre appels, un autre thread peut décider d'appeler également Log
. Cela signifie que vos appels WriteLine
seront désynchronisés. La solution consiste à utiliser une instruction lock
comme ceci:
private static readonly object _syncObject = new object();
public static void Log(string logMessage, TextWriter w) {
// only one thread can own this lock, so other threads
// entering this method will wait here until lock is
// available.
lock(_syncObject) {
w.WriteLine("{0} {1}", DateTime.Now.ToLongTimeString(),
DateTime.Now.ToLongDateString());
w.WriteLine(" :");
w.WriteLine(" :{0}", logMessage);
w.WriteLine("-------------------------------");
// Update the underlying file.
w.Flush();
}
}
Donc, maintenant vous avez un TextWriter
et un thread-safe Logger
.
Ça a du sens?
Lors de l'appel TextWriter.Synchronized protégera cette seule instance de TextWriter
, il ne synchronisera pas vos écritures de sorte qu'un appel "Log" reste ensemble à l'intérieur du fichier.
Si vous appelez Write
(ou Log
en utilisant l'instance interne TextWriter
) à partir de plusieurs threads, les appels individuels WriteLine
peuvent être entrelacés, ce qui rend votre date et heure timbres inutilisables.
J'utiliserais personnellement une solution de journalisation tierce qui existe déjà pour cela. Si ce n'est pas une option, la synchronisation vous-même (même avec un simple verrou) sera probablement plus utile que l'utilisation de TextWriter.Synchronized
wrapper.
Vous devriez regarder dans cette classe (qui fait partie de .NET 2.0), pas besoin de "créer" votre propre enregistreur. vous permet de vous connecter à un fichier texte, une vue d'événement, etc.
http://msdn.Microsoft.com/en-us/library/system.diagnostics.tracesource.aspx
Votre méthode "Log" peut ressembler à ceci (en supposant qu'il existe une variable membre intermal appelée "traceSource"):
public void Log(TraceEventType eventType, string message)
{
this.traceSource.TraceEvent(eventType, 0, message);
this.traceSource.Flush();
}
La prise en charge de ceci est une section de configuration qui nomme le TraceSource et a quelques paramètres de configuration. Il est supposé que lorsque vous construisez un TraceSource dans votre enregistreur, vous l'instanciez avec l'une des sources de trace nommées dans la configuration.
<system.diagnostics>
<sources>
<source name="Sample" switchValue="Information,ActivityTracing">
<listeners>
<add name="file"
initializeData="C:\temp\Sample-trace.log"
traceOutputOptions="DateTime"
type="System.Diagnostics.TextWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
</listeners>
</source>
</sources>
De plus, ne rendez pas votre enregistreur statique. À la place, utilisez Enterprise Library 5.0 Unity for Dependency Injection/IOC.
J'espère que cela t'aides!
Quelqu'un m'a signalé ce message tout en discutant de certains problèmes de journalisation aujourd'hui. Nous avons déjà de très bonnes réponses ici, mais j'ajoute ma réponse juste pour montrer une version plus simple de la classe Logger
qui fait exactement la même chose, de manière complètement Threadsafe
.
Une chose principale à noter ici, non TextWriter.Synchronized
est requis pour la sécurité des threads, car nous écrivons le fichier dans un lock
approprié.
Remarque: Cela a déjà été discuté dans la section commentaires de la réponse de x0n.
public static class Logger
{
static readonly object _locker = new object();
public static void Log(string logMessage)
{
try
{
var logFilePath = Path.Combine(@"C:\YourLogDirectoryHere", "Log.txt");
//Use this for daily log files : "Log" + DateTime.Now.ToString("yyyy-MM-dd") + ".txt";
WriteToLog(logMessage, logFilePath);
}
catch (Exception e)
{
//log log-exception somewhere else if required!
}
}
static void WriteToLog(string logMessage, string logFilePath)
{
lock (_locker)
{
File.AppendAllText(logFilePath,
string.Format("Logged on: {1} at: {2}{0}Message: {3}{0}--------------------{0}",
Environment.NewLine, DateTime.Now.ToLongDateString(),
DateTime.Now.ToLongTimeString(), logMessage));
}
}
}
Pour enregistrer quelque chose, appelez simplement en tant que
Logger.Log("Some important event has occurred!");
Et cela fera une entrée de journal comme celle-ci
Connecté le: 07 octobre 2015 à: 02:11:23
Message: Un événement important s'est produit!
--------------------
Si vous cherchez une méthode simple pour instrumenter votre code, la fonction existe déjà dans .NET:
http://msdn.Microsoft.com/en-us/library/system.diagnostics.trace.aspx
De plus, des outils tiers vous fourniront des solutions robustes pour la journalisation; les exemples incluent log4net , nLog et Enterprise Library .
Je recommande vraiment de ne pas réinventer la roue à ce sujet :)