fio/observer
Types
A single fio operation event emitted to a sink.
pub type Event {
Event(
op: String,
path: String,
outcome: Result(Nil, error.FioError),
bytes: option.Option(Int),
)
}
Constructors
-
Event( op: String, path: String, outcome: Result(Nil, error.FioError), bytes: option.Option(Int), )Arguments
- op
-
Short operation name: “read”, “write”, “copy”, “delete”, etc.
- path
-
Primary path argument of the operation.
- outcome
-
Ok(Nil)on success,Error(e)on failure. The actual return value (file content, etc.) is excluded to keep the event type generic and independent of the operation’s return type. - bytes
-
Byte count involved, when known. For read operations: bytes returned. For write operations: bytes written.
Nonewhen not applicable (e.g. delete, rename).
A sink consumes events produced by observed fio operations.
Implement this type to integrate with any observability backend: structured loggers, metrics systems, test recorders, or custom hooks. A sink receives events synchronously on the same thread/process as the caller.
pub type Sink =
fn(Event) -> Nil
Values
pub fn emit(
result: Result(a, error.FioError),
op: String,
path: String,
bytes: option.Option(Int),
sink: fn(Event) -> Nil,
) -> Result(a, error.FioError)
Emit an Event to sink after result is produced, then return result
unchanged.
This is the lowest-level building block. Use trace when you do not have
a byte count to report.
fio.read_bits("data.bin")
|> observer.emit("read_bits", "data.bin", option.None, my_sink)
pub fn fan_out(
first: fn(Event) -> Nil,
second: fn(Event) -> Nil,
) -> fn(Event) -> Nil
Combine two sinks into one: both receive every event in order.
Useful for fan-out — log to stdout AND record in a test buffer:
let combined = observer.fan_out(log_sink, test_recorder_sink)
fio.write("out.txt", data) |> observer.trace("write", "out.txt", combined)
pub fn format(event: Event) -> String
Format an Event as a human-readable string.
Useful when building simple logging sinks.
fn log_sink(event: Event) -> Nil {
io.println(observer.format(event))
}
pub fn noop_sink(event: Event) -> Nil
A no-op sink that discards all events. Useful as a default/placeholder argument when observability is optional.
pub fn copy(src, dest, sink: observer.Sink) {
fio.copy(src, dest) |> observer.trace("copy", src, sink)
}
// caller passes observer.noop_sink when not interested
copy("a.txt", "b.txt", observer.noop_sink)
pub fn trace(
result: Result(a, error.FioError),
op: String,
path: String,
sink: fn(Event) -> Nil,
) -> Result(a, error.FioError)
Like emit but without a byte count (bytes is always None).
Use this for operations where byte count is not meaningful (delete, rename, touch, create_directory, etc.):
fio.delete("old.txt")
|> observer.trace("delete", "old.txt", my_sink)
pub fn trace_bytes(
result: Result(BitArray, error.FioError),
op: String,
path: String,
sink: fn(Event) -> Nil,
) -> Result(BitArray, error.FioError)
Like trace but automatically infers the byte count from the result when
the operation returns a BitArray (e.g. fio.read_bits).
fio.read_bits("archive.tar.gz")
|> observer.trace_bytes("read_bits", "archive.tar.gz", my_sink)