Click or drag to resize

eigene Logger

Dieses Kapitel handelt von selbst erstellten Loggern. Über eigene Logger können Sie TreeEvents im Vishnu-Tree nach Ihren Vorstellungen aufbereiten und in eine Log-Datei schreiben.

eigene Logger

Wir werden uns nachfolgend zuerst die Logging-Ausgaben des Demo-Projekts LoggerDemo aus der Projektmappe ...\InPlug\TextFileLogger ansehen, danach die Einbindung des TextFileLoggers in das Demo-Programm und zuletzt Auszüge aus der Klasse TextFileLogger.cs.

Hinweis  Hinweis

Die Ausgabe des Demo-Programms selbst ist einfach ein Konsole-Fenster, dass Sie mit Enter wieder schließen können (also hier nicht weiter interessant).

Allgemeine Informationen über Logger und wie diese in der JobDecription.xml definiert werden können Sie unter Vishnu Akteure nachschlagen.

Das Demo-Projekt

Hier zuerst die Logging-Ausgaben des Demo-Projekts:

Der Demo-Job erzeugt zwei Logdateien, eine auf dem Standard-LogPath und eine direkt in einem neuen Unterverzeichnis: ...\TextFileLogger\TextFileLoggerDemo\bin\Debug\sub1\TextFileLoggerDemo.log

Ausgaben vom TextFileLogger auf dem Standard-Pfad
2021.07.03 09:41:13,961124 Event: Ereignis auf Standard-LogPath
        Knoten: ein Knoten|0815, Logical: False, Quelle: 4711
        AnyServer, Thread: 0001/08292, Tree: Test-Tree, Status: Done
        WorkingDirectory: C:\Users\<user>\AppData\Local\Temp\TextFileLoggerDemo\1844
Ausgaben vom TextFileLogger auf einem speziellen Pfad
2021.07.03 09:41:14,226374 Event: Ereignis auf speziellem LogPath
        Knoten: ein Knoten|0815, Logical: False, Quelle: 4711
        AnyServer, Thread: 0001/08292, Tree: Test-Tree, Status: Done
        WorkingDirectory: C:\Users\<user>\AppData\Local\Temp\TextFileLoggerDemo\1844
Wichtig  Wichtig

Der Standard-LogPath wird über die Einbindung der Vishnu.Interchange.dll gesetzt und ist per Voreinstellung
%TempDirectory%\TextFileLoggerDemo\<ProcessId>\TextFileLoggerDemo.log.

Wenn der TextFileLogger später von Vishnu aufgerufen wird, zeigt der Standard-LogPath auf die Vishnu-Logdatei.

Einbindung und Aufruf der Klasse Logger.cs im Demo-Projekt, siehe folgendes Code-Listing:

Program.cs im Demo-Programm
using System;
using Vishnu.Interchange;

namespace TextFileLogger
{
    class Program
    {
        static void Main(string[] args)
        {
            TextFileLogger myLogger = new TextFileLogger();
            myLogger.Log(null, new TreeParameters("Test-Tree", null),
              new TreeEvent("Ereignis auf Standard-LogPath", "4711", "0815", "ein Knoten", "",
                            false, NodeLogicalState.Done, null, null), null);
            myLogger.Log(@"sub1\TextFileLoggerDemo.log", new TreeParameters("Test-Tree", null),
              new TreeEvent("Ereignis auf speziellem LogPath", "4711", "0815", "ein Knoten", "",
                            false, NodeLogicalState.Done, null, null), null);
            Console.WriteLine("Ende mit Enter");
            Console.ReadLine();
        }
    }
}
Wichtig  Wichtig

Der oben gezeigte Aufruf von Logger im Demo-Projekt dient nur zur Veranschaulichung. Sie selbst müssen den Logger nicht aufrufen, sondern nur in einer JobDescription.xml konfigurieren. Vishnu kümmert sich dann später selbst um den Aufruf.

Der TextFileLogger

Der eigentliche Logger ist in unserem Beispiel die Klasse TextFileLogger.cs, siehe folgendes Code-Listing:

TextFileLogger.cs Auszug
...
/// <summary>
/// Loggt Vishnu-Ereignisse in ein Logfile.
/// </summary>
public class TextFileLogger : INodeLogger
{
    /// <summary>
    /// Übernahme von diversen Logging-Informationen und Ausgabe in eine Logdatei.
    /// </summary>
    /// <param name="loggerParameters">Spezifische Aufrufparameter oder null.</param>
    /// <param name="treeParameters">Für den gesamten Tree gültige Parameter oder null.</param>
    /// <param name="treeEvent">Objekt mit Informationen über das Ereignis.</param>
    /// <param name="additionalEventArgs">Enthält z.B. beim Event 'Exception' die zugehörige Exception.</param>
    public void Log(object loggerParameters, TreeParameters treeParameters, TreeEvent treeEvent, object additionalEventArgs)
    {
        // Setzen des Pfades zur Logdatei
        this.SetLogFilePath(loggerParameters, treeEvent);

        // Zusammenbauen der zu loggenden Nachricht
        string bigMessage = BuildLogMessage(treeParameters, treeEvent, additionalEventArgs);

        this.WriteLog(bigMessage);
    }

