必须用singleton()绑定日志类,bind()或extend()会导致每次make都新建实例,使配置失效、文件句柄重复打开、并发写入错乱;Log不可通过构造函数类型提示自动注入,须通过facade调用或手动make获取单例实例。

容器绑定日志类必须用 singleton(),不能用 bind() 或 extend()
ThinkPHP 的日志门面 think\facade\Log 底层依赖容器中注册的 think\Log 实例。如果只是 bind('log', Log::class),每次 app()->make('log') 都会新建实例,导致配置(如 channel、level)不生效、文件句柄重复打开、并发写入错乱。正确做法是显式声明单例:
app()->singleton('log', function ($app) { return new \think\Log($app); });- 或更推荐:直接绑定具体驱动类,例如
app()->singleton(\think\log\driver\File::class, function ($app) { return new \think\log\driver\File($app); });,再在Log构造时传入该驱动 - 若使用自定义日志类(如
App\Logger\AuditLogger),也必须singleton(AuditLogger::class, ...),否则中间件或服务中$app->make(AuditLogger::class)每次拿到的都不是同一个实例
自动注入 Request 时,Log 类不能靠类型提示注入
ThinkPHP 8.0 支持控制器方法参数自动注入 Request,但 Log 类不行——它不是通过反射类型名从容器拉取的“标准可注入服务”,而是由门面静态代理转发。你写 public function index(\think\Log $log) 会直接报 BindingResolutionException。
- 想在控制器里用日志,只应调用
think\facade\Log::write()或Log::record() - 若需在非控制器类(如 service、command)中使用,应在构造函数里声明
protected $log,并在__construct()中手动$this->log = app('log');,或改用依赖注入方式注册该 service 为 singleton 并配置构造参数 - 切勿在构造函数里写
public function __construct(\think\Log $log)并期望容器自动塞值——框架不识别这个类型链路
日志通道配置必须在容器启动前完成,否则无效
ThinkPHP 的日志通道(如 'pay'、'sql')是静态注册在 config/log.php 的 channels 数组里的。容器初始化时会读取该配置并绑定对应驱动;如果在 app()->make('log') 之后才调用 Log::channel('pay')->write(...),而 'pay' 未在 config 中预定义,就会 fallback 到默认 channel,甚至静默失败。
- 确认
config/log.php中'channels' => ['pay' => [...]]已存在,且'path'是绝对路径(如runtime_path() . 'log/pay/') - 修改 config 后必须清缓存:
php think clear:config,否则旧配置仍被加载 - 不要在中间件或控制器里动态
Log::extend('pay', [...])—— Swoole 长生命周期下,多次 extend 可能覆盖或冲突,且 CLI 模式不触发
自定义日志类被注入后,$app 参数不可省略
所有 ThinkPHP 日志驱动(包括 \think\log\driver\File)构造函数第一个参数都必须是 $app 实例,用于获取配置、请求上下文等。如果你写了一个继承 \think\Log 的子类,并试图绑定进容器,漏掉这个参数就会报错或行为异常。
- 错误示例:
app()->singleton(CustomLogger::class, function () { return new CustomLogger(); });→ 缺少$app,$this->getConfig()等方法全失效 - 正确写法:
app()->singleton(CustomLogger::class, function ($app) { return new CustomLogger($app); }); - 若该类还依赖其他服务(如数据库连接),应在构造函数声明并让容器自动注入,例如:
public function __construct(App $app, ConnectionInterface $db),前提是ConnectionInterface已在容器中绑定
容器对日志类的管理本质是“单例 + 配置驱动”,不是“类型自动发现”。任何绕过 singleton()、延迟注册、或忽略 $app 参数的操作,都会让日志写入不可控或静默丢失。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











