This is an automated email from the ASF dual-hosted git repository.

freeandnil pushed a commit to branch Feature/AsyncRemoteSyslogAppender
in repository https://gitbox.apache.org/repos/asf/logging-log4net.git

commit a33f8f1ac80bcbb55c15c1fe3c30d03fb35b85e6
Author: yogitasingh001 <[email protected]>
AuthorDate: Mon Jun 16 14:52:04 2025 +0200

    Asynchronous Sending for RemoteSyslogAppender in log4net (#253)
    
    Co-authored-by: yogita singh <[email protected]>
---
 src/log4net/Appender/RemoteSyslogAppender.cs | 59 +++++++++++++++++++++++++++-
 1 file changed, 57 insertions(+), 2 deletions(-)

diff --git a/src/log4net/Appender/RemoteSyslogAppender.cs 
b/src/log4net/Appender/RemoteSyslogAppender.cs
index a111200e..95a79a56 100644
--- a/src/log4net/Appender/RemoteSyslogAppender.cs
+++ b/src/log4net/Appender/RemoteSyslogAppender.cs
@@ -23,6 +23,10 @@
 using log4net.Util;
 using log4net.Layout;
 using System.Text;
+using System.Net.Sockets;
+using System.Threading.Tasks;
+using System.Threading;
+using System.Collections.Concurrent;
 
 namespace log4net.Appender;
 
@@ -255,6 +259,10 @@ public enum SyslogFacility
     Local7 = 23
   }
 
+  private readonly BlockingCollection<byte[]> _sendQueue = new();
+  private CancellationTokenSource? _cts;
+  private Task? _pumpTask;
+  
   /// <summary>
   /// Initializes a new instance of the <see cref="RemoteSyslogAppender" /> 
class.
   /// </summary>
@@ -367,7 +375,8 @@ protected override void Append(LoggingEvent loggingEvent)
         // Grab as a byte array
         byte[] buffer = Encoding.GetBytes(builder.ToString());
 
-        Client.SendAsync(buffer, buffer.Length, RemoteEndPoint).Wait();
+        //Client.SendAsync(buffer, buffer.Length, RemoteEndPoint).Wait();
+        _sendQueue.Add(buffer);
       }
     }
     catch (Exception e) when (!e.IsFatal())
@@ -424,8 +433,11 @@ public override void ActivateOptions()
   {
     base.ActivateOptions();
     _levelMapping.ActivateOptions();
+    // Start the background pump
+    _cts = new CancellationTokenSource();
+    _pumpTask = Task.Run(() => ProcessQueueAsync(_cts.Token), 
CancellationToken.None);
   }
-
+  
   /// <summary>
   /// Translates a log4net level to a syslog severity.
   /// </summary>
@@ -531,4 +543,47 @@ public class LevelSeverity : LevelMappingEntry
     /// </remarks>
     public SyslogSeverity Severity { get; set; }
   }
+
+  protected override void OnClose()
+  {
+    // Signal shutdown and wait for the pump to drain
+    _cts?.Cancel();
+    _pumpTask?.Wait(TimeSpan.FromSeconds(5)); // or your own timeout
+    base.OnClose();
+  }
+
+  private async Task ProcessQueueAsync(CancellationToken token)
+  {
+    // We create our own UdpClient here, so that client lifetime is tied to 
this task
+    using (var udp = new UdpClient())
+    {
+      udp.Connect(RemoteAddress?.ToString(), RemotePort);
+
+      try
+      {
+        while (!token.IsCancellationRequested)
+        {
+          // Take next message or throw when cancelled
+          byte[] datagram = _sendQueue.Take(token);
+          try
+          {
+            await udp.SendAsync(datagram, datagram.Length);
+          }
+          catch (Exception ex) when (!ex.IsFatal())
+          {
+            ErrorHandler.Error("RemoteSyslogAppender: send failed", ex, 
ErrorCode.WriteFailure);
+          }
+        }
+      }
+      catch (OperationCanceledException)
+      {
+        // Clean shutdown: drain remaining items if desired
+        while (_sendQueue.TryTake(out var leftover))
+        {
+          try { await udp.SendAsync(leftover, leftover.Length); }
+          catch { /* ignore */ }
+        }
+      }
+    }
+  }
 }
\ No newline at end of file

Reply via email to