diff --git a/rebar.config b/rebar.config
index 9c18ccf..e85ccb2 100644
--- a/rebar.config
+++ b/rebar.config
@@ -19,6 +19,8 @@
{xref_checks,[undefined_function_calls, undefined_functions, locals_not_used,
deprecated_function_calls, deprecated_functions]}.
+{plugins, [rebar3_ex_doc]}.
+
{hex, [
{doc, #{provider => ex_doc}}
]}.
diff --git a/src/telemetry.erl b/src/telemetry.erl
index 20339fd..d81239c 100644
--- a/src/telemetry.erl
+++ b/src/telemetry.erl
@@ -1,11 +1,3 @@
-%%%-------------------------------------------------------------------
-%% @doc `telemetry' allows you to invoke certain functions whenever a
-%% particular event is emitted.
-%%
-%% For more information see the documentation for {@link attach/4}, {@link attach_many/4}
-%% and {@link execute/2}.
-%% @end
-%%%-------------------------------------------------------------------
-module(telemetry).
-export([attach/4,
@@ -20,6 +12,14 @@
-include("telemetry.hrl").
+?MODULEDOC("""
+`telemetry` allows you to invoke certain functions whenever a
+particular event is emitted.
+
+For more information see the documentation for `attach/4`, `attach_many/4`
+and `execute/2`.
+""").
+
-type handler_id() :: term().
-type event_name() :: [atom(), ...].
-type event_measurements() :: map().
@@ -49,27 +49,31 @@
-import_lib("kernel/import/logger.hrl").
-%% @doc Attaches the handler to the event.
-%%
-%% `handler_id' must be unique, if another handler with the same ID already exists the
-%% `{error, already_exists}' tuple is returned.
-%%
-%% See {@link execute/3} to learn how the handlers are invoked.
-%%
-%% Note: due to how anonymous functions are implemented in the Erlang VM, it is best to use
-%% function captures (i.e. `fun mod:fun/4' in Erlang or `&Mod.fun/4' in Elixir) as event handlers
-%% to achieve maximum performance. In other words, avoid using literal anonymous functions
-%% (`fun(...) -> ... end' or `fn ... -> ... end') or local function captures (`fun handle_event/4'
-%% or `&handle_event/4' ) as event handlers.
-%%
-%% All the handlers are executed by the process dispatching event. If the function fails (raises,
-%% exits or throws) then the handler is removed and a failure event is emitted.
-%%
-%% Handler failure events `[telemetry, handler, failure]' should only be used for monitoring
-%% and diagnostic purposes. Re-attaching a failed handler will likely result in the handler
-%% failing again.
-%%
-%% Note that you should not rely on the order in which handlers are invoked.
+?DOC("""
+Attaches the handler to the event.
+
+`handler_id` must be unique, if another handler with the same ID already exists the
+`{error, already_exists}` tuple is returned.
+
+See `execute/3` to learn how the handlers are invoked.
+
+> #### Function Captures {: .info}
+>
+> Due to how anonymous functions are implemented in the Erlang VM, it is best to use
+> function captures (`fun mod:fun/4` in Erlang or `&Mod.fun/4` in Elixir) as event handlers
+> to achieve the best performance. In other words, avoid using literal anonymous functions
+> (`fun(...) -> ... end` or `fn ... -> ... end`) or local function captures (`fun handle_event/4`
+> or `&handle_event/4`) as event handlers.
+
+All the handlers are executed by the process dispatching event. If the function fails (raises,
+exits or throws) then the handler is removed and a failure event is emitted.
+
+Handler failure events `[telemetry, handler, failure]` should only be used for monitoring
+and diagnostic purposes. Re-attaching a failed handler will likely result in the handler
+failing again.
+
+Note that you should not rely on the order in which handlers are invoked.
+""").
-spec attach(HandlerId, EventName, Function, Config) -> ok | {error, already_exists} when
HandlerId :: handler_id(),
EventName :: event_name(),
@@ -78,26 +82,28 @@
attach(HandlerId, EventName, Function, Config) ->
attach_many(HandlerId, [EventName], Function, Config).
-%% @doc Attaches the handler to many events.
-%%
-%% The handler will be invoked whenever any of the events in the `event_names' list is emitted. Note
-%% that failure of the handler on any of these invocations will detach it from all the events in
-%% `event_name' (the same applies to manual detaching using {@link detach/1}).
-%%
-%% Note: due to how anonymous functions are implemented in the Erlang VM, it is best to use
-%% function captures (i.e. `fun mod:fun/4' in Erlang or `&Mod.fun/4' in Elixir) as event handlers
-%% to achieve maximum performance. In other words, avoid using literal anonymous functions
-%% (`fun(...) -> ... end' or `fn ... -> ... end') or local function captures (`fun handle_event/4'
-%% or `&handle_event/4' ) as event handlers.
-%%
-%% All the handlers are executed by the process dispatching event. If the function fails (raises,
-%% exits or throws) a handler failure event is emitted and then the handler is removed.
-%%
-%% Handler failure events `[telemetry, handler, failure]' should only be used for monitoring
-%% and diagnostic purposes. Re-attaching a failed handler will likely result in the handler
-%% failing again.
-%%
-%% Note that you should not rely on the order in which handlers are invoked.
+?DOC("""
+Attaches the handler to many events.
+
+The handler will be invoked whenever any of the events in the `event_names` list is emitted. Note
+that failure of the handler on any of these invocations will detach it from all the events in
+`event_name` (the same applies to manual detaching using `detach/1`).
+
+Note: due to how anonymous functions are implemented in the Erlang VM, it is best to use
+function captures (i.e. `fun mod:fun/4` in Erlang or `&Mod.fun/4` in Elixir) as event handlers
+to achieve maximum performance. In other words, avoid using literal anonymous functions
+(`fun(...) -> ... end` or `fn ... -> ... end`) or local function captures (`fun handle_event/4`
+or `&handle_event/4`) as event handlers.
+
+All the handlers are executed by the process dispatching event. If the function fails (raises,
+exits or throws) a handler failure event is emitted and then the handler is removed.
+
+Handler failure events `[telemetry, handler, failure]` should only be used for monitoring
+and diagnostic purposes. Re-attaching a failed handler will likely result in the handler
+failing again.
+
+Note that you should not rely on the order in which handlers are invoked.
+""").
-spec attach_many(HandlerId, [EventName], Function, Config) -> ok | {error, already_exists} when
HandlerId :: handler_id(),
EventName :: event_name(),
@@ -118,30 +124,31 @@ attach_many(HandlerId, EventNames, Function, Config) when is_function(Function,
end,
telemetry_handler_table:insert(HandlerId, EventNames, Function, Config).
-%% @doc Removes the existing handler.
-%%
-%% If the handler with given ID doesn't exist, `{error, not_found}' is returned.
+?DOC("""
+Removes the existing handler.
+
+If the handler with given ID doesn't exist, `{error, not_found}` is returned.
+""").
-spec detach(handler_id()) -> ok | {error, not_found}.
detach(HandlerId) ->
telemetry_handler_table:delete(HandlerId).
-%% @doc Emits the event, invoking handlers attached to it.
-%%
-%% When the event is emitted, the handler function provided to {@link attach/4} is called with four
-%% arguments:
-%%
-%% - the event name
-%% - the map of measurements
-%% - the map of event metadata
-%% - the handler configuration given to {@link attach/4}
-%%
-%%
-%% Best practices and conventions:
-%%
-%%
-%% While you are able to emit messages of any `event_name' structure, it is recommended that you follow the
-%% the guidelines laid out in {@link span/3} if you are capturing start/stop events.
-%%
+?DOC("""
+Emits the event, invoking handlers attached to it.
+
+When the event is emitted, the handler function provided to `attach/4` is called with four
+arguments:
+
+ * the event name
+ * the map of measurements
+ * the map of event metadata
+ * the handler configuration given to `attach/4`
+
+#### Best practices and conventions:
+
+While you are able to emit messages of any `event_name` structure, it is recommended that you follow the
+the guidelines laid out in `span/3` if you are capturing start/stop events.
+""").
-spec execute(EventName, Measurements, Metadata) -> ok when
EventName :: event_name(),
Measurements :: event_measurements() | event_value(),
@@ -176,143 +183,134 @@ execute([_ | _] = EventName, Measurements, Metadata) when is_map(Measurements) a
end,
lists:foreach(ApplyFun, Handlers).
-%% @doc Runs the provided `SpanFunction', emitting start and stop/exception events, invoking the handlers attached to each.
-%%
-%% The `SpanFunction' must return a `{result, stop_metadata}' or a `{result, extra_measurements, stop_metadata}` tuple.
-%%
-%% When this function is called, 2 events will be emitted via {@link execute/3}. Those events will be one of the following
-%% pairs:
-%%
-%% - `EventPrefix ++ [start]' and `EventPrefix ++ [stop]'
-%% - `EventPrefix ++ [start]' and `EventPrefix ++ [exception]'
-%%
-%%
-%% However, note that in case the current process crashes due to an exit signal
-%% of another process, then none or only part of those events would be emitted.
-%% Below is a breakdown of the measurements and metadata associated with each individual event.
-%%
-%% When providing `StartMetadata' and `StopMetadata', these values will be sent independently to `start' and
-%% `stop' events. If an exception occurs, exception metadata will be merged onto the `StartMetadata'. In general,
-%% it is highly recommended that `StopMetadata' should include the values from `StartMetadata'
-%% so that handlers, such as those used for metrics, can rely entirely on the `stop' event. Failure to include
-%% all of `StartMetadata' in `StopMetadata' can add significant complexity to event handlers.
-%%
-%% A default span context is added to event metadata under the `telemetry_span_context' key if none is provided by
-%% the user in the `StartMetadata'. This context is useful for tracing libraries to identify unique
-%% executions of span events within a process to match start, stop, and exception events. Metadata keys, which
-%% should be available to both `start' and `stop' events need to supplied separately for `StartMetadata' and
-%% `StopMetadata'.
-%%
-%% If `SpanFunction` returns `{result, extra_measurements, stop_metadata}`, then a map of extra measurements
-%% will be merged with the measurements automatically provided. This is useful if you want to return, for example,
-%% bytes from an HTTP request. The standard measurements `duration` and `monotonic_time` cannot be overridden.
-%%
-%% For `telemetry' events denoting the start of a larger event, the following data is provided:
-%%
-%%
-%%
-%% -
-%% Event:
-%% ```
-%% EventPrefix ++ [start]
-%% '''
-%%
-%% -
-%% Measurements:
-%% ```
-%% #{
-%% % The current system time in native units from
-%% % calling: erlang:system_time()
-%% system_time => integer(),
-%% monotonic_time => integer(),
-%% }
-%% '''
-%%
-%% -
-%% Metadata:
-%% ```
-%% #{
-%% telemetry_span_context => term(),
-%% % User defined metadata as provided in StartMetadata
-%% ...
-%% }
-%% '''
-%%
-%%
-%%
-%%
-%% For `telemetry' events denoting the stop of a larger event, the following data is provided:
-%%
-%%
-%% -
-%% Event:
-%% ```
-%% EventPrefix ++ [stop]
-%% '''
-%%
-%% -
-%% Measurements:
-%% ```
-%% #{
-%% % The current monotonic time minus the start monotonic time in native units
-%% % by calling: erlang:monotonic_time() - start_monotonic_time
-%% duration => integer(),
-%% monotonic_time => integer(),
-%% % User defined measurements when returning `SpanFunction` as a 3 element tuple
-%% }
-%% '''
-%%
-%% -
-%% Metadata:
-%% ```
-%% #{
-%% % An optional error field if the stop event is the result of an error
-%% % but not necessarily an exception.
-%% error => term(),
-%% telemetry_span_context => term(),
-%% % User defined metadata as provided in StopMetadata
-%% ...
-%% }
-%% '''
-%%
-%%
-%%
-%%
-%% For `telemetry' events denoting an exception of a larger event, the following data is provided:
-%%
-%%
-%% -
-%% Event:
-%% ```
-%% EventPrefix ++ [exception]
-%% '''
-%%
-%% -
-%% Measurements:
-%% ```
-%% #{
-%% % The current monotonic time minus the start monotonic time in native units
-%% % derived by calling: erlang:monotonic_time() - start_monotonic_time
-%% duration => integer(),
-%% monotonic_time => integer()
-%% }
-%% '''
-%%
-%% -
-%% Metadata:
-%% ```
-%% #{
-%% kind => throw | error | exit,
-%% reason => term(),
-%% stacktrace => list(),
-%% telemetry_span_context => term(),
-%% % User defined metadata as provided in StartMetadata
-%% ...
-%% }
-%% '''
-%%
-%%
-%%
+?DOC("""
+Runs the provided `SpanFunction`, emitting start and stop/exception events, invoking the handlers attached to each.
+
+The `SpanFunction` must return a `{result, stop_metadata}` or a `{result, extra_measurements, stop_metadata}` tuple.
+
+When this function is called, 2 events will be emitted via `execute/3`. Those events will be one of the following
+pairs:
+
+ * `EventPrefix ++ [start]` and `EventPrefix ++ [stop]`
+ * `EventPrefix ++ [start]` and `EventPrefix ++ [exception]`
+
+However, note that in case the current process crashes due to an exit signal
+of another process, then none or only part of those events would be emitted.
+Below is a breakdown of the measurements and metadata associated with each individual event.
+
+When providing `StartMetadata` and `StopMetadata`, these values will be sent independently to `start` and
+`stop` events. If an exception occurs, exception metadata will be merged onto the `StartMetadata`. In general,
+it is **highly recommended** that `StopMetadata` should include the values from `StartMetadata`
+so that handlers, such as those used for metrics, can rely entirely on the `stop` event. Failure to include
+all of `StartMetadata` in `StopMetadata` can add significant complexity to event handlers.
+
+A default span context is added to event metadata under the `telemetry_span_context` key if this key is not provided
+by the user in the `StartMetadata`. This context is useful for tracing libraries to identify unique
+executions of span events within a process to match start, stop, and exception events. Metadata keys which
+should be available to both `start` and `stop` events need to supplied separately for `StartMetadata` and
+`StopMetadata`.
+
+If `SpanFunction` returns `{result, extra_measurements, stop_metadata}`, then a map of extra measurements
+will be merged with the measurements automatically provided. This is useful if you want to return, for example,
+bytes from an HTTP request. The standard measurements `duration` and `monotonic_time` cannot be overridden.
+
+For `telemetry` events denoting the **start** of a larger event, the following data is provided:
+
+ * Event:
+
+ ```
+ EventPrefix ++ [start]
+ ```
+
+ * Measurements:
+
+ ```
+ #{
+ % The current system time in native units from
+ % calling: erlang:system_time()
+ system_time => integer(),
+ monotonic_time => integer(),
+ }
+ ```
+
+ * Metadata:
+
+ ```
+ #{
+ telemetry_span_context => term(),
+ % User defined metadata as provided in StartMetadata
+ ...
+ }
+ ```
+
+
+
+For `telemetry` events denoting the **stop** of a larger event, the following data is provided:
+
+ * Event:
+
+ ```
+ EventPrefix ++ [stop]
+ ```
+
+ * Measurements:
+
+ ```
+ #{
+ % The current monotonic time minus the start monotonic time in native units
+ % by calling: erlang:monotonic_time() - start_monotonic_time
+ duration => integer(),
+ monotonic_time => integer(),
+ % User defined measurements when returning `SpanFunction` as a 3 element tuple
+ }
+ ```
+
+ * Metadata:
+
+ ```
+ #{
+ % An optional error field if the stop event is the result of an error
+ % but not necessarily an exception.
+ error => term(),
+ telemetry_span_context => term(),
+ % User defined metadata as provided in StopMetadata
+ ...
+ }
+ ```
+
+For `telemetry` events denoting an **exception** of a larger event, the following data is provided:
+
+ * Event:
+
+ ```
+ EventPrefix ++ [exception]
+ ```
+
+ * Measurements:
+
+ ```
+ #{
+ % The current monotonic time minus the start monotonic time in native units
+ % by calling: erlang:monotonic_time() - start_monotonic_time
+ duration => integer(),
+ monotonic_time => integer()
+ }
+ ```
+
+ * Metadata:
+
+ ```
+ #{
+ kind => throw | error | exit,
+ reason => term(),
+ stacktrace => list(),
+ telemetry_span_context => term(),
+ % User defined metadata as provided in StartMetadata
+ ...
+ }
+ ```
+
+""").
-spec span(event_prefix(), event_metadata(), span_function()) -> span_result().
span(EventPrefix, StartMetadata, SpanFunction) ->
StartTime = erlang:monotonic_time(),
@@ -353,18 +351,22 @@ span(EventPrefix, StartMetadata, SpanFunction) ->
erlang:raise(Class, Reason, Stacktrace)
end.
-%% @equiv execute(EventName, Measurements, #{})
+?DOC("""
+Same as [`execute(EventName, Measurements, #{})`](`execute/3`).
+""").
-spec execute(EventName, Measurements) -> ok when
EventName :: event_name(),
Measurements :: event_measurements() | event_value().
execute(EventName, Measurements) ->
execute(EventName, Measurements, #{}).
-%% @doc Returns all handlers attached to events with given prefix.
-%%
-%% Handlers attached to many events at once using {@link attach_many/4} will be listed once for each
-%% event they're attached to.
-%% Note that you can list all handlers by feeding this function an empty list.
+?DOC("""
+Returns all handlers attached to events with given prefix.
+
+Handlers attached to many events at once using `attach_many/4` will be listed once for each
+event they're attached to.
+Note that you can list all handlers by feeding this function an empty list.
+""").
-spec list_handlers(event_prefix()) -> [handler()].
list_handlers(EventPrefix) ->
assert_event_prefix(EventPrefix),
@@ -410,7 +412,7 @@ assert_event_name(Term) ->
merge_ctx(#{telemetry_span_context := _} = Metadata, _Ctx) -> Metadata;
merge_ctx(Metadata, Ctx) -> Metadata#{telemetry_span_context => Ctx}.
-%% @private
+?DOC(false).
report_cb(#{handler_id := Id}) ->
{"The function passed as a handler with ID ~w is a local function.\n"
"This means that it is either an anonymous function or a capture of a function "
diff --git a/src/telemetry.hrl b/src/telemetry.hrl
index f1a8aaa..e8eaa2b 100644
--- a/src/telemetry.hrl
+++ b/src/telemetry.hrl
@@ -20,3 +20,11 @@
-else.
-define(LOG_WARNING(Msg, Args), error_logger:warning_msg(Msg, Args)).
-endif.
+
+-if(?OTP_RELEASE >= 27).
+-define(MODULEDOC(Str), -moduledoc(Str)).
+-define(DOC(Str), -doc(Str)).
+-else.
+-define(MODULEDOC(Str), -compile([])).
+-define(DOC(Str), -compile([])).
+-endif.
\ No newline at end of file
diff --git a/src/telemetry_test.erl b/src/telemetry_test.erl
index 61a4317..c813608 100644
--- a/src/telemetry_test.erl
+++ b/src/telemetry_test.erl
@@ -1,51 +1,77 @@
-%%%-------------------------------------------------------------------
-%% @doc Functions for testing execution of Telemetry events.
-%%
-%% Testing that the correct Telemetry events are emitted with the
-%% right measurements and metadata is essential for library authors.
-%% It helps to maintain stable APIs and avoid accidental changes
-%% to events.
-%% @end
-%%%-------------------------------------------------------------------
-
-module(telemetry_test).
+-include("telemetry.hrl").
+
+?MODULEDOC("""
+Functions for testing execution of Telemetry events.
+
+Testing that the correct Telemetry events are emitted with the
+right measurements and metadata is essential for library authors.
+It helps to maintain stable APIs and avoid accidental changes
+to events.
+""").
+
-export([attach_event_handlers/2, handle_event/4]).
-%% @doc Attaches a "message" handler to the given events.
-%%
-%% The attached handler sends a message to `destination_pid' every time it handles one of the
-%% events in `events'. The function returns a reference that you can use to make sure that
-%% messages come from this handler. This reference is also used as the handler ID, so you
-%% can use it to detach the handler with {@link telemetry:detach/1}.
-%%
-%% The shape of messages sent to `destination_pid' is:
-%%
-%% ```
-%% {Event, Ref, Measurements, Metadata}
-%% '''
-%%
-%% For example, in Erlang a test could look like this:
-%%
-%% ```
-%% Ref = telemetry_test:attach_event_handlers(self(), [[some, event]]),
-%% function_that_emits_the_event(),
-%% receive
-%% {[some, event], Ref, #{measurement := _}, #{meta := _}} ->
-%% telemetry:detach(Ref)
-%% after 1000 ->
-%% ct:fail(timeout_receive_attach_event_handlers)
-%% end.
-%% '''
-%%
-%% In Elixir, a similar test would look like this:
-%%
-%% ```
-%% ref = :telemetry_test.attach_event_handlers(self(), [[:some, :event]])
-%% function_that_emits_the_event()
-%% assert_received {[:some, :event], ^ref, %{measurement: _}, %{meta: _}}
-%% '''
-%%
+?DOC("""
+Attaches a "message" handler to the given events.
+
+The attached handler sends a message to `DestinationPID` every time it handles one of the
+events in `events`. The function returns a reference that you can use to make sure that
+messages come from this handler. This reference is also used as the handler ID, so you
+can use it to detach the handler with `telemetry:detach/1`.
+
+The shape of messages sent to `DestinationPID` is:
+
+
+
+### Erlang
+
+```erlang
+{Event, Ref, Measurements, Metadata}
+```
+
+### Elixir
+
+```elixir
+{event, ref, measurements, metadata}
+```
+
+
+
+## Examples
+
+
+
+### Erlang
+
+An example of a test in Erlang (using [`ct`](https://www.erlang.org/docs/23/man/ct)) could
+look like this:
+
+```erlang
+Ref = telemetry_test:attach_event_handlers(self(), [[some, event]]),
+function_that_emits_the_event(),
+receive
+ {[some, event], Ref, #{measurement := _}, #{meta := _}} ->
+ telemetry:detach(Ref)
+after 1000 ->
+ ct:fail(timeout_receive_attach_event_handlers)
+end.
+```
+
+### Elixir
+
+An example of an ExUnit test in Elixir could look like this:
+
+```elixir
+ref = :telemetry_test.attach_event_handlers(self(), [[:some, :event]])
+function_that_emits_the_event()
+assert_received {[:some, :event], ^ref, %{measurement: _}, %{meta: _}}
+```
+
+
+
+""").
-spec attach_event_handlers(DestinationPID, Events) -> reference() when
DestinationPID :: pid(),
Events :: [telemetry:event_name(), ...].
@@ -55,6 +81,6 @@ attach_event_handlers(DestPID, Events) when is_pid(DestPID) and is_list(Events)
telemetry:attach_many(Ref, Events, fun telemetry_test:handle_event/4, Config),
Ref.
-%% @hidden
+?DOC(false).
handle_event(Event, Measurements, Metadata, #{dest_pid := DestPID, ref := Ref}) ->
DestPID ! {Event, Ref, Measurements, Metadata}.