@@ -217,3 +217,78 @@ Hard delete a stream that should exist:
217217``` elixir
218218:ok = MyApp .EventStore .delete_stream (" stream2" , :stream_exists , :hard )
219219```
220+
221+ ## Telemetry
222+
223+ EventStore emits ` :telemetry ` events for public operations. Each instrumented
224+ operation publishes ` :start ` , ` :stop ` , and ` :exception ` events under the
225+ ` [:eventstore, operation, suffix] ` namespace.
226+
227+ The first pass covers these operations:
228+
229+ - ` :append_to_stream `
230+ - ` :link_to_stream `
231+ - ` :read_stream_forward `
232+ - ` :read_stream_backward `
233+ - ` :delete_stream `
234+ - ` :paginate_streams `
235+ - ` :subscribe_to_stream `
236+ - ` :delete_subscription `
237+ - ` :read_snapshot `
238+ - ` :record_snapshot `
239+ - ` :delete_snapshot `
240+ - ` :stream_batch_read `
241+
242+ Alias operations reuse the same event names. For example,
243+ ` read_all_streams_forward/3 ` emits ` [:eventstore, :read_stream_forward, ...] `
244+ with ` stream_uuid: "$all" ` in the metadata, and
245+ ` subscribe_to_all_streams/3 ` emits ` [:eventstore, :subscribe_to_stream, ...] `
246+ with the same stream identifier.
247+
248+ Stop metadata includes a normalized ` :result ` for all instrumented operations.
249+ Operations that return ` :ok ` emit ` result: :ok ` . Operations that return
250+ ` {:ok, value} ` also emit ` result: :ok ` so telemetry does not copy returned
251+ payloads such as event lists or subscription structs into metadata. When an
252+ operation returns ` {:error, reason} ` , stop metadata includes
253+ ` result: {:error, reason} ` .
254+
255+ Lazy stream APIs do not emit ` :stream_forward ` or ` :stream_backward ` spans.
256+ Instead, ` stream_forward/3 ` , ` stream_backward/3 ` , ` stream_all_forward/2 ` , and
257+ ` stream_all_backward/2 ` emit ` [:eventstore, :stream_batch_read, ...] ` once per
258+ batch read performed during enumeration. These events include ` :direction ` ,
259+ ` :start_version ` , and ` :requested_batch_size ` in start metadata, and add
260+ ` :event_count ` plus ` :result ` in stop metadata. Forward streaming may emit a
261+ final batch read with ` event_count: 0 ` to detect completion.
262+
263+ Measurements:
264+
265+ - ` :start ` includes ` %{system_time: System.system_time(), monotonic_time: native_time} `
266+ - ` :stop ` includes ` %{duration: native_time, monotonic_time: native_time} `
267+ - ` :exception ` includes ` %{duration: native_time, monotonic_time: native_time} `
268+
269+ Metadata always includes ` :event_store ` . Depending on the operation it may also
270+ include fields such as ` :name ` , ` :stream_uuid ` , ` :expected_version ` ,
271+ ` :event_count ` , ` :count ` , ` :start_version ` , ` :delete_type ` , ` :result ` ,
272+ ` :subscription_name ` , ` :source_uuid ` , and pagination options. Because
273+ EventStore now uses ` :telemetry.span/3 ` , emitted metadata also includes a
274+ ` telemetry_span_context ` key so handlers can correlate start/stop/exception
275+ events for the same operation execution.
276+
277+ Example handler:
278+
279+ ``` elixir
280+ events = [
281+ [:eventstore , :append_to_stream , :start ],
282+ [:eventstore , :append_to_stream , :stop ],
283+ [:eventstore , :append_to_stream , :exception ]
284+ ]
285+
286+ :telemetry .attach_many (
287+ " my-app-eventstore" ,
288+ events,
289+ fn event_name, measurements, metadata, _config ->
290+ IO .inspect ({event_name, measurements, metadata}, label: " eventstore.telemetry" )
291+ end ,
292+ nil
293+ )
294+ ```
0 commit comments