brazier/telemetry
Telemetry for Brazier, built on the Erlang telemetry library.
Handlers attach to event names and receive an Event for every
matching emit. Brazier’s own events all start with prefix and are
listed by catalogue; see docs/TELEMETRY.md for their measurements and
metadata.
- A handler that crashes is detached by
telemetry, which then emits[telemetry, handler, failure]. Brazier logs that event at error level. The process that emitted the event is never affected. - With no handler attached, an emit costs one
persistent_termread and one ETS lookup. - Metadata never carries session ids, tokens, cookies, event payloads, models, query strings or remote IPs.
let assert Ok(Nil) =
telemetry.attach("http-logger", [["brazier", "http", "request", "stop"]], fn(event) {
let assert Ok(duration) = telemetry.int(event, "duration")
as "every stop event measures its duration"
io.println("request took " <> int.to_string(duration))
})
as "the handler id is unique"
Types
pub type ConnectionOutcome {
Accepted
Rejected
Closed
}
Constructors
-
Accepted -
Rejected -
Closed
One emitted event, as a handler receives it. Measurement and metadata keys are strings; values keep the type they were emitted with.
pub type Event {
Event(
name: List(String),
measurements: dict.Dict(String, dynamic.Dynamic),
metadata: dict.Dict(String, dynamic.Dynamic),
)
}
Constructors
-
Event( name: List(String), measurements: dict.Dict(String, dynamic.Dynamic), metadata: dict.Dict(String, dynamic.Dynamic), )
Why the runtime forgot a connection without being told it disconnected.
pub type ForgetReason {
ConnectionSlow
ConnectionDown
}
Constructors
-
ConnectionSlowIts handler’s mailbox held more than the runtime’s limit: it was told to close (1013).
-
ConnectionDownIts handler had exited: a send found no process, or the runtime handled the DOWN of its monitor.
pub type NodeDirection {
NodeUp
NodeDown
}
Constructors
-
NodeUp -
NodeDown
pub type PresenceEvent {
Track
Untrack
LimitExceeded
}
Constructors
-
Track -
Untrack -
LimitExceeded
pub type ReplicaState {
ReplicaUp
ReplicaDown
ReplicaPermdown
}
Constructors
-
ReplicaUp -
ReplicaDown -
ReplicaPermdown
pub type RuntimeSpan {
Mount
HandleEvent
Navigate
}
Constructors
-
Mount -
HandleEvent -
Navigate
pub type SessionEvent {
Created
Revoked
Expired
Contention
}
Constructors
-
Created -
Revoked -
Expired -
Contention
pub type SubscriptionAction {
Subscribe
Unsubscribe
}
Constructors
-
Subscribe -
Unsubscribe
pub type TelemetryError {
AlreadyAttached(id: String)
NotAttached(id: String)
InvalidEventName(String)
InvalidHandlerId(String)
StartFailed(reason: String)
}
Constructors
-
AlreadyAttached(id: String)A handler with this id is already attached.
-
NotAttached(id: String)No handler with this id is attached.
-
InvalidEventName(String)The event name is empty or has an empty or over-long segment.
-
InvalidHandlerId(String)Handler ids must be 1 to 255 bytes.
-
StartFailed(reason: String)The
telemetryapplication could not be started.
Values
pub fn attach(
id: String,
events: List(List(String)),
handler: fn(Event) -> Nil,
) -> Result(Nil, TelemetryError)
Attach handler to every event in events. Each event name is a list
of segments, for example ["brazier", "pubsub", "broadcast"].
pub fn catalogue() -> List(List(String))
Every event name Brazier emits. Spans are listed as their start,
stop and exception events.
pub fn detach(id: String) -> Result(Nil, TelemetryError)
Detach the handler with this id.
pub fn execute(
name: List(String),
measurements: dict.Dict(String, Int),
metadata: dict.Dict(String, String),
) -> Result(Nil, TelemetryError)
Emit an event from application code. Keys stay strings, so this never
creates atoms. When no handler has ever been attached to name, there
is nothing to deliver and this returns Ok(Nil) without emitting.
pub fn int(event: Event, key: String) -> Result(Int, Nil)
An integer measurement, or an integer metadata value when no measurement has that key.
pub fn span(
name: List(String),
metadata: dict.Dict(String, String),
run: fn() -> a,
) -> a
Run run inside a span: name ++ ["start"] is emitted before it and
name ++ ["stop"] after it, with duration in native time units. If
run crashes, name ++ ["exception"] is emitted with kind, reason
and stacktrace, and the crash is re-raised unchanged.
Crashes if name is empty or has an empty segment: span names come
from code, so an invalid one is a programming error.
pub fn start() -> Result(Nil, TelemetryError)
Start the telemetry application and attach the logger for crashed
handlers. Idempotent. Brazier’s subsystems call it when they start.