Skip to content

Observability

The Modbus library provides comprehensive observability through OpenTelemetry metrics, distributed tracing, and structured logging.

Metrics

Setup

Register Modbus instrumentation with your OpenTelemetry configuration:

csharp
builder.Services.AddOpenTelemetry()
    .WithMetrics(metrics => metrics
        .AddModbusInstrumentation()
        .AddPrometheusExporter());

All metrics are exported via the Voltimax.Edge.Modbus meter.

Available Metrics

Connection Metrics

MetricTypeDescriptionTags
modbus_connection_attempts_totalCounterTotal connection attemptstarget
modbus_connection_failures_totalCounterFailed connection attemptstarget
modbus_connection_duration_secondsHistogramTime to establish connectiontarget, port, status
modbus_clients_created_totalCounterTotal clients createdtarget
modbus_clients_activeUpDownCounterCurrently active clientstarget
modbus_reconnection_attempts_totalCounterReconnection attemptstarget

Operation Metrics

MetricTypeDescriptionTags
modbus_operation_duration_secondsHistogramOperation execution timetarget, operation, slave_address
modbus_operation_failures_totalCounterFailed operationstarget, operation, slave_address
modbus_registers_read_countHistogramRegisters read per operationtarget, slave_address
modbus_registers_written_countHistogramRegisters written per operationtarget, slave_address

Example Queries

Prometheus queries:

promql
# Average operation duration by device
rate(modbus_operation_duration_seconds_sum[5m]) 
  / rate(modbus_operation_duration_seconds_count[5m])

# Operation failure rate per device
rate(modbus_operation_failures_total[5m])

# 95th percentile operation latency
histogram_quantile(0.95, 
  rate(modbus_operation_duration_seconds_bucket[5m]))

# Active client count
modbus_clients_active

# Connection success rate
rate(modbus_connection_attempts_total[5m]) - rate(modbus_connection_failures_total[5m])
  / rate(modbus_connection_attempts_total[5m])

Grafana dashboard panels:

json
{
  "title": "Modbus Operation Duration",
  "targets": [
    {
      "expr": "rate(modbus_operation_duration_seconds_sum[5m]) / rate(modbus_operation_duration_seconds_count[5m])",
      "legendFormat": "{{target}} - {{operation}}"
    }
  ]
}

Distributed Tracing

Setup

Register Modbus tracing with OpenTelemetry:

csharp
builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing
        .AddModbusInstrumentation()
        .AddOtlpExporter());

All traces use the Voltimax.Edge.Modbus activity source.

Span Structure

Connect operation:

text
Connect [2.3s]
  └─ TCP Connection [2.2s]

Read operation:

text
ReadHoldingRegisters [45ms]
  ├─ Modbus Request [40ms]
  └─ NumericConversion [<1ms]

Write operation:

text
WriteMultipleRegisters [52ms]
  └─ Modbus Request [52ms]

Span Attributes

Connection Spans

AttributeDescriptionExample
modbus.connection.targetHost or IP address192.168.1.100
modbus.connection.portTCP port502
modbus.connection.methodConnection typehostname, ip_address

Operation Spans

AttributeDescriptionExample
modbus.operationOperation typeread_holding, write_coil
modbus.slave_addressDevice unit ID1
modbus.start_addressFirst register/coil0
modbus.register_countNumber of registers2
modbus.function_codeModbus function code03, 16

Example Trace

text
[Span: Gateway Polling] 12.5s
  ├─ [Span: ReadHoldingRegisters] 45ms
  │    Attributes:
  │      modbus.operation: read_holding
  │      modbus.slave_address: 1
  │      modbus.start_address: 0
  │      modbus.register_count: 2
  │      modbus.connection.target: 192.168.1.100
  ├─ [Span: ReadHoldingRegisters] 42ms
  └─ [Span: WriteMultipleCoils] 38ms

Structured Logging

Setup

csharp
builder.Services.AddLogging(logging =>
{
    logging.AddConsole();
    logging.AddJsonConsole(); // For production
    logging.SetMinimumLevel(LogLevel.Information);
});

Log Levels

