From ecce363a73af4d9250a3322a0edef046f15fec31 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Sun, 22 Oct 2023 23:10:09 +0200 Subject: [PATCH] send telemetry on configuration --- .../elixir_ls_debugger/lib/debugger/server.ex | 42 +++++++++-- .../lib/language_server/cli.ex | 75 ++++++++++++------- .../lib/language_server/dialyzer.ex | 6 +- .../lib/language_server/server.ex | 52 ++++++++++++- 4 files changed, 136 insertions(+), 39 deletions(-) diff --git a/apps/elixir_ls_debugger/lib/debugger/server.ex b/apps/elixir_ls_debugger/lib/debugger/server.ex index 92199f2fa..b75fa683e 100644 --- a/apps/elixir_ls_debugger/lib/debugger/server.ex +++ b/apps/elixir_ls_debugger/lib/debugger/server.ex @@ -489,13 +489,22 @@ defmodule ElixirLS.Debugger.Server do 1 end - IO.puts( + message = "Mix task exited with reason\n#{Exception.format_exit(reason)}\nreturning code #{exit_code}" - ) - Output.debugger_console( - "Mix task exited with reason\n#{Exception.format_exit(reason)}\nreturning code #{exit_code}" - ) + IO.puts(message) + + Output.debugger_console(message) + + if reason != :normal do + Output.telemetry( + "debuggee_mix_task_error", + %{ + "elixir_ls.debuggee_mix_task_error" => message + }, + %{} + ) + end Output.send_event("exited", %{"exitCode" => exit_code}) Output.send_event("terminated", %{"restart" => false}) @@ -661,6 +670,29 @@ defmodule ElixirLS.Debugger.Server do # setBreakpoints, setFunctionBreakpoints and configurationDone Output.send_event("initialized", %{}) send(self(), :update_threads) + + Output.telemetry( + "dap_launch_config", + %{ + "elixir_ls.startApps" => to_string(Map.get(config, "startApps", false)), + "elixir_ls.debugAutoInterpretAllModules" => + to_string(Map.get(config, "debugAutoInterpretAllModules", true)), + "elixir_ls.stackTraceMode" => + to_string(Map.get(config, "stackTraceMode", "no_tail")), + "elixir_ls.exitAfterTaskReturns" => + to_string(Map.get(config, "exitAfterTaskReturns", true)), + "elixir_ls.noDebug" => to_string(Map.get(config, "noDebug", false)), + "elixir_ls.breakOnDbg" => to_string(Map.get(config, "breakOnDbg", true)), + "elixir_ls.env" => to_string(Map.has_key?(config, "env")), + "elixir_ls.requireFiles" => to_string(Map.has_key?(config, "requireFiles")), + "elixir_ls.debugInterpretModulesPatterns" => + to_string(Map.has_key?(config, "debugInterpretModulesPatterns")), + "elixir_ls.excludeModules" => to_string(Map.has_key?(config, "excludeModules")), + "elixir_ls.task" => to_string(Map.get(config, "task", ":default_task")) + }, + %{} + ) + config {:DOWN, ^ref, :process, _pid, reason} -> diff --git a/apps/language_server/lib/language_server/cli.ex b/apps/language_server/lib/language_server/cli.ex index 0eec29282..19e34f80c 100644 --- a/apps/language_server/lib/language_server/cli.ex +++ b/apps/language_server/lib/language_server/cli.ex @@ -108,33 +108,43 @@ defmodule ElixirLS.LanguageServer.CLI do end def check_otp_doc_chunks() do - supported = if match?({:error, _}, Code.fetch_docs(:erlang)) do - JsonRpc.show_message(:warning, "OTP compiled without EEP48 documentation chunks") + supported = + if match?({:error, _}, Code.fetch_docs(:erlang)) do + JsonRpc.show_message(:warning, "OTP compiled without EEP48 documentation chunks") + + Logger.warning( + "OTP compiled without EEP48 documentation chunks. Language features for erlang modules will run in limited mode. Please reinstall or rebuild OTP with appropriate flags." + ) + + false + else + true + end - Logger.warning( - "OTP compiled without EEP48 documentation chunks. Language features for erlang modules will run in limited mode. Please reinstall or rebuild OTP with appropriate flags." - ) - false - else - true - end JsonRpc.telemetry("eep48", %{"elixir_ls.eep48" => to_string(supported)}, %{}) end def check_elixir_sources() do enum_ex_path = Enum.module_info()[:compile][:source] - elixir_sources_available = unless File.exists?(enum_ex_path, [:raw]) do - dir = Path.join(enum_ex_path, "../../../..") |> Path.expand() + elixir_sources_available = + unless File.exists?(enum_ex_path, [:raw]) do + dir = Path.join(enum_ex_path, "../../../..") |> Path.expand() - Logger.notice( - "Elixir sources not found (checking in #{dir}). Code navigation to Elixir modules disabled." - ) - false - else - true - end - JsonRpc.telemetry("elixir_sources", %{"elixir_ls.elixir_sources" => to_string(elixir_sources_available)}, %{}) + Logger.notice( + "Elixir sources not found (checking in #{dir}). Code navigation to Elixir modules disabled." + ) + + false + else + true + end + + JsonRpc.telemetry( + "elixir_sources", + %{"elixir_ls.elixir_sources" => to_string(elixir_sources_available)}, + %{} + ) end def check_otp_sources() do @@ -145,16 +155,23 @@ defmodule ElixirLS.LanguageServer.CLI do |> to_string |> String.replace(~r/(.+)\/ebin\/([^\s]+)\.beam$/, "\\1/src/\\2.erl") - otp_sources_available = unless File.exists?(erlang_erl_path, [:raw]) do - dir = Path.join(erlang_erl_path, "../../../..") |> Path.expand() + otp_sources_available = + unless File.exists?(erlang_erl_path, [:raw]) do + dir = Path.join(erlang_erl_path, "../../../..") |> Path.expand() - Logger.notice( - "OTP sources not found (checking in #{dir}). Code navigation to OTP modules disabled." - ) - false - else - true - end - JsonRpc.telemetry("otp_sources", %{"elixir_ls.otp_sources" => to_string(otp_sources_available)}, %{}) + Logger.notice( + "OTP sources not found (checking in #{dir}). Code navigation to OTP modules disabled." + ) + + false + else + true + end + + JsonRpc.telemetry( + "otp_sources", + %{"elixir_ls.otp_sources" => to_string(otp_sources_available)}, + %{} + ) end end diff --git a/apps/language_server/lib/language_server/dialyzer.ex b/apps/language_server/lib/language_server/dialyzer.ex index 67318a15e..07bca28d7 100644 --- a/apps/language_server/lib/language_server/dialyzer.ex +++ b/apps/language_server/lib/language_server/dialyzer.ex @@ -31,12 +31,14 @@ defmodule ElixirLS.LanguageServer.Dialyzer do cond do not Code.ensure_loaded?(:dialyzer) -> - {:error, + # TODO is this check relevant? We check for dialyzer app in CLI + {:error, :no_dialyzer, "The current Erlang installation does not include Dialyzer. It may be available as a " <> "separate package."} not dialyzable?(System) -> - {:error, + # TODO is this relevant anymore? We require OTP 22+ (minimum for elixir 1.13) + {:error, :no_debug_info, "Dialyzer is disabled because core Elixir modules are missing debug info. " <> "You may need to recompile Elixir with Erlang >= OTP 20"} diff --git a/apps/language_server/lib/language_server/server.ex b/apps/language_server/lib/language_server/server.ex index 835ced986..8e041c685 100644 --- a/apps/language_server/lib/language_server/server.ex +++ b/apps/language_server/lib/language_server/server.ex @@ -1319,7 +1319,7 @@ defmodule ElixirLS.LanguageServer.Server do end defp dialyzer_enabled?(state = %__MODULE__{}) do - Dialyzer.check_support() == :ok and build_enabled?(state) and state.dialyzer_sup != nil + state.dialyzer_sup != nil end defp safely_read_file(file) do @@ -1384,8 +1384,27 @@ defmodule ElixirLS.LanguageServer.Server do end case Dialyzer.check_support() do - :ok -> :ok - {:error, msg} -> JsonRpc.show_message(:warning, msg) + :ok -> + JsonRpc.telemetry( + "dialyzer_support", + %{ + "elixir_ls.dialyzer_support" => to_string(true), + "elixir_ls.dialyzer_support_reason" => "" + }, + %{} + ) + + {:error, reason, msg} -> + JsonRpc.show_message(:warning, msg) + + JsonRpc.telemetry( + "dialyzer_support", + %{ + "elixir_ls.dialyzer_support" => to_string(false), + "elixir_ls.dialyzer_support_reason" => to_string(reason) + }, + %{} + ) end :ok @@ -1418,6 +1437,33 @@ defmodule ElixirLS.LanguageServer.Server do Tracer.set_project_dir(state.project_dir) end + JsonRpc.telemetry( + "lsp_config", + %{ + "elixir_ls.projectDir" => to_string(Map.has_key?(settings, "projectDir")), + "elixir_ls.autoBuild" => to_string(Map.get(settings, "autoBuild", true)), + "elixir_ls.dialyzerEnabled" => to_string(Map.get(settings, "dialyzerEnabled", true)), + "elixir_ls.fetchDeps" => to_string(Map.get(settings, "fetchDeps", false)), + "elixir_ls.suggestSpecs" => to_string(Map.get(settings, "suggestSpecs", true)), + "elixir_ls.autoInsertRequiredAlias" => + to_string(Map.get(settings, "autoInsertRequiredAlias", true)), + "elixir_ls.signatureAfterComplete" => + to_string(Map.get(settings, "signatureAfterComplete", true)), + "elixir_ls.enableTestLenses" => to_string(Map.get(settings, "enableTestLenses", false)), + "elixir_ls.languageServerOverridePath" => + to_string(Map.has_key?(settings, "languageServerOverridePath")), + "elixir_ls.envVariables" => to_string(Map.has_key?(settings, "envVariables")), + "elixir_ls.mixEnv" => to_string(Map.get(settings, "mixEnv", "test")), + "elixir_ls.mixTarget" => to_string(Map.get(settings, "mixTarget", "host")), + "elixir_ls.dialyzerFormat" => + if(Map.get(settings, "dialyzerEnabled", true), + do: Map.get(settings, "dialyzerFormat", "dialyxir_long"), + else: "" + ) + }, + %{} + ) + trigger_build(%{state | settings: settings}) end