web-dev-qa-db-fra.com

Implémentation de la classe de journalisation sécurisée

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();
    }
}
50
Sean Thoman

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?

92
x0n

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.

5
Reed Copsey

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!

2
Glenn Ferrie

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!
--------------------

1
Arghya C

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 :)

0
Dave R.