Coolify 生产环境配置 Laravel Nightwatch:采样、过滤与脱敏实战指南
【免费下载链接】coolifyAn open-source, self-hostable PaaS alternative to Vercel, Heroku & Netlify that lets you easily deploy static sites, databases, full-stack applications and 280+ one-click services on your own servers.项目地址: https://gitcode.com/GitHub_Trending/co/coolify
本指南以 Coolify 仓库内置的
configure-nightwatchAgent 技能为骨架,讲解如何在 Coolify(自托管 PaaS,基于 Laravel 12 构建)中为 Laravel Nightwatch 数据采集配置采样率、过滤规则与脱敏策略。读者学完后将能按事件类型(请求、命令、查询、缓存、任务、邮件、通知、异常、出站请求)分别控制采集开销与隐私边界,并在高流量与强隐私两种极端场景之间找到平衡。
Laravel Nightwatch 是 Coolify 使用的应用可观测性服务。仓库依赖中声明了"laravel/nightwatch": "^1.28.6"(见 composer.json),AGENTS.md 也将其列入技术栈(NIGHTWATCH v1)。在实际运行层面,Coolify 通过 .env.development.example 中的NIGHTWATCH_ENABLED与NIGHTWATCH_TOKEN两个环境变量控制开关与鉴权,config/constants.php 将其读取为constants.nightwatch.is_nightwatch_enabled;容器启动时,生产与开发镜像各自的 s6 服务脚本(docker/production/etc/s6-overlay/s6-rc.d/nightwatch-agent/run、docker/development/etc/s6-overlay/s6-rc.d/nightwatch-agent/run)会检测.env中是否为NIGHTWATCH_ENABLED=true,若是则执行php artisan nightwatch:agent拉起采集 Agent,否则进入休眠。也就是说:一旦开启了 Nightwatch,如何配置采样、过滤与脱敏,将直接决定可观测性价值、服务器开销与数据合规风险——这正是本文要解决的核心问题。
Nightwatch 数据采集的三个阶段
Nightwatch 对每个事件的处理链路是严格分阶段的,理解这一点是后续所有配置的前提:
- Sampling(采样)——决定哪些"入口"(请求、命令、定时任务)被采集。只有被采样的入口才会生成完整的追踪数据,未被采样的入口整条链路直接丢弃。
- Filtering(过滤)——入口已采样后,再按事件类型剔除具体事件(如噪声较大的缓存、邮件、查询记录)。
- Redaction(脱敏)——对被保留的事件做内容改写,移除或混淆敏感信息(PII、令牌、凭据)。与过滤不同,脱敏保留事件本身,只是净化其内容。
三阶段流程可抽象为下图:
Request/Command/Scheduled Task | v [Sampling?] ----NO----> Drop entire trace | YES v Events generated | v [Filtering?] ----YES---> Drop specific event | NO v [Redaction] ----------> Store modified data关键区别在于:采样控制的是"入口级"取舍(粒度最大、省流量最彻底),过滤控制的是入口内部某类子事件的取舍,脱敏则保留事件但清洗内容。三者可以叠加组合,例如"高流量下只采样 10% 请求 → 再滤掉 health 探活产生的缓存事件 → 最后对留下的查询 SQL 做密码字段脱敏"。
Sampling 采样配置:控制采集入口
采样决定哪些请求/命令/定时任务触发完整追踪。入口一旦被采样,其关联的所有子事件都会被采集。
全局采样率(环境变量)
通过环境变量配置三类入口的全局采样率:
# Default: 100% sampling (all requests/commands captured) NIGHTWATCH_REQUEST_SAMPLE_RATE=0.1 # Recommended: 10% of requests NIGHTWATCH_COMMAND_SAMPLE_RATE=1.0 # Capture all commands NIGHTWATCH_EXCEPTION_SAMPLE_RATE=1.0 # Always capture exceptions推荐实践:生产环境请求采样率从0.1(10%)起步,再按实际流量与排障需求动态调整。命令与异常建议保持1.0——命令(尤其定时任务)与异常量级远小于请求,全量采集的边际成本低、排障价值却很高。在 Coolify 这类"一次请求背后会派发大量队列任务与数据库操作"的平台中,入口采样率实际上是控制整体事件配额的第一道闸门。
基于路由的采样(Sample 中间件)
不同路由承载不同业务价值,可用Laravel\Nightwatch\Http\Middleware\Sample中间件按路由组施加差异化采样率:
use Illuminate\Support\Facades\Route; use Laravel\Nightwatch\Http\Middleware\Sample; // Sample admin routes at 100% Route::middleware(Sample::rate(1.0))->prefix('admin')->group(function () { // All admin routes sampled fully }); // Sample API routes at 5% Route::middleware(Sample::rate(0.05))->prefix('api')->group(function () { // API routes sampled sparingly }); // Always sample critical endpoints Route::post('/checkout', [CheckoutController::class, 'process']) ->middleware(Sample::always()); // Never sample health checks Route::get('/health', [HealthController::class, 'check']) ->middleware(Sample::never());Coolify 这类需要长期运行的平台尤其适合这种模式:对核心 API(如部署触发、Webhook 接收)使用Sample::always()保证关键路径永远可追踪,对探活与状态查询接口使用Sample::never()避免采集风暴。
未匹配路由的采样
404 页面和爬虫/扫描流量通常是纯粹的噪声,用Route::fallback把它们压到极低采样率即可:
Route::fallback(fn () => abort(404)) ->middleware(Sample::rate(0.01)); // 1% sampling for unmatched routes动态采样
采样决策也可以依赖运行时上下文(用户角色、请求属性等),通过Nightwatch::sample()门面方法在中间件中按业务规则动态触发:
use Closure; use Illuminate\Http\Request; use Laravel\Nightwatch\Facades\Nightwatch; class SampleAdminRequests { public function handle(Request $request, Closure $next) { if ($request->user()?->isAdmin()) { Nightwatch::sample(); // Always sample admin requests } return $next($request); } }命令采样:排除指定命令
个别命令(如schedule:finish、horizon:snapshot)本身无排障价值,可在AppServiceProvider::boot()中监听CommandStarting事件并调用Nightwatch::dontSample()主动排除:
use Illuminate\Console\Events\CommandStarting; use Illuminate\Support\Facades\Event; use Laravel\Nightwatch\Facades\Nightwatch; public function boot(): void { Event::listen(function (CommandStarting $event) { if (in_array($event->command, ['schedule:finish', 'horizon:snapshot'])) { Nightwatch::dontSample(); } }); }Vendor 命令
Nightwatch 默认自动忽略框架/内部命令。若确需观测 vendor 命令,可显式开启:
Nightwatch::captureDefaultVendorCommands();需要说明的是,command事件中的$event->command只有在命令具备名称时才可比较;对匿名命令可结合$event->command为空的情况做兜底判断。
Filtering 过滤配置:在采样之后剔除噪声
过滤发生在采样之后,用于进一步降低噪声与配额消耗。过滤 API 分为两类:环境变量一键全量关闭某类事件,或通过回调按规则精准剔除。
数据库查询
一键关闭全部查询采集:
NIGHTWATCH_IGNORE_QUERIES=true按 SQL 模式精准剔除(典型场景:调度、队列、会话、监控表自身产生的内部查询):
use Laravel\Nightwatch\Facades\Nightwatch; use Laravel\Nightwatch\Records\Query; public function boot(): void { // Filter job table queries (PostgreSQL) Nightwatch::rejectQueries(function (Query $query) { return str_contains($query->sql, 'into "jobs"'); }); // Filter cache table queries (MySQL) Nightwatch::rejectQueries(function (Query $query) { return str_contains($query->sql, 'from `cache`') || str_contains($query->sql, 'into `cache`'); }); }示例同时给出 PostgreSQL 双引号与 MySQL 反引号两种方言写法,提示了规则必须与实际数据库方言匹配这一易错点。
缓存事件
一键关闭全部缓存事件:
NIGHTWATCH_IGNORE_CACHE_EVENTS=true按缓存 key 模式过滤(支持精确匹配与正则):
Nightwatch::rejectCacheKeys([ 'my-app:users', // Exact match '/^my-app:posts:/', // Regex: starts with my-app:posts: '/^[a-zA-Z0-9]{40}$/', // Regex: session IDs ]);或用回调按 key 前缀过滤:
use Laravel\Nightwatch\Records\CacheEvent; Nightwatch::rejectCacheEvents(function (CacheEvent $cacheEvent) { return str_starts_with($cacheEvent->key, 'temp:'); });邮件事件
NIGHTWATCH_IGNORE_MAIL=true按主题精准过滤(例如营销邮件):
use Laravel\Nightwatch\Records\Mail; Nightwatch::rejectMail(function (Mail $mail) { return str_contains($mail->subject, 'Newsletter'); });通知事件
NIGHTWATCH_IGNORE_NOTIFICATIONS=true按通知频道过滤(例如丢弃只存库不实发的database频道):
use Laravel\Nightwatch\Records\Notification; Nightwatch::rejectNotifications(function (Notification $notification) { return $notification->channel === 'database'; });出站 HTTP 请求
NIGHTWATCH_IGNORE_OUTGOING_REQUESTS=true按 URL 过滤(例如内部埋点域名):
use Laravel\Nightwatch\Records\OutgoingRequest; Nightwatch::rejectOutgoingRequests(function (OutgoingRequest $request) { return str_contains($request->url, 'analytics.example.com'); });对 Coolify 而言,出站请求中面向 GitHub、各类云厂商 API 的调用往往高频且长尾,这里是最值得下功夫的过滤点。
队列任务
按任务类名过滤低价值任务:
use Laravel\Nightwatch\Records\QueuedJob; Nightwatch::rejectQueuedJobs(function (QueuedJob $job) { return $job->name === 'App\Jobs\LowPriorityJob'; });任务采样与父上下文解耦
队列任务默认继承父请求的采样上下文。若希望任务独立采样(如父请求未采样但任务本身值得观测),在队列消费前重置采样率:
use Illuminate\Support\Facades\Queue; public function boot(): void { Queue::before(fn () => Nightwatch::sample(rate: 0.5)); }Redaction 脱敏配置:保留事件但净化内容
脱敏与过滤的本质差异在于:脱敏不丢事件,只改写敏感字段,从而在保留可观测性的同时规避 PII/密钥泄露。
请求脱敏
请求头默认自动脱敏Authorization、Cookie、X-XSRF-TOKEN,可通过环境变量扩展列表:
# Customize redacted headers NIGHTWATCH_REDACT_HEADERS=Authorization,Cookie,Proxy-Authorization,X-API-Key请求体默认不采集,需显式开启后按字段名脱敏:
# Enable payload capture NIGHTWATCH_CAPTURE_REQUEST_PAYLOAD=true # Customize redacted fields NIGHTWATCH_REDACT_PAYLOAD_FIELDS=password,password_confirmation,ssn,credit_card更精细的场景走编程式脱敏(改写 URL、掩蔽 IP 尾段):
use Laravel\Nightwatch\Facades\Nightwatch; use Laravel\Nightwatch\Records\Request; Nightwatch::redactRequests(function (Request $request) { $request->url = str_replace('secret', '***', $request->url); $request->ip = preg_replace('/\d+$/', '***', $request->ip); });查询 SQL 脱敏
防止 SQL 中内联的令牌等敏感串进入采集链路:
use Laravel\Nightwatch\Records\Query; Nightwatch::redactQueries(function (Query $query) { $query->sql = str_replace('secret_token', '***', $query->sql); });缓存 key 脱敏
对含用户标识的 key 做掩蔽,保留前缀便于聚合分析:
use Laravel\Nightwatch\Records\CacheEvent; Nightwatch::redactCacheEvents(function (CacheEvent $cacheEvent) { $cacheEvent->key = str_replace('user:', 'user:***:', $cacheEvent->key); });命令参数脱敏
命令行常携带--password=xxx等参数,用正则改写为掩码:
use Laravel\Nightwatch\Records\Command; Nightwatch::redactCommands(function (Command $command) { $command->command = preg_replace('/--password=\S+/', '--password=***', $command->command); });异常消息脱敏
异常消息可能反射出内部信息(如 SQL、环境变量):
use Laravel\Nightwatch\Records\Exception; Nightwatch::redactExceptions(function (Exception $exception) { $exception->message = str_replace('secret', '***', $exception->message); });邮件主题与出站请求 URL 脱敏
use Laravel\Nightwatch\Records\Mail; Nightwatch::redactMail(function (Mail $mail) { $mail->subject = str_replace('Invoice #', 'Invoice ***', $mail->subject); });use Laravel\Nightwatch\Records\OutgoingRequest; Nightwatch::redactOutgoingRequests(function (OutgoingRequest $outgoingRequest) { $outgoingRequest->url = preg_replace('/api_key=\w+/', 'api_key=***', $outgoingRequest->url); });所有redact*回调都接受对应的Records\*数据对象并允许原地改写字段,返回null/无返回值即表示按改写后内容存储——这使脱敏规则可以自由叠加正则与字符串处理,覆盖绝大多数场景。
按事件类型速查:采样、过滤与脱敏一览
为便于落地,此处汇总各事件类型可用的配置手段(同见配套速查文档 .agents/skills/configure-nightwatch/reference.md):
| 事件类型 | 采样 | 过滤 | 脱敏 |
|---|---|---|---|
| Requests | NIGHTWATCH_REQUEST_SAMPLE_RATE、路由中间件 | 不适用 | Headers、payload、URL、IP |
| Commands | NIGHTWATCH_COMMAND_SAMPLE_RATE、事件监听 | 不适用 | 命令参数 |
| Queries | 继承父上下文 | rejectQueries()、NIGHTWATCH_IGNORE_QUERIES | SQL 语句 |
| Cache | 继承父上下文 | rejectCacheKeys()、rejectCacheEvents()、NIGHTWATCH_IGNORE_CACHE_EVENTS | Cache key |
| Jobs | 继承父上下文、Queue::before | rejectQueuedJobs() | 不适用 |
| 继承父上下文 | rejectMail()、NIGHTWATCH_IGNORE_MAIL | 主题 | |
| Notifications | 继承父上下文 | rejectNotifications()、NIGHTWATCH_IGNORE_NOTIFICATIONS | 不适用 |
| Outgoing Requests | 继承父上下文 | rejectOutgoingRequests()、NIGHTWATCH_IGNORE_OUTGOING_REQUESTS | URL |
| Exceptions | NIGHTWATCH_EXCEPTION_SAMPLE_RATE | 不适用 | 异常消息 |
两张对照关系值得记住:一是采样只对请求/命令/异常三类入口生效,其余事件类型均继承父上下文,想独立控制需借助Queue::before等解耦手段;二是Requests/Commands/Exceptions 没有过滤阶段,其"去噪"只能靠采样或脱敏完成。
生产环境推荐配置组合
高流量应用:保守采样 + 过滤噪声
# Conservative sampling NIGHTWATCH_REQUEST_SAMPLE_RATE=0.01 # 1% of requests NIGHTWATCH_COMMAND_SAMPLE_RATE=0.1 # 10% of commands NIGHTWATCH_EXCEPTION_SAMPLE_RATE=1.0 # Always capture exceptions # Filter noisy events NIGHTWATCH_IGNORE_CACHE_EVENTS=true NIGHTWATCH_IGNORE_QUERIES=true # Or filter specific queries programmatically强隐私应用:默认不采集 + 头部脱敏
# Disable sensitive data collection NIGHTWATCH_CAPTURE_REQUEST_PAYLOAD=false NIGHTWATCH_REDACT_HEADERS=Authorization,Cookie,Proxy-Authorization,X-XSRF-TOKEN # Or use redaction in AppServiceProvider平衡配置(推荐起点)
# Sample rates NIGHTWATCH_REQUEST_SAMPLE_RATE=0.1 NIGHTWATCH_COMMAND_SAMPLE_RATE=1.0 NIGHTWATCH_EXCEPTION_SAMPLE_RATE=1.0 # Filter obvious noise programmatically # Redact PII as needed平衡配置与技能正文建议一致:请求 10%、命令与异常全量,噪声用reject*回调处理,PII 按需脱敏。
常见组合模式
把以上 API 组合成可复用模板,能快速覆盖高频需求:
健康检查不采样:
Route::get('/health', fn() => ['status' => 'ok']) ->middleware(Sample::never());排除内部监控类查询(Telescope/Pulse 等):
Nightwatch::rejectQueries(fn($q) => str_contains($q->sql, 'telescope') || str_contains($q->sql, 'pulse') );保护缓存 key 中的用户 ID:
Nightwatch::redactCacheEvents(fn($e) => $e->key = preg_replace('/user:\d+/', 'user:***', $e->key) );在 Coolify 仓库语境下,可以设想与自身基础设施的对应关系:coolify:servers:*一类的分布式缓存键、面向 GitHub 的 Webhook 出站请求、Horizon 派发的大量部署任务队列,都是采样/过滤/脱敏规则的天然候选对象——把"入口采样 + 子事件过滤 + 内容脱敏"三层策略叠加,即可在不牺牲排障能力的前提下,把 Nightwatch 的事件量与敏感面控制在预算与合规允许的范围内。
配置后的验证清单
完成配置后,逐项核对(完整清单见 .agents/skills/configure-nightwatch/reference.md):
- 采样率是否匹配实际流量规模(入口量级与配额预算成比例)
- 噪声事件已过滤(缓存事件、特定查询、内部域名出站请求等)
- 敏感数据已脱敏(PII、令牌、凭据、含用户 ID 的缓存键)
- 异常始终全量采集以保证可调试性
- 先在开发环境以
NIGHTWATCH_REQUEST_SAMPLE_RATE=1.0全量验证规则生效 - 上线后在 Nightwatch 仪表盘持续监控事件配额消耗,回退调整采样率
建议的落地顺序是:开发环境全量采样验证过滤/脱敏规则 → 按业务路由与命令先配好reject*/redact*→ 再逐步收紧采样率 → 上线后依据配额曲线微调。最后提醒:本指南基于 Coolify 当前仓库(Laravel 12 +laravel/nightwatch ^1.28.6)所依赖的 Nightwatch v1 行为编写,方法论的落地形态(Sample中间件、reject*/redact*门面方法、各NIGHTWATCH_*环境变量)均已在本仓库的 .agents/skills/configure-nightwatch/SKILL.md 与配套速查 .agents/skills/configure-nightwatch/reference.md 中有据可查;升级 Nightwatch 版本时,仍应以对应版本的官方文档为最终准绳。
【免费下载链接】coolifyAn open-source, self-hostable PaaS alternative to Vercel, Heroku & Netlify that lets you easily deploy static sites, databases, full-stack applications and 280+ one-click services on your own servers.项目地址: https://gitcode.com/GitHub_Trending/co/coolify
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考