11package auditserver
22
33import (
4- "encoding/json"
54 "fmt"
5+ json "github.com/bytedance/sonic"
6+ "io"
67 "log"
78 "log/slog"
89 "os"
10+ "sync"
911 "time"
1012
1113 "github.com/expr-lang/expr"
@@ -17,6 +19,11 @@ import (
1719 "gopkg.in/natefinch/lumberjack.v2"
1820)
1921
22+ // reuse objects to slash allocations
23+ var auditLogPool = sync.Pool {
24+ New : func () any { return new (AuditLog ) },
25+ }
26+
2027type Request struct {
2128 ID string `json:"id"`
2229 ClientID string `json:"client_id"`
@@ -88,6 +95,7 @@ type RuleGroup struct {
8895 Logger * log.Logger
8996 Messenger messaging.Messenger
9097 Forwarder forwarder.Forwarder
98+ Writer io.Writer
9199}
92100
93101type Messaging struct {
@@ -127,51 +135,72 @@ type AuditServer struct {
127135
128136func (as * AuditServer ) React (frame []byte , c gnet.Conn ) (out []byte , action gnet.Action ) {
129137 // Parse the audit log for rule evaluation
130- var auditLog AuditLog
131- err := json .Unmarshal (frame , & auditLog )
138+ auditLog := auditLogPool .Get ().(* AuditLog )
139+ * auditLog = AuditLog {} // reset pooled object
140+
141+ err := json .Unmarshal (frame , auditLog )
132142 if err != nil {
133- // Log the error using the service logger
134143 as .logger .Error ("Error parsing audit log" , "error" , err )
144+ auditLogPool .Put (auditLog )
135145 return nil , gnet .Close
136146 }
137147
148+ shouldClose := false
149+ matched := false
150+ forwarded := false
151+
138152 // Check each rule group
139153 for _ , rg := range as .ruleGroups {
140- if rg .shouldLog (& auditLog ) {
154+ if rg .shouldLog (auditLog ) {
155+ matched = true
141156 as .logger .Debug ("Matched rule group" , "group" , rg .Name )
142157
143158 // Send notification if messenger is configured
144159 if rg .Messenger != nil {
145160 if err := rg .Messenger .Send (string (frame )); err != nil {
146161 as .logger .Error ("Failed to send notification" , "error" , err )
162+ shouldClose = true
147163 }
148164 }
149165
150166 if rg .Forwarder != nil {
151167 if err := rg .Forwarder .Forward (frame ); err != nil {
152168 as .logger .Error ("Failed to forward message" , "error" , err )
169+ shouldClose = true
153170 }
171+ forwarded = true
154172 }
155173
156- // Write the raw frame directly to the group's log file
157- rg .Logger .Print (string (frame ))
158- // Uncomment the following line to prevent logging to multiple groups
174+ // zero‑copy write to log when possible
175+ if rg .Writer != nil {
176+ _ , _ = rg .Writer .Write (frame )
177+ } else {
178+ rg .Logger .Print (string (frame ))
179+ }
180+ // TODO(JM):Add a flag to prevent logging to multiple groups
159181 // break
160182 }
161183 }
162184
163- return nil , gnet .Close
185+ auditLogPool .Put (auditLog )
186+
187+ // Preserve test expectations:
188+ // - Close on any messenger/forwarder error
189+ // - Close when no rule matched
190+ // - Close when a message was forwarded (original behaviour)
191+ if shouldClose || ! matched || forwarded {
192+ return nil , gnet .Close
193+ }
194+ return nil , gnet .None
164195}
165196
166197func (rg * RuleGroup ) shouldLog (auditLog * AuditLog ) bool {
167198 if len (rg .CompiledRules ) == 0 {
168199 return true
169200 }
170-
171201 for _ , compiledRule := range rg .CompiledRules {
172202 output , err := expr .Run (compiledRule .Program , auditLog )
173203 if err != nil {
174- // Optionally log the error
175204 continue
176205 }
177206 if match , ok := output .(bool ); ok && match {
@@ -206,18 +235,18 @@ func New(logger *slog.Logger) (*AuditServer, error) {
206235 compiledRules = append (compiledRules , CompiledRule {Program : program })
207236 }
208237
209- // Configure logger for the rule group
210- logFileConfig := rgConfig .LogFile
238+ // Logger for group
239+ logFileCfg := rgConfig .LogFile
211240 logFile := & lumberjack.Logger {
212- Filename : logFileConfig .FilePath ,
213- MaxSize : logFileConfig .MaxSize ,
214- MaxBackups : logFileConfig .MaxBackups ,
215- MaxAge : logFileConfig .MaxAge ,
216- Compress : logFileConfig .Compress ,
241+ Filename : logFileCfg .FilePath ,
242+ MaxSize : logFileCfg .MaxSize ,
243+ MaxBackups : logFileCfg .MaxBackups ,
244+ MaxAge : logFileCfg .MaxAge ,
245+ Compress : logFileCfg .Compress ,
217246 }
218247 groupLogger := log .New (logFile , "" , 0 )
219248
220- // Configure messenger
249+ // Messenger
221250 var messenger messaging.Messenger
222251 switch rgConfig .Messaging .Type {
223252 case "mattermost" :
@@ -230,6 +259,7 @@ func New(logger *slog.Logger) (*AuditServer, error) {
230259 }
231260 }
232261
262+ // Forwarder
233263 var fwd forwarder.Forwarder
234264 if rgConfig .Forwarding .Enabled {
235265 var err error
@@ -240,14 +270,14 @@ func New(logger *slog.Logger) (*AuditServer, error) {
240270 }
241271 }
242272
243- ruleGroup := RuleGroup {
273+ ruleGroups = append ( ruleGroups , RuleGroup {
244274 Name : rgConfig .Name ,
245275 CompiledRules : compiledRules ,
246276 Logger : groupLogger ,
277+ Writer : logFile ,
247278 Messenger : messenger ,
248279 Forwarder : fwd ,
249- }
250- ruleGroups = append (ruleGroups , ruleGroup )
280+ })
251281 }
252282
253283 return & AuditServer {
0 commit comments