ModBoss.Telemetry (modboss v0.2.0)

Copy Markdown View Source

Telemetry events emitted by ModBoss.

ModBoss emits telemetry events for reads and writes using the :telemetry library.

:telemetry is an optional dependency. If it is not included in your application's dependencies, all telemetry calls become no-ops at compile time with zero runtime overhead.

Recompilation required

Telemetry availability is determined at compile time. If you add or remove :telemetry as a dependency after ModBoss has been compiled, you must recompile ModBoss (e.g. mix deps.compile modboss --force).

Per-operation events

These events wrap the full ModBoss.read/4 or ModBoss.write/4 call (which may contain multiple batched Modbus requests). They are not emitted for validation errors (e.g. unknown mapping names or unreadable/unwritable mappings).

For descriptions of measurements and metadata, see below.

Read Start

# Event
[:modboss, :read, :start]

# Measurements
%{
  system_time: integer(),
  monotonic_time: integer()
}

# Metadata:
%{
  schema: module(),
  names: [atom()],
  context: map()
}

Read Stop

# Event
[:modboss, :read, :stop]

# Measurements
%{
  duration: integer(),
  monotonic_time: integer(),
  batches: non_neg_integer(),
  total_attempts: pos_integer(),
  objects_requested: non_neg_integer(),
  addresses_read: non_neg_integer(),
  gap_addresses_read: non_neg_integer(),
  largest_gap: non_neg_integer()
}

# Metadata
%{
  schema: module(),
  names: [atom()],
  context: map(),
  result: term()
}

Read Exception

# Event
[:modboss, :read, :exception]

# Measurements
%{
  duration: integer(),
  monotonic_time: integer()
}

# Metadata
%{
  schema: module(),
  names: [atom()],
  context: map(),
  kind: atom(),
  reason: term(),
  stacktrace: list()
}

Write Start

# Event
[:modboss, :write, :start]

# Measurements
%{
  system_time: integer(),
  monotonic_time: integer()
}

# Metadata
%{
  schema: module(),
  names: [atom()],
  context: map()
}

Write Stop

# Event
[:modboss, :write, :stop]

# Measurements
%{
  duration: integer(),
  monotonic_time: integer(),
  batches: non_neg_integer(),
  total_attempts: pos_integer(),
  objects_requested: non_neg_integer()
}

# Metadata
%{
  schema: module(),
  names: [atom()],
  context: map(),
  result: term()
}

Write Exception

# Event
[:modboss, :write, :exception]

# Measurements
%{
  duration: integer(),
  monotonic_time: integer()
}

# Metadata
%{
  schema: module(),
  names: [atom()],
  context: map(),
  kind: atom(),
  reason: term(),
  stacktrace: list()
}

Per-callback events

These events wrap each individual invocation of your read_func or write_func callback—one contiguous address range of one object type.

For descriptions of measurements and metadata, see below.

Read Callback Start

# Event
[:modboss, :read_callback, :start]

# Measurements
%{
  system_time: integer(),
  monotonic_time: integer()
}

# Metadata
%{
  schema: module(),
  names: [atom()],
  context: map(),
  object_type: atom(),
  starting_address: non_neg_integer(),
  address_count: pos_integer(),
  attempt: pos_integer(),
  max_attempts: pos_integer()
}

Read Callback Stop

# Event
[:modboss, :read_callback, :stop]

# Measurements
%{
  duration: integer(),
  monotonic_time: integer(),
  gap_addresses_read: non_neg_integer(),
  largest_gap: non_neg_integer()
}

# Metadata
%{
  schema: module(),
  names: [atom()],
  context: map(),
  object_type: atom(),
  starting_address: non_neg_integer(),
  address_count: pos_integer(),
  attempt: pos_integer(),
  max_attempts: pos_integer(),
  result: term()
}

Read Callback Exception

# Event
[:modboss, :read_callback, :exception]

# Measurements
%{
  duration: integer(),
  monotonic_time: integer()
}

# Metadata
%{
  schema: module(),
  names: [atom()],
  context: map(),
  object_type: atom(),
  starting_address: non_neg_integer(),
  address_count: pos_integer(),
  attempt: pos_integer(),
  max_attempts: pos_integer(),
  kind: atom(),
  reason: term(),
  stacktrace: list()
}

Write Callback Start

# Event
[:modboss, :write_callback, :start]

# Measurements
%{
  system_time: integer(),
  monotonic_time: integer()
}

# Metadata
%{
  schema: module(),
  names: [atom()],
  context: map(),
  object_type: atom(),
  starting_address: non_neg_integer(),
  address_count: pos_integer(),
  attempt: pos_integer(),
  max_attempts: pos_integer()
}

Write Callback Stop

# Event
[:modboss, :write_callback, :stop]

# Measurements
%{
  duration: integer(),
  monotonic_time: integer()
}

# Metadata
%{
  schema: module(),
  names: [atom()],
  context: map(),
  object_type: atom(),
  starting_address: non_neg_integer(),
  address_count: pos_integer(),
  attempt: pos_integer(),
  max_attempts: pos_integer(),
  result: term()
}

Write Callback Exception

# Event
[:modboss, :write_callback, :exception]

# Measurements
%{
  duration: integer(),
  monotonic_time: integer()
}

# Metadata
%{
  schema: module(),
  names: [atom()],
  context: map(),
  object_type: atom(),
  starting_address: non_neg_integer(),
  address_count: pos_integer(),
  attempt: pos_integer(),
  max_attempts: pos_integer(),
  kind: atom(),
  reason: term(),
  stacktrace: list()
}

Measurement details

  • duration — elapsed time in native time units. Convert with System.convert_time_unit(duration, :native, :millisecond).
  • batches — number of batches attempted for the operation. Each contiguous address range of one object type is one batch. Does not account for retries.
  • total_attempts — total number of read_func/write_func invocations, including retries. Equal to batches when no retries were needed. The difference of total_attempts - batches is the number of retries.
  • objects_requested — total Modbus objects (registers/coils) covered by attempted callbacks.
  • addresses_read — total addresses attempted on the wire, including gap addresses (read operations only).
  • gap_addresses_read — gap addresses read and discarded (read events only).
  • largest_gap — largest address gap bridged (read events only).

Partial failures

When an operation requires multiple callbacks and one fails partway through, measurements reflect what was attempted (including the failed callback), not what was planned. For example, if a read groups into 3 address batches and the 2nd fails, batches will be 2, and objects_requested and addresses_read will cover only those first 2 batches.

Metadata details

  • schema — the schema module (e.g. MyDevice.Schema).
  • names — mapping name(s) as a list of atoms. On per-operation events, all requested names; on per-callback events, only the names in that batch.
  • context — the value of the :context option passed to ModBoss.read/4 or ModBoss.write/4. Always present; defaults to %{} when not provided.
  • result — the raw result: {:ok, value} or {:error, reason} for reads; :ok or {:error, reason} for writes.
  • object_type:holding_register, :input_register, :coil, or :discrete_input.
  • starting_address — the starting address for the request.
  • address_count — number of addresses in the request.
  • attempt — which attempted callback invocation this is, from 1 up to max_attempts.
  • max_attempts — the configured maximum number of attempts for this callback.