@@ -49,15 +49,12 @@ public sealed class App : IDnsApplication, IDnsQueryLogger, IDnsQueryLogs
4949
5050 [ ThreadStatic ]
5151 private static StringBuilder ? _sb ;
52- Config _config ;
53-
5452 private Channel < LogEntry > ? _channel ;
53+ Config ? _config ;
5554 private DuckDBConnection ? _conn ;
5655 private Task ? _consumerTask ;
5756 private bool _disposed ;
5857 private IDnsServer ? _dnsServer ;
59- private bool _enableLogging ;
60- private int _maxQueueSize ; // Maximum number of log entries in the queue, default 200,000
6158 #endregion variables
6259
6360 #region IDisposable
@@ -248,6 +245,83 @@ private async Task ProcessLogsAsync()
248245 BulkInsert ( batch ) ;
249246 }
250247
248+ private async Task RetentionLoopAsync ( )
249+ {
250+ // Initial delay
251+ await Task . Delay ( TimeSpan . FromMinutes ( 1 ) ) ;
252+
253+ while ( ! _disposed )
254+ {
255+ try
256+ {
257+ await RunRetentionAsync ( ) ;
258+ }
259+ catch ( Exception ex )
260+ {
261+ _dnsServer ? . WriteLog ( ex ) ;
262+ }
263+
264+ await Task . Delay ( TimeSpan . FromMinutes ( 15 ) ) ;
265+ }
266+ }
267+
268+ private async Task RunRetentionAsync ( )
269+ {
270+ if ( _conn is null || _config is null )
271+ return ;
272+
273+ using DuckDBCommand cmd = _conn . CreateCommand ( ) ;
274+
275+ long deleted = 0 ;
276+
277+ /* ---------------------------------
278+ Max records
279+ --------------------------------- */
280+
281+ if ( _config . MaxLogRecords > 0 )
282+ {
283+ cmd . CommandText = @"
284+ DELETE FROM dns_logs
285+ WHERE timestamp NOT IN (
286+ SELECT timestamp
287+ FROM dns_logs
288+ ORDER BY timestamp DESC
289+ LIMIT $limit
290+ );" ;
291+
292+ cmd . Parameters . Clear ( ) ;
293+ cmd . Parameters . Add (
294+ new DuckDBParameter ( "limit" , _config . MaxLogRecords ) ) ;
295+
296+ deleted += await cmd . ExecuteNonQueryAsync ( ) ;
297+ }
298+
299+ /* ---------------------------------
300+ Max days
301+ --------------------------------- */
302+
303+ if ( _config . MaxLogDays > 0 )
304+ {
305+ DateTime cutoff =
306+ DateTime . UtcNow . AddDays ( - _config . MaxLogDays ) ;
307+
308+ cmd . CommandText =
309+ "DELETE FROM dns_logs WHERE timestamp < $cutoff;" ;
310+
311+ cmd . Parameters . Clear ( ) ;
312+ cmd . Parameters . Add (
313+ new DuckDBParameter ( "cutoff" , cutoff ) ) ;
314+
315+ deleted += await cmd . ExecuteNonQueryAsync ( ) ;
316+ }
317+
318+ if ( deleted > 0 )
319+ {
320+ cmd . Parameters . Clear ( ) ;
321+ cmd . CommandText = "CHECKPOINT;" ;
322+ await cmd . ExecuteNonQueryAsync ( ) ;
323+ }
324+ }
251325 #endregion private
252326
253327 #region public
@@ -258,6 +332,7 @@ public async Task InitializeAsync(IDnsServer dnsServer, string config)
258332
259333 JsonSerializerOptions options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true } ;
260334 _config = JsonSerializer . Deserialize < Config > ( config , options ) ;
335+ _config ??= new Config ( ) ;
261336 Validator . ValidateObject ( _config , new ValidationContext ( _config ) , validateAllProperties : true ) ;
262337
263338 if ( ! System . IO . Path . IsPathRooted ( _config . DbPath ) )
@@ -276,6 +351,7 @@ public async Task InitializeAsync(IDnsServer dnsServer, string config)
276351 await CreateSchemaAsync ( ) ;
277352
278353 _consumerTask = Task . Run ( ProcessLogsAsync ) ;
354+ _ = Task . Run ( RetentionLoopAsync ) ;
279355 }
280356
281357 public Task InsertLogAsync (
@@ -285,7 +361,7 @@ public Task InsertLogAsync(
285361 DnsTransportProtocol protocol ,
286362 DnsDatagram response )
287363 {
288- if ( _config . EnableLogging )
364+ if ( _config ! . EnableLogging )
289365 _channel ! . Writer . TryWrite (
290366 new LogEntry ( timestamp , request , remoteEP , protocol , response ) ) ;
291367
@@ -588,11 +664,16 @@ public LogEntry(DateTime timestamp, DnsDatagram request, IPEndPoint remoteEP, Dn
588664
589665 private class Config
590666 {
667+ [ JsonPropertyName ( "dbPath" ) ]
668+ public string DbPath { get ; set ; } = "querylogs.db" ;
669+
591670 [ JsonPropertyName ( "enableLogging" ) ]
592671 public bool EnableLogging { get ; set ; } = true ;
672+ [ JsonPropertyName ( "maxLogDays" ) ]
673+ public int MaxLogDays { get ; set ; } = 30 ;
593674
594- [ JsonPropertyName ( "dbPath " ) ]
595- public string DbPath { get ; set ; } = "querylogs.db" ;
675+ [ JsonPropertyName ( "maxLogRecords " ) ]
676+ public long MaxLogRecords { get ; set ; } = 1_000_000 ;
596677
597678 [ JsonPropertyName ( "maxQueueSize" ) ]
598679 public int MaxQueueSize { get ; set ; } = 200_000 ;
0 commit comments