Skip to main content

Logging

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

In comparison, the bindings use a rigid logging interface with a single callback method to record a message. Configurable options include:

  • LogLevel that controls which messages are generated
  • How and if to print the time as part of the message
  • Line or JSON based output
note

The LogLevel is set to Info by default. This will record Info, Warn, and Error messages. 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
let log_level: tracing::Level = args.log_level.into();

tracing_subscriber::fmt()
.with_max_level(log_level)
.with_target(false)
.init();
note

The bindings use the tracing_subscriber crate internally. If you use Rust, you can pick which tracing backend to use.

Example output​

Logs include contextual metadata so you can:

  • Determine which communication session produced the message
  • Understand what state the software was in when the event occurred
INFO dnp3-master-tcp-client{endpoint="127.0.0.1:20000"}: connected to 127.0.0.1:20000
INFO dnp3-master-tcp-client{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-client{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-client{endpoint="127.0.0.1:20000"}:task{type=Function(DisableUnsolicited) dest=1024}: device restart detected (address == 1024)
INFO dnp3-master-tcp-client{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-client{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​

Configure protocol decoding per communication channel (TCP socket, serial port, etc.) by setting DecodeLevel when creating a master or outstation, or adjust it at runtime. This controls decoding 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

See the language-specific API documentation for enumeration values. Decoding can show individual field values 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

Protocol decoding is always output at the Info log level. If left enabled, it can be too verbose in a production system. When you're debugging a communication issue, try adjusting the application-layer decoding first to gain visibility into the messages exchanged.

caution

Application-layer object value decoding redacts the passwords and authentication keys used in file transfer. The transport, link, and physical layers can log raw bytes, which include these secrets.