Logging

The library provides highly contextual logging using the tracing crate. Rust users should refer to the tracing documentation for details.

The bindings expose a more rigid logging interface with a single callback method to record a message. Configurable options include:

  • LogLevel which will trigger messages to be generated
  • Inclusion of Rust module or level information as part of the message
  • How and if the time should be printed as part of the message
  • Line or JSON based output
note

The LogLevel should be set to Info by default which will cause Info, Warn, and Error messages to be recorded. The Debug and Trace levels are generally only useful if debugging an issue with the underlying runtime.

Protocol decoding is always logged at the Info level and is configured separately on a per session basis.

Configuration#

// initialize logging
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
.with_target(false)
.init();
note

The bindings use the tracing_subscriber crate internally. Rust users may pick which tracing backend they want to use.

Example Output#

The logs provide a wealth of contextual metadata. This allows the user to:

  • Determine which communication session produced the message
  • Understand what state the software was in when the event occurred
INFO DNP3-Master-TCP{endpoint="127.0.0.1:20000"}: connected to 127.0.0.1:20000
INFO DNP3-Master-TCP{endpoint="127.0.0.1:20000"}:Task{type=Function(DisableUnsolicited) dest=1024}: APP TX - ctrl: [fir: true fin: true con: false uns: false seq: 0] func: DisableUnsolicited ... (len = 9)
INFO DNP3-Master-TCP{endpoint="127.0.0.1:20000"}:Task{type=Function(DisableUnsolicited) dest=1024}: APP RX - ctrl: [fir: true fin: true con: true uns: true seq: 0] func: UnsolicitedResponse iin1: [DEVICE_RESTART] iin2: [] ... (len = 0)
WARN DNP3-Master-TCP{endpoint="127.0.0.1:20000"}:Task{type=Function(DisableUnsolicited) dest=1024}: device restart detected (address == 1024)
INFO DNP3-Master-TCP{endpoint="127.0.0.1:20000"}:Task{type=Function(DisableUnsolicited) dest=1024}: APP TX - ctrl: [fir: true fin: true con: false uns: true seq: 0] func: Confirm ... (len = 0)
INFO DNP3-Master-TCP{endpoint="127.0.0.1:20000"}:Task{type=Function(DisableUnsolicited) dest=1024}: APP RX - ctrl: [fir: true fin: true con: false uns: false seq: 0] func: Response iin1: [DEVICE_RESTART] iin2: [] ... (len = 0)

Protocol Decoding#

Protocol decoding can be configured on a per-communication channel basis, e.g. all of the traffic on a TCP socket or a serial port. The user specifies the DecodeLevel when creating a master or outstation, but may adjust this at runtime. This struct controls the level of decoding (including none) that will take place for each layer of the protocol stack:

  • Application-layer headers, object headers, and object values
  • Transport-layer header and payload
  • Link-layer header and payload
  • Physical-layer length and data bytes

Refer to language specific API documentation for the meaning of each enumeration value. The decoding can even show the value of individual fields within objects:

APP TX - ctrl: [fir: true fin: true con: false uns: false seq: 1] func: Write ... (len = 6)
g80v1 : Internal Indications - Packed Format - 1-byte start/stop - [7, 7]
index: 7 false
note

Protocol decoding is always output at the Info log level. It can be quite verbose in a production system if left enabled. When debugging a communication issue, users should first try adjusting the application-layer decoding to gain visibility in the messages being exchanged.