引发错误时,在堆栈跟踪中包含函数参数



在Erlang中,当引发错误时,我可以通过向error:传递第二个参数来将参数包括到当前函数中

-module(foo).
-export([foo/2]).
foo(A, B) ->
error(something_went_wrong, [A, B]).

这意味着参数将在堆栈跟踪中可见,这可以使调试更容易:

> foo:foo(1, 2).
foo:foo(1, 2).
** exception error: something_went_wrong
in function  foo:foo/2
called as foo:foo(1,2)

在Erlang和Elixir中,函数子句错误时都会自动发生这种情况:

defmodule Foo do
def foo(a, b) when false do
:ok
end
end
> Foo.foo(1, 2)
** (FunctionClauseError) no function clause matching in Foo.foo/2    

The following arguments were given to Foo.foo/2:

# 1
1

# 2
2

foo.ex:2: Foo.foo/2

除了直接调用:erlang.error之外,是否有一种方法可以在Elixir中为显式引发的异常包含函数参数?我查看了Kernel.raise的文档,没有发现任何合适的内容。

这感觉像是一种逃避,但我认为最好的方法是在消息中的变量上使用inspect/2,例如

raise ArgumentError, 
message: "There was a problem with the inputs: #{inspect(a)} #{inspect(b)}"
# Yields:    
# ** (ArgumentError) There was a problem with the inputs: "foo" "bar"

Elixir defexception文档中的示例或多或少都是这样做的,只是附加到一个自定义异常模块,类似于:

defmodule ABError do
defexception [:message]
end
a = "foo"
b = "bar"
raise ABError,
message: "There was a problem with the inputs: #{inspect(a)} #{inspect(b)}"
# Yields:
# ** (ABError) There was a problem with the inputs: "foo" "bar"

有了Erlang的背景,您可能能够更清楚地了解所有这些是如何通过异常模块返回的

没有通用的方法可以做到这一点,因为elixir中的异常只是一个结构。另一方面,这为您提供了灵活性,例如,在前面提到的FunctionClauseError中使用的灵活性,即

defexception [:module, :function, :arity, :kind, :args, :clauses]

因此,当编译器提出它时,message被构造为包括与原始错误一起传递的参数。


为了实现这一点,通常使用Kernel.defexception/1定义自定义异常,接受参数并从Exception行为实现message/1blame/2回调。一个很好的例子是FunctionClauseError实现的来源,我在上面链接了它。

最新更新