fix(agent): 审计写入失败不再静默

审计日志是证据而不是控制手段:写失败不能让请求失败,否则磁盘写满会变成一次远程
拒绝服务——已经执行成功的指令会因为日志追加不上而开始报错,那是坏错了地方。

但它也不能消失。静默丢审计条目是那种"运维以为自己有记录、其实没有"的失败,而这份
日志的全部意义就是事后能回答"谁在什么时候要求了什么"。现在写失败会进 agent 自己的
日志,磁盘写满表现为一行反复出现,而不是一片空白。

顺带把 Close 之后的语义写清楚:Close 会丢掉 sink,此后的写入返回 nil——这是刻意的,
因为一个正在关闭的进程与在途请求赛跑时,为每一个恰好到达的请求报一次失败是噪声
而不是信息。这条差别在 agent 日志里看得见。

测试用真实失败路径(写一个已关闭的文件)而不是填满文件系统,并断言 exec.record 把
它报了出来。

Go 全包、Node 112 项、Swift 101 项通过。
This commit is contained in:
chick
2026-09-17 01:52:59 +08:00
parent 3a9eec5de2
commit 9608a7ebc0
3 changed files with 76 additions and 4 deletions
+11 -2
View File
@@ -59,6 +59,11 @@ func NullAudit() *Audit { return &Audit{} }
// Write appends one entry. A write failure is returned to the caller rather than panicking: the
// audit log is evidence, not a control, and a full disk must not become a remote denial of service.
//
// Returning nil after Close is deliberate and is the one case where a dropped entry is not an error:
// a shutdown path racing an in-flight request would otherwise log a failure for every request that
// happened to arrive at that moment, which is noise rather than information. The executor logs what it
// sees either way, so the difference is visible in the agent's log.
func (a *Audit) Write(entry Entry) error {
a.mu.Lock()
defer a.mu.Unlock()
@@ -75,8 +80,12 @@ func (a *Audit) Write(entry Entry) error {
return a.sink.Sync()
}
// Close releases the file. Subsequent writes are silently dropped, which is the right behaviour for
// a shutdown path: failing the request that happens to race with a clean exit buys nothing.
// Close releases the file.
//
// A write after Close is dropped, and dropped silently is the wrong word for it: the executor reports
// every write error it sees, and this returns one, so a request racing a clean shutdown shows up as a
// warning rather than as a missing line nobody notices. The alternative — failing the request — buys
// nothing, because the command it describes has already run or been refused.
func (a *Audit) Close() error {
a.mu.Lock()
defer a.mu.Unlock()