From 7f4a2eaff7665ff10610820dcc4d7bbbe8f9d0ed Mon Sep 17 00:00:00 2001 From: akash-akya Date: Sun, 7 Sep 2025 21:41:11 +0530 Subject: [PATCH] Add configurable debug logging module --- lib/ex_cmd/log.ex | 19 +++++++++++++++++++ lib/ex_cmd/process.ex | 21 ++++++++++----------- lib/ex_cmd/process/proto.ex | 9 ++++----- lib/ex_cmd/process/state.ex | 1 - test/ex_cmd_exit_test.exs | 3 ++- 5 files changed, 35 insertions(+), 18 deletions(-) create mode 100644 lib/ex_cmd/log.ex diff --git a/lib/ex_cmd/log.ex b/lib/ex_cmd/log.ex new file mode 100644 index 0000000..d91d3de --- /dev/null +++ b/lib/ex_cmd/log.ex @@ -0,0 +1,19 @@ +defmodule ExCmd.Log do + @moduledoc false + + require Logger + + @spec debug(String.t(), keyword) :: :ok + def debug(msg, opts \\ []) do + if Application.get_env(:ex_cmd, :enable_debug_logs) do + Logger.debug(msg, opts) + else + :ok + end + end + + @spec error(String.t(), keyword) :: :ok + def error(msg, opts \\ []) do + Logger.error(msg, opts) + end +end diff --git a/lib/ex_cmd/process.ex b/lib/ex_cmd/process.ex index 2b61c6f..0ff8a89 100644 --- a/lib/ex_cmd/process.ex +++ b/lib/ex_cmd/process.ex @@ -272,14 +272,13 @@ defmodule ExCmd.Process do use GenServer + alias ExCmd.Log alias ExCmd.Process.Exec alias ExCmd.Process.Operations alias ExCmd.Process.Pipe alias ExCmd.Process.Proto alias ExCmd.Process.State - require Logger - defmodule Error do defexception [:message] end @@ -579,7 +578,7 @@ defmodule ExCmd.Process do @impl true def handle_cast({:prepare_exit, caller, timeout}, state) do - Logger.debug("prepare_exit: #{timeout}") + Log.debug("prepare_exit: #{timeout}") state = close_pipes(state, caller) case maybe_shutdown(state) do @@ -666,7 +665,7 @@ defmodule ExCmd.Process do {:exit_sequence, current_stage, timeout, kill_timeout}, %{status: status} = state ) do - Logger.debug("exit_sequence, #{current_stage} #{timeout} #{kill_timeout}, #{inspect(state)}") + Log.debug("exit_sequence, #{current_stage} #{timeout} #{kill_timeout}, #{inspect(state)}") cond do status != :running -> @@ -699,7 +698,7 @@ defmodule ExCmd.Process do end def handle_info({port, {:exit_status, odu_exit_status}}, %{port: port} = state) do - Logger.debug("port exit with status #{odu_exit_status} state: #{inspect(state)}") + Log.debug("port exit with status #{odu_exit_status} state: #{inspect(state)}") state = cond do @@ -722,7 +721,7 @@ defmodule ExCmd.Process do # we are only interested in Port exit signals def handle_info({:EXIT, port, reason}, %State{port: port} = state) when reason != :normal do - Logger.debug("port exit with error state: #{inspect(state)}") + Log.debug("port exit with error state: #{inspect(state)}") state = state @@ -733,7 +732,7 @@ defmodule ExCmd.Process do end def handle_info({:EXIT, port, :normal}, %State{port: port} = state) do - Logger.debug("port exit normally state: #{inspect(state)}") + Log.debug("port exit normally state: #{inspect(state)}") maybe_shutdown(state) end @@ -744,19 +743,19 @@ defmodule ExCmd.Process do {:DOWN, owner_ref, :process, _pid, reason}, %State{monitor_ref: owner_ref} = state ) do - Logger.debug("process owner exit: state: #{inspect(state)}") + Log.debug("process owner exit: state: #{inspect(state)}") {:stop, reason, state} end def handle_info({:DOWN, _ref, :process, pid, _reason}, state) do - Logger.debug("pipe owner exit: state: #{inspect(state)}") + Log.debug("pipe owner exit: state: #{inspect(state)}") state = close_pipes(state, pid) maybe_shutdown(state) end @spec maybe_shutdown(State.t()) :: {:stop, :normal, State.t()} | {:noreply, State.t()} defp maybe_shutdown(state) do - Logger.debug("maybe_shutdown: state: #{inspect(state)}") + Log.debug("maybe_shutdown: state: #{inspect(state)}") open_pipes_count = state.pipes @@ -764,7 +763,7 @@ defmodule ExCmd.Process do |> Enum.count(&Pipe.open?/1) if open_pipes_count == 0 && !(state.status in [:init, :running]) do - Logger.debug("shutting down state: #{inspect(state)}") + Log.debug("shutting down state: #{inspect(state)}") {:stop, :normal, state} else {:noreply, state} diff --git a/lib/ex_cmd/process/proto.ex b/lib/ex_cmd/process/proto.ex index 1a7189a..03097cb 100644 --- a/lib/ex_cmd/process/proto.ex +++ b/lib/ex_cmd/process/proto.ex @@ -1,10 +1,9 @@ defmodule ExCmd.Process.Proto do @moduledoc false + alias ExCmd.Log alias Mix.Tasks.Compile.Odu - require Logger - @doc false defmacro send_input, do: 1 @@ -102,7 +101,7 @@ defmodule ExCmd.Process.Proto do end def close(port, stream) when is_port(port) do - Logger.debug("Closing stream: #{stream}") + Log.debug("Closing stream: #{stream}") :ok end @@ -117,11 +116,11 @@ defmodule ExCmd.Process.Proto do os_pid = receive do {^port, {:data, <>}} -> - Logger.debug("Command started. os pid: #{os_pid}") + Log.debug("Command started. os pid: #{os_pid}") os_pid {^port, {:data, <>}} -> - Logger.error("Failed to start odu. reason: #{reason}") + Log.error("Failed to start odu. reason: #{reason}") raise ArgumentError, message: "Failed to start odu" after 5_000 -> diff --git a/lib/ex_cmd/process/state.ex b/lib/ex_cmd/process/state.ex index 27c29ff..ac3f083 100644 --- a/lib/ex_cmd/process/state.ex +++ b/lib/ex_cmd/process/state.ex @@ -74,7 +74,6 @@ defmodule ExCmd.Process.State do @spec put_operation(State.t(), Operations.operation()) :: {:ok, t} | {:error, term} def put_operation(%State{operations: ops} = state, operation) do with {:ok, ops} <- Operations.put(ops, operation) do - # dbg(ops) {:ok, %State{state | operations: ops}} end end diff --git a/test/ex_cmd_exit_test.exs b/test/ex_cmd_exit_test.exs index 53fd62e..51bf930 100644 --- a/test/ex_cmd_exit_test.exs +++ b/test/ex_cmd_exit_test.exs @@ -4,7 +4,8 @@ defmodule ExCmdExitTest do # currently running `elixir` command is not working in Windows @tag os: :unix test "if it kills external command on abnormal vm exit" do - ex_cmd_expr = ~S{ExCmd.stream!(["cat"]) |> Stream.run()} + ex_cmd_expr = + ~S{:ok = Application.put_env(:ex_cmd, :enable_debug_logs, true); ExCmd.stream!(["cat"]) |> Stream.run()} port = Port.open(