The package's :telemetry events.
No event carries a plaintext, a key of any kind, an encryption context
value, a :key selector, a partition id, or an Encryptor.Error's
:engine term. No event carries a per-tenant dimension unless the vault
opted in with telemetry_tenant_ref: true, and then it is the keyed
tenant_ref and never the partition id.
Handlers run on the calling process, so a slow handler is a slow encrypt.
This module is the single definition site for the vocabulary (ADR-0006
decision 3) and the only module in lib/ that calls
:telemetry.execute/3. The event set is closed: adding a name, a
measurement, or a metadata key is additive, and removing or renaming one is
a breaking change to a consumer's case and to their dashboards, so it
takes an amendment to that record.
Attaching
:telemetry.attach_many(
"my-app-encryptor",
Encryptor.Telemetry.events(),
&MyApp.Handler.handle/4,
nil
):telemetry detaches a handler that raises, permanently and silently, for
the lifetime of the VM. attach_many/4's detach is total, so one malformed
branch takes every event with it; attaching one handler id per name - which
is what events/0 is for - bounds that to the event that raised.
The events
:start and :stop name a span pair and nothing else. Every other event is
a point event, named in the past tense, so an attach list can be read
without reading the record.
| Event | Kind | Fires |
|---|---|---|
[:encryptor, :vault, :started] | point | a vault's supervisor came up with its configuration frozen |
[:encryptor, :vault, :stopped] | point | Encryptor.Vault.Lifecycle erased the frozen configuration |
[:encryptor, :vault, :start_refused] | point | configuration resolution refused, before any process existed |
[:encryptor, :cache, :recycled] | point | the recycler dropped and restarted the cache child |
[:encryptor, :encrypt, :start] / [..., :stop] | span | encrypt/2 |
[:encryptor, :decrypt, :start] / [..., :stop] | span | decrypt/2 |
[:encryptor, :rekey, :start] / [..., :stop] | span | rekey/2 |
[:encryptor, :provider, :start] / [..., :stop] | span | one encryption_key/2 or decryption_keys/2 round trip |
The eight span halves - four pairs - are specified by ADR-0006 and emitted
by the paths they instrument (that record's decision 10), all of which are
written. events/0 returns twelve names: the four point events above and
those eight halves.
Measurements
| Measurement | Unit | On |
|---|---|---|
duration | :native | every span stop, and :recycled |
system_time | :native | every span start and every point event |
size | bytes | [:encryptor, :encrypt, :start] and [:encryptor, :decrypt, :stop] |
candidates | count | [:encryptor, :provider, :stop] on a successful decryption_keys/2 |
Metadata
Metadata is an allow-list. No struct rides verbatim: not a
Encryptor.Vault.Config, not a key descriptor, not a keyring, a CMM, a
client, a context map, a ciphertext, a plaintext, an Encryptor.Error or
its :engine term.
| Key | Type | On |
|---|---|---|
vault | module() | every event |
operation | Encryptor.Error.operation/0 | spans, :start_refused |
span_ref | reference() | span halves |
outcome | :ok | :error | span stops, :recycled |
reason_tag | reason_tag/0 | when outcome is :error |
provider | module() | provider spans |
callback | :encryption_key | :decryption_keys | provider spans |
cache | boolean() | [:encryptor, :vault, :started] |
profile | :single | :tenant | [:encryptor, :vault, :started] |
reference_check | :verified | :unpinned | [:encryptor, :vault, :started] |
tenant_ref | String.t() | the four span names' halves, only when :telemetry_tenant_ref is on |
The opt-in tenant dimension
With telemetry_tenant_ref: true, every encrypt, decrypt, rekey and
provider event carries tenant_ref - ADR-0003 decision 5's keyed reference
for the tenant the call routed to. It is a pseudonym and not an identifier:
it does not contain the tenant identifier and cannot be reversed into it.
Anyone holding the vault's reference subkey can re-identify it, by deriving
the reference for a candidate tenant and comparing, and so can anyone who
can enumerate or guess your tenant identifiers. Telemetry metadata is
forwarded verbatim by handlers you did not write to vendors whose retention
you did not choose. Turning this on is a decision about that, and it is off
by default.
It is vault configuration, refused as true on a :single vault, and the
reference is derived once per operation and threaded through both halves of
the operation span and through the nested provider span's halves. When the
option is off the key is absent from the metadata map rather than
present as nil: a handler tells "this host did not opt in" from any value
by Map.has_key?/2, and a nil in a :telemetry_metrics tag is a
dimension value. Cardinality is your tenant count, which is what you asked
for and is your vendor's bill (ADR-0006 amendment A).
Summary
Types
Every metadata key any event may carry, and nothing else.
The metadata tag for a failure.
The operations that open a span pair.
Functions
Every event name this package emits. The single definition site.
The metadata tag for a reason term. Never the term itself.
Types
@type metadata() :: %{ optional(:vault) => module(), optional(:operation) => Encryptor.Error.operation(), optional(:span_ref) => reference(), optional(:outcome) => :ok | :error, optional(:reason_tag) => reason_tag(), optional(:provider) => module(), optional(:callback) => :encryption_key | :decryption_keys, optional(:cache) => boolean(), optional(:profile) => Encryptor.Vault.Config.profile(), optional(:reference_check) => :verified | :unpinned, optional(:tenant_ref) => String.t() }
Every metadata key any event may carry, and nothing else.
@type reason_tag() ::
:decrypt_failed
| :vault_not_started
| :missing_config
| :invalid_config
| :unknown_key
| :encryption_context_conflict
| :reserved_context_key
| :key_unavailable
| :invalid_key_descriptor
| :provider_not_started
| :missing_optional_dependency
| :missing_required_context_keys
| :invalid_context_value
| :invalid_selector
| :not_provisionable
The metadata tag for a failure.
The head of an Encryptor.Error.reason/0, never the term: every member
but :decrypt_failed is a tagged tuple whose second element is caller data
- a selector, a context key, a config path, a module - and that data does not leave the process in a metric. The set is closed, and extends only when the error vocabulary does, which is itself an ADR-gated act (ADR-0001 decision 10). That closure is what makes it safe as a metric dimension: a backend keying on it has a bounded label set no matter what a caller passes.
@type span_name() :: :encrypt | :decrypt | :rekey | :provider
The operations that open a span pair.
Functions
@spec events() :: [[atom(), ...], ...]
Every event name this package emits. The single definition site.
A host attaches against this rather than hand-copying names the record may later extend.
iex> [:encryptor, :cache, :recycled] in Encryptor.Telemetry.events()
true
iex> length(Encryptor.Telemetry.events())
12
@spec reason_tag(Encryptor.Error.reason()) :: reason_tag()
The metadata tag for a reason term. Never the term itself.
iex> Encryptor.Telemetry.reason_tag({:key_unavailable, "acct_9f21"})
:key_unavailable
iex> Encryptor.Telemetry.reason_tag(:decrypt_failed)
:decrypt_failedIt is deliberately not elem(reason, 0): :decrypt_failed is a bare atom,
and a fallthrough that reached elem/2 on a term the record did not
anticipate would either raise inside an emit or leak whatever the term was.