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 withSystem.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 ofread_func/write_funcinvocations, including retries. Equal tobatcheswhen no retries were needed. The difference oftotal_attempts - batchesis 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:contextoption passed toModBoss.read/4orModBoss.write/4. Always present; defaults to%{}when not provided.result— the raw result:{:ok, value}or{:error, reason}for reads;:okor{: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 tomax_attempts.max_attempts— the configured maximum number of attempts for this callback.