Hyperf多进程定时任务日志分级打印的关键是为每个任务进程独立配置日志通道并动态获取Logger实例:1.在logger.php中定义多个通道(如task_order、task_order_debug);2.在Timer类中通过LoggerFactory按需获取指定通道Logger;3.可选添加进程/协程ID等上下文增强追踪;4.注意目录权限、避免构造函数提前调用及环境变量控制级别。

Hyperf 多进程定时任务中,日志分级打印的关键在于:为每个定时任务进程独立配置日志处理器,并按级别(debug/info/warn/error)分别写入不同文件,避免混杂、便于排查。核心是利用 LoggerFactory 动态创建带命名与通道的 Logger 实例,并在 Command 或 Timer 中正确注入使用。
1. 配置多通道日志(config/autoload/logger.php)
定义多个日志通道,对应不同级别或任务类型:
'channels' => [
'task_order' => [
'driver' => \Hyperf\Logger\Driver\FileDriver::class,
'handler' => \Monolog\Handler\StreamHandler::class,
'level' => 'info',
'constructor' => [
'stream' => BASE_PATH . '/runtime/logs/task/order.log',
'level' => \Monolog\Level::Info,
],
'processors' => [\Hyperf\Logger\Processor\UidProcessor::class],
],
'task_order_debug' => [
'driver' => \Hyperf\Logger\Driver\FileDriver::class,
'handler' => \Monolog\Handler\StreamHandler::class,
'level' => 'debug',
'constructor' => [
'stream' => BASE_PATH . '/runtime/logs/task/order_debug.log',
'level' => \Monolog\Level::Debug,
],
'processors' => [\Hyperf\Logger\Processor\UidProcessor::class],
],
'task_notify' => [
'driver' => \Hyperf\Logger\Driver\FileDriver::class,
'handler' => \Monolog\Handler\StreamHandler::class,
'level' => 'warning',
'constructor' => [
'stream' => BASE_PATH . '/runtime/logs/task/notify.log',
'level' => \Monolog\Level::Warning,
],
],
],
2. 在定时任务类中按需获取分级 Logger
不复用默认 LoggerInterface,而是通过 LoggerFactory 显式创建指定通道的 Logger:
use Hyperf\Logger\LoggerFactory;
use Psr\Log\LoggerInterface;
<p>class OrderCheckTimer
{
public function __construct(
private readonly LoggerFactory $loggerFactory,
) {
}</p><pre class="brush:php;toolbar:false;">#[Timer(interval: 60 * 1000, coroutine: false)]
public function checkUnpaidOrders(): void
{
// 主业务日志 → info 级别,进 order.log
$logger = $this->loggerFactory->get('task_order', 'default');
$logger->info('Start checking unpaid orders');
// 调试细节 → debug 级别,进 order_debug.log
$debugLogger = $this->loggerFactory->get('task_order_debug', 'default');
$debugLogger->debug('Query condition: {condition}', ['condition' => ['status' => 'unpaid']]);
try {
// ... business logic
} catch (\Exception $e) {
// 错误日志 → 自动落到 error 级别(若通道支持)或显式 warn/error
$logger->error('Order check failed', ['exception' => $e->getMessage()]);
}
}}
3. (可选)为每个 Timer 进程绑定独立日志上下文
若需区分多实例(如多个协程并发执行同一 Timer),可加入进程/协程 ID 标识:
$logger->withContext([
'pid' => getmypid(),
'cid' => \Co::getCid(),
'task' => 'order_check_v2',
]);
配合 UidProcessor 可自动注入请求/任务唯一 ID,增强追踪性。
4. 验证与注意事项
- 确保
runtime/logs/task/目录存在且有写权限 - 多进程场景下,
coroutine: false的 Timer 默认以子进程运行,各进程日志互不影响 - 避免在
__construct中提前调用get()—— LoggerFactory 在容器未完全就绪时可能返回空实例 - 如需动态切换日志级别(如调试时开 debug),可通过环境变量控制通道配置,而非硬编码











