laravel telescope 默认记录所有日志等条目,第三方包调试日志(如含api密钥)会被原样捕获;需在config/telescope.php的filter闭包中按channel、上下文或请求头等特征精准过滤,monolog配置无效。

Telescope 日志里为什么会出现敏感依赖的调试信息?
因为 Laravel Telescope 默认会记录所有 Log、Query、Request、Event 等 entry,而很多第三方包(比如 guzzlehttp/guzzle、spatie/laravel-backup)在出错或调试时会主动调用 Log::debug() 或写入 stderr,这些日志一旦被 Telescope 拦截,就会原样存进数据库并显示在 UI 里——包括 API 密钥、数据库连接串、临时 token 等。
如何在 config/telescope.php 中过滤掉特定依赖的日志?
Telescope 提供了 filter 闭包,在这里可以按日志上下文、通道名、消息内容甚至调用栈来源做拦截。关键不是“屏蔽整个包”,而是识别它打日志时留下的特征。
- 第三方包通常会用自定义日志通道,比如
backup、guzzle、paystack,检查它们的配置是否注册了独立 channel,并在filter中拒绝该 channel - 有些包会在日志上下文里塞标识,例如
['package' => 'guzzle']或['source' => 'StripeWebhookController'],可用$entry->content['context'] ?? []判断 - 极简但高风险的做法:匹配日志消息是否含
API_KEY、secret、password等关键词(不推荐用于生产,漏判率高)
示例(放在 config/telescope.php 的 filter 位置):
return function (Telescope $telescope) {
return $telescope->filter(function (IncomingEntry $entry) {
// 屏蔽 backup channel 的所有日志
if ($entry->channel === 'backup') {
return false;
}
// 屏蔽 Guzzle 请求中带 Authorization header 的 debug 日志
if ($entry->type === 'log' &&
isset($entry->content['context']['request']['headers']['Authorization'])) {
return false;
}
return true;
});
};
为什么不能只靠 Log::stack() 或 Monolog 处理器过滤?
Telescope 是独立于 Laravel 日志系统的“监听层”,它通过事件订阅(Illuminate\Log\Events\MessageLogged)捕获日志,早于 Monolog 的处理器执行。也就是说,即使你在 logging.php 里给某个 channel 配了 null handler,Telescope 依然能拿到原始日志对象。
- Monolog 过滤器(如
LevelFilterHandler)只影响最终输出,不影响 Telescope 拦截 -
Log::stack(['single'])->withoutLogging(...)这类动态控制对 Telescope 无效 - 真正起效的只有 Telescope 自己的
filter闭包,且必须在TelescopeServiceProvider的register()阶段就绑定
排除后还要注意哪些隐蔽泄露点?
日志过滤只是第一层。很多包会把敏感信息写进异常堆栈、HTTP 请求体、队列 job 的 $data 字段,或者通过 Telescope::recordException() 自动上报——这些不会被 log filter 拦住。
- 检查
telescope.php中ignore_paths是否包含你的 webhook controller 路径(避免请求体被录) - 重写
Telescope::filter()时,务必同时判断$entry->type是request、exception、job,而不仅是log - 某些包(如
laravel/scout)会在搜索失败时把完整查询参数记为 exception message,得单独清理$entry->content['exception']->getMessage()
最稳妥的方式是:先打开 Telescope 的本地存储(driver => database),手动查几条疑似敏感 entry 的 content 字段结构,再针对性写 filter 条件——别猜,直接看数据长什么样。










