怎么在请求 [结束] 时能捕捉到__destruct 里抛出的异常 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
dzdh
V2EX    PHP

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

  •  
  •   dzdh Jun 15, 2021 2139 views
    This topic created in 1777 days ago, the information mentioned may be changed or developed.

    __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 replies    2021-06-16 09:21:44 +08:00
    Rache1
        1
    Rache1  
       Jun 15, 2021   1
    弄个类,然后存静态变量,在中间件后置获取这这个变量的内容,然后入库不就好了嘛
    rockyliang
        2
    rockyliang  
       Jun 15, 2021
    这个问题我没看太懂,我的理解是,你是想将数据表的增删改操作记录到日志里,但这个和在__destruct 里抛出异常有什么关系?
    dzdh
        3
    dzdh  
    OP
       Jun 15, 2021
    @rockyliang

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

    突然想到还是不行,因为监听的是 `saved/updated/deleted` 所以不能 更改操作成功了但是日志没记录上(比如因为异常)。
    dzdh
        8
    dzdh  
    OP
       Jun 15, 2021
    @wangxin13g 的确是 hook,目前除了__destruct 想不到其他方案。比如 Laravel 的 dispatch()返回的 PendingDispatch 就是在 __destruct 的时候执行`queue::push`方法。
    Rache1
        9
    Rache1  
       Jun 15, 2021
    @dzdh 那就注册一个异常处理器,然后去那里面处理噻
    wangxin13g
        10
    wangxin13g  
       Jun 15, 2021
    @dzdh 那考虑一下加个消息队列解耦,消息体放更新前后数据,消费者消费消息生成日志。把日志放数据库的行为非常不好
    dzdh
        11
    dzdh  
    OP
       Jun 16, 2021
    @faqqcn 但会脱离当前事务吧?会导致事务其实已经提交了。ExceptionHandler 里实际上是事务结束后的 Uncaught Exception
    Rache1
        12
    Rache1  
       Jun 16, 2021
    @dzdh 那你在调用 $next($request) 外面包一个 try...catch 试试,把中间件优先级设置最前
    About     Help     Advertise     Blog     API     FAQ     Solana     3509 Online   Highest 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 41ms UTC 11:26 PVG 19:26 LAX 04:26 JFK 07:26
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86