我之前曾使用 AOP 风格的代码将逻辑与日志记录分开,并且对结果非常满意。我认识到对 AOP 的看法各不相同,但我想在 Elixir 中找到一个解决方案,即使我最终没有在产品中使用它。
我见过的最接近的例子是 ExUnit 内部的设置回调,它允许在每个测试运行之前执行代码;我想做类似的事情,但无法通过 ExUnit 源代码来掌握那里的直觉。
以代码形式:
defmodule Project.Logic do
LoggingInjection.inject Project.Logging
def work_do_stuff(arg) do
#...
#returns some_result
end
end
Run Code Online (Sandbox Code Playgroud)
在单独的代码文件中:
defmodule Project.Logging do
#called just before Project.Logic.work_do_stuff with the same args
def before_work_do_stuff(arg) do
Log.write("about to work_do_stuff with #{inspect arg}")
end
# def after_work_do_stuff(some_result) implicitly defined as no-op,
# but could be overridden.
end
Run Code Online (Sandbox Code Playgroud)
最后,真正的问题是:启用这个魔法的代码是什么?
defmodule LoggingInjection do
defmacro inject(logging_module) do
#What goes here?
end
end
Run Code Online (Sandbox Code Playgroud)
它不是 AOP,但如果您有兴趣观察运行时的函数调用,您可以考虑查看 Erlang 跟踪。
例如,您可以用来:dbg设置函数调用的动态跟踪。以下是跟踪所有调用的方法IO:
iex> :dbg.tracer
iex> :dbg.p(:all, [:call])
iex> :dbg.tp(IO, [{:_, [], [{:return_trace}]}])
(<0.53.0>) call 'Elixir.IO':puts(stdio,<<"\e[33m{:ok, [{:matched, :nonode@nohost, 28}, {:saved, 1}]}\e[0m">>)
(<0.53.0>) returned from 'Elixir.IO':puts/2 -> ok
(<0.59.0>) call 'Elixir.IO':gets(stdio,<<"iex(4)> ">>)
Run Code Online (Sandbox Code Playgroud)
我偶尔会使用此功能连接到正在运行的 BEAM 节点,并分析正在运行的系统。:dbg.stop_clear完成后请务必停止跟踪。
如果您想手动处理跟踪消息并对它们执行一些特定操作(例如将它们记录到文件中),您可以使用:erlang.trace. 这是一个gen_server跟踪对各个模块的调用的简单示例:
defmodule Tracer do
use GenServer
def start(modules), do: GenServer.start(__MODULE__, modules)
def init(modules) do
:erlang.trace(:all, true, [:call])
for module <- modules do
:erlang.trace_pattern({module, :_, :_}, [{:_, [], [{:return_trace}]}])
end
{:ok, nil}
end
def handle_info({:trace, _, :call, {mod, fun, args}}, state) do
IO.puts "called #{mod}.#{fun}(#{Enum.join(Enum.map(args, &inspect/1), ",")})"
{:noreply, state}
end
def handle_info({:trace, _, :return_from, {mod, fun, arity}, res}, state) do
IO.puts "#{mod}.#{fun}/#{arity} returned #{res}"
{:noreply, state}
end
def handle_info(_, state), do: {:noreply, state}
end
Run Code Online (Sandbox Code Playgroud)
要使用它,只需启动它并提供您想要跟踪的模块列表:
iex(2)> Tracer.start([IO])
{:ok, #PID<0.84.0>}
called Elixir.IO.puts(:stdio,"\e[33m{:ok, #PID<0.84.0>}\e[0m")
Elixir.IO.puts/2 returned ok
call Elixir.IO.gets(:stdio,"iex(3)> ")
Run Code Online (Sandbox Code Playgroud)
跟踪非常强大,您可以用它做各种各样的事情。我自己没有将它用于日志系统,所以我不能说它会有多大的问题,所以如果你走这条路,我建议你小心并观察性能,因为太多的跟踪可能会让你的系统超载。
| 归档时间: |
|
| 查看次数: |
1603 次 |
| 最近记录: |