Zum Inhalt springen

Anleitung

Ablaufverfolgung

Die Ablaufverfolgung des OPC UA Stacks im SDK einschalten, Masken und Ausgabe wählen, Einträge selbst ausgeben und die Protokolle als sensibel behandeln.

Anleitung

Ablaufverfolgung ​

Beteiligte TypenOpc.Ua.Utils

Die Ablaufverfolgung (engl. Trace) schreibt Einträge über die Abläufe in Client und Server, zum Beispiel zur Fehlersuche bei Verbindungen und Zertifikaten. Das OPC UA .NET SDK hat dafür keine eigene API. Es enthält den OPC UA Stack der OPC Foundation, und dessen Ablaufverfolgung steuern Sie über die statische Klasse Opc.Ua.Utils. Die Einstellungen gelten für den ganzen Prozess, also für alle Clients und Server der Anwendung.

Standardverhalten ​

Im NuGet-Paket ist die Ablaufverfolgung ausgeschaltet: Keine Maske ist gesetzt, und es ist keine Datei festgelegt. Das hängt vom Build des SDK ab, nicht vom Build Ihrer Anwendung. Auch in einem Debug-Build Ihrer Anwendung schreibt das SDK deshalb nichts, solange Sie die Ablaufverfolgung nicht selbst einschalten.

Ablaufverfolgung einschalten ​

Beteiligte TypenOpc.Ua.Utils

Um die Einträge in eine Datei zu schreiben, legen Sie drei Dinge fest:

  1. Datei: Utils.SetTraceLog legt die Datei fest. Der zweite Parameter (deleteExisting) legt fest, ob das SDK eine vorhandene Datei zuerst löscht. Sonst hängt es die Einträge an.
  2. Masken: Utils.SetTraceMask legt fest, welche Einträge das SDK schreibt (Abschnitt Masken).
  3. Ausgabe: Utils.SetTraceOutput legt fest, wohin das SDK schreibt (Abschnitt Ausgabe).

So schalten Sie die Ablaufverfolgung ein:

csharp
string logFilePath = Path.Combine(AppContext.BaseDirectory, "opcua-trace.log");

Utils.SetTraceLog(logFilePath, deleteExisting: true);
Utils.SetTraceMask(
        Utils.TraceMasks.Error
        | Utils.TraceMasks.Information
        | Utils.TraceMasks.Security
        | Utils.TraceMasks.StartStop);

Utils.SetTraceOutput(Utils.TraceOutput.FileOnly);
vbnet
Dim logFilePath As String = Path.Combine(AppContext.BaseDirectory, "opcua-trace.log")

Utils.SetTraceLog(logFilePath, deleteExisting:=True)
Utils.SetTraceMask( _
        Utils.TraceMasks.Error _
        Or Utils.TraceMasks.Information _
        Or Utils.TraceMasks.Security _
        Or Utils.TraceMasks.StartStop)

Utils.SetTraceOutput(Utils.TraceOutput.FileOnly)

Verwenden Sie für die Datei einen absoluten Pfad, zum Beispiel im Verzeichnis der Anwendung (AppContext.BaseDirectory). Einen relativen Pfad löst das SDK gegen das temporäre Verzeichnis auf (Path.GetTempPath()), nicht gegen das Verzeichnis der Anwendung.

Wenn die Datei größer als 10 MB wird, löscht das SDK sie und beginnt eine neue Datei. Ältere Einträge gehen dabei verloren. Wenn Sie Einträge länger aufbewahren müssen, geben Sie sie selbst aus (Abschnitt Einträge selbst ausgeben).

Masken ​

Die Masken von Utils.TraceMasks sind Flags. Das SDK schreibt einen Eintrag nur, wenn seine Maske in der gesetzten Maske enthalten ist. Mehrere Masken kombinieren Sie mit dem bitweisen Oder.

