默认情况下,Hyperf 的 @Trace 注解仅触发性能追踪(如 SkyWalking),不自动打业务日志;要实现方法进出结构化日志,必须手动编写 Aspect 切面类,且目标方法所在类须为容器管理对象(如标记 @Controller/@Service)、非 private/static,并正确配置 @Aspect 与切入点。

Hyperf中用@Trace注解标记方法后,日志埋点没生效?
默认情况下,Hyperf 的 @Trace 注解仅触发性能追踪(如 SkyWalking),不自动打业务日志。要实现「方法进入/退出时输出结构化日志」,必须手动编写 Aspect 切面类,不能只依赖注解本身。
关键前提是:目标方法所在类必须是 Hyperf 的容器管理对象(即通过 @Inject 或 make() 获取的实例),否则 AOP 代理不会生效。
- 确保方法所在类被
@Controller、@Service或@Component等注解标记 - 方法不能是
private或static—— AOP 无法代理 - 切面类需继承
AbstractAspect并正确配置@Aspect
如何写一个支持 @LogTrace 的自定义切面类
Hyperf 不内置 @LogTrace,需要自己定义注解 + 切面。推荐路径:先建注解类,再写切面逻辑,最后注册到容器。
示例步骤:
- 新建注解
App\Annotation\LogTrace,带level和includeArgs属性 - 新建切面类
App\Aspect\LogTraceAspect,继承AbstractAspect - 在
pointCut()中匹配所有带@LogTrace的 public 方法:execution(public * ..*->*(..)) && @annotation(App\Annotation\LogTrace) - 在
process()中用$this->logger->info()打入参、耗时、返回或异常
注意:$proceedingJoinPoint->getArguments() 返回的是原始参数数组,若需 JSON 化日志,得手动 json_encode($args, JSON_UNESCAPED_UNICODE),避免中文乱码。
为什么切面里 $proceedingJoinPoint->getClassName() 返回空?
这是常见错觉——getClassName() 实际返回的是代理类名(类似 Proxy\Hyperf\Di\Container),不是原始类。真要获取目标类,得用 $proceedingJoinPoint->getOriginalClass()。
-
getClassName()→ 代理类名(无业务意义) -
getOriginalClass()→ 原始类的 FQCN(如App\Controller\UserController) -
getMethodName()→ 方法名(安全可用)
日志中想打印「UserController.login」这种格式,必须拼接 $proceedingJoinPoint->getOriginalClass() . '.' . $proceedingJoinPoint->getMethodName()。
切面中记录耗时但结果被缓存了怎么办?
如果方法上同时用了 @Cacheable,AOP 切面会在缓存命中时仍执行(因为 CacheAspect 是另一个切面,顺序靠前),导致日志重复且耗时不准确。
解决办法只有两个:
- 调整切面优先级:给
LogTraceAspect加@Aspect(order=100),确保它在CacheAspect(order=50)之后执行;但即使如此,缓存命中时$proceedingJoinPoint->proceed()不会真正调用原方法,耗时仍是 0ms - 更可靠的做法:不在切面里测耗时,改用
Hyperf\Utils\Timer::tick()或直接读取microtime(true)手动计时,绕过代理链干扰
实际项目中,建议对缓存敏感的方法单独处理,比如加个 skipTiming=true 参数,避免误导性日志。











