You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Elixir装饰器实现Appsignal埋点时编译时socket为nil,如何运行时取值?

问题根源

你的代码报错是因为get_socket函数是在编译阶段执行的:你在quote块内用unquote(get_socket(context))的写法,会在编译期直接调用get_socket获取结果再插入生成的代码中。此时context.args拿到的是函数参数的AST语法节点,不是运行时的实际参数值,用IEx.Info.info检查编译期的AST节点永远不可能匹配到运行时才存在的Socket结构体,必然返回nil。

解决方案

把Socket查找逻辑放到quote块内部,改为运行时执行,直接遍历函数的实际入参匹配Socket结构体即可,无需在编译期做判断。

通用实现(不依赖参数位置,适配所有LiveView回调)

import Appsignal.Phoenix.LiveView, only: [live_view_action: 4]

def instrument(name, body, context) do
  # 仅提取参数的AST,编译期不做逻辑处理
  args_ast = context.args

  quote bind_quoted: [name: name, module: context.module, args_ast: args_ast] do
    # 运行时遍历实际入参查找Socket
    socket = Enum.find(args_ast, fn
      %Phoenix.LiveView.Socket{} = sock -> sock
      _ -> false
    end)

    live_view_action(module, name, socket, fn ->
      unquote(body)
    end)
  end
end

精简实现(仅适配handle_event,性能更高)

LiveView的handle_event回调参数顺序是固定的,第三个参数永远是Socket,可以直接提取对应位置的AST,省去运行时遍历开销:

import Appsignal.Phoenix.LiveView, only: [live_view_action: 4]

def instrument(name, body, context) do
  socket_ast = Enum.at(context.args, 2)
  
  quote bind_quoted: [name: name, module: context.module, socket_ast: socket_ast] do
    live_view_action(module, name, socket_ast, fn ->
      unquote(body)
    end)
  end
end

两种实现都完全兼容你期望的使用方式,不需要修改业务代码里的handle_event逻辑。

内容的提问来源于stack exchange,提问作者Sergio Tapia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 21:06:06