MaskeWertEinträge
None0x0keine Einträge in der Datei
Error0x1alle Ausnahmen und Fehler, zum Beispiel abgelehnte Zertifikate, Fehler im Kanal und Fehler von OpcClient und OpcServer
Information0x2allgemeine Meldungen, zum Beispiel geschlossene oder neu verbundene Kanäle und Vorgänge in den Zertifikatspeichern. Einträge ohne eigene Maske erhalten diese Maske.
StackTrace0x4keine eigene Kategorie: hängt an Fehlermeldungen den Stack-Trace an und protokolliert jede ServiceResultException
Service0x8Aufruf und Abschluss jedes Service-Aufrufs
ServiceDetail0x10Anzahl der Nodes je Read- und RegisterNodes-Aufruf im Server
Operation0x20nicht verwendet
OperationDetail0x40nur Debug-Meldungen eines Debug-Builds des Stacks und die Wrapper für OPC Classic
StartStop0x80nur Start und Stopp des zentralen Node-Managers im Server
ExternalSystem0x100nicht verwendet
Security0x200Audit-Meldungen zu sicheren Kanälen (Secure Channels) und Zertifikaten, zum Beispiel neu erzeugte und automatisch akzeptierte Zertifikate
All0x7FFFFFFFalle Einträge

Für die Fehlersuche beginnen Sie mit Error und Information. Bei Problemen mit Verbindungen und Zertifikaten nehmen Sie Security hinzu, für die Abfolge der Service-Aufrufe Service. StackTrace brauchen Sie nur, wenn Sie den Ursprung eines Fehlers suchen. Service und StackTrace erzeugen viele Einträge.

Ausgabe ​

Die Werte von Utils.TraceOutput legen fest, wohin das SDK die Einträge schreibt:

AusgabeZiel
Offkeine Ausgabe
FileOnlynur in die Datei
DebugAndFilein die Datei und zusätzlich in die Debug-Ausgabe (Debug.WriteLine)
StdOutAndFilewie FileOnly: Das SDK schreibt dabei nicht auf die Konsole

Wenn Sie die Einträge auf der Konsole oder in einem eigenen Protokoll brauchen, geben Sie sie selbst aus.

Einträge selbst ausgeben ​

Beteiligte TypenOpc.Ua.Utils

Für ein eigenes Ziel, zum Beispiel das Protokoll Ihrer Anwendung, registrieren Sie einen Handler für das Ereignis Utils.Tracing.TraceEventHandler. Die Ereignisdaten enthalten die Maske des Eintrags (TraceMask), den Formattext (Format), seine Argumente (Arguments) und bei Einträgen zu Ausnahmen die Ausnahme selbst (Exception). Den Text des Eintrags bilden Sie aus Format und Arguments, zum Beispiel mit string.Format. Einträge zu Ausnahmen haben keine Argumente, ihr Text steht schon vollständig in Format; rufen Sie string.Format deshalb nur auf, wenn Argumente vorhanden sind. Typ und Meldung der Ausnahme enthält der Text bereits; Exception brauchen Sie nur, wenn Sie die Ausnahme als Objekt auswerten wollen.

csharp
Utils.Tracing.TraceEventHandler += HandleTraceEvent;

private static void HandleTraceEvent(
        object sender,
        TraceEventArgs e)
{
    if ((e.TraceMask & (Utils.TraceMasks.Error | Utils.TraceMasks.Security)) == 0)
        return;

    Console.WriteLine(FormatTraceText(e));
}

private static string FormatTraceText(TraceEventArgs e)
{
    // Messages without arguments, e.g. those of exceptions, arrive formatted already.
    if (e.Arguments == null || e.Arguments.Length == 0)
        return e.Format;

    return string.Format(CultureInfo.InvariantCulture, e.Format, e.Arguments);
}
vbnet
AddHandler Utils.Tracing.TraceEventHandler, AddressOf HandleTraceEvent

Private Shared Sub HandleTraceEvent( _
        ByVal sender As Object, _
        ByVal e As TraceEventArgs)
    If (e.TraceMask And (Utils.TraceMasks.Error Or Utils.TraceMasks.Security)) = 0 Then Return

    Console.WriteLine(FormatTraceText(e))
End Sub

Private Shared Function FormatTraceText(ByVal e As TraceEventArgs) As String
    'Messages without arguments, e.g. those of exceptions, arrive formatted already.
    If e.Arguments Is Nothing OrElse e.Arguments.Length = 0 Then Return e.Format

    Return String.Format(CultureInfo.InvariantCulture, e.Format, e.Arguments)
End Function

