File 0001-Inform-user-about-usage-of-local-functions-as-handle.patch of Package telemetry

From a2e98a6474212a518fd1406d457e39839ae51e0b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Jan=20Niemier?= <lukasz@niemier.pl>
Date: Wed, 30 Jun 2021 19:14:23 +0200
Subject: [PATCH] Inform user about usage of local functions as handlers (#84)

This provides immediate feedback for the Telemetry user about improper
handler function definition.
---
 src/telemetry.erl        | 24 ++++++++++++++++++++++++
 test/telemetry_SUITE.erl | 34 +++++++++++++++++++++++++++++++++-
 2 files changed, 57 insertions(+), 1 deletion(-)

diff --git a/src/telemetry.erl b/src/telemetry.erl
index 782f3c6..19452d0 100644
--- a/src/telemetry.erl
+++ b/src/telemetry.erl
@@ -16,6 +16,8 @@
          execute/3,
          span/3]).
 
+-export([report_cb/1]).
+
 -include("telemetry.hrl").
 
 -type handler_id() :: term().
@@ -45,6 +47,8 @@
               span_result/0,
               span_function/0]).
 
+-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
@@ -91,6 +95,17 @@ attach(HandlerId, EventName, Function, Config) ->
       Config :: handler_config().
 attach_many(HandlerId, EventNames, Function, Config) when is_function(Function, 4) ->
     assert_event_names(EventNames),
+    case erlang:fun_info(Function, type) of
+        {type, external} ->
+            ok;
+        {type, local} ->
+            ?LOG_INFO(#{handler_id => HandlerId,
+                        event_names => EventNames,
+                        function => Function,
+                        config => Config,
+                        type => local},
+                      #{report_cb => fun ?MODULE:report_cb/1})
+    end,
     telemetry_handler_table:insert(HandlerId, EventNames, Function, Config).
 
 %% @doc Removes the existing handler.
@@ -345,3 +360,12 @@ assert_event_name(Term) ->
 -spec merge_ctx(event_metadata(), any()) -> event_metadata().
 merge_ctx(#{telemetry_span_context := _} = Metadata, _Ctx) -> Metadata;
 merge_ctx(Metadata, Ctx) -> Metadata#{telemetry_span_context => Ctx}.
+
+%% @hidden
+report_cb(#{handler_id := Id}) ->
+    {"Function passed as a handler with ID ~w is local function.\n"
+     "This mean that it is either anonymous function or capture of function "
+     "without module specified. That may cause performance penalty when calling "
+     "such handler. For more details see note in `telemetry:attach/4` "
+     "documentation.\n\n"
+     "https://hexdocs.pm/telemetry/telemetry.html#attach-4", [Id]}.
diff --git a/test/telemetry_SUITE.erl b/test/telemetry_SUITE.erl
index 9a9007e..58df646 100644
--- a/test/telemetry_SUITE.erl
+++ b/test/telemetry_SUITE.erl
@@ -14,7 +14,7 @@ all() ->
      handler_on_multiple_events, remove_all_handler_on_failure,
      list_handler_on_many, detach_from_all, old_execute, default_metadata,
      off_execute, invoke_successful_span_handlers, invoke_exception_span_handlers,
-     spans_generate_unique_default_contexts].
+     spans_generate_unique_default_contexts, logs_on_local_function].
 
 init_per_suite(Config) ->
     application:ensure_all_started(telemetry),
@@ -403,6 +403,34 @@ spans_generate_unique_default_contexts(Config) ->
         1000 -> ct:fail(timeout_receive_echo)
     end.
 
+logs_on_local_function(Config) ->
+    HandlerId = ?config(id, Config),
+    Event = [some, action],
+
+    OldConfig = logger:get_primary_config(),
+    logger:add_primary_filter(logs_on_local_function, {fun ?MODULE:send_logs/2,
+                                                       self()}),
+    logger:update_primary_config(#{level => info}),
+
+    try
+        telemetry:attach(HandlerId, Event, fun raise_on_event/4, []),
+        receive
+            {log, #{msg := {report, #{handler_id := HandlerId}}}} -> ok
+        after
+            1000 -> ct:fail(timeout_receive_log)
+        end,
+
+        Fun = fun(_Event, _Measurements, _Meta, _Config) -> ok end,
+        telemetry:attach(HandlerId, Event, Fun, []),
+        receive
+            {log, #{msg := {report, #{handler_id := HandlerId}}}} -> ok
+        after
+            1000 -> ct:fail(timeout_receive_log)
+        end
+    after
+        logger:set_primary_config(OldConfig)
+    end.
+
 % Ensure calling execute is safe when the telemetry application is off
 off_execute(_Config) ->
     application:stop(telemetry),
@@ -414,3 +442,7 @@ echo_event(Event, Measurements, Metadata, #{send_to := Pid} = Config) ->
 
 raise_on_event(_, _, _, _) ->
     throw(got_event).
+
+send_logs(Event, Pid) ->
+    Pid ! {log, Event},
+    ignore.
-- 
2.35.3

openSUSE Build Service is sponsored by