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.
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:
- File:
Utils.SetTraceLogsets the file. The second parameter (deleteExisting) defines whether the SDK deletes an existing file first. Otherwise, it appends the entries. - Masks:
Utils.SetTraceMaskdefines which entries the SDK writes (section Masks). - Output:
Utils.SetTraceOutputdefines where the SDK writes (section Output).
To turn on tracing:
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);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.
| Mask | Value | Entries |
|---|---|---|
None | 0x0 | no entries in the file |
Error | 0x1 | all exceptions and errors, for example rejected certificates, channel errors, and errors of OpcClient and OpcServer |
Information | 0x2 | general messages, for example closed or reconnected channels and operations in the certificate stores. Entries without a mask of their own get this mask. |
StackTrace | 0x4 | no category of its own: appends the stack trace to error messages and logs every Service |
Service | 0x8 | call and completion of every service call |
ServiceDetail | 0x10 | number of nodes per Read and RegisterNodes call in the server |
Operation | 0x20 | not used |
OperationDetail | 0x40 | only debug messages of a debug build of the stack and the wrappers for OPC Classic |
StartStop | 0x80 | only start and stop of the central node manager in the server |
ExternalSystem | 0x100 | not used |
Security | 0x200 | audit messages on secure channels and certificates, for example newly created and automatically accepted certificates |
All | 0x7FFFFFFF | all 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:
| Output | Target |
|---|---|
Off | no output |
FileOnly | the file only |
DebugAndFile | the file and also the debug output (Debug.) |
StdOutAndFile | same 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.
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);
}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 FunctionKeep 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:
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();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.
<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.

