@@ -4,9 +4,11 @@ use super::event::AuditEvent;
44use super :: query:: AuditQuery ;
55use super :: store:: { AuditError , AuditResult , AuditStore } ;
66use std:: fs:: { File , OpenOptions } ;
7+ use std:: future:: Future ;
78use std:: io:: { BufRead , BufReader , Write } ;
89use std:: path:: PathBuf ;
9- use std:: sync:: Mutex ;
10+ use std:: pin:: Pin ;
11+ use std:: sync:: { Arc , Mutex } ;
1012
1113/// Configuration for file-based audit store.
1214#[ derive( Debug , Clone ) ]
@@ -45,19 +47,29 @@ impl FileAuditStoreConfig {
4547 }
4648}
4749
48- /// File-based audit store (JSON Lines format).
49- pub struct FileAuditStore {
50+ /// Internal state for FileAuditStore
51+ #[ derive( Debug ) ]
52+ struct FileAuditStoreInner {
5053 config : FileAuditStoreConfig ,
5154 writer : Mutex < Option < File > > ,
5255}
5356
57+ /// File-based audit store (JSON Lines format).
58+ #[ derive( Clone , Debug ) ]
59+ pub struct FileAuditStore {
60+ inner : Arc < FileAuditStoreInner > ,
61+ }
62+
5463impl FileAuditStore {
5564 /// Create a new file-based audit store.
5665 pub fn new ( config : FileAuditStoreConfig ) -> AuditResult < Self > {
57- let store = Self {
66+ let inner = FileAuditStoreInner {
5867 config,
5968 writer : Mutex :: new ( None ) ,
6069 } ;
70+ let store = Self {
71+ inner : Arc :: new ( inner) ,
72+ } ;
6173 store. open_writer ( ) ?;
6274 Ok ( store)
6375 }
@@ -70,24 +82,25 @@ impl FileAuditStore {
7082 /// Open or create the file writer.
7183 fn open_writer ( & self ) -> AuditResult < ( ) > {
7284 let mut writer = self
85+ . inner
7386 . writer
7487 . lock ( )
7588 . map_err ( |e| AuditError :: WriteError ( format ! ( "Failed to acquire lock: {}" , e) ) ) ?;
7689
7790 // Create parent directories if they don't exist
78- if let Some ( parent) = self . config . file_path . parent ( ) {
79- if !parent. exists ( ) && self . config . create_if_missing {
91+ if let Some ( parent) = self . inner . config . file_path . parent ( ) {
92+ if !parent. exists ( ) && self . inner . config . create_if_missing {
8093 std:: fs:: create_dir_all ( parent) . map_err ( |e| {
8194 AuditError :: IoError ( format ! ( "Failed to create directories: {}" , e) )
8295 } ) ?;
8396 }
8497 }
8598
8699 let file = OpenOptions :: new ( )
87- . create ( self . config . create_if_missing )
88- . append ( self . config . append )
100+ . create ( self . inner . config . create_if_missing )
101+ . append ( self . inner . config . append )
89102 . write ( true )
90- . open ( & self . config . file_path )
103+ . open ( & self . inner . config . file_path )
91104 . map_err ( |e| AuditError :: IoError ( format ! ( "Failed to open file: {}" , e) ) ) ?;
92105
93106 * writer = Some ( file) ;
@@ -96,8 +109,8 @@ impl FileAuditStore {
96109
97110 /// Check if rotation is needed and perform it.
98111 fn check_rotation ( & self ) -> AuditResult < ( ) > {
99- if let Some ( max_size) = self . config . max_file_size {
100- if let Ok ( metadata) = std:: fs:: metadata ( & self . config . file_path ) {
112+ if let Some ( max_size) = self . inner . config . max_file_size {
113+ if let Ok ( metadata) = std:: fs:: metadata ( & self . inner . config . file_path ) {
101114 if metadata. len ( ) >= max_size {
102115 self . rotate ( ) ?;
103116 }
@@ -109,6 +122,7 @@ impl FileAuditStore {
109122 /// Rotate the log file.
110123 fn rotate ( & self ) -> AuditResult < ( ) > {
111124 let mut writer = self
125+ . inner
112126 . writer
113127 . lock ( )
114128 . map_err ( |e| AuditError :: WriteError ( format ! ( "Failed to acquire lock: {}" , e) ) ) ?;
@@ -123,12 +137,13 @@ impl FileAuditStore {
123137 . unwrap_or ( 0 ) ;
124138
125139 let rotated_path = self
140+ . inner
126141 . config
127142 . file_path
128143 . with_extension ( format ! ( "{}.log" , timestamp) ) ;
129144
130145 // Rename current file
131- std:: fs:: rename ( & self . config . file_path , & rotated_path)
146+ std:: fs:: rename ( & self . inner . config . file_path , & rotated_path)
132147 . map_err ( |e| AuditError :: IoError ( format ! ( "Failed to rotate file: {}" , e) ) ) ?;
133148
134149 // Open new file
@@ -140,7 +155,7 @@ impl FileAuditStore {
140155
141156 /// Read all events from the file.
142157 fn read_all_events ( & self ) -> AuditResult < Vec < AuditEvent > > {
143- let path = & self . config . file_path ;
158+ let path = & self . inner . config . file_path ;
144159
145160 if !path. exists ( ) {
146161 return Ok ( Vec :: new ( ) ) ;
@@ -178,6 +193,7 @@ impl AuditStore for FileAuditStore {
178193 self . check_rotation ( ) ?;
179194
180195 let mut writer = self
196+ . inner
181197 . writer
182198 . lock ( )
183199 . map_err ( |e| AuditError :: WriteError ( format ! ( "Failed to acquire lock: {}" , e) ) ) ?;
@@ -195,6 +211,18 @@ impl AuditStore for FileAuditStore {
195211 Ok ( ( ) )
196212 }
197213
214+ fn log_async (
215+ & self ,
216+ event : AuditEvent ,
217+ ) -> Pin < Box < dyn Future < Output = AuditResult < ( ) > > + Send + ' _ > > {
218+ let store = self . clone ( ) ;
219+ Box :: pin ( async move {
220+ tokio:: task:: spawn_blocking ( move || store. log ( event) )
221+ . await
222+ . map_err ( |e| AuditError :: IoError ( format ! ( "Task join error: {}" , e) ) ) ?
223+ } )
224+ }
225+
198226 fn get ( & self , id : & str ) -> AuditResult < Option < AuditEvent > > {
199227 let events = self . read_all_events ( ) ?;
200228 Ok ( events. into_iter ( ) . find ( |e| e. id == id) )
@@ -238,14 +266,15 @@ impl AuditStore for FileAuditStore {
238266
239267 fn clear ( & self ) -> AuditResult < ( ) > {
240268 let mut writer = self
269+ . inner
241270 . writer
242271 . lock ( )
243272 . map_err ( |e| AuditError :: WriteError ( format ! ( "Failed to acquire lock: {}" , e) ) ) ?;
244273
245274 * writer = None ;
246275
247276 // Truncate the file
248- File :: create ( & self . config . file_path )
277+ File :: create ( & self . inner . config . file_path )
249278 . map_err ( |e| AuditError :: IoError ( format ! ( "Failed to clear file: {}" , e) ) ) ?;
250279
251280 // Reopen
@@ -257,6 +286,7 @@ impl AuditStore for FileAuditStore {
257286
258287 fn flush ( & self ) -> AuditResult < ( ) > {
259288 let mut writer = self
289+ . inner
260290 . writer
261291 . lock ( )
262292 . map_err ( |e| AuditError :: WriteError ( format ! ( "Failed to acquire lock: {}" , e) ) ) ?;
0 commit comments