Beachten Sie dabei zwei Punkte:

  • Filtern: Das SDK löst das Ereignis für jeden Eintrag aus, auch für Einträge, die die gesetzte Maske ausschließt. Prüfen Sie deshalb im Handler selbst die Maske des Eintrags und verwerfen Sie, was Sie nicht brauchen.
  • Puffern: Das SDK ruft den Handler synchron im Thread auf, der den Eintrag schreibt, zum Beispiel im Thread einer Anfrage oder eines Timers. Ein langsamer Handler bremst deshalb Client und Server. Legen Sie den Eintrag im Handler nur in eine Warteschlange und geben Sie ihn in einem eigenen Thread aus. Das Muster dafür zeigt die Seite Abläufe im SDK.

So legt der Handler die Einträge nur in eine Warteschlange, und ein eigener Thread gibt sie aus:

csharp
var traceMessages = new BlockingCollection<string>();

// The traced code waits for the handler, so the handler only queues the text.
Utils.Tracing.TraceEventHandler += (sender, e) => traceMessages.Add(FormatTraceText(e));

var traceWriter = new Thread(() => {
    foreach (string message in traceMessages.GetConsumingEnumerable()) {
        Console.WriteLine(message);
    }
});

traceWriter.IsBackground = true;
traceWriter.Start();
vbnet
Dim traceMessages = New BlockingCollection(Of String)()

'The traced code waits for the handler, so the handler only queues the text.
AddHandler Utils.Tracing.TraceEventHandler, Sub(sender, e) traceMessages.Add(FormatTraceText(e))

Dim traceWriter = New Thread(Sub()
    For Each message As String In traceMessages.GetConsumingEnumerable()
        Console.WriteLine(message)
    Next
End Sub)

traceWriter.IsBackground = True
traceWriter.Start()

Per XML-Konfiguration einschalten ​

Die Ablaufverfolgung lässt sich auch in der XML-Konfiguration einstellen, mit dem Element TraceConfiguration und seinen Elementen OutputFilePath (Datei), DeleteOnLoad (vorhandene Datei beim Laden löschen) und TraceMasks (Masken als Zahl). Das SDK wendet diese Einstellungen nur an, wenn Sie die Konfiguration mit LoadClientConfigFile beziehungsweise LoadServerConfigFile laden (siehe Konfiguration per XML-Datei). Die Standardkonfiguration des SDK enthält kein solches Element.

Das Element TraceConfiguration ist ein direktes Kind des Wurzelelements. Es steht nach ServerConfiguration, ClientConfiguration, DiscoveryServerConfiguration und Extensions und vor DisableHiResClock. An einer anderen Stelle lädt die Datei nicht, oder das SDK ignoriert die Einstellung ohne Meldung. Der Pfad in OutputFilePath darf Umgebungsvariablen wie %LocalApplicationData% enthalten. Der Stack löst sie zu einem absoluten Pfad auf.

xml
  <TraceConfiguration>
    <OutputFilePath>%LocalApplicationData%\MyClientAppName\Logs\opcua-trace.log</OutputFilePath>
    <DeleteOnLoad>true</DeleteOnLoad>
    <TraceMasks>643</TraceMasks>
  </TraceConfiguration>

Inhalt der Einträge schützen ​

Die Einträge können Angaben enthalten, die Rückschlüsse auf Ihre Anlage und ihre Benutzer erlauben, zum Beispiel:

  • Adressen von Endpunkten
  • Subject und Thumbprint von Zertifikaten
  • Pfade der Zertifikatspeicher
  • Angaben zu Sitzungen mit dem Benutzernamen (Server, Maske Information)
  • Werte, bei denen eine Umwandlung fehlgeschlagen ist

Protokolle sind sensibel

Behandeln Sie Trace-Dateien und eigene Protokolle wie andere vertrauliche Betriebsdaten: Beschränken Sie den Zugriff auf die Datei, löschen Sie sie, wenn sie nicht mehr gebraucht wird, und geben Sie sie nur geprüft weiter, zum Beispiel an den Support. Schalten Sie umfangreiche Masken im Betrieb nur zur Fehlersuche ein.

Für ein Betriebsprotokoll eines Clients genügen oft die Zustandswechsel der Verbindung, siehe Zustandswechsel protokollieren. Ein Server kann Anfragen über eigene Ereignisse protokollieren, siehe Anfragen überwachen.