Skip to content

How-to guide

Tracing

Turn on tracing of the OPC UA stack in the SDK, choose masks and output, write entries to your own target, and treat the logs as sensitive data.

This content was machine translated and may contain errors. Read in German

How-to guide

Tracing ​

Types usedOpc.Ua.Utils

Tracing writes entries about what happens inside clients and servers, for example to troubleshoot connections and certificates. The OPC UA .NET SDK has no API of its own for this. It contains the OPC UA stack of the OPC Foundation, and you control the stack's tracing through the static class Opc.Ua.Utils. The settings apply to the whole process, that is, to all clients and servers of the application.

Default behavior ​

In the NuGet package, tracing is off: no mask is set and no file is defined. This depends on the build of the SDK, not on the build of your application. Even in a debug build of your application, the SDK therefore writes nothing until you turn tracing on yourself.

Turn on tracing ​

Types usedOpc.Ua.Utils

To write the entries to a file, set three things:

  1. File: Utils.SetTraceLog sets the file. The second parameter (deleteExisting) defines whether the SDK deletes an existing file first. Otherwise, it appends the entries.
  2. Masks: Utils.SetTraceMask defines which entries the SDK writes (section Masks).
  3. Output: Utils.SetTraceOutput defines where the SDK writes (section Output).

To turn on tracing:

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)

Use an absolute path for the file, for example in the application directory (AppContext.BaseDirectory). The SDK resolves a relative path against the temporary directory (Path.GetTempPath()), not against the application directory.

When the file grows beyond 10 MB, the SDK deletes it and starts a new file. Older entries are lost. If you must keep entries longer, write them to your own target (section Write entries to your own target).

Masks ​

The masks of Utils.TraceMasks are flags. The SDK writes an entry only if its mask is contained in the mask you set. Combine several masks with a bitwise OR.

MaskValueEntries
None0x0no entries in the file
Error0x1all exceptions and errors, for example rejected certificates, channel errors, and errors of OpcClient and OpcServer
Information0x2general messages, for example closed or reconnected channels and operations in the certificate stores. Entries without a mask of their own get this mask.
StackTrace0x4no category of its own: appends the stack trace to error messages and logs every ServiceResultException
Service0x8call and completion of every service call
ServiceDetail0x10number of nodes per Read and RegisterNodes call in the server
Operation0x20not used
OperationDetail0x40only debug messages of a debug build of the stack and the wrappers for OPC Classic
StartStop0x80only start and stop of the central node manager in the server
ExternalSystem0x100not used
Security0x200audit messages on secure channels and certificates, for example newly created and automatically accepted certificates
All0x7FFFFFFFall entries

For troubleshooting, start with Error and Information. For problems with connections and certificates, add Security; for the sequence of service calls, add Service. You only need StackTrace to find the origin of an error. Service and StackTrace produce many entries.

Output ​

The values of Utils.TraceOutput define where the SDK writes the entries:

OutputTarget
Offno output
FileOnlythe file only
DebugAndFilethe file and also the debug output (Debug.WriteLine)
StdOutAndFilesame as FileOnly: the SDK does not write to the console

If you need the entries on the console or in your own log, write them yourself.

Write entries to your own target ​

Types usedOpc.Ua.Utils

To use your own target, for example the log of your application, register a handler for the event Utils.Tracing.TraceEventHandler. The event data contains the mask of the entry (TraceMask), the format text (Format), its arguments (Arguments), and for entries about exceptions the exception itself (Exception). Build the text of the entry from Format and Arguments, for example with string.Format. Entries about exceptions have no arguments. Their text is already complete in Format, so call string.Format only if there are arguments. The text already contains the type and message of the exception; you need Exception only if you want to evaluate the exception as an object.

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

Keep two points in mind:

  • Filter: The SDK raises the event for every entry, including entries that the mask you set excludes. Check the mask of the entry in the handler and discard what you do not need.
  • Buffer: The SDK calls the handler synchronously in the thread that writes the entry, for example the thread of a request or a timer. A slow handler therefore slows down clients and servers. In the handler, only put the entry into a queue and write it out in a separate thread. The SDK internals page shows this pattern.

In the following example, the handler only puts the entries into a queue, and a separate thread writes them out:

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

Turn on tracing in the XML configuration ​

You can also set up tracing in the XML configuration, with the element TraceConfiguration and its elements OutputFilePath (file), DeleteOnLoad (delete an existing file on load), and TraceMasks (masks as a number). The SDK applies these settings only if you load the configuration with LoadClientConfigFile or LoadServerConfigFile (see Configuration through an XML file). The default configuration of the SDK contains no such element.

The element TraceConfiguration is a direct child of the root element. It comes after ServerConfiguration, ClientConfiguration, DiscoveryServerConfiguration, and Extensions, and before DisableHiResClock. Placed elsewhere, the file does not load, or the SDK ignores the setting without a message. The path in OutputFilePath may contain environment variables such as %LocalApplicationData%. The stack resolves them to an absolute path.

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

Protect the content of the entries ​

The entries can contain details that reveal information about your plant and its users, for example:

  • endpoint addresses
  • subject and thumbprint of certificates
  • paths of the certificate stores
  • session details including the user name (server, mask Information)
  • values that failed to convert

Logs are sensitive

Treat trace files and your own logs like other confidential operating data: restrict access to the file, delete it when it is no longer needed, and pass it on only after review, for example to support. In production, turn on extensive masks only for troubleshooting.

For an operating log of a client, the state changes of the connection are often enough, see Log state changes. A server can log requests through its own events, see Monitor requests.