V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
dzdh
V2EX  ›  PHP

怎么在请求 [结束] 时能捕捉到__destruct 里抛出的异常

  •  
  •   dzdh · 2021-06-15 13:11:19 +08:00 · 1689 次点击
    这是一个创建于 1290 天前的主题,其中的信息可能已经有所发展或是发生改变。

    __destruct里抛出异常已经不会产生致命错误了

    场景是用户操作日志,laravel 框架,所有模型事件都实现了一个接口类ChangesLoggerInterface,然后当任意一个模型触发任意事件都会进到一个 ChangesLoggerListener的监听器中。

    然后想实现:

    当 saved\updated\deleted 的时候记录下哪些字段从 xx 改成了 bb 。

    然后一次请求中肯定不止一个对象发生变动可能是多个比如电商下单接口可能会是order:saved,user:updated(last_order_dateline)

    所以最终是实现在一次请求中记录一行数据(在 logs 表中)。

    所以在 ChangesLoggerListener中 想调用一个 Logger的日志记录类,调用Logger::push,这样在当 Logger 触发__destruct的时候获取 $this->data(array) 拼成一行数据入库。

    问题是:

    __destruct必须是在refcount为 0 的时候才触发。所以如果业务逻辑里有 try...catch.. 的时候,实际上__destruct里的异常是不会被 catch 到的。

    造成 refcount 不为 0 的原因是因为用了 Laravel 的 Facade 实现的接管的单例。

    还有什么其他方案嘛?

    12 条回复    2021-06-16 09:21:44 +08:00
    Rache1
        1
    Rache1  
       2021-06-15 14:26:52 +08:00   ❤️ 1
    弄个类,然后存静态变量,在中间件后置获取这这个变量的内容,然后入库不就好了嘛
    rockyliang
        2
    rockyliang  
       2021-06-15 14:45:10 +08:00
    这个问题我没看太懂,我的理解是,你是想将数据表的增删改操作记录到日志里,但这个和在__destruct 里抛出异常有什么关系?
    dzdh
        3
    dzdh  
    OP
       2021-06-15 15:18:22 +08:00
    @rockyliang

    因为现在的方案是在一个 recorder 的__destruct 里做的,insert into log..... 但是如果这个处理失败会抛出异常(比如还有可能会创建异步任务,http rest 的 queue http 超时啥的会抛出异常)
    dzdh
        4
    dzdh  
    OP
       2021-06-15 15:18:43 +08:00
    @faqqcn 也是个思路
    dzdh
        5
    dzdh  
    OP
       2021-06-15 15:19:57 +08:00
    @faqqcn 甚至可以一个 provider 搞定
    wangxin13g
        6
    wangxin13g  
       2021-06-15 16:02:22 +08:00
    不推荐用__destruct 来做任何事情,不熟悉 laravel,看你的说法应该得用类似 Hook 的机制而不是用__destruct
    dzdh
        7
    dzdh  
    OP
       2021-06-15 17:11:52 +08:00
    @faqqcn

    突然想到还是不行,因为监听的是 `saved/updated/deleted` 所以不能 更改操作成功了但是日志没记录上(比如因为异常)。
    dzdh
        8
    dzdh  
    OP
       2021-06-15 17:13:31 +08:00
    @wangxin13g 的确是 hook,目前除了__destruct 想不到其他方案。比如 Laravel 的 dispatch()返回的 PendingDispatch 就是在 __destruct 的时候执行`queue::push`方法。
    Rache1
        9
    Rache1  
       2021-06-15 17:42:07 +08:00
    @dzdh 那就注册一个异常处理器,然后去那里面处理噻
    wangxin13g
        10
    wangxin13g  
       2021-06-15 17:45:24 +08:00
    @dzdh 那考虑一下加个消息队列解耦,消息体放更新前后数据,消费者消费消息生成日志。把日志放数据库的行为非常不好
    dzdh
        11
    dzdh  
    OP
       2021-06-16 09:05:58 +08:00
    @faqqcn 但会脱离当前事务吧?会导致事务其实已经提交了。ExceptionHandler 里实际上是事务结束后的 Uncaught Exception
    Rache1
        12
    Rache1  
       2021-06-16 09:21:44 +08:00
    @dzdh 那你在调用 $next($request) 外面包一个 try...catch 试试,把中间件优先级设置最前
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3600 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 49ms · UTC 04:28 · PVG 12:28 · LAX 20:28 · JFK 23:28
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.