Packages
appsignal
2.15.2
2.17.4
2.17.3
2.17.2
2.17.1
2.17.0
2.16.0
2.15.11
2.15.10
2.15.9
2.15.8
2.15.7
2.15.6
2.15.5
2.15.4
2.15.3
2.15.2
2.15.1
2.15.0
2.14.1
2.14.0
2.13.4
2.13.3
2.13.2
2.13.1
2.13.0
2.12.3
2.12.2
2.12.1
2.12.0
2.11.0
2.10.2
2.10.1
2.10.0
2.9.2
2.9.1
2.9.0
2.8.3
2.8.2
2.8.1
2.8.0
2.7.13
2.7.12
2.7.11
2.7.10
2.7.9
2.7.8
2.7.7
2.7.6
2.7.5
2.7.4
2.7.3
2.7.2
2.7.1
2.7.0
2.7.0-beta.1
2.6.1
2.6.0
2.5.3
2.5.2
2.5.1
2.5.0
2.4.3
2.4.2
2.4.1
2.4.0
2.3.1
2.3.0
2.2.19
2.2.18
2.2.17
2.2.16
2.2.15
2.2.14
2.2.13
2.2.12
retired
2.2.11
2.2.10
2.2.9
2.2.8
2.2.7
2.2.6
2.2.5
2.2.4
2.2.3
2.2.2
2.2.1
2.2.0
2.1.15
2.1.14
2.1.13
2.1.12
2.1.11
2.1.10
2.1.9
2.1.8
2.1.7
2.1.7-beta.1
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.8
2.0.7
2.0.6
2.0.6-beta.1
2.0.5
retired
2.0.4
retired
2.0.3
retired
2.0.2
retired
2.0.1
retired
2.0.0
retired
2.0.0-beta.11
2.0.0-beta.10
2.0.0-beta.9
2.0.0-beta.8
2.0.0-beta.7
2.0.0-beta.6
2.0.0-beta.5
2.0.0-beta.4
2.0.0-beta.3
2.0.0-beta.2
2.0.0-beta.1
2.0.0-alpha.7
2.0.0-alpha.6
2.0.0-alpha.5
2.0.0-alpha.4
2.0.0-alpha.3
2.0.0-alpha.2
2.0.0-alpha.1
1.13.5
1.13.4
1.13.3
1.13.2
1.13.1
1.13.0
1.13.0-beta.2
1.13.0-beta.1
1.12.1
1.12.0
1.12.0-beta.3
1.12.0-beta.2
1.12.0-beta.1
1.11.8
1.11.7
1.11.6
1.11.5
1.11.4
1.11.3
1.11.2
1.11.1
1.11.0
1.11.0-beta.1
1.10.13
1.10.12
1.10.11
1.10.10
1.10.9
1.10.8
1.10.7
1.10.6
1.10.5
1.10.4
1.10.3
1.10.2
1.10.1
1.10.0
1.9.4
1.9.3
1.9.2
1.9.1
1.9.1-beta.1
1.9.0
1.9.0-beta.1
1.9.0-alpha.1
1.8.2
1.8.1
1.8.1-beta.2
1.8.1-beta.1
1.8.0
1.7.2
1.7.1
1.7.0
1.7.0-beta.2
1.7.0-beta.1
1.7.0-alpha.4
1.7.0-alpha.3
1.7.0-alpha.2
1.7.0-alpha.1
1.6.7
1.6.6
1.6.6-beta.2
1.6.6-beta.1
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.6.0-beta.1
1.6.0-alpha.1
1.5.1
1.5.0
1.5.0-beta.2
1.5.0-beta.1
1.4.11-beta.1
1.4.10
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.4.0-alpha.1
1.3.6
1.3.6-beta.1
1.3.5
1.3.4
1.3.4-beta.1
1.3.3
1.3.2
1.3.1
1.3.0
1.3.0-beta.3
1.3.0-beta.2
1.3.0-beta.1
1.2.3
1.2.2
1.2.1
1.2.0
1.1.1
1.1.0
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
1.0.0-rc.1
0.13.0
0.12.3
0.12.2
0.12.1
0.12.0
0.11.6
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.2
0.9.1
0.9.0
0.8.0
0.7.1
0.7.0
0.6.2
0.6.1
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
0.0.9
0.0.8
0.0.7
0.0.6
0.0.5
0.0.4
0.0.3
0.0.2
0.0.1
Collects error and performance data from your Elixir applications and sends it to AppSignal
Current section
Files
Jump to
Current section
Files
lib/appsignal/utils/argument_cleaner.ex
defmodule Appsignal.Utils.Literal do
defstruct [:value]
end
defimpl Inspect, for: Appsignal.Utils.Literal do
def inspect(%Appsignal.Utils.Literal{value: value}, _opts), do: value
end
defmodule Appsignal.Utils.ArgumentCleaner do
@maximum_iterable_count 4
@maximum_recursion 2
def clean_literal(argument), do: %Appsignal.Utils.Literal{value: clean(argument)}
def clean(argument), do: clean(argument, @maximum_recursion)
def clean(argument, _) when is_nil(argument), do: inspect(argument)
def clean(argument, _) when is_atom(argument), do: inspect(argument)
def clean(argument, _) when is_binary(argument), do: "\"...\""
def clean(argument, _) when is_bitstring(argument), do: "<<...>>"
def clean(argument, recurse) when is_map(argument) do
{struct, map} = Map.pop(argument, :__struct__)
contents = if Enum.empty?(map), do: "", else: do_clean_map(map, recurse, struct)
if struct do
"%#{Appsignal.Utils.module_name(struct)}{#{contents}}"
else
"%{#{contents}}"
end
end
def clean(argument, _) when is_integer(argument), do: "integer()"
def clean(argument, _) when is_float(argument), do: "float()"
def clean(argument, _) when is_pid(argument), do: "#PID<...>"
def clean(argument, _) when is_port(argument), do: "#Port<...>"
def clean(argument, _) when is_reference(argument), do: "#Reference<...>"
def clean(argument, _) when is_function(argument) do
inspected_function = inspect(argument)
if String.starts_with?(inspected_function, "#Function<") do
# Represent the function as `fn _, _ -> ... end`, as this allows
# to express the arity clearly while redacting the rest.
arity = :erlang.fun_info(argument)[:arity]
["fn", placeholders(arity), "-> ... end"]
|> Enum.reject(&is_nil/1)
|> Enum.join(" ")
else
# As it is not an anonymous function, calling `inspect` on it already
# expresses it in the Module.function/arity form, which is fine.
inspected_function
end
end
def clean({}, _), do: "{}"
def clean(argument, recurse) when is_tuple(argument) do
"{#{do_clean_tuple(argument, recurse)}}"
end
def clean([], _), do: "[]"
def clean(argument, recurse) when is_list(argument) do
"[#{do_clean_list(argument, recurse)}]"
end
def clean(_, _), do: "any()"
defp do_clean_tuple(_, 0), do: "..."
defp do_clean_tuple(tuple, recurse) do
tuple_list = Tuple.to_list(tuple)
if Enum.count(tuple_list) <= @maximum_iterable_count do
Enum.map_join(tuple_list, ", ", &clean(&1, recurse - 1))
else
"..."
end
end
defp do_clean_list(list, @maximum_recursion) do
# If it's a small keyword list (often used for options as the last argument)
# keep the keys and clean the values. Otherwise, redact all values.
if Enum.count(list) <= @maximum_iterable_count and Keyword.keyword?(list) do
Enum.map_join(list, ", ", fn {key, value} ->
"#{to_string(key)}: #{clean(value, @maximum_recursion - 1)}"
end)
else
"..."
end
end
defp do_clean_list(_, _), do: "..."
defp do_clean_map(_, 0, _), do: "..."
defp do_clean_map(map, recurse, nil) do
# Only print cleaned keys and values for small maps that are not structs.
# Otherwise, omit all keys and values.
if Enum.count(map) <= @maximum_iterable_count do
has_atom_keys = Enum.all?(map, fn {key, _} -> is_atom(key) end)
separator = if has_atom_keys, do: ": ", else: " => "
map
|> Enum.sort(fn {key_a, _}, {key_b, _} -> key_a <= key_b end)
|> Enum.map_join(", ", fn {key, value} ->
clean_key = if has_atom_keys, do: to_string(key), else: clean(key, recurse - 1)
"#{clean_key}#{separator}#{clean(value, recurse - 1)}"
end)
else
"..."
end
end
defp do_clean_map(_, _, _), do: "..."
defp placeholders(0), do: nil
defp placeholders(arity) do
Enum.map_join(1..arity, ", ", fn _ -> "_" end)
end
end