Elixir 编译时代码注入/AOP [英] Elixir compile-time code injection / AOP

查看:32
本文介绍了Elixir 编译时代码注入/AOP的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我以前使用过 AOP 风格的代码将逻辑与日志分开,并且对结果非常满意.我承认对 AOP 的看法各不相同,但我想在 Elixir 中找出解决方案,即使我最终没有在生产中使用它.

I've previously used AOP-style code to separate Logic from Logging, and been very pleased with the results. I recognize that opinions on AOP vary, but I'd like to figure out a solution in Elixir, even if I don't end up using it in prod.

我见过的最接近的例子是 ExUnit 内部的 setup 回调,它允许在每次测试运行之前执行代码;我想做类似的事情,但一直无法通过 ExUnit 源代码来掌握那里的直觉.

The closest example I've seen is the setup callback inside of ExUnit, which allows execution of code before each test runs; I'd like to do something similar, but haven't been able to muddle through the ExUnit source to grasp the intuitions there.

代码形式:

defmodule Project.Logic do
    LoggingInjection.inject Project.Logging

    def work_do_stuff(arg) do
        #...
        #returns some_result
    end
end

在单独的代码文件中:

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

最后,真正的问题是:启用这种魔法的代码是什么?

and finally, the real question: What is the code to enable this magic?

defmodule LoggingInjection do
    defmacro inject(logging_module) do
        #What goes here?
    end
end

推荐答案

它不是 AOP,但如果你有兴趣在运行时观察函数调用,你可以考虑查看 Erlang trace.

It's not AOP, but if you're interested in observing function calls at runtime, you can consider looking into Erlang trace.

例如,您可以使用 :dbg 来设置函数调用的动态跟踪.以下是跟踪对 IO 的所有调用的方法:

For example, you can use :dbg to setup dynamic traces of function calls. Here's how to trace all calls to 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)> ">>)

我偶尔会使用此功能连接到正在运行的 BEAM 节点,并分析正在运行的系统.完成后,请务必使用 :dbg.stop_clear 停止跟踪.

I occasionally use this feature to connect to a running BEAM node, and analyze the running system. Be sure to stop traces with :dbg.stop_clear once you're done.

如果您想手动处理跟踪消息并对它们做一些特定的事情(例如将它们记录到文件中),您可以使用 :erlang.trace.这是一个简单的 gen_server 跟踪对各种模块的调用:

If you want to manually handle trace messages and do something specific about them (e.g. log them to a file), you can use :erlang.trace. Here's a simple gen_server that traces calls to various modules:

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

要使用它,只需启动它并提供要跟踪的模块列表:

To use it, just start it and provide the list of modules you want to trace:

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)> ")

跟踪功能非常强大,您可以用它做各种事情.我自己没有用它做日志系统,所以我不能说它会有多大问题,所以如果你走这条路,我建议你小心观察性能,因为太多的跟踪可能使您的系统过载.

Tracing is very powerful and you can do all sorts of thing with it. I didn't use it for a logging system myself, so I can't say how much of a problem it will be, so if you take this road, I advise you to be careful and observe performance, because too much of tracing might overload your system.

这篇关于Elixir 编译时代码注入/AOP的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