    ...
    // Übernimmt entweder einen übergebenen, speziellen Pfad zu einer Logdatei
    // oder setzt den Default Pfad. Legt ggf. das Zielverzeichnis an.
    private void SetLogFilePath(object loggerParameters, TreeEvent treeEvent)
    {
        if (loggerParameters != null)
        {
            this._debugFile = loggerParameters.ToString();
        }
        else
        {
            this._debugFile = treeEvent.ReplaceWildcards("%DebugFile%");
        }
        if (!Directory.Exists(Path.GetDirectoryName(this._debugFile)))
        {
            Directory.CreateDirectory(Path.GetDirectoryName(this._debugFile));
        }
    }

    // Baut aus den übergebenen Parametern einen einzigen formatierten string zusammen.
    private string BuildLogMessage(TreeParameters treeParameters, TreeEvent treeEvent, object additionalEventArgs)
    {
        string indent = "        ";
        string addInfos = indent;
        if (treeEvent.Name.Contains("Exception"))
        {
            addInfos += (additionalEventArgs as Exception).Message;
        }
        if (treeEvent.Name.Contains("ProgressChanged"))
        {
            addInfos += String.Format("Fortschritt {0:d3}%", Convert.ToInt32(additionalEventArgs));
        }
        string timestamp = System.String.Format(System.Globalization.CultureInfo.CurrentCulture,
            "{0:yyyy.MM.dd HH:mm:ss,ffffff}", new object[] { treeEvent.Timestamp });
        StringBuilder bigMessage = new StringBuilder(timestamp + " Event: " + treeEvent.Name);
        string IdName = treeEvent.NodeName + "|" + treeEvent.SenderId;
        bigMessage.Append(Environment.NewLine + indent + "Knoten: " + IdName);
        bigMessage.Append(", Logical: " + treeEvent.Logical);
        bigMessage.Append(", Quelle: " + treeEvent.SourceId);
        bigMessage.Append(Environment.NewLine + indent + treeEvent.ReplaceWildcards("%MachineName%")
            + ", Thread: " + treeEvent.ThreadId.ToString());
        bigMessage.Append(", Tree: " + treeParameters.ToString());
        if (addInfos.Trim() != "")
        {
            bigMessage.Append(Environment.NewLine + addInfos);
        }
        if (!String.IsNullOrEmpty(treeEvent.NodePath))
        {
            bigMessage.Append(Environment.NewLine + indent + treeEvent.NodePath);
        }
        bigMessage.Append(", Status: " + treeEvent.State.ToString());
        bigMessage.Append(Environment.NewLine + indent + "WorkingDirectory: "
            + treeEvent.ReplaceWildcards("%WorkingDirectory%"));
        return bigMessage.ToString();
    }

    // Die Routine versucht, in eine möglicherweise von mehreren Knoten gleichzeitig
    // genutzte Logdatei zu schreiben; wirft im Fehlerfall keine Exception, sondern
    // versucht es wieder, bis ein Zähler abgelaufen ist.
    // Im ungünstigsten Fall kann der Logging-Versuch fehlschlagen.
    private void WriteLog(string message)
    {
        int maxTries = 5;
        StreamWriter streamWriter;
        int i = 0;
        do
        {
            try
            {
                using (streamWriter = new StreamWriter(new FileStream(this._debugFile,
                    FileMode.Append, FileAccess.Write), Encoding.Default))
                {
                    streamWriter.WriteLine(message);
                    i = maxTries; // erfolgreich, Loop beenden
                }

            }
            catch (SystemException)
            {
                Thread.Sleep(10); // 10 tausendstel Sekunden warten
            }
        } while (++i < maxTries);
    }
...

Die Klasse TextFileLogger implementiert die von Vishnu bereitgestellte Schnittstelle Vishnu.InterchangeINodeLogger. Die einzige Routine Log(...) übernimmt den Pfad zur Logdatei (Unterroutine SetLogFilePath), baut aus den übergebenen Parametern einen zusammenhängenden Meldungstext (Unterroutine BuildLogMessage) und ruft zuletzt die interne Routine WriteLog auf, in der versucht wird, die Meldung in die Logdatei zu schreiben.

Wichtig  Wichtig

Da bei der späteren Verwendung in Vishnu unter Umständen mehrere Logger gleichzeitig versuchen, in dieselbe Logdatei zu schreiben, kann es dazu kommen, dass ein oder mehrere Schreibversuche schiefgehen. Diese Logger-Implementierung versucht es deshalb insgesamt fünfmal, bevor (ohne weitere Fehlermeldung) aufgegeben wird.

Siehe auch