LevelEvents
TraceRegister-level data (verbose)
DebugConnection details, endpoints
InformationConnection lifecycle, successful operations
WarningReconnection attempts, retries, circuit breaker events
ErrorOperation failures, connection errors
CriticalUnrecoverable errors

Log Scopes

All log entries include scopes with contextual information:

json
{
  "Timestamp": "2026-02-16T10:30:45.123Z",
  "Level": "Information",
  "Message": "Successfully connected to Modbus device",
  "Target": "192.168.1.100",
  "Port": 502,
  "Operation": "Connect",
  "DurationMs": 245
}

Key Log Events

Connection Events

csharp
// Connection established
[Information] Successfully connected to target={Target} port={Port} in {DurationMs}ms

// Connection failed
[Error] Failed to connect to target={Target} port={Port} after {DurationMs}ms
  Exception: System.Net.Sockets.SocketException: Connection refused

// Connection timeout
[Warning] Connection to target={Target} port={Port} timed out after {TimeoutMs}ms

Reconnection Events

csharp
// Reconnection started
[Warning] Connection lost to target={Target} port={Port}, attempting reconnect

// Reconnection attempt
[Information] Starting reconnection attempt {Attempt}/{MaxAttempts} for target={Target}

// Reconnection succeeded
[Information] Reconnection successful for target={Target} after {Attempts} attempts

Circuit Breaker Events

csharp
// Circuit opened
[Warning] Circuit breaker opened for target={Target}, break_duration={Duration}s

// Circuit half-open
[Information] Circuit breaker half-open for target={Target}, testing recovery

// Circuit closed
[Information] Circuit breaker closed for target={Target}, normal operation resumed

Operation Events

csharp
// Operation started (Debug)
[Debug] Starting {Operation} at address={StartAddress} count={RegisterCount} slave={SlaveAddress}

// Operation completed (Trace)
[Trace] Completed {Operation} in {DurationMs}ms

// Operation failed
[Error] {Operation} failed for target={Target} slave={SlaveAddress}
  Exception: System.InvalidOperationException: Illegal data address

Filtering Logs

Reduce verbosity in production:

json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Voltimax.Edge.Modbus": "Warning"
    }
  }
}

Debug connection issues:

json
{
  "Logging": {
    "LogLevel": {
      "Voltimax.Edge.Modbus": "Debug"
    }
  }
}

Trace register data:

json
{
  "Logging": {
    "LogLevel": {
      "Voltimax.Edge.Modbus": "Trace"
    }
  }
}

Dashboard Examples

Grafana Dashboard

Sample dashboard configuration for monitoring Modbus clients:

Panels:

  1. Active Connections - modbus_clients_active
  2. Operation Latency (p95) - histogram_quantile(0.95, modbus_operation_duration_seconds_bucket)
  3. Failure Rate - rate(modbus_operation_failures_total[5m])
  4. Reconnection Events - rate(modbus_reconnection_attempts_total[5m])
  5. Registers per Second - rate(modbus_registers_read_count_sum[5m])

Application Insights

Query successful operations:

kusto
traces
| where customDimensions.CategoryName == "Voltimax.Edge.Modbus"
| where message contains "Successfully connected"
| summarize count() by bin(timestamp, 5m), tostring(customDimensions.Target)

Query operation failures:

kusto
traces
| where customDimensions.CategoryName == "Voltimax.Edge.Modbus"
| where severityLevel >= 3 // Error and Critical
| project timestamp, message, customDimensions.Target, customDimensions.Operation

Alerting

Sample Alerts

High failure rate:

yaml
alert: ModbusHighFailureRate
expr: rate(modbus_operation_failures_total[5m]) > 0.1
for: 5m
annotations:
  summary: "High Modbus operation failure rate for {{ $labels.target }}"

Circuit breaker open:

yaml
alert: ModbusCircuitBreakerOpen
expr: modbus_circuit_breaker_state == 1
for: 1m
annotations:
  summary: "Modbus circuit breaker open for {{ $labels.target }}"

Operation latency:

yaml
alert: ModbusHighLatency
expr: histogram_quantile(0.95, rate(modbus_operation_duration_seconds_bucket[5m])) > 1.0
for: 5m
annotations:
  summary: "High Modbus operation latency for {{ $labels.target }}"